介紹
你是否會覺得有時候進入運行模式的時候非常慢壶谒,這是因為 Unity 需要保證你每次進入游戲的時候都是一樣的膳沽,即使在退出游戲的時候你沒有進行還原操作。當(dāng)然這只是一部分原因陨界,還有 Start 內(nèi)邏輯復(fù)雜或者腳本過多等原因痛阻。下面介紹如果減少 Unity 自己的初始化操作阱当,下面內(nèi)容基本翻譯于 Configurable Enter Play Mode 和 Domain Reloading。
進入運行模式的配置
??運行模式可以讓你的項目直接在編輯器中運行录淡,運行效果跟你將它打包出來之后的運行效果一樣油坝,在運行模式你所做的修改在退出該模式之后都會被還原(這里只包括場景中的修改,如果你在代碼中修改了一個資源彼水,比如一個材質(zhì)球的基色凤覆,那么不會被還原)。
??這是因為在開始運行之后慈俯,Unity 做了兩件事情來保證它看起來是跟打包之后運行的效果一樣:
- 它重置了所有的腳本狀態(tài)(也叫作域重載)
- 它重新加載了這個場景
??這兩個操作會消耗一些時間拥峦,并且當(dāng)你的腳本和場景更復(fù)雜的時候略号,它們耗時也更長。
??提高快速進入突梦、退出運行模式的能力在開發(fā)中是非常重要的羽利。更快的進入、退出運行模式也意味著快速的測試項目中的改動娃闲。
??因為在開發(fā)中快速迭代是非常重要的匾浪,但這種重置有可能成為速度的瓶頸蛋辈,所以 Unity 提供了一種方式可以讓開發(fā)者自己選擇在進入運行模式的時候要不要域重載或者場景重載。
??下面一張圖描述了禁用域重載或者場景重載的影響:
如何配置
在 2019.3 (或許更低也可以,我試過 2018.4挂洛,是沒有這個選項的)以上的 Unity 版本中眠砾,找到 Edit->Project Settings->Editor->Enter Play Mode Settings。
- 三個選擇框都不勾励堡,其實相當(dāng)于將三個都勾堡掏,也就是會啟用域重載和場景重載
- 只勾選第一個泉唁,則代表不啟用域重載和場景重載
- 勾選第一個,下面兩個二選一則代表只啟用一種重載
這個功能還在試驗階段扮休,后面還有可能會進行改動拴鸵。
域重載
??域重載重置你的腳本狀態(tài),它默認(rèn)是啟用的(上面三個勾默認(rèn)是都不勾)八堡。它會提供一個完全干凈的腳本初始化狀態(tài)秕重,會重置所有的靜態(tài)變量厉膀,將所有 Handler 里面注冊的方法都清空。這樣可以保證在編輯器中每一次運行都跟將項目打包出來后第一次運行一樣凳兵。
??這會耗費一定時間企软,隨著場景和腳本的復(fù)雜程度增加仗哨,時間也會增加,不利于項目的快速迭代萨醒,所以 Unity 提供了關(guān)閉域重載的方法苇倡。
??當(dāng)關(guān)閉了域重載之后,進入運行模式的速度會變快晓褪,因為 Unity 不會再做初始化操作涣仿。這時就需要你自己手動完成初始化,你需要自己添加一些代碼來保證腳本中某些狀態(tài)在運行時會被還原埃元。
??當(dāng)域重載被禁用時媚狰,當(dāng)你更新腳本或者重新導(dǎo)入時 Unity 依然會根據(jù)你的 auto-refresh settings 刷新腳本狀態(tài)崭孤。
在禁用域重載之后該做什么?
??你需要調(diào)整腳本中的靜態(tài)變量和靜態(tài)事件 Handler 來保證在剛進入運行模式的時候腳本狀態(tài)是正確的遗锣。
靜態(tài)變量
??當(dāng)域重載被禁用的時候嗤形,靜態(tài)變量的值將不會自動恢復(fù)成原來的值,你需要明確的寫出讓它恢復(fù)的代碼笔咽。
??以下面的代碼為例叶组,其中有一個靜態(tài)的變量 counter
历造,當(dāng)用戶按下了 Jump 鍵的時候它會自增。當(dāng)沒有禁用域重載的時候侣监,每次運行它的值都是 0橄霉;當(dāng)禁用域重載的時候荒典,每次運行時它的值都是上一次運行結(jié)束時的值。
using UnityEngine;
public class StaticCounterExample : MonoBehaviour
{
// this counter will not reset to zero when Domain Reloading is disabled
static int counter = 0;
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Jump"))
{
counter++;
Debug.Log("Counter: " + counter);
}
}
}
?? 為了保證即使在禁用了域重載的時候覆糟,counter
依然能被正確的歸零滩字,我們要使用 [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
標(biāo)簽來明確的對 counter
初始化御吞。
using UnityEngine;
public class StaticCounterExampleFixed : MonoBehaviour
{
static int counter = 0;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void Init()
{
Debug.Log("Counter reset.");
counter = 0;
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Jump"))
{
counter++;
Debug.Log("Counter: " + counter);
}
}
}
靜態(tài)事件處理器
??這里的靜態(tài)事件處理器是由 Static event handlers
翻譯而來的陶珠。舉個例子,就是 public static event Action TestActionHandler;
??當(dāng)禁用域重載的時候诀蓉,Unity 不會在運行模式退出的時候把靜態(tài)事件處理器里面已經(jīng)注冊的方法刪除渠啤。這會導(dǎo)致回調(diào)函數(shù)的多次執(zhí)行添吗,比如在第一次運行的時候碟联,回調(diào)函數(shù)被正常注冊,第二次運行的時候玄帕,處理器(Handler)里面已經(jīng)有這個回調(diào)函數(shù)了裤纹,但它又被注冊了一遍,所以當(dāng)處理器去廣播的時候(也就是 Handler?.Invoke()
的時候)锡移,該回調(diào)函數(shù)會被執(zhí)行兩遍漆际。
??以下面的代碼為例奸汇,我們在 Start
里面給 Application.quitting
注冊了一個回調(diào)函數(shù)往声,注意這里的 Application.quitting
是靜態(tài)的浩销,它的聲明如下:
public static event Action quitting;
??當(dāng)域重載沒被禁用的時候听哭,Unity 會在運行的時候自動清空 Application.quitting
陆盘,所以每次運行回調(diào)函數(shù)都只會被注冊一次普筹。
??當(dāng)域重載被禁用的時候,Application.quitting
將不會被自動清空隘马√溃看下面的例子,第一次運行結(jié)束的時候祟霍,Quitting
會被打印一次杏头;第二次運行結(jié)束的時候 Quitting
會被打印兩次......但實際上我們都希望每次運行結(jié)束后, Quitting
都只會被打印一次沸呐。
using UnityEngine;
public class StaticEventExample : MonoBehaviour
{
void Start()
{
Debug.Log("Registering quit function");
Application.quitting += Quit;
}
static void Quit()
{
Debug.Log("Quitting!");
}
}
??為了保證即使在域重載被禁用的情況下醇王,我們也能正確的使用靜態(tài)事件處理器,我們需要使用 [RuntimeInitializeOnLoadMethod]
標(biāo)簽崭添,并且明確的寫出要刪除已經(jīng)被注冊的方法。用法如下:
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");
}
}
??對于運行時腳本(非編輯器相關(guān)),你需要使用 [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
來手動初始化靜態(tài)變量和靜態(tài)事件處理器呼渣。
??對于編輯器腳本棘伴,你需要使用 [InitializeOnEnterPlayMode]
來初始化靜態(tài)變量或者靜態(tài)事件處理器。
筆者補充
static void Quit()
{
Debug.Log("Quitting!");
}
??上面例子中的 Quit
這個回調(diào)方法也是靜態(tài)的屁置,但這并不影響焊夸,加不加這個靜態(tài)結(jié)果都一樣,依然會被注冊多次蓝角,因為起決定性作用是 Application.quitting
是靜態(tài)的阱穗。
??它的聲明是 public static event Action quitting;
,如果變成 public event Action quitting;
那么就不會被注冊多次使鹅。