Você já teve que pensar em como tornar o gerenciamento de cena em seu projeto menos doloroso? Quando você tem um jogo bastante simples no qual existem apenas algumas cenas acontecendo uma após a outra, então, freqüentemente, tudo corre bem. Mas quando o número de cenas aumenta e as transições entre elas se tornam mais complicadas - elas podem ser carregadas em uma ordem diferente e o comportamento de algumas delas deve depender dos parâmetros de entrada - a tarefa se torna menos trivial.
Abaixo estão várias abordagens para resolvê-lo que tenho visto com mais frequência:
- Arquivos - ao passar de uma cena para outra, todos os dados necessários são gravados em um arquivo JSON / XML e, quando a próxima cena é carregada, eles são lidos de volta. No mínimo, é lento (falando sobre leitura e gravação em um arquivo) e o processo de depuração se torna menos conveniente.
- Uma enorme classe estática que lida com todas as transições de cena possíveis. Eles são muito semelhantes a objetos divinos e muitas vezes causam vazamentos de memória, bem como dores na parte inferior das costas quando um novo desenvolvedor tenta entender o que está acontecendo nessas mil linhas de código estático.
- DontDestroyOnLoad GameObject - Esta abordagem é semelhante à anterior, mas o GameObject é apresentado em uma cena com vários links no Inspetor. Na verdade, este é um daqueles singletons que cada um de nós viu na maioria dos projetos ...
Quero mostrar uma abordagem que venho usando há anos. Ajuda a tornar as transições mais transparentes para o desenvolvedor, torna mais fácil entender onde e o que está acontecendo e também depurar.
Em todas as cenas que tenho SceneController. Ele é responsável por encaminhar todos os links necessários e inicializar objetos-chave. Em certo sentido, pode ser considerado o ponto de entrada da cena. Eu uso uma classe para representar argumentos, SceneArgse cada cena tem sua própria classe que representa seus argumentos e herda dela 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, . , . . !