Unity ECS System在什麼時候更新?如何自定義這個更新的時機?

horeaper發表於2024-07-25

在什麼時候更新?

在其他使用者程式碼都執行完之後。

去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分別獲得InitializationSystemGroupSimulationSystemGroupPresentationSystemGroup;然後作為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介面裡就看不到了,也算是一個小缺點吧。

相關文章