技术咨询:如何将Addressables保存为JSON、读取并还原至原对象
刚好做过类似的需求,我给你拆解一下完整的流程,其实核心是把Addressables里能序列化的元数据存成JSON,之后再用这些元数据反向加载资源并恢复状态——毕竟Addressables的加载句柄(AsyncOperationHandle)本身是没法直接序列化的,得抽离有用的信息。
核心思路
Addressables的核心是通过「地址/GUID/标签」定位资源,所以我们不需要序列化资源本身(也做不到),只需要保存:
- 资源的Addressables定位标识(比如地址字符串)
- 资源的类型(方便后续动态加载)
- 你需要恢复的资源状态(比如GameObject的位置、自定义组件的参数等)
把这些信息打包成一个可序列化的类,再转成JSON保存;读取时反向解析JSON,拿到这些信息后调用Addressables加载资源,最后恢复状态。
步骤1:定义可序列化的元数据类
首先要写一个能被Unity的JsonUtility序列化的类,用来存储我们需要的所有信息:
using UnityEngine; using System; [Serializable] public class AddressableSaveData { // Addressables资源的定位地址 public string ResourceAddress; // 资源的类型全名,用来动态加载时获取Type public string ResourceTypeFullName; // 示例:保存GameObject的位置信息 public Vector3 ObjectPosition; // 示例:保存GameObject的旋转信息 public Quaternion ObjectRotation; // 你可以根据需求添加更多自定义字段,比如组件的参数、状态标记等 }
步骤2:将Addressables资源的元数据保存为JSON
当你加载完目标资源后,提取需要的信息,转成JSON并保存到本地:
using UnityEngine; using System.IO; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public void SaveAddressableToJson() { // 假设我们要保存的资源地址是"EnemyPrefab" string targetAddress = "EnemyPrefab"; AsyncOperationHandle<GameObject> loadHandle = Addressables.LoadAssetAsync<GameObject>(targetAddress); loadHandle.Completed += operation => { if (operation.Status == AsyncOperationStatus.Succeeded) { GameObject loadedObj = operation.Result; // 构建元数据对象 AddressableSaveData saveData = new AddressableSaveData { ResourceAddress = targetAddress, ResourceTypeFullName = typeof(GameObject).FullName, ObjectPosition = loadedObj.transform.position, ObjectRotation = loadedObj.transform.rotation }; // 转成格式化的JSON字符串 string jsonContent = JsonUtility.ToJson(saveData, prettyPrint: true); // 保存到Unity推荐的可读写路径(不同平台通用) string savePath = Path.Combine(Application.persistentDataPath, "EnemySave.json"); File.WriteAllText(savePath, jsonContent); // 记得释放加载句柄,避免内存泄漏 Addressables.Release(loadHandle); } else { Debug.LogError($"加载资源失败:{operation.OperationException}"); } }; }
步骤3:从JSON文件读取元数据
需要加载时,先读取本地的JSON文件,解析成我们定义的元数据类:
using UnityEngine; using System.IO; public AddressableSaveData LoadAddressableDataFromJson() { string loadPath = Path.Combine(Application.persistentDataPath, "EnemySave.json"); if (File.Exists(loadPath)) { string jsonContent = File.ReadAllText(loadPath); // 解析JSON到元数据对象 return JsonUtility.FromJson<AddressableSaveData>(jsonContent); } else { Debug.LogWarning("找不到保存的JSON文件"); return null; } }
步骤4:用元数据加载并恢复原始对象
拿到解析后的元数据后,调用Addressables加载资源,并恢复之前保存的状态:
using UnityEngine; using System; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public void LoadAndRestoreAddressable(AddressableSaveData saveData) { if (saveData == null) return; // 根据保存的类型全名获取Type Type resourceType = Type.GetType(saveData.ResourceTypeFullName); if (resourceType == null) { Debug.LogError($"无法找到资源类型:{saveData.ResourceTypeFullName}"); return; } // 用反射动态调用LoadAssetAsync(因为类型是动态的) MethodInfo loadMethod = typeof(Addressables).GetMethod("LoadAssetAsync").MakeGenericMethod(resourceType); AsyncOperationHandle loadHandle = (AsyncOperationHandle)loadMethod.Invoke(null, new object[] { saveData.ResourceAddress }); loadHandle.Completed += operation => { if (operation.Status == AsyncOperationStatus.Succeeded) { UnityEngine.Object loadedObj = operation.Result; if (loadedObj is GameObject go) { // 实例化对象(如果需要),或者直接修改已加载的对象 GameObject instance = Instantiate(go); // 恢复之前保存的状态 instance.transform.position = saveData.ObjectPosition; instance.transform.rotation = saveData.ObjectRotation; } // 释放加载句柄 Addressables.Release(loadHandle); } else { Debug.LogError($"加载资源失败:{operation.OperationException}"); } }; }
一些关键注意事项
- 序列化限制:
JsonUtility只能序列化标记了[Serializable]的类,且字段必须是public或者标记了[SerializeField];如果需要序列化更复杂的类型(比如List),也要确保对应的类是可序列化的。 - 地址有效性:保存的ResourceAddress必须和Addressables Groups里配置的地址完全一致,否则加载会失败。
- 路径选择:务必使用
Application.persistentDataPath作为保存路径,这是Unity在所有平台上都保证可读写的路径,不要用StreamingAssets(只读)或者自定义绝对路径。 - 内存管理:记得在加载完成后释放
AsyncOperationHandle,避免内存泄漏;如果需要长期持有资源,可以用Addressables.ReleaseInstance替代。
内容的提问来源于stack exchange,提问作者Andrys
相关产品推荐
相关产品推荐

