iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
Vibe Coding

從課堂半成品到完整發布:獨立遊戲的30天重生記系列 第 15

【DAY15】Unity 2D 敘事遊戲開發:JSON 紀錄點 (Save Point) 存讀檔架構實作

  • 分享至 

  • xImage
  •  

一、前言:
今天我們將採用最穩定、擴充性極佳的 Save Point (紀錄點) JSON 存檔系統!透過 C# 的 JsonUtility,只需將玩家解鎖的紀錄點資料(包含場景名稱、金錢、聲譽與儲存時間)寫入硬碟 JSON 純文字檔,就能以極低的開發成本實現 100% 穩定的跨場景存讀檔!

二、Save Point 系統設計架構
在我們的辦公桌決策遊戲中,Save Point 運作邏輯如下:
1. 結構化資料結構 (SaveItem & SaveData):

  • SaveItem:記錄單一紀錄點的關鍵資訊(紀錄點名稱、場景名稱、當前金錢、當前聲譽、存檔時間)。
  • SaveData:使用 List 管理所有已解鎖的紀錄點清單。

2. 全域存讀檔管理器 (SavePointManager):

  • 套用 Singleton (單例模式)DontDestroyOnLoad,讓存檔管理器在遊戲開局載入後常駐於背景。
  • 提供 SaveAtCheckPoint() 方法:新增或更新紀錄點,並寫入本機硬碟 JSON 檔。
  • 提供 LoadAllCheckPoints() 方法:讀取歷史紀錄點清單,供主選單與 Settings 選單檢視。

三、核心程式碼實作

1. 存檔資料結構 (SaveData.cs)
在 Assets/Scripts/ 資料夾下建立 SaveData.cs 腳本:

using System;
using System.Collections.Generic;
/// 單一紀錄點資料結構
[Serializable]
public class SaveItem { 
    public string saveName;   // 紀錄點顯示名稱 (例如: "Day 1 辦公桌", "Day 2 辦公桌")
    public string sceneName;  // 對應的 Unity 場景名稱
    public int money;         // 當時的金錢
    public int reputation;    // 當時的聲譽
    public string saveTime;   // 存檔時間 (例如: "2026/08/31 12:00")
}
/// 總存檔資料庫 (包含所有解鎖的紀錄點)
[Serializable]
public class SaveData {
    public List<SaveItem> saveList = new List<SaveItem>();
}

2. 存讀檔核心管理器 (SavePointManager.cs)
在 Assets/Scripts/ 資料夾下建立 SavePointManager.cs 腳本:

using UnityEngine;
using System.IO;
using System.Collections.Generic;
public class SavePointManager : MonoBehaviour {
    public static SavePointManager Instance { get; private set; }

    private string saveFilePath;
    public SaveData currentSaveData = new SaveData();

