C# 从持有接口的集合返回类而不是接口
我想创建一个小实体组件系统示例,并创建一些组件,如C# 从持有接口的集合返回类而不是接口,c#,entity-component-system,C#,Entity Component System,我想创建一个小实体组件系统示例,并创建一些组件,如 internal struct Position : IComponent { public int X { get; set; } public int Y { get; set; } } 及 每个组件都实现一个当前为空的接口IComponent。当系统在实体中循环时,我希望快速找到相关组件 我考虑创建一个字典,将组件类型作为键,将当前实体的组件作为值 我从公共字典组件{get;} 我可以使用myEntity.compone
internal struct Position : IComponent
{
public int X { get; set; }
public int Y { get; set; }
}
及
每个组件都实现一个当前为空的接口IComponent
。当系统在实体中循环时,我希望快速找到相关组件
我考虑创建一个字典,将组件类型作为键,将当前实体的组件作为值
我从公共字典组件{get;}
我可以使用myEntity.components.add(typeof(Movement),newmovement()作为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;
}
}
}
}
}
内部类移动:ISystem
{
公共无效更新()
{
foreach(EntityPool.activeEntities.Values中的实体)//遍历所有实体
{
字典组件=实体组件;
if(组件TryGetValue(类型(位置),输出位置组件))
{
if(components.TryGetValue(typeof(MovementSpeed)、out MovementSpeed movementSpeedComponent))
{
//测试:移动(1*movementspeed)单位
positionComponent.X+=movementSpeedComponent.Value;
positionComponent.Y+=movementSpeedComponent.Value;
}
}
}
}
}
如果(components.TryGetValue(typeof(Position),out Position positionComponent))
由于字典的值本身不返回所需类型的组件而崩溃,它将返回接口
我怎样才能让它工作
(是的,我知道我可以使用ECS框架,但为了学习,我想自己做)简单的回答:你不能。如果dictionary的类型为
dictionary
,则它将仅返回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;
}
}
公共静态类字典扩展
{
公共静态TComponent GetValueOrNull(此字典目录),其中TComponent:IComponent
{
dict.TryGetValue(类型为(t组件),输出i组件);
返回组件作为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
}
}
}
}
内部类移动:ISystem
{
公共无效更新()
{
foreach(EntityPool.activeEntities.Values中的实体)//遍历所有实体
{
var posComponent=entity.Components.GetValueOrNull();
if(posComponent!=null)
{
//一些代码
}
}
}
}
如果插入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;
}
}
}
foreach(EntityPool中的实体)//遍历所有实体
{
字典组件=实体组件;
if(组件.TryGetValue(类型(位置),输出IComponent位置组件))
{
位置位置=(位置)位置组件;
if(components.TryGetValue(typeof(MovementSpeed),out-IComponent-movementSpeedComponent))
{
MovementSpeed速度=(MovementSpeed)MovementSpeed组件;
//测试:移动(1*movementspeed)单位
位置X+=速度值;
位置Y+=速度值;
}
}
}
使用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;
}
});
}
public void Update2()
{
列表=新列表();
list.OfType().ToList().ForEach(p=>
{
var speed=list.OfType().FirstOrDefault();
if(速度值)
{
p、 X=速度.Value.Value;
p、 Y=速度.Value.Value;
}
});
}
您需要使用反射来查找并获取指定成员的值。更好的方法是将成员放在界面中。我不确定我是否明白你在做什么,但这就是使用接口的目的。为什么你要嵌套if
s?@JonathanWood你介意再解释一点吗?@Fabjan因为这个系统应该只更新附加了位置和移动速度组件的实体,并从字典中将其作为i组件
,然后将其投射到位置
。因为你只拉位置
,演员阵容应该总是成功的(尽管我会使用is
或as
来确保并防止出现无效的卡斯特例外情况
),这不是一个坏答案,但要知道,TryGet有一个更普遍的惯例。。。。方法。它们倾向于返回bool(如果得到则为true,如果没有则为false),并将实际值作为out参数返回。(参见字典中的一个例子,但即使是int.TryParse()这样的各种东西也基本上是相同的约定)。是的,我将名称改为GetValueOrnull我知道很难得到结果,但他真的不可能吗?这种方法怎么样?尤其是动态关键字。该解决方案的缺点是运行时的性能较慢,并且对对象的操作有很大的限制。
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;
}
});
}