泛型单例
本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 43 课,共 64 课。
第 3 单元中的单例只需 5 行代码,每个管理器都会重复编写这些代码:静态的 Instance、用于设置它的 Awake,以及重复实例检查。泛型基类可以把这些代码只写一次。每个管理器都从这个基类派生,并将自身类型作为名称:
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
public static T Instance { get; private set; }
protected virtual void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this as T;
}
}
public class ScoreManager : Singleton<ScoreManager>
{
public int score;
public void Add(int points) { score += points; }
}
public class SoundManager : Singleton<SoundManager>
{
public void Play(string clip) { Debug.Log("Playing " + clip); }
}// 在任何脚本中
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");泛型类的静态成员分别属于由它生成的每种类型:Singleton<ScoreManager> 和 Singleton<SoundManager> 是两种各自拥有一个 Instance 的类型,因此这些管理器不会共用同一个 Instance。ScoreManager.Instance 的类型已经是 ScoreManager,这正是 T 的作用。在基类中,this 只被视为 Singleton<T>,因此 this as T 会将它转换为它实际所属的管理器类型。
这个基类是抽象类:Unity 只会将管理器放到对象上。如果管理器需要自己的 Awake,就不能直接编写 void Awake()。这样会隐藏基类的方法(编译器会发出警告),Unity 也只会调用管理器自己的方法,Instance 永远不会被设置。请重写该方法,并先调用基类的方法:
protected override void Awake()
{
base.Awake();
score = 0;
}挑战
中等为两个管理器提供一个在场景加载后仍能保留的基类。
- 在
PersistentSingleton中,编写protected virtual void Awake():第一个实例成为Instance并保留下来(DontDestroyOnLoad);后续实例会销毁自身。 Wallet和MusicPlayer继承它;删除它们的Instance和 Wallet 的 Awake。MusicPlayer重写 Awake:调用base.Awake(),然后,如果它就是 Instance,则记录Now playing: Adventure。
按“开始”,前往森林再回来:获得 1 枚金币。然后按“检查”。
亲手试试
using UnityEngine;
public abstract class PersistentSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
public static T Instance { get; private set; }
// Awake:切换场景时保留第一个实例,销毁之后创建的实例
}
Unity 游戏架构中的所有课
自己练习: Unity 在线编辑器