Unity ECS System在什么时候更新?如何自定义这个更新的时机?

在什么时候更新?

在其他用户代码都执行完之后。

去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界面里就看不到了,也算是一个小缺点吧。

posted @ 2024-07-25 19:42  horeaper  阅读(55)  评论(0编辑  收藏  举报