ME.ECS - it's ECS implementation for Unity Engine with full state automatic rollbacks.
In general ME.ECS should be used for multiplayer real-time strategy games games because of Network support out of the box with automatic rollbacks. You can set up tick time for your game and system should store your state and automatically sync game instances using minimum traffic (just user RPC calls, no full game sync required).
Store states checkpoints and all added events sorting by custom order. Can simulate world tick by tick to restore state with deterministic logic.
Send RPCs through any network transport implemented from the interface, serialize and deserialize data. By default is the EventRunner for StatesHistoryModule, just send all incoming events to the network and receive events from transport and send them into StatesHistoryModule.
Synchronizing current world state to views. Automatically destroy and create views (with pools), sync with current entities state and process all ticks correctly to restore visual state even objects already destroyed for a long time ago.
Module just show FPS/FPSMax/FPSMin in world viewer.
The container for all components like modules, systems, etc. You can store multiple worlds with different states, entities, components, modules and systems.
User-side data storage. Just need to implement a couple of methods:
int GetHash() // If possible returns the most unique hash
void Initialize(IWorld<State> world, bool freeze, bool restore) // Register all filter and component storages in the world
void CopyFrom(State other) // copies other state into current
void OnRecycle() // return all used resources into pools
Modules do visual update on the beginning of the frame and on the beginning of every tick.
Systems do visual update at the end of the frame and on the ending of every tick.
Entities are storing base data of your objects like position, rotation, user data, etc.
Components are working with a certain Entity type and implements AdvanceTick (in which you can add logic of your tick) and CopyFrom. Or you can use components like a markers. Btw, you can use IComponentOnce interface to be sure all components removed at the end of current tick.
Also you can add IComponentShared
and IComponentSharedOnce
to store any shared data.
Markers needed to implement UI events or something that doesn't exist in game state.
// Initialize new world with custom tick time and custom world id
// If customWorldId ignored - it will setup automatically
WorldUtilities.CreateWorld(ref this.world, 0.133f, [customWorldId]);
this.world.AddModule<StatesHistoryModule>(); // Add custom states history module
this.world.AddModule<NetworkModule>(); // Add custom network module
// Create new state and set it by default
this.world.SetState(WorldUtilities.CreateState<State>());
// Register point source prefab with custom views provider
// GameObject (Will call Instantiate/Destroy)
this.pointViewSourceId = this.world.RegisterViewSource<Point, UnityGameObjectProvider>(this.pointSource);
// Register unit source prefab with custom views provider
// Particles (Will draw particles instead of regular GameObjects)
this.unitViewSourceId = this.world.RegisterViewSource<Unit, UnityParticlesProvider>(this.unitSource);
// Register unit source prefab with auto views provider
// Here provider should be choosen by unitSource2 type
this.unitViewSourceId2 = this.world.RegisterViewSource<Unit>(this.unitSource2);
...
// Create default data for all players at this level
var p1 = this.world.AddEntity(new Point() {
position = new Vector3(0f, 0f, 3f),
unitsCount = 99f,
increaseRate = 1f
});
var p2 = this.world.AddEntity(new Point() {
position = new Vector3(0f, 0f, -3f),
unitsCount = 1f,
increaseRate = 1f
});
...
// Attach views onto entities
// You can attach any count of views on each entity
// But here are some limitations: for now you couldn't attach one source twice, only different sources for one entity allowed.
this.world.InstantiateView<Point>(this.pointViewSourceId, p1); // Add view with id pointViewSourceId onto p1 Entity
this.world.InstantiateView<Point>(this.pointViewSourceId, p2); // Add view with id pointViewSourceId onto p2 Entity
...
// Add custom systems
this.world.AddSystem<InputSystem>();
this.world.AddSystem<PointsSystem>();
this.world.AddSystem<UnitsSystem>();
...
// Save current world state as a Reset State.
// It's very important to do after the scene loaded and all default entities were set.
// Sure after that you could run any of API methods, but be sure you call them through RPC calls.
this.world.SaveResetState();
- Implement automatic states history with rollback system (100% done)
- Decrease initialization time and memory allocs (90% done)
- Random support to generate random numbers, store RandomState in game state (100% done)
- Add full game example (80% done)
- Add auto sync on packets drop (TCP) (100% done)
- Add auto sync on packets drop (UDP) (20% done)
- Views module (100% done)
- Implement UnityGameObjectProvider (100% done)
- Implement UnityParticlesProvider (95% done) - MeshFilter/MeshRenderer support, inner ParticleSystem effects support, but rewind is not fully implemented.
- Implement UnityDrawMeshProvider (90% done) - only MeshFilter/MeshRenderer support added
- Add particle system simulation support on state change (100% done)
- Add shared components support (100% done)
- Add multithreading support (15% done)
- Preformance refactoring (70% done)