运行模式是Unity使用过程中的核心要素。随着Unity项目变得更加复杂,进入运行模式会需要更多的时间。

进入和退出运行模式的速度越快,意味着开发者进行关卡修改和测试的速度也就越快。我们在Unity 2019.3 Beta版中推出一项实验性功能:Configurable Enter Play Mode运行模式启用可配置化

Unity 自启动时间长 unity点击运行自动暂停_运行模式

当编辑器进入运行模式时,Unity会执行两件事情:重设脚本状态,即进行Domain Reload域重新加载,以及重新加载场景。

这里会花费不少时间,并且项目越复杂,在运行模式中测试新改动的等待时间就更长。在Unity 2019.3 Beta版中,我们可以选择禁用“域重新加载”或“场景重新加载”,也可以同时禁用两者。

根据我们的测试结果,该功能可以节省多达50-90%的等待时间。

我们在一款AA级游戏、FPS示例项目、《Megacity》演示项目和空白项目中,测试了Configurable Enter Play Mode功能的效果。下图展示了在这4个项目中,编辑器进入运行模式的时间,数字越小,代表效果越好。

Unity 自启动时间长 unity点击运行自动暂停_Unity 自启动时间长_02

点击File > Project Settings > Editor,我们找到Enter Play Mode Options(启用运行模式选项),并可以选择是否启用Reload Domain(重新加载域)和Reload Scene(重新加载场景)。

Unity 自启动时间长 unity点击运行自动暂停_重置_03

这些选项允许我们在代码没有改动的情况下,从Enter Play Mode进程中禁用域或场景的重新加载。如果想要在进入运行模式前重置游戏状态,我们也可以通过API和回调来启用这项功能。

下图展示了禁用域重新加载和重新加载场景前后,启用运行模式流程的区别。

Unity 自启动时间长 unity点击运行自动暂停_unity3d运行后自动暂停_04

 

了解如何配置运行模式,请访问:

https://docs.unity3d.com/2019.3/Documentation/Manual/ConfigurableEnterPlayMode.html

了解Unity启用运行模式过程中的更多细节,请访问:

https://docs.unity3d.com/2019.3/Documentation/Manual/ConfigurableEnterPlayModeDetails.html 

小提示:该功能仍处于实验阶段,并非所有Unity资源包都可以在禁用域和场景重新加载的情况下继续使用。

禁用域重新加载后,如何正确修改脚本

避免重新加载域非常简单,但也会带来一些问题。我们需要调整静态字段和静态事件处理器,来确保在进入运行模式时脚本状态能够被正确地重置。

下面的代码示例中有一个计数器,当玩家按下跳跃按键时,则计数器的数字会增加。当启用域重新加载时,计数器会在进入运行模式时自动重置为0。

然而当禁用域重新加载后,计数器不会重置。计数器会在进入和退出运行模式时保留当前数值。这意味着在编辑器中第二次运行项目时,如果计数器在前一次运行中有数值改动,那么第二次运行时,计数器可能不会从0开始。

public class StaticCounterExample : MonoBehaviour
{
// 该计数器在禁用域重新加载时不会重置为0
       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不会在退出运行模式时注销静态事件处理器上的方法。如果代码中将方法注册到静态事件处理器上,这会让情况变得复杂。

例如:在编辑器中第一次运行项目时,方法会被正常注册。然而,在第二次运行项目时,这些方法会在第一次注册的基础上再注册一次,于是在触发事件时,方法会调用两次。

下面的代码会在静态事件处理器Application.quitting上注册一个方法。

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");
       }
}