一、前言:
當玩家完成了一段劇情、做出的關鍵抉擇影響了金錢與聲譽後,下一個面臨的技術挑戰就是:「如何讓這些資料在切換場景(Scene Change)後依然被留存,不會重置?」
今天【DAY 11】我們要深入講解 Unity 中最經典且高效的跨場景資料傳遞機制—— 「單例模式(Singleton)」 與 DontDestroyOnLoad,並展示如何將金錢(Money)與聲譽(Reputation)在跨場景轉換時無縫同步!
二、跨場景資料傳遞的核心需求
在文字冒險或 2D 敘事遊戲中,場景切換非常頻繁(例如:從「辦公室 OfficeScene」切換到「主角家 HomeScene」)。若沒有妥善管理跨場景資料,會面臨以下問題:
1. 狀態歸零問題: 預設情況下,Unity 載入新場景會銷毀前一個場景的所有物件,導致玩家做出的抉擇(如扣除 $50****0,000)在新 Scene 被重置。
2. 全域存取混亂: 其他腳本(如對話系統、選擇題面板)需要隨時修改金錢或聲譽,若沒有統一的集中大腦,程式碼會變得極難維護。
3. 切換場景後的 UI 引用失效(NullReferenceException): 常駐物件切換場景後,原本綁定的舊 UI 物件已被銷毀,若直接寫入會導致系統報錯。
三、核心程式實作:PlayerStatsManager.cs 單例常駐機制
我們將資料的讀取、寫入與管理集中在 PlayerStatsManager.cs 中,利用 DontDestroyOnLoad 與單例模式(Singleton),讓數值管理器常駐在記憶體中:
1.單例初始化與跨場景不銷毀 (Awake)
利用 Awake() 檢查實例,並將自己設定為 DontDestroyOnLoad:
public class PlayerStatsManager : MonoBehaviour {
// 單例模式實例:全域唯一存取點
public static PlayerStatsManager Instance { get; private set; }
[Header("玩家狀態數據")]
public int money = 1000000;
public int reputation = 15;
void Awake() {
if (Instance == null) {
Instance = this;
DontDestroyOnLoad(gameObject); // 關鍵:切換場景時不銷毀此物件
}
else {
Destroy(gameObject); // 避免載入新場景時重複建立第二個 Manager
return;
}
}
}
2. 數值變動與安全 UI 更新 (ChangeMoney / UpdateStatusUI)
當玩家做出劇情選擇時呼叫此方法,除了修改記憶體中的數值,也會透過空值檢查(!= null)安全地更新 UI:
/// 扣除或增加金錢,並自動同步更新 UI
public void ChangeMoney(int amount) {
money += amount;
UpdateStatusUI();
Debug.Log($"[保險箱] 金錢變動: {amount}。目前: {money}");
}
/// 更新當前場景的 UI 文字 (內含空值防呆)
public void UpdateStatusUI() {
if (moneyText != null)
moneyText.text = "金錢: " + money.ToString("N0"); // N0 自動格式化為千分位 (例: 1,000,000)
if (reputationText != null)
reputationText.text = "聲譽: " + reputation.ToString();
}
3. 對話與選擇系統鏈接呼叫
在劇情選擇點(例如 DAY 09 的 ChoiceManager)確定選項後,可以直接透過單例一行程式碼更新數值:
// 選擇 YES 後扣除 50 萬金錢,直接透過 Singleton 呼叫
PlayerStatsManager.Instance.ChangeMoney(-500000);
四、Unity Inspector 與跨場景測試
在 Unity 實作跨場景資料同步時,需要確保 UI 組件與腳本欄位正確連結:
1. 跨場景數值橋樑與 UI 綁定
在 Hierarchy 建立名為 InstanceManager 的遊戲物件,並掛載 NewPlayerStatsManager 腳本。在 Inspector 面板中,將當前場景 Canvas 下的 money 與 reputation 文字元件(TextMeshProUGUI)分別拖入 New Scene Money Text 與 New Scene Reputation Text 欄位。當新場景載入時,此腳本會自動將常駐數值刷新至當前 UI 上。

圖一、點選 Hierarchy 中 InstanceManager 物件時的 Inspector 截圖
2. 跨場景切換與數值承接實測(OfficeScene ➔ HomeScene)
當玩家在 OfficeScene 做出扣除金錢的抉擇後,跳轉至 HomeScene 時,InstanceManager 成功向常駐單例索取最新數據,使 HomeScene 的 UI 面板瞬間顯示出扣除後的金額與聲譽,達到無縫承接的效果!

圖二、第五幕最後-Office(數值經選項後變動)

圖三、第六幕開頭-Home(和第五幕最後相符)
五、開發實戰經驗:單例模式生命週期與 UI 解耦
在實務開發中使用 DontDestroyOnLoad 單例有以下兩點關鍵經驗分享:
1. 處理切換場景後的「舊 UI 引用失效」: 因為 PlayerStatsManager 本身不會被銷毀,但新場景的 Canvas UI 是重新生成的!這就是為什麼在 UpdateStatusUI() 中一定要加上 if (moneyText != null) 空值檢查。在較複雜的架構中,亦可由新場景的 UI 控制器在 Start() 時向 PlayerStatsManager.Instance 註冊並主動索取最新數據。
2. ToString("N0") 字串格式化: 在處理遊戲內的貨幣時,直接顯示 1000000 視覺體驗較差。呼叫 .ToString("N0") 能讓 C# 自動幫數字加上千分位逗號(顯示為 1,000,000),極大地提升了遊戲介面的質感!
六、今日成果與小結
最後別忘了傳到GitHub做紀錄,遊戲核心系統/管理器放在feature/game-systems分支:
1.檢查修改狀態
git status
2.加入暫存區並提交 Commit
git add .
git commit -m "feat: 實作 PlayerStatsManager 單例常 Jod (DontDestroyOnLoad) 與跨場景數值同步機制"
3.推送到遠端開發分支
git push origin feature/game-systems