    private void Awake() {
        // Singleton 單例模式:確保跨場景不被銷毀
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
   
            // 設定本機硬碟存檔路徑
            saveFilePath = Path.Combine(Application.persistentDataPath, "savepoint.json");
            
            // 遊戲開局自動載入歷史紀錄點
            LoadAllCheckPoints();
        }
        else {
            Destroy(gameObject);
        }
    }
    /// 新增或更新 Save Point 紀錄點 (當玩家蓋章或達成條件時呼叫)
    /// <param name="saveName">顯示名稱 (例如: Day 1 辦公桌)</param>
    /// <param name="sceneName">Unity 場景名稱</param>
    /// <param name="money">當前金錢</param>
    /// <param name="reputation">當前聲譽</param>
    public void SaveAtCheckPoint(string saveName, string sceneName, int money, int reputation) {
        // 檢查是否已經存在同名的紀錄點
        SaveItem existingItem = currentSaveData.saveList.Find(item => item.saveName == saveName);
        if (existingItem != null)  {
            existingItem.money = money;
            existingItem.reputation = reputation;
            existingItem.saveTime = System.DateTime.Now.ToString("yyyy/MM/dd HH:mm");
        }
        else {
            SaveItem newItem = new SaveItem {
                saveName = saveName,
                sceneName = sceneName,
                money = money,
                reputation = reputation,
                saveTime = System.DateTime.Now.ToString("yyyy/MM/dd HH:mm")
            };
            currentSaveData.saveList.Add(newItem);
        }
        // 將數據轉成 JSON 並寫入檔案
        string json = JsonUtility.ToJson(currentSaveData, true);
        File.WriteAllText(saveFilePath, json);
        Debug.Log($"[SavePoint] 已成功寫入紀錄點: {saveName} (路徑: {saveFilePath})");
    }
    /// 從硬碟讀取所有紀錄點
    public void LoadAllCheckPoints() {
        if (File.Exists(saveFilePath)) {
            string json = File.ReadAllText(saveFilePath);
            currentSaveData = JsonUtility.FromJson<SaveData>(json);
            Debug.Log($"[SavePoint] 成功讀取歷史存檔,共 {currentSaveData.saveList.Count} 筆紀錄點。");
        }
        else {
            currentSaveData = new SaveData();
            Debug.Log("[SavePoint] 未發現舊存檔,建立初始清單。");
        }
    }
    /// 檢查是否有存檔紀錄
    public bool HasSaveFile() {
        return File.Exists(saveFilePath) && currentSaveData.saveList.Count > 0;
    }
    /// 清除存檔 (新遊戲時使用)
    public void DeleteSaveFile() {
        if (File.Exists(saveFilePath)) {
            File.Delete(saveFilePath);
            currentSaveData = new SaveData();
            Debug.Log("[SavePoint] 舊存檔已刪除。");
        }
    }
}

四、Unity 現場配置與驗收流程

1. Hierarchy 物件配置:
1)開啟第一個遊戲場景(例如 SampleScene 或 MainMenu)。
2)點擊右鍵建立空物件,命名為 SavePointManager。
3)將 SavePointManager.cs 腳本拖曳掛載上該物件。
https://ithelp.ithome.com.tw/upload/images/20260921/20184073W8icLi2pOV.png
圖一、掛載示意圖

2. 驗證 JSON 實體檔案產出:
執行遊戲並呼叫存檔方法後,系統會自動在硬碟的 Application.persistentDataPath(Windows 路徑為 AppData\LocalLow\DefaultCompany\crown\)產生 savepoint.json 檔案。
https://ithelp.ithome.com.tw/upload/images/20260921/20184073SOXG49pOM7.png
圖二、本機 JSON 存檔實體檔案 (savepoint.json) 產出驗證

五、今日成果與小結

  • 完成輕量化存檔資料結構 (SaveData.cs):使用列表結構管理所有解鎖紀錄點的數值與時間。
  • 完成本機 JSON 存讀檔重心 (SavePointManager.cs):無需維護資料庫即可支援跨平台穩定的存讀檔作業。
  • 全域常駐設定完畢:透過 DontDestroyOnLoad 確保存檔總管在背景默默運作,完全不用在每一幕手動重複設定!

因為從這一天開始,專案正式跨入了「資料持久化與存讀檔系統(Save/Load System)」的範疇,這和前面的鄉比起來,在功能模組上是完全獨立的。所以今天要開一個名為 feature/save-system 的新分支來專門處理存讀檔相關的開發與提交。

1.建立並切換到新分支:
git checkout -b feature/save-system

2.檢查修改狀態
git status

3.加入暫存區並提交 Commit
git add .
git commit -m "feat: 實作 SavePointManager 與 SaveData JSON 本機存讀檔架構與 Singleton 常駐管理器"

4.推送到遠端開發分支
git push origin feature/save-system


上一篇
【DAY14】Unity 2D 敘事遊戲開發:辦公桌 UI 視窗置頂與陳情表單 (PaperManager) 決策機制
下一篇
【DAY16】Unity 2D 敘事遊戲開發:自動存檔埋點設計——AutoSavePoint 與協程時序控制避坑
系列文
從課堂半成品到完整發布:獨立遊戲的30天重生記16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言