首页 文章

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

提问于
浏览
2

我想创建一个小的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 . 当系统循环遍历实体时,我希望快速找到相关组件 .

我想过创建一个Dictionary,它将组件类型作为键,将当前实体的组件作为值 .

我从 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 回答

  • 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

    如果插入IComponent类型的项目,则只能将该项目检索为IComponent . 如果您向Dictionary询问特定类型,则可以直接转换为该类型 .

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

相关问题