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

Unity跨场景控制组件启禁报错:ArgumentException:场景无效

Fix: "ArgumentException: The scene is invalid" in Unity Scene Component Setup

First, let's break down why you're hitting this error: You're trying to access root GameObjects from "First Level" before the scene is loaded into memory. When you call SceneManager.GetSceneByName("First Level") prior to LoadScene, Unity returns an invalid scene instance because that scene hasn't been loaded yet—hence the "invalid scene" exception.

Here are two solid, practical solutions to fix this and achieve your goal of toggling components based on the button clicked:

Solution 1: Use Scene Load Callback with Mode Tracking

This approach uses a persistent flag (via PlayerPrefs) to store the selected game mode, then adjusts components automatically once the target scene finishes loading.

First, register a scene load event in your main menu script:

private void Start()
{
    // Hook into the scene loaded event to run post-load logic
    SceneManager.sceneLoaded += OnTargetSceneLoaded;
}

private void OnTargetSceneLoaded(Scene loadedScene, LoadSceneMode loadMode)
{
    // Only process the scene we care about
    if (loadedScene.name != "First Level") return;

    // Retrieve the stored game mode (1 = PVP, 0 = VS CPU)
    bool isPlayerVsPlayer = PlayerPrefs.GetInt("SelectedGameMode", 0) == 1;

    // Find the gameplay root object and adjust component states
    foreach (GameObject rootObj in loadedScene.GetRootGameObjects())
    {
        if (rootObj.CompareTag("--Gameplay"))
        {
            rootObj.GetComponentInChildren<StaticPlayerCtr_p2>().enabled = isPlayerVsPlayer;
            rootObj.GetComponentInChildren<AI>().enabled = !isPlayerVsPlayer;
            rootObj.GetComponentInChildren<UnityEngine.AI.NavMeshAgent>().enabled = !isPlayerVsPlayer;
        }
    }
}

// Clean up the event listener to prevent memory leaks
private void OnDestroy()
{
    SceneManager.sceneLoaded -= OnTargetSceneLoaded;
}

Then update your button click methods to just set the mode and trigger loading:

public void PlayerVsPlayer() 
{ 
    // Store that we're entering PVP mode
    PlayerPrefs.SetInt("SelectedGameMode", 1);
    SceneManager.LoadScene("First Level"); 
} 

public void VsCPU() 
{ 
    // Store that we're entering VS CPU mode
    PlayerPrefs.SetInt("SelectedGameMode", 0);
    SceneManager.LoadScene("First Level"); 
} 

public void QuitGame() 
{ 
    Application.Quit(); 
}

Solution 2: Async Scene Load with Post-Load Processing

If you prefer not to use PlayerPrefs, you can load the scene asynchronously and modify components immediately after it finishes loading:

using System.Threading.Tasks; // Don't forget this namespace for async/await

public async void PlayerVsPlayer() 
{ 
    // Start loading the scene in the background
    AsyncOperation loadOperation = SceneManager.LoadSceneAsync("First Level");

    // Wait until the scene is fully loaded into memory
    while (!loadOperation.isDone)
    {
        await Task.Yield();
    }

    // Now the scene is valid—access its root objects
    Scene targetScene = SceneManager.GetSceneByName("First Level");
    foreach (GameObject rootObj in targetScene.GetRootGameObjects())
    {
        if (rootObj.CompareTag("--Gameplay"))
        {
            rootObj.GetComponentInChildren<StaticPlayerCtr_p2>().enabled = true;
            rootObj.GetComponentInChildren<AI>().enabled = false;
            rootObj.GetComponentInChildren<UnityEngine.AI.NavMeshAgent>().enabled = false;
        }
    }
} 

// Repeat the pattern for the VsCPU button
public async void VsCPU() 
{ 
    AsyncOperation loadOperation = SceneManager.LoadSceneAsync("First Level");

    while (!loadOperation.isDone)
    {
        await Task.Yield();
    }

    Scene targetScene = SceneManager.GetSceneByName("First Level");
    foreach (GameObject rootObj in targetScene.GetRootGameObjects())
    {
        if (rootObj.CompareTag("--Gameplay"))
        {
            rootObj.GetComponentInChildren<StaticPlayerCtr_p2>().enabled = false;
            rootObj.GetComponentInChildren<AI>().enabled = true;
            rootObj.GetComponentInChildren<UnityEngine.AI.NavMeshAgent>().enabled = true;
        }
    }
} 

public void QuitGame() 
{ 
    Application.Quit(); 
}

Quick Optimization Tips

  • Replace hardcoded scene names and tags with constants (e.g., private const string TargetScene = "First Level";) to avoid typos and make updates easier.
  • Instead of using GetComponentInChildren every time, consider adding direct references to these components in a script attached to the --Gameplay object—this is faster and more reliable.
  • If you're using PlayerPrefs, add PlayerPrefs.Save() if you want the mode selection to persist between game sessions (not strictly necessary for in-session switching).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:54:22