Unity 永久儲存資料 使用 Json 格式
分類
說明
這次我打算要在遊戲上儲存最高分的紀錄,我的需求有以下2點
- 儲存的格式使用 JSON
- 要永久保存,避免更新 iOS 版本時被洗掉
為了方便,我設計了第一版的簡單儲存機制,這個版本方便簡單好用,全 Unity 內建程式,沒有使用任何外掛,但檔案沒有加密,對於有安全性考量的資料,建議加密,或者儲存在伺服器端
儲存程式
使用靜態類別,讀取和儲存方法可以直接使用,
SimpleStorage.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public static class SimpleStorage
{
/// <summary>
/// 成就檔案路徑
/// </summary>
public static readonly string AchievementFile =
$"{Application.persistentDataPath}/Achievement.json";
/// <summary>
/// 讀取檔案,找不到或發生任何例外都會回傳空物件
/// </summary>
/// <typeparam name="T">資料型態</typeparam>
/// <param name="path">路徑</param>
/// <returns></returns>
public static T Load<T>(string path)
{
try
{
string jsonData = File.ReadAllText(path);
return JsonUtility.FromJson<T>(jsonData);
}
catch (FileNotFoundException)
{
Debug.Log($"找不到 {path}");
}
catch (Exception exception)
{
Debug.LogError(exception.Message);
}
return (T)Activator.CreateInstance(typeof(T));
}
/// <summary>
/// 存檔
/// </summary>
/// <param name="path">路徑</param>
/// <param name="data">資料</param>
public static void Save(string path, object data)
{
string jsonData = JsonUtility.ToJson(data);
File.WriteAllText(path, jsonData);
}
}
Application.persistentDataPath
是 Unity 用來永久儲存資料的路徑
JsonUtility 這個類別是用來處理將物件轉換成 Json 格式或者將 Json 格式轉換回物件
(T)Activator.CreateInstance(typeof(T))
這個是一種叫反射實例化的程式,T 在 c# 代表泛型,也就是 T 可以是我自定義的 MyClass 或 MyOtherClass 等等,但是程式不能寫 return new T()
,所以就有了 (T)Activator.CreateInstance(typeof(T))
,但這個寫法不能實例化建構子有參數的類別,否則會發生錯誤,在 Load()
方法中,如果沒發生意外,那就會回傳 T 型態的物件,發生任何意外都會回傳 T 型態,剛實例化的物件。
儲存結構
現在的設計是每一個檔案會儲存一個類別,類別需自行定義,這裡假設我要儲存最高分,這裡預期都會有預設值,在找不到檔案時,實例化類別時就會使用預設值
Achievement.cs
/// <summary>
/// 成就紀錄
/// </summary>
public class Achievement
{
/// <summary>
/// 最高分
/// </summary>
public int TopScore = 0;
}
使用範例
假設有一個遊戲分數 score,在玩遊戲的過程中會不斷地變化,在遊戲生命週期結束時,我就要更新最高分。
Example.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Example : MonoBehaviour
{
/// <summary>
/// 遊戲分數
/// </summary>
private int score;
private void OnDestroy()
{
Achievement achievement = SimpleStorage.Load<Achievement>(
SimpleStorage.AchievementFile);
if (achievement.TopScore < score)
{
achievement.TopScore = score;
SimpleStorage.Save(SimpleStorage.AchievementFile, achievement);
}
}
}
最後你就能在你想要顯示最高分的地方,使用上面的讀取程式,取得最高分。
更新版 SimpleStorage
原版 SimpleStorage 是共享靜態類別,此版本是抽象類別,子類別需繼承 SimpleStorage,功能都一樣,只是在呼叫讀取、存檔時寫的程式比較短。
SimpleStorage.cs
using System;
using System.IO;
using UnityEngine;
/// <summary>
/// 簡易的手機版儲存腳本
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SimpleStorage<T> where T : SimpleStorage<T>, new()
{
/// <summary>
/// 預設的存檔路徑
/// </summary>
protected static readonly string FilePath =
$"{Application.persistentDataPath}/{typeof(T).Name}.json";
/// <summary>
/// 讀取預設檔案,找不到或發生任何例外都會回傳空物件
/// </summary>
/// <returns></returns>
public static T Load()
{
try
{
string jsonData = File.ReadAllText(FilePath);
return JsonUtility.FromJson<T>(jsonData);
}
catch (FileNotFoundException)
{
Debug.Log($"找不到 {FilePath}");
}
catch (Exception exception)
{
Debug.LogError(exception.Message);
}
return new T();
}
/// <summary>
/// 儲存在預設檔案
/// </summary>
public void Save()
{
string jsonData = JsonUtility.ToJson(this);
File.WriteAllText(FilePath, jsonData);
}
}
使用方式
假設我實作一個設定存檔,紀錄遊戲設定。
SettingsStorage.cs
/// <summary>
/// 設定存擋
/// </summary>
public class SettingsStorage : SimpleStorage<SettingsStorage>
{
/// <summary>
/// 背景音樂是否開啟
/// </summary>
public bool BackgroundMusicOn = true;
/// <summary>
/// 音效是否開啟
/// </summary>
public bool SoundEffectOn = true;
}
接著我就可以如下使用,以下是一個開關 UI。
BGMToggle.cs
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 背景音樂開關
/// </summary>
public class BGMToggle : MonoBehaviour
{
private Toggle toggle;
private SettingsStorage settingsStorage;
private void Awake()
{
toggle = GetComponent<Toggle>();
settingsStorage = SettingsStorage.Load();
toggle.isOn = settingsStorage.BackgroundMusicOn;
}
/// <summary>
/// 切換開關
/// </summary>
/// <param name="isOn">開關</param>
public void OnValueChange(bool isOn)
{
settingsStorage.BackgroundMusicOn = isOn;
settingsStorage.Save();
MusicPlayer.Instance.AudioSource.volume = isOn ? 1f : 0f;
}
}
可以看到 Load()
和 Save()
不用再帶入參數了。
結論
大家可以依照自己的專案需求,考慮要使用哪個版本,然後再進行調整。
一杯咖啡的力量,勝過千言萬語的感謝。
支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!