跨场景保留
本课属于 Coddy Unity 学习旅程的“打造真正的游戏”单元。第 20 课,共 60 课。
加载场景会销毁旧场景中的所有对象,但有些内容必须在整个游戏过程中保留下来,例如玩家从一个关卡带到下一个关卡的金币。DontDestroyOnLoad(gameObject) 会将对象从当前场景移出,放入一个独立的场景中。在“开始”时,这个场景在 Hierarchy 中名为 DontDestroyOnLoad,加载场景时不会影响它。它只对根对象有效:如果对象是子对象,Unity 会记录一条警告,并且不会保留任何内容。被保留对象的子对象也会一并保留,但它只指向而未包含的对象不会保留:场景加载后,指向旧场景对象的字段会保存一个已销毁的对象,它与 null 相等。
public class GameSession : MonoBehaviour
{
public static GameSession Instance;
public int coins;
void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
}这个 GameSession 位于 Menu 场景中。点击“开始”后,游戏从 Menu 进入 Level1,再返回 Menu。Menu 会重新创建其中的所有对象,包括一个新的 GameSession,而之前保留的对象仍然存在。如果 Awake 中没有检查,就会有两个 GameSession,而且新对象会接管 Instance,金币数为 0。有了这个检查,新对象会发现 Instance 已经设置,于是销毁自身并停止运行,而那个经历了各个关卡的会话则会保留下来。
return 很重要,因为 Destroy 会等到帧末尾才执行。如果没有它,Awake 的剩余部分仍会在新对象上运行:Instance = this; 会让所有脚本都指向一个即将被销毁的对象。
挑战
简单让同一个计时器在每次场景加载后继续运行。
- 在
Awake()中,如果Instance != null,调用Destroy(gameObject);并执行return;。否则设置Instance,并调用DontDestroyOnLoad(gameObject);。 - 加载场景会销毁旧关卡的文本,因此在
Update()中、文本赋值语句之前,如果clockText == null,就设置clockText = GameObject.Find("ClockText").GetComponent<TMP_Text>();。
点击运行,前往 Level2 再返回:同一个计时器会继续计时。然后点击检查。
亲手试试
using UnityEngine;
using TMPro;
public class RunClock : MonoBehaviour
{
public static RunClock Instance;
public TMP_Text clockText;
private float seconds;
void Awake()
{
Instance = this;
}
void Update()
{
seconds += Time.deltaTime;
clockText.text = "Time: " + seconds.ToString("0.0");
}
public void Split()
{
Debug.Log("Split: " + seconds.ToString("0.0"));
}
}
打造真正的游戏中的所有课
自己练习: Unity 在线编辑器