单例
本课属于 Coddy Unity 学习旅程的“打造真正的游戏”单元。第 13 课,共 60 课。
Inspector 引用需要有可供拖入的对象。Spawner 在游戏过程中生成的敌人来自预制体,而预制体只能引用资源和自身的组件,不能引用场景中的对象,所以它们的 GameManager 字段是空的。通常的解决办法是使用单例:GameManager 将自身存储在一个字段中,每个脚本都可以通过类名访问这个字段。
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
private int kills;
void Awake()
{
Instance = this;
}
public void EnemyDown()
{
kills++;
Debug.Log("Kills: " + kills);
}
}// Enemy.cs,挂在敌人预制体上
public void Die()
{
GameManager.Instance.EnemyDown();
Destroy(gameObject);
}static 表示这个字段属于类,而不是某个对象。每个 GameManager 对象都会有自己的 kills,但整个游戏中只有一个 Instance,你可以通过类名访问它:GameManager.Instance。任何脚本都能读取它,包括刚刚生成的敌人,而且不需要为它填写自己的字段。
管理器会在 Awake 中设置 Instance = this;,其中 this 指的是脚本本身。Unity 会在运行任何 Start 之前,先运行场景中的每个 Awake,因此其他脚本从自己的 Start 开始就能使用 GameManager.Instance。如果场景中有第二个 GameManager,它会用自身覆盖 Instance;“场景和关卡”一章会介绍如何阻止这种情况。
挑战
简单让流星通过单例访问 GameManager。
- 在
GameManager.cs中,添加public static GameManager Instance;,并添加包含Instance = this;的Awake()。 - 在
Meteor.cs中,删除game字段,并改为调用GameManager.Instance.MeteorLanded()和GameManager.Instance.MeteorCaught()。
按下“开始”并接住一颗流星:界面会显示 Caught: 1,控制台中没有错误。然后点击“检查”。
亲手试试
using UnityEngine;
using TMPro;
public class GameManager : MonoBehaviour
{
public TMP_Text caughtText;
public TMP_Text shieldText;
private int caught;
private int shield = 3;
public void MeteorCaught()
{
caught++;
caughtText.text = "Caught: " + caught;
}
public void MeteorLanded()
{
shield--;
shieldText.text = "Shield: " + shield;
}
}
打造真正的游戏中的所有课
自己练习: Unity 在线编辑器