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

Unity多场景加载:如何让Level_01的Start()在激活场景设置后执行?

解决Unity场景加载中Start()在场景激活前执行的问题

首先,咱们来拆解一下你遇到的问题根源:当你用LoadSceneAsync以Additive模式加载场景时,当AsyncOperation.isDone变为true的那一刻,Unity已经完成了场景的加载,会立即调用该场景内所有启用的MonoBehaviour的Start()方法——这一步发生在你调用SceneManager.UnloadSceneAsync和SceneManager.SetActiveScene之前,所以你的Level_01的Start()会在激活场景切换前就执行,导致后续的Update里的日志先打出StartScene,下一帧才切换到Level_01。

下面给你几个可行的解决方案,按推荐程度排序:

方案1:利用AsyncOperation.allowSceneActivation控制场景激活时机

这是最贴合Unity加载流程的方案,通过暂停场景的自动激活,先完成旧场景卸载和激活场景设置,再允许新场景激活,确保Start()在场景激活后执行。

修改你的SceneLoader脚本中的协程:

IEnumerator LoadSceneAsync(string sceneName) {
    Scene currentScene = SceneManager.GetActiveScene();
    
    // 加载新场景,但禁止自动激活
    AsyncOperation sceneLoadingOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
    sceneLoadingOperation.allowSceneActivation = false;

    // 等待加载进度到0.9(allowSceneActivation为false时,进度最高到0.9)
    while (sceneLoadingOperation.progress < 0.9f) {
        yield return null;
    }

    // 先卸载当前场景,确保资源清理完成
    yield return SceneManager.UnloadSceneAsync(currentScene);

    // 现在允许新场景激活
    sceneLoadingOperation.allowSceneActivation = true;

    // 等待场景完全激活
    while (!sceneLoadingOperation.isDone) {
        yield return null;
    }

    // 确保新场景被设为激活场景(这一步其实在allowSceneActivation设为true后会自动完成,但保险起见可以加上)
    SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName));
}

这样调整后,Level_01的Start()会在场景被正式激活后才触发,完全符合你的预期。

方案2:监听场景激活事件替代Start()

如果不想修改加载流程,可以在Level_01的脚本里放弃依赖Start(),转而监听Unity的场景激活事件,确保初始化逻辑只在场景激活后执行。

修改Level_01的脚本:

private void OnEnable() {
    // 注册场景激活事件
    SceneManager.sceneActivated += OnSceneActivated;
}

private void OnDisable() {
    // 取消注册,避免内存泄漏
    SceneManager.sceneActivated -= OnSceneActivated;
}

private void OnSceneActivated(Scene activatedScene, LoadSceneMode loadMode) {
    // 只处理当前场景激活的情况
    if (activatedScene.name == "Level_01") {
        Debug.Log("Level_01 start called");
        // 这里放置原来Start()中的初始化逻辑
    }
}

这种方式的好处是不需要改动加载管理器的代码,适合场景较多、不想统一修改加载流程的项目。

方案3:手动触发初始化方法

另一种思路是完全绕过Start(),在加载流程完成、场景激活后,手动调用场景内脚本的初始化方法。

首先给Level_01的脚本添加一个公开的初始化方法:

public void Init() {
    Debug.Log("Level_01 start called");
    // 原来Start()中的逻辑放到这里
}

然后修改SceneLoader的协程,在设置激活场景后手动调用:

IEnumerator LoadSceneAsync(string sceneName) {
    Scene currentScene = SceneManager.GetActiveScene();
    AsyncOperation sceneLoadingOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
    
    while(!sceneLoadingOperation.isDone) { 
        yield return null; 
    }

    yield return SceneManager.UnloadSceneAsync(currentScene);
    
    Scene newScene = SceneManager.GetSceneByName(sceneName);
    SceneManager.SetActiveScene(newScene);

    // 遍历新场景的所有根对象,找到需要初始化的脚本并调用方法
    foreach (GameObject rootObj in newScene.GetRootGameObjects()) {
        Level_01_Script[] levelScripts = rootObj.GetComponentsInChildren<Level_01_Script>(true);
        foreach (Level_01_Script script in levelScripts) {
            script.Init();
        }
    }
}

这个方案适合需要精确控制初始化时机的场景,比如需要传递参数给场景内的脚本时。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 12:17:39