Using the Entity-Component-System pattern I want to connect some systems with events. So some systems shouldn't run in a loop, they should just run on demand.
Given the example of a Health system a Death system should only run when a component gets below 1 health.
I thought about having two types of systems. The first type is a periodic system. This runs once per frame, for example a Render or Movement System. The other type is an event based system. As mentioned before a connection between Health and Death.
First I created a basic interface used by both system types.
internal interface ISystem
{
List<Guid> EntityCache { get; } // Only relevant entities get stored in there
ComponentRequirements ComponentRequirements { get; } // the required components for this system
void InitComponentRequirements();
void InitComponentPools(EntityManager entityManager);
void UpdateCacheEntities(); // update all entities from the cache
void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
Further I created the interfaces
internal interface IReactiveSystem : ISystem
{
// event based
}
and
internal interface IPeriodicSystem : ISystem
{
// runs in a loop
}
but I'm not sure if they will be necessary. There is no problem using
foreach (ISystem system in entityManager.Systems)
{
system.UpdateCacheEntities();
}
but I don't want to run a system if not needed.
There are two types of Events, a ChangeEvent
and a ExecuteEvent
. The first gets triggered when a value from a component has changed. The second one gets triggered when something should be done with a specific entity.
If you Need or want to you can have a look at the EntityManager
the ComponentRequirements
and the usage of the ECS
An example System would be something like this
internal class HealthSystem : IReactiveSystem
{
public HealthSystem(EntityManager entityManager)
{
InitComponentRequirements();
InitComponentPools(entityManager);
}
private Dictionary<Guid, HealthComponent> healthComponentPool;
public List<Guid> EntityCache { get; } = new List<Guid>();
public ComponentRequirements ComponentRequirements { get; } = new ComponentRequirements();
public void InitComponentRequirements()
{
ComponentRequirements.AddRequiredType<HealthComponent>();
}
public void InitComponentPools(EntityManager entityManager)
{
healthComponentPool = entityManager.GetComponentPoolByType<HealthComponent>();
}
public void UpdateCacheEntities()
{
for (int i = 0; i < EntityCache.Count; i++)
{
UpdateCacheEntity(EntityCache[i]);
}
}
public void UpdateCacheEntity(Guid cacheEntityId)
{
Health healthComponent = healthComponentPool[cacheEntityId];
healthComponent.Value += 10; // just some tests
// update UI
}
}
How can I create ChangeEvents
and ExecuteEvents
for the different systems?
EDIT
Is there a way to add event delegates to the components to run a specific system for this entity on change if a change event is listening or on demand if an execute event is listening?
By mentioning ChangeEvent
and ExecuteEvent
I just mean event delegates.
Currently I could do something like this
internal class HealthSystem : IReactiveSystem
{
//… other stuff
IReactiveSystem deathSystem = entityManager.GetSystem<Death>(); // Get a system by its type
public void UpdateCacheEntity(Guid cacheEntityId)
{
// Change Health component
// Update UI
if(currentHealth < 1) // call the death system if the entity will be dead
{
deathSystem.UpdateCacheEntity(cacheEntityId);
}
}
}
But I was hoping to achieve a better architecture by using event delegates to make systems communicate and share data between each other.