轻松无忧地管理Unity中的场景

您是否曾经想过如何减轻项目中的场景管理的痛苦?当您有一个非常简单的游戏,其中只有几个场景一个接一个地运行时,通常情况下一切都会顺利进行。但是,当场景数量增加并且它们之间的过渡变得更加复杂时-它们可以以不同的顺序加载,并且其中某些行为应取决于输入参数-任务变得不那么琐碎了。



以下是我最常看到的几种解决方法:



  • 文件-从一个场景移动到另一个场景时,所有必要的数据都将写入JSON / XML文件,而在加载下一个场景时,将回读它们。至少,它很慢(谈到读取和写入文件),并且调试过程变得不太方便。
  • 一个巨大的静态类,处理所有可能的场景转换。它们与神圣的对象非常相似,并且经常会导致内存泄漏,以及当新开发人员试图了解这千行静态代码中发生了什么时的后背疼痛。
  • DontDestroyOnLoad GameObject-这种方法与以前的方法类似,但是GameObject是在场景中呈现的,在Inspector中具有许多链接。实际上,这是我们每个人在大多数项目中都看到过的那些单例之一。


我想向您展示我已经使用多年的方法。它有助于使转换对开发人员更透明,使您更容易了解发生的位置和发生的事情以及调试。



在每个场景中我都有SceneController他负责转发所有必要的链接并初始化关键对象。从某种意义上讲,可以将其视为场景的入口点。我用一个类来表示参数,SceneArgs并且每个场景都有自己的类,它表示它的参数从它并继承SceneArgs



public abstract class SceneArgs
{
    public bool IsNull { get; private set; }
}


, , SceneController.



public abstract class SceneController<TController, TArgs> : MonoBehaviour
        where TController : SceneController<TController, TArgs>
        where TArgs       : SceneArgs, new()
{
    protected TArgs Args { get; private set; }

    private void Awake()
    {
        Args = SceneManager.GetArgs<Tcontroller, TArgs>();

        OnAwake();
    }

    protected virtual void OnAwake() {}
}


. , params object[] args. . , . , , — , , ( ) , , . , IDE , . params object[] args , , , . ( ), . where, SceneController.



, name buildIndex , LoadScene() LoadSceneAsync() Unity API. , SceneControllerAttribute, . , buildIndex , , , .



[AttributeUsage(AttributeTargets.Class)]
public sealed class SceneControllerAttribute : Attribute
{
    public string SceneName { get; private set; }

    public SceneControllerAttribute(string name)
    {
        SceneName = name;
    }
}


, MainMenu. , :



public sealed class MainMenuArgs : SceneArgs
{
    // args' properties
}



[SceneControllerAttribute]
public sealed class MainMenuController : SceneController<MainMenuController, MainMenuArgs>
{
    protected override void OnAwake()
    {
        // scene initialization
    }
}


, ( , ). , . SceneManager. , , . . — . .



public static class SceneManager
{
    private static readonly Dictionary<Type,  SceneArgs> args;

    static SceneManager()
    {
        args = new Dictionary<Type,  SceneArgs>();
    }

    private static T GetAttribute<T>(Type type) where T : Attribute
    {
        object[] attributes = type.GetCustomAttributes(true);

        foreach (object attribute in attributes)
            if (attribute is T targetAttribute)
                return targetAttribute;

        return null;
    }

    public static AsyncOperation OpenSceneWithArgs<TController, TArgs>(TArgs sceneArgs)
        where TController   : SceneController<TController, TArgs>
        where TArgs         :  SceneArgs, new()
    {
        Type                     type       = typeof(TController);
        SceneControllerAttribute attribute  = GetAttribute<SceneControllerAttribute>(type);

        if (attribute == null)
            throw new NullReferenceException($"You're trying to load scene controller without {nameof(SceneControllerAttribute)}");

        string sceneName = attribute.SceneName;

        if (sceneArgs == null)
            args.Add(type, new TArgs { IsNull = true });

        return UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName);
    }

    public static TArgs GetArgs<TController, TArgs>()
        where TController   : SceneController<TController, TArgs>
        where TArgs         :  SceneArgs, new()
    {
        Type type = typeof(TController);

        if (!args.ContainsKey(type) || args[type] == null)
            return new TArgs { IsNull = true };

        TArgs sceneArgs = (TArgs)args[type];

        args.Remove(type);

        return sceneArgs;
    }
}


. OpenSceneWithArgs() (TController) , , (TArgs) , , (sceneArgs). , SceneManager , TController SceneControllerAttribute. , , TController. sceneArgs . - , TArgs IsNull true. , Unity API LoadSceneAsyn() , SceneControllerAttribute.



Awake(). , SceneController, TController SceneManager.GetArgs(), , , .



, SceneManager, . , . . !




All Articles