It happend that way that grouping ECS system sets by so-called 'features' considered a good practice to organize your code to make it more scalable and maintainable. Unfortunately, some ECS frameworks are lacking of this must-have feature, so this tiny project is aims to solve this issue
- First of all, you need to implement the
IEcsFeatureRunner
interface. An easy way to do that is to derive from theEcsFeatureRunner
abstract class, which already implementsIEcsFeatureRunner
and contains all the basic functionality:
public class MyFeatureRunner : EcsFeatureRunner
{
}
...
private IEcsFeatureRunner _featureRunner;
...
_featureRunner = new MyFeatureRunner();
- Add all your features:
_featureRunner.AddFeature(new HealthFeature())
.AddFeature(new MovementFeature())
.AddFeature(new NetworkFeature());
- Call
EcsFeatureRunner
callbacks in your environment. For Unity, here's an example:
private void Start()
{
_featureRunner.Init();
}
private void Update()
{
_featureRunner.Update();
}
private void FixedUpdate()
{
_featureRunner.PhysicsUpdate();
}
private void OnDestroy()
{
_featureRunner.Destroy();
}
To create a feature, implement some of the following interfaces:
IEcsInitFeature
: Contains anInit
method called onEcsFeatureRunner.Init()
.IEcsUpdateFeature
: Contains anUpdate
method called onEcsFeatureRunner.Update()
.IEcsPhysicsUpdateFeature
: Contains aPhysicsUpdate
method called onEcsFeatureRunner.PhysicsUpdate()
.IEcsDestroyFeature
: Contains aDestroy
method called onEcsFeatureRunner.Destroy()
.
Extend your implementation of EcsFeatureRunner
with your own feature types if you need more types of callbacks.
Some ECS frameworks share similarities in their API design. They typically offer a class for managing the game world and another class for handling component storage. For instance, both LeoECS Lite and Morpeh provide EcsWorld
and World
as the game world classes, and IEcsPool
and Stash
as component storage.
To enhance the convenience of working with these frameworks, you can create features more comfortably by utilizing generic versions of the IEcsFeature
interfaces. These interfaces include additional fields that contribute to an improved development experience.
- Feature interfaces:
using Leopotam.EcsLite;
public interface ILeoLiteEcsInitFeature : IEcsInitFeature<EcsWorld>
{
}
public interface ILeoLiteEcsUpdateFeature : IEcsUpdateFeature<EcsWorld, IEcsSystems>
{
}
public interface ILeoLiteEcsPhysicsUpdateFeature : IEcsPhysicsUpdateFeature<EcsWorld, IEcsSystems>
{
}
public interface ILeoLiteEcsDestroyFeature : IEcsDestroyFeature<EcsWorld>
{
}
- Example of feature:
public class LeoEcsFeature : ILeoLiteEcsInitFeature, ILeoLiteEcsUpdateFeature, ILeoLiteEcsPhysicsUpdateFeature, ILeoLiteEcsDestroyFeature
{
public EcsWorld World { get; set; } // provided by generic IEcsFeature
public IEcsSystems UpdateSystems { get; set; } // provided by generic IEcsUpdateFeature
public IEcsSystems PhysicsUpdateSystems { get; set; } // provided by generic IEcsPhysicsUpdateFeature
public void Init()
{
World = new EcsWorld();
UpdateSystems = new EcsSystems(World);
PhysicsUpdateSystems = new EcsSystems(World);
UpdateSystems.Add(new SomeSystem())
.Add(new AnotherSystem())
.Add(new CleanupSystem());
PhysicsUpdateSystems.Add(new SomePhysicsSystem())
.Add(new AnimationSystem())
.Add(new YetAnotherSystem());
UpdateSystems.Init();
PhysicsUpdateSystems.Init();
}
public void Update()
{
UpdateSystems.Run();
}
public void PhysicsUpdate()
{
PhysicsUpdateSystems.Run();
}
public void Destroy()
{
UpdateSystems.Destroy();
PhysicsUpdateSystems.Destroy();
World.Destroy();
}
}
- Feature interfaces:
using Scellecs.Morpeh;
public interface IMorpehEcsInitFeature : IEcsInitFeature<World>
{
}
public interface IMorpehEcsUpdateFeature : IEcsUpdateFeature<World, SystemsGroup>
{
}
public interface IMorpehEcsDestroyFeature : IEcsDestroyFeature<World>
{
}
- Example of feature:
// Morpeh world can handle IFixedSystem in the same system container with ISystem
public class MorpehEcsFeature : IMorpehEcsInitFeature, IMorpehEcsUpdateFeature, IEcsPhysicsUpdateFeature, IMorpehEcsDestroyFeature
{
public World World { get; set; } // provided by generic IEcsFeature
public SystemsGroup UpdateSystems { get; set; } // provided by generic IEcsUpdateFeature
public void Init()
{
World = World.Create();
UpdateSystems = World.CreateSystemsGroup();
UpdateSystems.AddSystem(new SomeSystem());
UpdateSystems.AddSystem(new AnotherSystem());
UpdateSystems.AddSystem(new CleanupSystem());
UpdateSystems.AddSystem(new SomePhysicsFixedSystem());
UpdateSystems.AddSystem(new AnimationFixedSystem());
UpdateSystems.AddSystem(new YetAnotherFixedSystem());
World.AddSystemsGroup(0, UpdateSystems);
}
public void Update()
{
World.Update(Time.deltaTime);
}
public void PhysicsUpdate()
{
World.FixedUpdate(Time.deltaTime);
}
public void Destroy()
{
World.Dispose();
}
}