在什麼時候更新?
在其他使用者程式碼都執行完之後。
去Netcode的ClientServerBootstrap
裡可以找到CreateLocalWorld
函式,裡面有類似這樣的程式碼:
public static World CreateLocalWorld(string defaultWorldName = "Default World")
{
var world = new World(defaultWorldName, WorldFlags.Game);
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);
ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
return world;
}
其中ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop
就是將World更新附加到UnityEngine.LowLevel.PlayerLoop
後面的函式。這個函式內容也很簡單,本質上是透過world.GetExistingSystemManaged
分別獲得InitializationSystemGroup
,SimulationSystemGroup
和PresentationSystemGroup
;然後作為PlayerLoopSystem
的SubSystem,構造出新的PlayerLoop之後,透過PlayerLoop.SetPlayerLoop
應用到當前環境裡。
沒有Netcode的情況下也是類似的,只不過是透過DefaultWorldInitialization
這個類來完成了。
如何自定義?
知道在哪裡建立的,自然就知道該怎麼自定義了。只要把ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop
換成自己的東西就行了。比如:
var initGroup = world.GetExistingSystemManaged<InitializationSystemGroup>();
var simGroup = world.GetExistingSystemManaged<SimulationSystemGroup>();
var presGroup = world.GetExistingSystemManaged<PresentationSystemGroup>();
//找個地方呼叫initGroup.Update()
//找個地方呼叫simGroup.Update()
//找個地方呼叫presGroup.Update()
//順序不要搞錯了
以FishNet為例,就可以在TimeManager.OnPostTick
裡執行所有WorldSystemFilterFlags.ServerSimulation
的System,於是這些基於GameObject的網路庫也就可以用ECS設計模式了。
不過自定義了更新之後,在Editor的Systems介面裡就看不到了,也算是一個小缺點吧。