エンティティコンポーネントシステムをどのように構成するかについては、だれもが独自のアイデアを持っているため、これは答えるのが難しい質問です。私にできる最善のことは、私にとって最も役立つとわかったいくつかのことをあなたと共有することです。
エンティティ
私はECSにファットクラスのアプローチをとっています。おそらく、プログラミングの極端な方法が(人間の生産性の観点から)非常に非効率的であることがわかったためです。そのために、私にとってのエンティティは、より専門的なクラスによって継承される抽象クラスです。エンティティには、いくつかの仮想プロパティと、このエンティティが存在する必要があるかどうかを示す単純なフラグがあります。レンダーシステムに関する質問と比較すると、これはEntity
次のようになります。
public abstract class Entity {
public bool IsAlive = true;
public virtual SpatialComponent Spatial { get; set; }
public virtual ImageComponent Image { get; set; }
public virtual AnimationComponent Animation { get; set; }
public virtual InputComponent Input { get; set; }
}
部品
コンポーネントは、彼らがいないという点で、「愚か」ですやるか知っている何かを。それらは他のコンポーネントへの参照がなく、通常は関数がありません(私はC#で作業するので、プロパティを使用してゲッター/セッターを処理します-関数がある場合、それらは保持しているデータの取得に基づいています)。
システム
システムはそれほど「愚か」ではありませんが、まだ馬鹿げたオートマトンです。それらには、システム全体のコンテキストがなく、他のシステムへの参照がなく、個別の処理を行うために必要ないくつかのバッファーを除いて、データを保持していません。システムによっては、専用のUpdate
、Draw
メソッド、または場合によってはその両方がある場合があります。
インターフェース
インターフェースは私のシステムの重要な構造です。それらはSystem
、何が処理できるか、何ができるかを定義するために使用されますEntity
。レンダリングに関連するインターフェースは、IRenderable
およびIAnimatable
です。
インターフェースは、使用可能なコンポーネントをシステムに通知するだけです。たとえば、レンダリングシステムは、エンティティの境界ボックスと描画する画像を知っている必要があります。私の場合、それは次のようになりますSpatialComponent
とImageComponent
。したがって、次のようになります。
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
RenderingSystem
では、レンダリングシステムはどのようにしてエンティティを描画するのでしょうか。それは実際には非常に単純なので、簡単なクラスを表示してアイデアを提供します。
public class RenderSystem {
private SpriteBatch batch;
public RenderSystem(SpriteBatch batch) {
this.batch = batch;
}
public void Draw(List<IRenderable> list) {
foreach(IRenderable obj in list) {
this.batch.draw(
obj.Image.Texture,
obj.Spatial.Position,
obj.Image.Source,
Color.White);
}
}
}
クラスを見ると、レンダーシステムは何であるかさえ知りませんEntity
。それが知っているすべてはそれでありIRenderable
、それは単にそれらを描くためにそれらのリストが与えられます。
すべての仕組み
新しいゲームオブジェクトを作成する方法と、それらをシステムに供給する方法も理解するのに役立ちます。
エンティティの作成
すべてのゲームオブジェクトはEntityと、そのゲームオブジェクトが実行できることを説明する適切なインターフェイスを継承します。画面上でアニメーション化されるほぼすべては次のようになります。
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
システムへの給餌
と呼ばれる単一のリストに、ゲームの世界に存在するすべてのエンティティのリストを保持しますList<Entity> gameObjects
。次に、各フレームでそのリストをふるいにかけ、オブジェクトの参照をList<IRenderable> renderableObjects
、やなどのインターフェイスタイプに基づいて、より多くのリストにコピーしますList<IAnimatable> animatableObjects
。このように、異なるシステムが同じエンティティを処理する必要がある場合、それらを処理できます。次に、それらのリストをシステムUpdate
またはDraw
メソッドのそれぞれに渡し、システムに機能を任せます。
アニメーション
あなたはアニメーションシステムがどのように機能するか知りたいかもしれません。私の場合、IAnimatableインターフェースを確認したい場合があります。
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
ここで注目すべき重要なImageComponent
点は、IAnimatable
インターフェースの側面が読み取り専用ではないことです。それはセッターを持っています。
ご想像のとおり、アニメーションコンポーネントはアニメーションに関するデータのみを保持しています。フレーム(画像コンポーネント)、現在のフレーム、描画される1秒あたりのフレーム数、最後のフレーム増分からの経過時間、およびその他のオプションのリスト。
アニメーションシステムは、レンダリングシステムと画像コンポーネントの関係を利用します。エンティティの画像コンポーネントを変更するだけで、アニメーションのフレームが増加します。このようにして、アニメーションはレンダリングシステムによって間接的にレンダリングされます。