Unity架构之域重新加载

域重新加载

域重新加载将重置脚本状态,默认情况下会启用域重新加载。此功能为您提供了全新的脚本状态,并会在您每次进入运行模式时重置所有静态字段和已注册的处理程序。这意味着每次在 Unity Editor 中进入运行模式时,您的项目就会采用与在构建后首次启动时非常相似的方式开始运行。

域重新加载需要一定的时间,并且随着项目中脚本的数量和复杂性的增加,需要的时间也会增加。如果需要很长时间才能进入运行模式,那么对项目进行快速迭代将变得越来越困难。因此,Unity 提供了关闭“域重新加载”的选项。

禁用域重新加载

To disable Domain Reloading:

  1. Go to Edit > Project Settings > Editor
  2. Make sure Enter Play Mode Options is enabled.
  3. Disable Reload Domain

禁用域重新加载后,进入运行模式的速度会更快,因为 Unity 不会每次都重置脚本状态。但是,这种情况下就需要由您自己来确保在进入运行模式时脚本状态会重置。为此,需要添加代码以便在运行模式启动时重置脚本状态。

禁用域重新加载后,在您更新或重新导入脚本时,Unity 仍会根据自动刷新设置来刷新脚本状态。

修改脚本以确保在禁用域重新加载时正确执行:

为确保脚本状态在运行模式下正确重置,需要对脚本中的静态字段和静态事件处理程序进行调整。

静态字段

禁用域重新加载后,代码中的静态字段的值不会自动重置为其原始值。需要添加用于明确执行此操作的代码。

下面的代码示例具有一个静态计数器字段,在用户按下 Jump 按钮时该计数器会递增。启用域重新加载后,在进入运行模式时,计数器自动重置为零。禁用域重新加载后,计数器不会重置,而是会在进入和退出运行模式时保持其值不变。这意味着在 Editor 中第二次运行项目时,如果计数器在上一次运行中发生了变化,则计数器可能不会为零。

using UnityEngine;

public class StaticCounterExample : MonoBehaviour
{
// 禁用域重新加载时,此计数器不会重置为零
    static int counter = 0; 

    // 每帧调用一次 Update
    void Update()
    {
        if (Input.GetButtonDown("Jump"))
    {
    counter++;
    Debug.Log("Counter: " + counter);
    }
    }
}

要确保即使禁用了域重新加载,计数器也会重置,必须使用 [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 属性,并显式重置该值:

using UnityEngine;

public class StaticCounterExampleFixed : MonoBehaviour
{
    static int counter = 0;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void Init()
    {
    Debug.Log("Counter reset.");
    counter = 0;   
    }

    // 每帧调用一次 Update
    void Update()
    {
    if (Input.GetButtonDown("Jump"))
    {
    counter++;
    Debug.Log("Counter: " + counter);
    }
    }
}

静态事件处理程序

禁用域重新加载后,退出运行模式时,Unity 不会从静态事件处理程序中注销方法。如果您的代码会在静态事件处理程序中注册方法,则可能导致问题复杂化。例如,在 Editor 中第一次运行项目时会正常注册方法。但是,在第二次运行项目时,这些方法将在第一次注册之外再次注册,从而导致事件发生时将这些方法调用两次。

例如,以下代码在静态事件处理程序 Application.quitting 中注册了一个方法,并启用了域重新加载,当运行模式启动时,Unity 会自动重置事件处理程序,因此该方法只注册了一次。但是,在禁用域重新加载的情况下,不会清除事件处理程序,因此在 Editor 中第二次运行项目时,该方法将第二次注册,并在事件发生时被调用两次,但通常不希望发生这种情况。

using UnityEngine;
public class StaticEventExample : MonoBehaviour
{
    void Start()
    {
    Debug.Log("Registering quit function");
    Application.quitting += Quit;
    }


    static void Quit()
    {
    Debug.Log("Quitting!");
    }
}

禁用域重新加载后,上面的示例在您每次进入运行模式时都会再次添加 Quit 方法。这会导致每次退出运行模式时再次发出“Quitting”消息。

为确保即使禁用了域重新加载,事件处理程序也会重置,必须使用 [RuntimeInitializeOnLoadMethod] 属性,并显式注销该方法,以使其不会被添加两次。

using UnityEngine;
public class StaticEventExampleFixed : MonoBehaviour
{
    [RuntimeInitializeOnLoadMethod]
    static void RunOnStart()
    {
    Debug.Log("Unregistering quit function");
    Application.quitting -= Quit;
    }

    void Start()
    {
    Debug.Log("Registering quit function");
    Application.quitting += Quit;
    }

    static void Quit()
    {
    Debug.Log("Quitting the Player");
    }
}

对于运行时脚本,必须使用 [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 属性来重置静态字段和事件处理程序。

对于 Editor 脚本(比如自定义的 Editor 窗口或使用静态对象的 Inspector),必须使用 [InitializeOnEnterPlayMode] 属性来重置静态字段和事件处理程序。