Unity如何在场景加载后、Awake/OnEnable前调用函数?
解决方案
最优方案:异步加载+控制场景激活时机
这是最可靠且无需重复配置的方法,利用Unity异步加载的特性,在场景资源加载完成但未激活时执行存档加载,确保逻辑优先于所有对象的Awake和OnEnable:
using System.Threading.Tasks; using UnityEngine.SceneManagement; public class SceneLoader : MonoBehaviour { private SaveSystemManager saveSystemManager; private void Awake() { saveSystemManager = GetComponent<SaveSystemManager>(); // 标记为 DontDestroyOnLoad,跨场景保留 DontDestroyOnLoad(gameObject); } public async void LoadTargetScene(int sceneIndex, LevelLoadParameters levelLoadParamaters) { // 加载前存档(如果需要) if (levelLoadParamaters.saveGameDataBeforeLoad) { saveSystemManager.Save(); } // 异步加载场景,暂停激活流程 AsyncOperation loadOperation = SceneManager.LoadSceneAsync(sceneIndex); loadOperation.allowSceneActivation = false; // 等待资源加载完成(进度到0.9f是Unity异步加载的临界点,代表资源已全部加载) while (loadOperation.progress < 0.9f) { await Task.Yield(); } // 执行存档加载逻辑,此时新场景对象已加载但未激活,Awake/OnEnable尚未触发 if (levelLoadParamaters.loadGameDataAfterLoad) { saveSystemManager.Load(); } // 激活场景,触发所有对象的Awake/OnEnable loadOperation.allowSceneActivation = true; } }
关键逻辑说明
allowSceneActivation = false会让场景加载停在资源加载完成的阶段,此时新场景的所有对象已存在于内存,但未被激活,Awake和OnEnable不会执行。- 进度到0.9f是Unity的内置机制,代表场景资源已全部加载完成,剩下的10%进度对应场景激活步骤。
- 执行完存档加载后再激活场景,就能保证所有对象的初始化逻辑基于最新的存档数据。
备选方案:全局初始化管理器+脚本执行顺序
如果不想用异步加载,可以创建一个全局的初始化管理器:
- 创建一个空对象,添加初始化脚本,标记为
DontDestroyOnLoad。 - 在Project Settings -> Script Execution Order中,将该脚本的执行顺序设为所有脚本的最顶端(数值越小越先执行)。
- 在脚本的
Awake方法中监听SceneManager.activeSceneChanged事件,当场景切换完成后立即执行存档加载:
using UnityEngine.SceneManagement; public class GlobalInitializer : MonoBehaviour { private SaveSystemManager saveSystemManager; private LevelLoadParameters currentLoadParams; private void Awake() { saveSystemManager = GetComponent<SaveSystemManager>(); DontDestroyOnLoad(gameObject); SceneManager.activeSceneChanged += OnSceneChanged; } // 调用此方法触发场景加载 public void StartSceneLoad(int sceneIndex, LevelLoadParameters parameters) { currentLoadParams = parameters; if (currentLoadParams.saveGameDataBeforeLoad) { saveSystemManager.Save(); } SceneManager.LoadScene(sceneIndex); } private void OnSceneChanged(Scene oldScene, Scene newScene) { if (currentLoadParams?.loadGameDataAfterLoad ?? false) { saveSystemManager.Load(); currentLoadParams = null; } } }
这个方案的核心是利用脚本执行顺序,让全局管理器的Awake(以及事件回调)优先于场景内其他对象的Awake执行。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

