在基于组件的游戏设计中共享字段

God*_*eer 3 c# xna

在使用XNA在C#中完成基于组件的游戏引擎之前,我认为这是最后一次大的逻辑飞跃.我定义了我的Entity类和抽象组件.我的问题出现在我的EntityFactory中.

当我想创建一个新实体时,我将一个EntityType枚举传递给工厂中的静态方法,并通过一个switch/case语句查找要组合在一起的组件.问题是,我正在尝试创建一种方法,组件可以与同一实体中的其他组件共享字段,而无需访问所有内容.例如,如果两个组件具有表示位置的Vector2字段,则它们都应指向相同的Vector2.

我可以通过初始化实体工厂中的所有字段并要求将它们传递到组件的构造函数(并使用ref作为基元)来实现这一点,但这将非常难以维护,因为任何时候我扩展或更改了组件,我将不得不在工厂中的每个使用组件的地方重写代码.我真的想避免这种解决方案,但如果我找不到更好的方法,我会忍受它.

我目前的解决方案是创建一个名为Attribute的包装类.它包含两个字段:

private AttributeType type;
private Object data;
Run Code Online (Sandbox Code Playgroud)

属性类型是枚举,表示属性的用途.所以在位置,旋转,纹理等枚举中有条目.

EntityFactory创建一个空的属性列表,并将其传递给每个组件构造函数.setField方法将由组件的构造函数调用,而不是初始化字段.这是Attribute类和setField方法.

 public class Attribute
{
    private AttributeType type;
    private Object data;

    public AttributeType Type
    {
        get { return this.type; }
    }
    public Object Data
    {
        get { return this.data; }
    }

    public Attribute(AttributeType type, Object data)
    {
        this.type = type;
        this.data = data;
    }

    public static void setField<T>(List<Attribute> attributeList, AttributeType type, out T field, T defaultValue)
    {
        bool attributeFound = false;
        field = defaultValue;

        foreach (Attribute attribute in attributeList)
        {
            if (attribute.Type == type)
            {
                field = (T)attribute.Data;
                attributeFound = true;
                break;
            }
        }

        if (!attributeFound)
        {
            attributeList.Add(new Attribute(type, field));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是当属性包含基本类型的数据时.我考虑在Attribute类中编写一个方法

public void getData<T>(out T field) { field = this.data; }
Run Code Online (Sandbox Code Playgroud)

但是我似乎无法使用ref将数据传递给Attribute构造函数.我不能使属性通用,因为它不会进入列表.我只是想知道是否有办法处理值类型以及引用类型数据,或者我在这整个事情中某处出现了逻辑错误.

And*_*ell 10

Snarky版本:恭喜你重新改造了这个变量.厉害.或者,充其量是接口上的属性.

实用版本:

我可以看到你的设计有一些问题.

第一个问题就是它很复杂.最好避免并发症,除非你有一个令人信服的和存在的原因(即:不是"可能在将来"需要).否则YAGNI.你应该总是尝试直接在代码中表达概念,然后再创建系统来在数据中表达这些概念(就像我所说的关于重新发明变量的内容;也考虑到这一点).

但是我们假设您确实有充分的理由进行基于组件的设计......

第二个问题是拳击.拳击发生在任何地方你存储的值类型(例如:int,float,Vector2,任意struct)直接作为基准类型(例如:object,IEquatable).盒装对象是不可变的 - 所以每次你的位置改变时,都会创建一个新的盒装对象.拳击变量是(相对)慢.盒装对象存储在堆上 - 因此它们会在垃圾收集过程中被考虑,并可能导致垃圾收集.所以你在问题中提出的设计将会表现得非常糟糕.

我假设您对基于组件的设计的想法与本文中解释的类似.这是一个有用的图表:

http://cowboyprogramming.com/images/eyh/Fig-2.gif

这让我想到了第三个问题:你不应该拥有多个持有职位的组件!(你似乎是想办法更细化的比你需要,在你的设计.)

基本上,基于组件的设计是关于重塑class,而不是变量.在正常设计中,您可能具有如下"渲染"功能:

public void Draw()
{
    spriteBatch.Draw(texture, this.Position, Color.White);
}
Run Code Online (Sandbox Code Playgroud)

但是在基于组件的设计中,您将拥有DrawPosition不同的类中.顺便说一下,我会实现以下接口:

interface IRenderComponent { void Draw(); }
interface IPositionComponent { Vector2 Position { get; set; } }
Run Code Online (Sandbox Code Playgroud)

那怎么Draw访问Position?好吧,你需要一种表达方式this(如果你要重新发明类,可能是你需要包含的最重要的概念).

你会怎么做?这是一个粗略的设计理念:

我会使每个组件类继承自Component具有属性的类Self.我会使用一种机制来Self返回某种形式ComposedObject,以便通过接口访问构成组合对象的任何其他组件.所以你的渲染组件可能看起来像:

class SimpleRenderer : Component, IRenderComponent
{
    public void Draw()
    {
        sb.Draw(texture, Self.Get<IPositionComponent>().Position, Color.White);
    }
}
Run Code Online (Sandbox Code Playgroud)

(这类似于GameServiceContainer(即:Game.Services属性).这里的想法是ComposedObject每个接口都不应该有多个实例.如果接口数量很少,ComposedObject甚至不需要使用列表 - 只需直接存储每个接口.你可以但是,组件可以实现多个接口.)

现在,如果这对您来说过于冗长,也许您可​​以ComposedObject为常见的数据片段添加一些便利属性(或使用扩展方法)Position,例如:

public Vector2 Position { get { return Get<IPositionComponent>().Position; } }
Run Code Online (Sandbox Code Playgroud)

然后你的绘图功能可以简单地这样:

spriteBatch.Draw(texture, Self.Position, Color.White);
Run Code Online (Sandbox Code Playgroud)