在什么时候更新?
在其他用户代码都执行完之后。
去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界面里就看不到了,也算是一个小缺点吧。