隐藏组件系统的详细程度,并以通用方式为用户提供更优雅的界面
本文关键字:用户 方式 界面 系统 组件 程度 隐藏 | 更新日期: 2023-09-27 18:34:54
>我有一个实体组件系统,可以让我创建自定义实体并在运行时组装它们的功能。此过程包括创建新的实体实例,然后向其添加组件实例。缺点是现在,每次我想修改对象的X坐标时,我都需要做,obj.GetComponent<TransformComponent>().X
丑陋的。
我当然可以创建一个扩展 Entity 并添加一些固定组件的类,然后以属性的形式为这些组件添加速记,以便当我想更改 X 坐标时,我只需要执行obj.Transform.X
。如果没有更好的解决方案,这将是我的首选,但是,如果有一种通用方法将这些速记添加到实体的任何子类中,我更希望。例如,我可以创建一个名为 ITransformable 的接口来定义 Transform 属性,但是每个类都必须包含该属性的相同实现,这是多余的。
我考虑的另一个选择是具有接口,但仅将它们用作扩展方法的标记。然后,我可以在ITransformable上实现一个名为Transform()
的方法,以获取正确的组件。这样做的问题是我不能将接口限制为仅实体类,因此我无法访问扩展方法中的 GetComponent 方法,而且Transform().X
看起来仍然很丑陋。我可以做一些类型转换,但随后它严重变成了一个黑客。
还有其他理智的选择吗?
我在今年早些时候为此编写了一个解决方案: 组件和组件实体
组件之间的关系通过带注释的属性和方法定义,如下所示:
// Link to component of type B through a property.
// The name doesn't matter.
[ComponentLink]
B B { get; set; }
// Called when components are added or removed.
// The parameter type acts as a filter.
[NotifyComponentLinked]
void Added(object o)
{ Console.WriteLine(this.GetType().Name + " linked to " + o.GetType().Name + "."); }
[NotifyComponentUnlinked]
void Removed(object o)
{ Console.WriteLine(this.GetType().Name + " unlinked from " + o.GetType().Name + "."); }
// Attaches to events in compenents of type D and E.
// Rewriting this with Lambda Expressions may be possible,
// but probably would be less concise due to lack of generic attributes.
//
// It should be possible to validate them automatically somehow, though.
[EventLink(typeof(D), "NumberEvent")]
[EventLink(typeof(E), "NumberEvent")]
void NumberEventHandler(int number)
{ Console.WriteLine("Number received by F: " + number); }
ComponentEntities 项目包含将自身作为组件添加到自身或添加到其中的实体的集合,以避免全局单一实例。如果需要包含组件和组件测试(使用示例(项目的 VS 解决方案,请克隆捆绑存储库。
组件的许可证是 LGPL,到目前为止我没有许可组件实体(我刚才将存储库设置为公共(,但如果您需要,您可以在大约 10 分钟内从我上面写的内容中编写等效的东西。
你可以用DynamicObject做这样的事情。
http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx
下面是一个示例:
using System;
using System.Collections.Generic;
using System.Dynamic;
class Program
{
static void Main(string[] args)
{
dynamic ent = new Entity();
ent.Components.Add(new Transform() { X = 5, Y = 8 });
Console.WriteLine(ent.Transform.X);
ent.Transform.X = 12;
Console.WriteLine(ent.Transform.X);
Console.ReadKey();
}
}
class Entity : DynamicObject
{
public List<Component> Components = new List<Component>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
for (int i = 0; i < Components.Count; i++)
{
Component component = Components[i];
if (component.GetType().Name == binder.Name)
{
result = component;
return true;
}
}
result = null;
return false;
}
}
class Component
{
}
class Transform : Component
{
public float X;
public float Y;
}