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

Unity Addressable:不加载至RAM获取所有资源位置并存入列表

Addressable地图资源按需加载与内存优化方案

问题根源分析

  • 你当前的代码仅释放了实例化操作的句柄,但没有销毁场景中实例化出的GameObject,导致这些实例一直占用内存。
  • 通过new AssetReference(location.PrimaryKey.ToString())创建的AssetReference未被正确管理,可能造成资源引用残留。
  • 提前实例化所有资源违背了“按需加载”的初衷,直接导致内存过载。

核心优化思路

  • 仅预加载资源位置信息,不提前加载任何资源本体
  • 玩家靠近时才实例化对应区块,远离时立即销毁实例并释放资源
  • 严格管理异步操作句柄与资源引用的生命周期

优化后的代码实现

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class MapChunkLoader : MonoBehaviour
{
    // 存储所有地图区块的资源位置与初始坐标
    private List<(IResourceLocation location, Vector3 position)> _mapChunkData = new();

    // 记录已加载的区块实例与对应的操作句柄
    private Dictionary<IResourceLocation, (GameObject instance, AsyncOperationHandle<GameObject> handle)> _loadedChunks = new();

    private void Start()
    {
        StartCoroutine(PreloadMapLocations());
    }

    // 预加载所有地图资源的位置信息,不加载资源本身
    private IEnumerator PreloadMapLocations()
    {
        var loadLocationsOp = Addressables.LoadResourceLocationsAsync("map");
        yield return loadLocationsOp;

        if (loadLocationsOp.Status == AsyncOperationStatus.Succeeded)
        {
            foreach (var location in loadLocationsOp.Result)
            {
                // 临时加载资源获取初始坐标,之后立即释放
                var tempLoadOp = Addressables.LoadAssetAsync<GameObject>(location);
                yield return tempLoadOp;

                if (tempLoadOp.Status == AsyncOperationStatus.Succeeded)
                {
                    _mapChunkData.Add((location, tempLoadOp.Result.transform.position));
                    Addressables.Release(tempLoadOp);
                }
            }
        }
        // 释放资源位置列表的加载句柄
        Addressables.Release(loadLocationsOp);
    }

    // 玩家靠近时调用:加载指定地图区块
    public void LoadMapChunk(IResourceLocation location)
    {
        if (_loadedChunks.ContainsKey(location)) return;

        var instantiateOp = Addressables.InstantiateAsync(location);
        instantiateOp.Completed += op =>
        {
            if (op.Status == AsyncOperationStatus.Succeeded)
            {
                var instance = op.Result;
                var chunkData = _mapChunkData.Find(d => d.location == location);
                instance.transform.position = chunkData.position;
                _loadedChunks.Add(location, (instance, op));
            }
        };
    }

    // 玩家远离时调用:卸载指定地图区块
    public void UnloadMapChunk(IResourceLocation location)
    {
        if (_loadedChunks.TryGetValue(location, out var chunkData))
        {
            Destroy(chunkData.instance);
            Addressables.Release(chunkData.handle);
            _loadedChunks.Remove(location);
        }
    }

    // 场景销毁时清理所有资源
    private void OnDestroy()
    {
        foreach (var chunk in _loadedChunks.Values)
        {
            Destroy(chunk.instance);
            Addressables.Release(chunk.handle);
        }
        _loadedChunks.Clear();
    }
}

额外注意事项

  • 区块拆分:将2GB的地图拆分为更小的独立区块(比如按100x100地形单位划分),降低单次加载的内存压力。
  • 内存监控:用Unity Profiler的Memory模块实时追踪内存变化,确认资源是否被正确释放。
  • 资源分组:把同区域的地图区块归到同一个Addressable组,可优化加载效率。
  • 错误处理:所有异步操作都要检查Status,避免失败操作残留无效引用。

内容的提问来源于stack exchange,提问作者marietta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 04:20:21