---
title: "Unity场景管理和切换"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/unity-scene-management/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/05-游戏图形与运行时/Unity/Unity场景管理和切换.md"
content_hash: 7621b6fc5a9be046d0671d11ada9ef075eb4596c140fc7105651a5920bf28612
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# Unity场景管理和切换

> 创建时间：2020/8/18 14:13

  * Unity场景管理和切换
    * 关于yield return
    * API
      * SceneManager.LoadScene
        * 切换时相关Mono内存问题
      * LoadSceneMode
      * 其他
    * 示例
      * 进度条异步加载场景
      * 从AssetBundle加载场景
      * 分组加载卸载场景
    * Ref

## 关于yield return

对于yield return，目前我还无法深入理解其底层实现原理，无法看懂他编译成的IL代码。但是目前可以理解他的使用方法与机制。
yield return会每次返回后记录返回的位置，待等待的时间到了之后，从返回的位置继续往下执行。

## API

### SceneManager.LoadScene

When using SceneManager.LoadScene, the scene **loads in the next frame** , that is it does not load immediately. This **semi-asynchronous** behavior can cause frame stuttering and can be confusing because load does not complete immediately.

Because loading is set to complete in the next rendered frame, calling SceneManager.LoadScene forces all previous AsyncOperations to complete, even if AsyncOperation.allowSceneActivation is set to false.

> 如果场景名称重复，路径不同：
>  The given sceneName can either be the Scene name only, without the .unity extension, or the path as shown in the BuildSettings window still without the .unity extension. If only the Scene name is given this will load the first Scene in the list that matches. If you have multiple Scenes with the same name but different paths, you should use the full path.

#### 切换时相关Mono内存问题

场景切换的时候：Load a scene non-additively. This will destroy all Objects in the current scene and invoke `Resources.UnloadUnusedAssets` automatically.
Destroy掉前一个场景中的GameObject，如果GameObject上挂载的Monobehaviour没有被其他地方引用的话，调用GC.Collect()会回收掉他占用的内存。

虽然GameObject被Destroy掉了， 如果Monobehaviour被其他在场景切换时无法销毁的对象所引用，那么其Mono层的对象是无法被回收的。比如：MonoBehaviour类中的某个函数注册了SceneManager类的sceneLoaded事件，那么这个对象就被SceneManager静态地引用了，该对象就无法销毁，Mono层的内存就无法释放。

### LoadSceneMode

(TODO: 是否会自动卸载，有待验证)
使用Single模式，关闭已经加载的所有场景（不是卸载，卸载是另一个函数 —— UnloadSceneAsync()），只加载一个新场景，新场景被添加到SceneManager的目录中。

### 其他

SetActiveScene() 激活已加载的场景，如果场景未加载，返回false。

GetActiveScene() 获取已激活的场景。

CreateScene() 运行时创建一个新场景。

MergeScenes(SceneManagement.Scene sourceScene, SceneManagement.Scene destinationScene) 将源场景的内容合并到目标场景中，并删除源场景。 源场景根目录下的所有游戏对象都将移动到目标场景的根目录。需要注意的是，该函数具有破坏性 —— 合并完成后，源场景将被销毁。

MoveGameObjectToScene()
将GameObject从其当前场景移动到新场景。
只能将根游戏对象从一个场景移动到另一个场景。 这意味着要移动的GameObject不能是其场景中任何其他GameObject的子对象。 这仅适用于将GameObjects移动到已加载的场景（LoadSceneMode.Additive）。 如果要加载单个场景，请确保在要移动到新场景的GameObject上使用DontDestroyOnLoad，否则Unity会在加载新场景时删除它。

UnloadSceneAsync()
销毁与给定场景关联的所有GameObject，并从SceneManager中移除场景。给定的场景名称可以是完整的场景路径，“构建设置”窗口中显示的路径，也可以是场景名称。
注意：

  1. 由于它是异步的，因此无法保证完成时间。

  2. 资产目前尚未卸载。 为了释放资产内存，可以调用 Resources.UnloadUnusedAssets() 。

  3. 如果没有要加载的场景，则无法使用 UnloadSceneAsync() 。

## 示例

### 进度条异步加载场景

AsyncOperation的progress（0-1）属性在allowSceneActivation 为false时，最大加载到0.9就会暂停，直到allowSceneActivation 为true时才会继续加载0.9-1.0的这10%。
直到progress = 1.0时 isDone = true。

> 在Unity编辑器模式下是看不出来进度条正常变化的，只有导出项目后才能看到正常进度条

```csharp
class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;
        dyn = dyn + 3;
        //obj = obj + 3; 报错

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

```

对于LoadSceneAsync，如果不使用yield return，也会在后台线程自动完成异步加载，只是无法一帧一帧获取当前的加载进度，无法实现加载完之后调用的逻辑。
通过while+yield return的方式，是为了保证每帧执行一次while中的语句块，从而监控加载过程，并在加载结束后触发相应的逻辑。

### 从AssetBundle加载场景

```csharp
class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;
        dyn = dyn + 3;
        //obj = obj + 3; 报错

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

```

### 分组加载卸载场景

```csharp
class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;
        dyn = dyn + 3;
        //obj = obj + 3; 报错

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

```

## Ref

<https://blog.csdn.net/Ha1f_Awake/article/details/93319307>
