使用参数变量加载Unity场景

21
在我的游戏中,有一个地图视图,包含一个50x50的网格瓷砖。当您单击瓷砖时,您会被发送到该瓷砖的视图并攻击它们等。从代码角度来看,这些“瓷砖”的唯一区别是瓷砖ID,即网格上的数字。该数字将在初始化时传递给服务器以处理其余部分。
显然,由于这是瓷砖之间唯一的区别,创建“场景1”,“场景2”...“场景2500”并调用SceneManager.LoadScene以切换到特定的瓷砖视图是错误的做法。
我可以在单击瓷砖时使用DontDestroyOnLoad()来保留瓷砖ID以便在场景切换时使用,但是1)它只接受gameobjects而不仅是int变量2)我不需要/想要将该变量保留超过在瓷砖视图的init中,因此虽然它可行,但似乎有点过度设计。
是否有更好的方法来传递参数到场景加载?

1
我想你可以创建一个静态类来保存信息,如果你不需要它与游戏对象交互(继承自MonoBehaviour)。等一下,我给你举个例子。 - Fredrik Schön
3个回答

46

你可以创建一个静态类来保存信息。这个类不会附加到任何游戏对象上,也不会在切换场景时被销毁。它是 static 的,这意味着只能有一个;你不能写 StaticClassName scn = new StaticClassName() 来创建新的静态类。你可以通过 StaticClassName.SomeStaticMethod() 直接访问它们,而且可以从任何地方访问。请参见此示例,了解如何将值存储在变量中,在更改场景后在该场景中使用它(请注意添加的 using UnityEngine.SceneManagement;):

在名称为“Test”的场景中附加到GameObject的普通Unity脚本:

using UnityEngine;
using UnityEngine.SceneManagement;

public class TestingScript : MonoBehaviour 
{
    void Start()
    {
        StaticClass.CrossSceneInformation = "Hello Scene2!";
        SceneManager.LoadScene("Test2");
    }
}
一个新的静态类(不继承自 MonoBehaviour),它保存信息:
public static class StaticClass 
{
    public static string CrossSceneInformation { get; set; }
}

位于场景“Test2”中的游戏对象上附有一个脚本:

using UnityEngine;

public class TestingScript2: MonoBehaviour 
{
    void Start () 
    {
        Debug.Log(StaticClass.CrossSceneInformation);
    }
}

如果您因某些原因需要创建更多实例,则不需要使整个类静态。如果从类(而不是变量)中删除static,则仍然可以通过StaticClass.CrossSceneInformation访问静态变量,但还可以使用StaticClass sc = new StaticClass();。使用此sc,您可以使用类的非静态成员,但不能使用static CrossSceneInformation,因为只能有一个(因为它是静态的)。


2
我通常只为需要一些上下文信息的场景创建一个这样的类。 - Dunno
1
我非常喜欢这个!正是我所需要的。感谢您提醒我静态变量的含义,我一直在随意使用它们。 - DasBeasto
好的。那么整个解决方案中的所有静态字段都会在游戏启动时初始化吗? - KevinVictor
是的。但是,静态构造函数只有在类(或类中的字段)第一次被调用时才会被调用。 - Fredrik Schön
2
这似乎不是正确的做法,不是吗?我希望有一个使用LoadSceneParameters的示例...这很不符合OOD。 - Dylan Brams
显示剩余5条评论

3

既然问题已经得到回答,我想分享一个预制解决方案,以便其他人或者甚至是提问者使用。

该脚本使用键值对方法来存储和修改静态类中的变量,这意味着它可以跨场景使用,因此您可以将其用作跨场景持久存储,所以您只需要将脚本导入到Unity项目中并使用API(请参见下面的示例):

using System.Collections.Generic;

/// <summary>
/// A simple static class to get and set globally accessible variables through a key-value approach.
/// </summary>
/// <remarks>
/// <para>Uses a key-value approach (dictionary) for storing and modifying variables.</para>
/// <para>It also uses a lock to ensure consistency between the threads.</para>
/// </remarks>
public static class GlobalVariables
{

    private static readonly object lockObject = new object();
    private static Dictionary<string, object> variablesDictionary = new Dictionary<string, object>();

    /// <summary>
    /// The underlying key-value storage (dictionary).
    /// </summary>
    /// <value>Gets the underlying variables dictionary</value>
    public static Dictionary<string, object> VariablesDictionary => variablesDictionary;

    /// <summary>
    /// Retrieves all global variables.
    /// </summary>
    /// <returns>The global variables dictionary object.</returns>
    public static Dictionary<string, object> GetAll()
    {
        return variablesDictionary;
    }

    /// <summary>
    /// Gets a variable and casts it to the provided type argument.
    /// </summary>
    /// <typeparam name="T">The type of the variable</typeparam>
    /// <param name="key">The variable key</param>
    /// <returns>The casted variable value</returns>
    public static T Get<T>(string key)
    {
        if (variablesDictionary == null || !variablesDictionary.ContainsKey(key))
        {
            return default(T);
        }

        return (T)variablesDictionary[key];
    }

    /// <summary>
    /// Sets the variable, the existing value gets overridden.
    /// </summary>
    /// <remarks>It uses a lock under the hood to ensure consistensy between threads</remarks>
    /// <param name="key">The variable name/key</param>
    /// <param name="value">The variable value</param>
    public static void Set(string key, object value)
    {
        lock (lockObject)
        {
            if (variablesDictionary == null)
            {
                variablesDictionary = new Dictionary<string, object>();
            }
            variablesDictionary[key] = value;
        }
    }

}

你可以在位于主菜单场景中的脚本中使用它,比如:

public class MainMenuScript : MonoBehaviour
{

    void Start()
    {

        // Load a sample level
        LoadLevel(12);
    }

    public void LoadLevel(int level)
    {
        GlobalVariables.Set("currentLevelIndex", level);
        UnityEngine.SceneManagement.SceneManager.LoadScene("Game");
    }

}

另一个在游戏场景内:

public class GameScript : MonoBehaviour
{

    void Start()
    {
        int levelIndex = GlobalVariables.Get<int>("currentLevelIndex");
        Debug.Log(levelIndex); // Outputs 12
    }

}

因此,在“主菜单”场景中分配的数据可以从“游戏”或任何其他场景访问。 了解有关脚本及其用法示例的更多信息 >

1
我会使用MyScript:Singleton<MyScript> {}。 - Nick Turner
这是真正的解决方案。 - Fenikkel

-10

马克普!完美而简单的代码!

但是您加载场景的方法不起作用。

您可以使用另一种方法:

UnityEngine.SceneManagement.SceneManager.LoadScene("Vuforia-4-Spheric");

3
这是相同的方法,但是指定整个命名空间而不是导入它。如果您在文件开头添加 using UnityEngine.SceneManagement;,您就可以像这样使用它 SceneManager.LoadScene("scene"). :) - Fredrik Schön

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接