Unity反复切换加载场景耗时逐次暴增问题排查
问题根因
这是Unity开发里非常典型的场景切换资源泄漏问题,和你贴的Profiler采样特征完全匹配,核心问题按影响大小排序:
- 常驻对象重复堆叠,没有做单例校验
你用LoadSceneMode.Single切场景时,只有标记了DontDestroyOnLoad的物体会保留,其余场景物体会被正常卸载。从GameObject.Activate调用次数随切换次数指数级上涨、单次加载GC冲到3GB的特征看,你肯定给玩家、交互按钮这类跨场景常驻对象加了DontDestroyOnLoad,但主场景、所有房屋场景里都预先放置了这些对象的实例,也没有做重复实例销毁逻辑:切一次场景就会多留一份常驻对象,切十几次后物体总量就会堆到几万,加载时激活所有物体的耗时自然会涨到20秒以上。你采样到的81920次Activate调用,就是十几次切换后冗余物体堆出来的量级。 - 场景加载事件逻辑写错,状态标记完全失效
你代码里写了OnSceneLoaded回调想重置加载状态,但既没有把这个方法注册到SceneManager.sceneLoaded事件上,也没有在加载协程结束后重置状态。更糟的是isLoading标记是挂在每个门组件上的,每次场景加载后门都是新生成的实例,这个标记根本挡不住重复加载。 - 冗余初始化逻辑拉高场景加载开销
每个门组件在Start里都调用FindGameObjectWithTag找玩家、找交互按钮,场景里门多的时候,这部分查找开销也会随物体总量上涨被放大。
修复方案
按顺序改完就能彻底解决:
- 给所有跨场景常驻对象(玩家、全局UI、全局管理器)加单例校验,从根源解决物体堆叠问题
在常驻对象的Awake生命周期里加判断:如果场景里已经存在同类型的常驻实例,直接销毁当前对象,只保留唯一实例,再对唯一实例调用DontDestroyOnLoad。参考实现:
改完可以反复切几次场景,看Hierarchy面板里玩家、交互按钮这类对象永远只有1个,不会随切换次数增加。private void Awake() { // 以玩家为例,其他常驻对象逻辑相同 GameObject existPlayer = GameObject.FindGameObjectWithTag("Player"); if (existPlayer != null && existPlayer != gameObject) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } - 修正异步加载逻辑,去掉无效的事件回调
你现在不需要靠sceneLoaded事件重置状态,直接在加载协程末尾把加载标记复位就行,同时加全局防重复触发判断:
如果后续确实要用到IEnumerator LoadYourAsyncScene(string sceneName) { if (isLoading) yield break; isLoading = true; AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName); while (!asyncLoad.isDone) { yield return null; } // 加载完成直接复位状态 isLoading = false; }sceneLoaded事件,记得在OnEnable里注册、OnDisable里注销,不要留事件泄漏。 - 去掉冗余的查找逻辑
把玩家、交互按钮的引用存在全局管理器里,所有门组件直接从管理器拿引用,不要每个门都在Start里做全局查找;把Update里每帧执行的IsTouching判断换成OnTriggerStay2D碰撞回调,减少日常运行的无效开销。
验证标准
改完后连续切换场景20次以上,看Profiler中加载阶段的GameObject.Activate次数稳定在当前场景固有物体量级(通常几十到几百,不会涨到几万),加载阶段GC开销稳定在百MB级,切换耗时稳定在1秒内即为正常。
内容的提问来源于stack exchange,提问作者narnia649
相关产品推荐
相关产品推荐

