Search code examples
c#entity-component-system

return class instead of interface from collection holding interfaces


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)


Solution

  • 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
                }
            }
        }
    }