這篇在講什麼
存檔與讀檔幾乎是大多數遊戲都會碰到的基礎系統。這篇在整理《Endless End》當時為什麼沒有直接用 PlayerPrefs,而是改成 JSON 檔案加 ScriptableObject 的做法。
當時先比較了兩條路
對 Unity 小專案來說,最常見的簡易方案通常有兩種:
- PlayerPrefs
- JSON
PlayerPrefs 的好處是簡單,SetInt、SetFloat、SetString 很快就能把資料存起來。
但我最後沒有把它拿來做正式進度保存,原因是它更適合保存偏好設定,而不是結構稍微複雜的遊戲狀態。
為什麼我沒有用 PlayerPrefs
1. 型別與結構限制太大
PlayerPrefs 原生只適合處理:
intfloatstring
如果遊戲資料裡還有列表、複合狀態或多段進度,就會變成要把每個欄位拆得很零碎,讀寫都不舒服。

2. 比較適合偏好設定,不太適合遊戲進度
像下面這些東西,用 PlayerPrefs 很合理:
- 音量設定
- 畫面或語言設定
- 一些開關選項
但如果是關卡進度、玩家位置、物件狀態這類資料,就會開始顯得太扁平。
我最後用的是 JSON 存檔
我採用的做法是:
- 先把需要保存的資料集中成一個資料模型
- 用
JsonUtility.ToJson把它轉成字串 - 寫到本機檔案
- 讀檔時再反過來覆寫回資料物件
這套做法的優點很直接:
- 資料結構集中
- 容易擴充
- 檔案內容可檢查
而且對這個專案來說,Unity 內建的 JsonUtility 已經夠用了,不需要額外依賴第三方套件。
資料模型怎麼設計
第一步是先做一個專門承載存檔資料的模型,讓所有需要保存的欄位都集中在同一處。
我當時把它做成 ScriptableObject,並加上 CreateAssetMenu。這樣它不只是序列化格式,同時也能作為 Editor 裡可直接檢查的資料資產。
這樣的好處是:
- 欄位集中,容易管理
- 可以直接在 Editor 觀察資料
- 也方便提供預設值或重置流程


存檔流程
真正的存檔流程不複雜:
- 決定存檔路徑
- 用
JsonUtility.ToJson把GameData轉成字串 - 用
File.WriteAllText寫到.json檔案

在《Endless End》裡,我把資料存到 StreamingAssets,也就是 Application.streamingAssetsPath。
成功後,專案就會得到一份像 GameSave.json 這樣的存檔檔案。

讀檔流程
讀檔端我用的是這個順序:
- 先確認存檔檔案是否存在
- 如果存在,就讀取 JSON 內容
- 準備一份
GameData - 用
JsonUtility.FromJsonOverwrite把資料覆寫回去 - 把目前使用中的存檔引用切到這份資料
- 如果讀檔失敗,就回退到預設資料


之後玩家角色和受進度影響的場景物件,再根據這份 currSave 做初始化。
當時踩過的 Unity Editor 限制
我一開始有碰到 AssetDatabase 相關做法,但最後都拿掉了。
原因很單純:AssetDatabase 屬於 UnityEditor 命名空間,它很適合做 Editor 工具,但不應直接進入正式 Build。
如果把這類程式混進正式存讀檔流程,打包時很容易直接出錯。
我自己的整理
真正重要的不是技術名詞,而是你要先回答:哪些資料要保存、什麼時候保存、讀回來之後哪些系統要重新套用。
存讀檔系統最麻煩的地方,往往不是 API,而是遊戲狀態邊界。
先記住的重點
- PlayerPrefs 適合偏好設定,不太適合複雜進度資料
- JSON 方案比較適合集中管理多欄位遊戲狀態
ScriptableObject可以同時扮演資料模型與可檢查資產- 正式流程裡要避開
UnityEditor/AssetDatabase依賴 - 存讀檔設計的核心,是先定義清楚要保存哪些狀態