вернуть интерфейс вместо интерфейса из коллекции, содержащей интерфейсы - PullRequest
0 голосов
/ 09 декабря 2018

Я хочу создать небольшой пример Entity-Component-System и создать несколько компонентов, таких как

internal struct Position : IComponent
{
    public int X { get; set; }
    public int Y { get; set; }
}

и

internal struct MovementSpeed : IComponent
{
    public int Value { get; set; }
}

Каждый компонент реализует пустой интерфейс IComponent.Когда система циклически перебирает сущности, я хотел бы быстро найти соответствующие компоненты.

Я думал о создании словаря, в котором тип компонента будет содержать ключ, а компонент текущей сущности - значение.

Я начал с public Dictionary<Type, IComponent> Components { get; }

Я могу добавить компоненты, используя myEntity.Components.Add(typeof(Movement), new Movement() as IComponent);

, но как я могу вернуть компонент?Я создал пример системы движений

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)) падает, потому что значение словаря не возвращает компонент нужного типа, а возвращает интерфейс.

Как я могузаставить это работать?

(Да, я знаю, что мог бы использовать структуру ECS, но я хотел бы сделать это самостоятельно в учебных целях)

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Если вы вставляете элемент типа IComponent, вы можете получить его только как IComponent.Если вы запрашиваете словарь для определенного типа, вы можете привести его к типу напрямую.

        foreach (Entity entity in EntityPool) // Loop through all entities
        {
            Dictionary<Type, IComponent> components = entity.Components;
            if (components.TryGetValue(typeof(Position), out IComponent positionComponent))
            {
                Position position = (Position)positionComponent;
                if (components.TryGetValue(typeof(MovementSpeed), out IComponent movementSpeedComponent))
                {
                    MovementSpeed speed = (MovementSpeed)movementSpeedComponent;
                    // TEST: move (1 * movementspeed) units
                    position.X += speed.Value;
                    position.Y += speed.Value;
                }
            }
        }

С linq у вас есть очень эффективный способ работы со списком.Возможно, это лучший способ для вас.Вот пример:

    public void Update2()
    {
        List<IComponent> list = new List<IComponent>();
        list.OfType<Position>().ToList().ForEach(p =>
        {
            var speed = list.OfType<MovementSpeed?>().FirstOrDefault();
            if (speed.HasValue)
            {
                p.X = speed.Value.Value;
                p.Y = speed.Value.Value;
            }
        });
    }
0 голосов
/ 09 декабря 2018

Краткий ответ: Вы не можете.Если словарь имеет тип Dictionary<Type, IComponent>, он вернет только IComponent.

Однако вы можете создать метод расширения для этого:

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

И использовать его:

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