Hello,
I am current working on an RTS game using the ECS.
I came across a few interesting topics which i solved more or less hacky and i’d like to hear if there are better ways to do it.
The first thing is gaining fine-grained control of the world updates, in my game i got 2 worlds - one for simulation and one for rendering.
The issue i am facing however is that i want to invoke the ECS updates from inside my own deterministic fixed lockstep implementation, which also should support being paused (stop invoking the ECS, in order to for example resync the game after a hash mismatched, more on hashing later) and be executed at fixed framerates.
What i am doing right now is creating my simulation world and keeping track of all created managers like this:
systems.Add(this.simulationWorld.CreateManager<UnitMovementCS>());
systems.Add(this.simulationWorld.CreateManager<UnitCommandMoveCS>());
I disabled the player loop update for my simulation world only by calling ScriptBehaviourUpdateOrder.UpdatePlayerLoop(new World[ ] { renderingWorld });
At the point where i want to update the ECS i then call ScriptBehaviourManager.Update() on all previously created managers.
foreach (var system in this.systems)
system.Update();
This works, but it doesnt feel quite right and i cant imagine thats the intended way of keeping manual update control.
It would be very nice to have some World.Update() method that updates / ticks the world and also properly handles UpdateAfter / UpdateBefore / UpdateInGroup without being forced to execute the ECS updates in the player loop. Is something like this planned or are we supposed to update the systems ourselves?
Another topic i came across is verifying multiple game states are in sync.
In order to do this im hashing my game state into one hash that is exchanged between sessions and used to test if the own hash matches the others.
Right now im hashing quite some management stuff outside of ECS, but thats not an issue. The issue is hashing the ECS data (which is a lot, that was the point i looked into ECS after all :P) is quite inefficient this way:
var em = ECSUtil.simulationEntityManager;
ComponentDataFromEntity<UnitData> ud = em.GetComponentDataFromEntity<UnitData>(true);
ComponentDataFromEntity<UnitMovementData> umd = em.GetComponentDataFromEntity<UnitMovementData>(true);
ComponentDataFromEntity<DeterministicRenderTransformData> dtd = em.GetComponentDataFromEntity<DeterministicRenderTransformData>(true);
foreach (var unit in this._units)
{
unsafe
{
var entity = unit.entity;
if (ud.Exists(entity))
{
var _ud = ud[unit.entity];
context.HashCRC32("UnitData", (byte*)&_ud, sizeof(UnitData));
}
if (umd.Exists(entity))
{
var _umd = umd[unit.entity];
context.HashCRC32("UnitMovementData", (byte*)&_umd, sizeof(UnitMovementData));
}
if (ud.Exists(entity))
{
var _dtd = ud[unit.entity];
context.HashCRC32("UnitTransformData", (byte*)&_dtd, sizeof(DeterministicRenderTransformData));
}
}
}
This results in pretty bad timings however, see this profiler screenshot:
It would be very nice if we could either directly recieve byte array references or pointers on the locations where ECS stores its data, or even better if the ECS itself could hash all data for us.
Is something like this planned or is there a better approach?
I have been using the ECS intensively now for some days and checked out most of the features and i have to say, so far it has been a really interesting way of solving problems.
Apart from those 2 things i explained above i didnt encounter any issues at all with the ECS (I would just be happy about a bit more documentation, even if just C# summaries). It works, performance is insane and it seems perfect for projects like RTS games