I want to create a small Entity-Component-System example and created some components like
internal struct Position : IComponent
{
public int X { get; set; }
public int Y { get; set; }
}
and
internal struct MovementSpeed : IComponent
{
public int Value { get; set; }
}
Every component implements a currently empty interface IComponent
. When the system is looping through the entities I would like to find the relevant components fast.
I thought about creating a Dictionary that holds the component type as the key and the component of the current entity as the value.
I started with public Dictionary<Type, IComponent> Components { get; }
I can add components using myEntity.Components.Add(typeof(Movement), new Movement() as IComponent);
but how can I return a component? I created a movement-system example
internal class Movement : ISystem
{
public void Update()
{
foreach (Entity entity in EntityPool.activeEntities.Values) // Loop through all entities
{
Dictionary<Type, IComponent> components = entity.Components;
if (components.TryGetValue(typeof(Position), out Position positionComponent))
{
if (components.TryGetValue(typeof(MovementSpeed), out MovementSpeed movementSpeedComponent))
{
// TEST: move (1 * movementspeed) units
positionComponent.X += movementSpeedComponent.Value;
positionComponent.Y += movementSpeedComponent.Value;
}
}
}
}
}
if (components.TryGetValue(typeof(Position), out Position positionComponent))
crashes because the value of the dictionary does not return the component of desired Type itself, it returns the interface.
How can I make it work?
(Yeah I know I could use a ECS framework but I would like to do it on my own for learning purposes)
The short answer: You can't. If dictionary is of type Dictionary<Type, IComponent>
then it will return only IComponent
.
However you could create an extension method for this:
public static class DictionaryExtensions
{
public static TComponent GetValueOrNull<TComponent>(this Dictionary<Type, IComponent> dict) where TComponent : IComponent
{
dict.TryGetValue(typeof(TComponent), out IComponent component);
return component as TComponent;
}
}
And use it:
internal class Movement : ISystem
{
public void Update()
{
foreach (Entity entity in EntityPool.activeEntities.Values) // Loop through all entities
{
var posComponent = entity.Components.GetValueOrNull<Position>();
if (posComponent != null)
{
// some code
}
}
}
}