プロジェクトのシーン管理の負担を軽減する方法を考えなければならなかったことがありますか?シーンが次々と進む非常に単純なゲームの場合、多くの場合、すべてがスムーズに進みます。しかし、シーンの数が増え、シーン間の遷移がより複雑になると(シーンは異なる順序でロードでき、一部のシーンの動作は入力パラメーターに依存するはずです)、タスクはそれほど簡単ではなくなります。
以下は、私が最も頻繁に見た問題を解決するためのいくつかのアプローチです。
- ファイル-あるシーンから別のシーンに移動するときに、必要なすべてのデータがJSON / XMLファイルに書き込まれ、次のシーンがロードされるときに、それらが読み戻されます。少なくとも、速度が遅く(ファイルの読み取りと書き込みと言えば)、デバッグプロセスの利便性が低下します。
- 考えられるすべてのシーン遷移を処理する巨大な静的クラス。それらは神聖なオブジェクトに非常に似ており、新しい開発者がこの数千行の静的コードで何が起こっているのかを理解しようとすると、メモリリークや腰の痛みを引き起こすことがよくあります。
- DontDestroyOnLoad GameObject-このアプローチは前のアプローチと似ていますが、GameObjectは、インスペクターに多数のリンクがあるシーンで表示されます。実際、これは私たち一人一人がほとんどのプロジェクトで見たシングルトンの1つです...
私が長年使ってきたアプローチをお見せしたいと思います。開発者にとって遷移をより透過的にするのに役立ち、どこで何が起こっているのかを理解しやすくなり、デバッグも容易になります。
すべてのシーンで私は持ってい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, . , . . !