私は大学生で、ゲーム開発は私にとって少し趣味です。私は新しいゲームを書いていて、ゲームエンジンをより柔軟に(そして将来の取り組みのための強固な基盤を提供できるようにするために)開発するための新しいアプローチを探しています。ゲームエンティティを表すために深い継承に依存するのではなく(継承ツリーのルートノードとリーフノードが重い場合や、場合によってはダイヤモンドの継承につながる可能性がある)方法に関するいくつかの記事を見つけましたが、より良いアプローチはロジックをモデル化することですコンポーネント内(より良い、動作と属性)。
これを示すプレゼンテーションは次のとおりです。http: //www.gdcvault.com/free/gdc-canada-09 「ゲームオブジェクトコンポーネントアーキテクチャの理論と実践...」
XNAとC#を使用してこれを実装しています。私はエンティティクラスがあります:
class Entity
{
private Game1 mGame;
private Dictionary<AttributeType, object> mAttributes;
private Dictionary<BehaviorType, Behavior> mBehaviors;
public Entity(Game game)
{
mGame = game;
mAttributes = new Dictionary<AttributeType, object>();
mBehaviors = new Dictionary<BehaviorType, Behavior>();
}
public Game GetGame()
{
return mGame;
}
//Attributes
public void SetAttribute<T>(AttributeType attributeType, T value)
{
if (mAttributes.ContainsKey(attributeType))
{
mAttributes[attributeType] = value;
OnMessage<T>(MessageType.ATTRIBUTE_UPDATED, attributeType, value);
}
else
{
mAttributes.Add(attributeType, value);
OnMessage<T>(MessageType.ATTRIBUTE_CREATED, attributeType, value);
}
}
public T GetAttribute<T>(AttributeType attributeType)
{
if (!mAttributes.ContainsKey(attributeType))
{
throw new KeyNotFoundException("GetAttribute: Attribute with type: " + attributeType.ToString() + " not found.");
}
return (T)mAttributes[attributeType];
}
public bool HasAttribute(AttributeType attributeType)
{
return mAttributes.ContainsKey(attributeType);
}
//Behaviors
public void SetBehavior(BehaviorType behaviorType, Behavior behavior)
{
if (mBehaviors.ContainsKey(behaviorType))
{
mBehaviors[behaviorType] = behavior;
}
else
{
mBehaviors.Add(behaviorType, behavior);
}
}
public Behavior GetBehavior(BehaviorType behaviorType)
{
if (!mBehaviors.ContainsKey(behaviorType))
{
throw new KeyNotFoundException("GetBehavior: Behavior with type: " + behaviorType.ToString() + " not found.");
}
return mBehaviors[behaviorType];
}
public bool HasBehavior(BehaviorType behaviorType)
{
return mBehaviors.ContainsKey(behaviorType);
}
public Behavior RemoveBehavior(BehaviorType behaviorType)
{
if (!mBehaviors.ContainsKey(behaviorType))
{
throw new KeyNotFoundException("RemoveBehavior: Behavior with type: " + behaviorType.ToString() + " not found.");
}
Behavior behavior = mBehaviors[behaviorType];
mBehaviors.Remove(behaviorType);
return behavior;
}
public void OnUpdate(GameTime gameTime)
{
foreach (Behavior behavior in mBehaviors.Values)
{
behavior.OnUpdate(gameTime);
}
}
public void OnMessage<T>(MessageType messageType, AttributeType attributeType, T data)
{
foreach (Behavior behavior in mBehaviors.Values)
{
behavior.OnMessage<T>(messageType, attributeType, data);
}
}
}
「AttributeType」と「BehaviorType」は単なる列挙型です。
public enum AttributeType
{
POSITION_2D,
VELOCITY_2D,
TEXTURE_2D,
RGB_8888
}
動作クラスは、他のすべての動作のスーパークラスです。ビヘイビアーは自己完結型でモジュール型であることを意味します(たとえば、エンティティをSpriteBatchにレンダリングする方法のみを知っているSpriteBatchRenderBehaviorがあり、このフレームワークを使用する新しいゲームで使用できるはずです)。
abstract class Behavior
{
//Reference to owner to access attributes
private Entity mOwner;
public Behavior(Entity owner)
{
mOwner = owner;
}
public Entity GetOwner()
{
return mOwner;
}
protected void SetAttribute<T>(AttributeType attributeType, T value)
{
mOwner.SetAttribute<T>(attributeType, value);
}
protected T GetAttribute<T>(AttributeType attributeType)
{
return (T)mOwner.GetAttribute<T>(attributeType);
}
public abstract void OnUpdate(GameTime gameTime);
public abstract void OnMessage<T>(MessageType messageType, AttributeType attributeType, T data);
}
したがって、エンティティにはラベル付けされた属性(POSITION_2D、TEXTURE_2Dなど)のバッグがあり、各動作はget / setメソッドを通じて所有者の属性にアクセスできます。私のエンティティの属性を動作間で共有する必要があるという問題が発生しています(これが実際に問題であるかどうかはわかりません)。基本的に、ゲームの世界でエンティティの位置を変更する動作がある場合、それはエンティティの位置属性を変更します。次に、SpriteBatchDrawBehaviorをエンティティに追加します。これは、位置を持つエンティティに依存します(そのため、エンティティをどこに描画するかがわかります)。私の問題は、移動動作の前に描画動作を追加した場合、エンティティがまだ位置属性を取得しているという保証がないことです(その場合、キーが見つからないという例外がスローされます)。
描画動作(またはこの属性に依存するその他の動作)を追加する前に位置を確実に確保する動作を追加することで、エンティティに位置があることを常に確認できますが、これにより、これらの動作がすべきではない。
もう1つのオプションは、位置動作から継承する移動動作から描画動作を継承することです(ただし、これにより、ダイヤモンドの継承などの問題に戻ります。また、親クラスとの実際の関連性がほとんどない、またはまったくない子クラス、その他同様の依存関係よりも)。
もう1つのオプションは、「私の所有者がポジションを持っていない場合は、彼のポジション属性を作成する」などの私の動作にチェックを書き込むことですが、これにより問題が発生します。新しい属性にはどのデフォルト値を選択する必要がありますか?などなど
助言がありますか?私は何かを見落としているか?
ありがとう!