Unity中ScriptableObject修改保存及兼容打包的场景构建方法咨询
这个问题在Unity开发里太常见了——毕竟EditorUtility.SetDirty()和EditorSceneManager.MarkSceneDirty()这类API本来就是编辑器专属的,打包后完全无法使用。要实现构建包内的持久化修改,核心思路是将ScriptableObject的数据序列化到本地可写目录的文件中,而不是依赖编辑器的资产脏标记机制。
具体实现步骤
1. 确保ScriptableObject类可序列化
首先,你的ScriptableObject类需要保证字段能被Unity序列化(公开字段默认支持,私有字段可加[SerializeField]标记):
using UnityEngine; [CreateAssetMenu(fileName = "PlayerSettings", menuName = "ScriptableObjects/PlayerSettings")] public class PlayerSettings : ScriptableObject { public int highScore; public string playerNickname; [SerializeField] private float soundVolume; // 私有字段也能被序列化 // 封装私有字段的修改逻辑 public void SetSoundVolume(float volume) { soundVolume = Mathf.Clamp(volume, 0f, 1f); } }
2. 编写保存方法:序列化到本地文件
用Unity自带的JsonUtility把数据转成JSON格式,存储到Application.persistentDataPath——这个目录在所有平台都是可写的,适合保存用户自定义数据:
using System.IO; using UnityEngine; public static class SaveManager { // 通用保存方法,支持任意ScriptableObject子类 public static void SaveScriptableObject<T>(T data, string fileName) where T : ScriptableObject { string json = JsonUtility.ToJson(data); string savePath = Path.Combine(Application.persistentDataPath, $"{fileName}.json"); File.WriteAllText(savePath, json); } }
3. 编写加载方法:从文件恢复数据
加载时读取本地文件,用JsonUtility.FromJsonOverwrite()直接覆盖已有ScriptableObject实例的字段,这样能保持原有的引用关系,避免出现新实例导致的引用丢失问题:
public static class SaveManager { // 上面的Save方法... // 通用加载方法,覆盖传入的ScriptableObject实例数据 public static void LoadScriptableObject<T>(T data, string fileName) where T : ScriptableObject { string loadPath = Path.Combine(Application.persistentDataPath, $"{fileName}.json"); if (File.Exists(loadPath)) { string json = File.ReadAllText(loadPath); JsonUtility.FromJsonOverwrite(json, data); } // 如果文件不存在,保持原ScriptableObject的默认值 } }
4. 在业务代码中调用保存和加载
比如在游戏启动时加载数据,退出或用户修改设置时保存:
public class GameManager : MonoBehaviour { public PlayerSettings playerSettings; // 在Inspector中引用你的ScriptableObject private void Start() { // 启动时加载之前保存的用户设置 SaveManager.LoadScriptableObject(playerSettings, "PlayerSettings"); } private void OnApplicationQuit() { // 退出游戏时保存当前设置 SaveManager.SaveScriptableObject(playerSettings, "PlayerSettings"); } // 用户修改设置后即时保存 public void OnSettingsUpdated() { SaveManager.SaveScriptableObject(playerSettings, "PlayerSettings"); } }
额外优化建议
- 编辑器与运行时兼容:如果你想在编辑器调试时依然能保存到原始资产文件(方便预览修改),可以用条件编译区分环境:
public void SaveSettings() { #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); UnityEditor.AssetDatabase.SaveAssets(); #else SaveManager.SaveScriptableObject(this, "PlayerSettings"); #endif }
这样在编辑器中调试时用编辑器API保存到资产,打包后自动切换到文件存储。
避免修改原始资产:打包后的原始ScriptableObject资产是只读的,绝对不要尝试修改它们,所有用户自定义数据都要通过序列化到
persistentDataPath的文件来实现。复杂数据处理:如果你的ScriptableObject包含字典、自定义类等
JsonUtility难以直接序列化的类型,可以实现ISerializationCallbackReceiver接口处理序列化逻辑,或者导入第三方序列化库(比如Newtonsoft Json的Unity适配包)。
内容的提问来源于stack exchange,提问作者user664653

