Pernahkah Anda berpikir tentang bagaimana membuat manajemen adegan dalam proyek Anda tidak terlalu menyakitkan? Ketika Anda memiliki permainan yang cukup sederhana di mana hanya ada beberapa adegan yang terjadi satu demi satu, seringkali, semuanya berjalan lancar. Tetapi ketika jumlah adegan bertambah dan transisi di antara mereka menjadi lebih rumit - mereka dapat dimuat dalam urutan yang berbeda dan perilaku beberapa di antaranya harus bergantung pada parameter masukan - tugas menjadi tidak terlalu sepele.
Di bawah ini adalah beberapa pendekatan untuk menyelesaikannya yang paling sering saya lihat:
- File - saat berpindah dari satu adegan ke adegan lain, semua data yang diperlukan ditulis ke file JSON / XML, dan saat adegan berikutnya dimuat, mereka dibaca kembali. Paling tidak, ini lambat (berbicara tentang membaca dan menulis ke file), dan proses debugging menjadi kurang nyaman.
- Kelas statis besar yang menangani semua kemungkinan transisi adegan. Mereka sangat mirip dengan objek ilahi dan cukup sering menyebabkan kebocoran memori, serta rasa sakit di punggung bawah ketika pengembang baru mencoba memahami apa yang terjadi dalam ribuan baris kode statis ini.
- DontDestroyOnLoad GameObject - Pendekatan ini mirip dengan yang sebelumnya, tetapi GameObject disajikan dalam sebuah adegan dengan banyak tautan di Inspector. Faktanya, ini adalah salah satu lajang yang masing-masing dari kita telah lihat di sebagian besar proyek ...
Saya ingin menunjukkan kepada Anda pendekatan yang telah saya gunakan selama bertahun-tahun. Ini membantu membuat transisi lebih transparan bagi pengembang, menjadi lebih mudah untuk memahami di mana dan apa yang terjadi, dan juga untuk men-debug.
Di setiap adegan yang saya miliki SceneController. Ini bertanggung jawab untuk meneruskan semua tautan yang diperlukan dan menginisialisasi objek utama. Dalam arti tertentu, itu bisa dianggap sebagai titik masuk dari sebuah adegan. Saya menggunakan kelas untuk mewakili argumen, SceneArgsdan setiap adegan memiliki kelasnya sendiri yang mewakili argumennya dan mewarisinya 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, . , . . !