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
相关产品推荐
相关产品推荐

