You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

技术咨询:如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 21:32:55