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

Unity runtime动态添加Prefab:场景优化与大对象处理咨询

Unity Runtime动态加载GameObject的最佳实践与实现方案

针对你4个场景动态加载60个预制体的需求,以下是内存性能优化、对象复用、大对象处理的具体方案,附代码示例:

一、内存与性能最佳实践

  • 资源打包选择:优先用Addressable Assets System,相比Resources文件夹,它支持按需加载、资源分组、版本管理,能更精准控制内存占用;如果是新手入门,也可以先从Resources入手,再逐步迁移到Addressable。
  • 按需加载+及时卸载:只在用户选择对应对象时加载其预制体,切换对象/场景时,立即销毁旧对象并调用Resources.UnloadUnusedAssets()(Resources方式)或Addressables.ReleaseInstance()(Addressable方式)释放内存,避免内存泄漏。
  • 场景加载模式:使用SceneManager.LoadSceneAsync()以异步方式加载场景,避免主线程阻塞;场景加载时选择LoadSceneMode.Single,确保每次只有一个环境场景处于激活状态。
  • 清理冗余组件:检查预制体上的多余组件(比如未使用的Collider、空脚本),减少运行时性能开销。

二、对象复用方案

如果用户会重复选择同一个3D对象,没必要每次重新加载,可通过简单的对象池实现复用:

  • 维护一个字典Dictionary<string, GameObject>,存储已加载过的对象实例,键为预制体的路径/名称。
  • 当用户选择对象时,先检查字典:
    • 存在则直接启用对象,重置位置、旋转等属性;
    • 不存在则加载预制体,实例化后存入字典,禁用时仅设置SetActive(false)而非销毁。
  • 注意:如果60个对象几乎不会重复被选中,对象池意义不大,直接加载后销毁更省内存。

三、大对象处理方案

对于高面数、多纹理的大对象,重点从加载和渲染优化入手:

  • 异步加载:用Addressables.LoadAssetAsync()或Resources.LoadAsync()异步加载,避免卡顿,加载过程中可显示加载动画。
  • LOD分组:给大对象添加LOD Group组件,根据相机距离自动切换不同细节的模型,减少远距渲染开销。
  • 资源压缩:在Player Settings中开启纹理压缩(移动端用ASTC,PC用BC压缩),开启网格压缩,降低资源内存占用。
  • 资源拆分:将大对象的模型、纹理、动画拆分打包,优先加载低分辨率占位资源,等场景加载完成后再后台加载高分辨率资源。

四、代码示例

1. Resources方式基础实现(新手友好)

using UnityEngine;
using UnityEngine.SceneManagement;

public class ObjectLoader : MonoBehaviour
{
    private GameObject currentActiveObject;
    private const string PrefabPath = "Prefabs/";

    // 切换场景并加载指定对象
    public void LoadSceneAndObject(string sceneName, string prefabName)
    {
        SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single).completed += (op) =>
        {
            UnloadCurrentObject();
            LoadObjectFromResources(prefabName);
        };
    }

    private void LoadObjectFromResources(string prefabName)
    {
        GameObject prefab = Resources.Load<GameObject>(PrefabPath + prefabName);
        if (prefab != null)
        {
            currentActiveObject = Instantiate(prefab, Vector3.zero, Quaternion.identity);
            // 可根据需求设置父节点或位置
            // currentActiveObject.transform.SetParent(yourSpawnPoint.transform);
        }
        else
        {
            Debug.LogError($"未找到预制体:{PrefabPath + prefabName}");
        }
    }

    private void UnloadCurrentObject()
    {
        if (currentActiveObject != null)
        {
            Destroy(currentActiveObject);
            currentActiveObject = null;
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    }
}

2. Addressable方式实现(推荐)

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;

public class AddressableObjectLoader : MonoBehaviour
{
    private GameObject currentActiveObject;
    private AsyncOperationHandle<GameObject> currentLoadHandle;

    public void LoadSceneAndObject(string sceneName, string prefabAddress)
    {
        SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single).completed += (op) =>
        {
            UnloadCurrentObject();
            LoadObjectFromAddressable(prefabAddress);
        };
    }

    private void LoadObjectFromAddressable(string prefabAddress)
    {
        currentLoadHandle = Addressables.InstantiateAsync(prefabAddress, Vector3.zero, Quaternion.identity);
        currentLoadHandle.Completed += (handle) =>
        {
            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                currentActiveObject = handle.Result;
            }
            else
            {
                Debug.LogError($"加载预制体失败:{prefabAddress}");
            }
        };
    }

    private void UnloadCurrentObject()
    {
        if (currentActiveObject != null)
        {
            Addressables.ReleaseInstance(currentActiveObject);
            currentActiveObject = null;
        }
        if (currentLoadHandle.IsValid())
        {
            Addressables.Release(currentLoadHandle);
        }
    }

    private void OnDestroy()
    {
        UnloadCurrentObject();
    }
}

3. 对象复用示例(基于Resources)

using UnityEngine;
using System.Collections.Generic;

public class ObjectPoolLoader : MonoBehaviour
{
    private Dictionary<string, GameObject> objectPool = new Dictionary<string, GameObject>();
    private GameObject currentActiveObject;
    private const string PrefabPath = "Prefabs/";

    public void LoadObject(string prefabName)
    {
        if (currentActiveObject != null)
        {
            currentActiveObject.SetActive(false);
        }

        if (objectPool.ContainsKey(prefabName))
        {
            currentActiveObject = objectPool[prefabName];
            currentActiveObject.SetActive(true);
            currentActiveObject.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
        }
        else
        {
            GameObject prefab = Resources.Load<GameObject>(PrefabPath + prefabName);
            if (prefab != null)
            {
                currentActiveObject = Instantiate(prefab, Vector3.zero, Quaternion.identity);
                objectPool.Add(prefabName, currentActiveObject);
            }
            else
            {
                Debug.LogError($"未找到预制体:{PrefabPath + prefabName}");
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 08:20:26