从收集保持接口返回类而不是接口

时间:2018-12-09 20:34:16

标签: c# entity-component-system

我想创建一个小的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))崩溃,因为字典的值不返回所需Type本身的组件,而是返回接口。

我如何使其工作?

(是的,我知道我可以使用ECS框架,但出于学习目的,我想自己做)

2 个答案:

答案 0 :(得分:5)

简短的答案:您不能。如果字典的类型为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
            }
        }
    }
}

答案 1 :(得分:1)

如果插入类型为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;
            }
        });
    }