コンポーネントの動作をオーバーライドする


7

コンポーネントベースのエンティティシステムで動作のオーバーライドを実装する方法を考えていました。具体的な例として、エンティティには、ダメージ、回復、殺されるなどのヒースコンポーネントがあります。エンティティには、キャラクターが受けるダメージの量を制限する鎧コンポーネントもあります。

これまでにコンポーネントベースのシステムでこのような動作を実装したことはありますか?
どうやってやったの?

誰もこれまでにこれをしたことがないのなら、なぜそうだと思いますか? コンポーネントの動作をオーバーライドすることに特に問題があることはありますか?

以下は私がそれがうまくいくと想像する方法の大まかなスケッチです。エンティティのコンポーネントは順序付けられています。最前線のユーザーは、最初にインターフェースのサービスを受ける機会を得ます。それがどのように行われるかについては詳しく説明しません。悪意のあるdynamic_castsを使用すると仮定します(実際は使用しませんが、RTTIがなくても最終的な効果は同じです)。

class IHealth
{
public:
   float get_health( void ) const = 0;
   void do_damage( float amount ) = 0;
};

class Health : public Component, public IHealth
{
public:
   void do_damage( float amount )
   {
      m_damage -= amount;
   }
private:
   float m_health;
};

class Armor : public Component, public IHealth
{
public:
   float get_health( void ) const
   {
      return next<IHealth>().get_health();
   }

   void do_damage( float amount )
   {
      next<IHealth>().do_damage( amount / 2 );
  }
};

entity.add( new Health( 100 ) );
entity.add( new Armor() );
assert( entity.get<IHealth>().get_health() == 100 );
entity.get<IHealth>().do_damage( 10 );
assert( entity.get<IHealth>().get_health() == 95 );

私がこれを行うことを提案している方法について特にナイーブなものはありますか?


ここで私の答えを参照してください:gamedev.stackexchange.com/questions/13916/…。自分の回答へのリンクが不快な場合は、MODで削除してください。
レイン

2
IHealthに追加のインターフェイスを作成し、IHealthにComponentから継承させ、ArmorとHealthにIHealthのみから継承させる代わりに、さらに下位の多重継承を使用する特定の理由はありますか?
TravisG 2011年

@heishe:コンポーネントが2つの異なるインターフェース、つまりIHealthとを実装したいのではないかと心配しましたIKnockback。これら2つのコンポーネントを単一のクラス階層に結合することは意味がありません。多重継承は常に厄介な問題であり、シールドからIHealthすべての呼び出しを派生させてシールドに転送するプロキシメンバークラスをシールドに使用させることを検討していました。その実装手法では、追加の非仮想メソッド呼び出し(オプティマイザがインライン化できる場合がある)を犠牲にしてMIはありません。いずれの場合もAPI( 、addgetnextなど)が同じです。
deft_code

回答:


3

あなたはそれを少し複雑にしすぎているか、十分に複雑にしていないと思います。

私が提案する方向性の1つは、ヘルスを取得してダメージインターフェースを実行することです。

したがって、おそらくアイデアとして、Armorコンポーネントは次のようになります。

class Armor : public Component, public IDamageReceiver
{
public:
   void do_damage( float amount )
   {
      // just assume that subtract_health isn't used in client code maybe
      this.get<IHealth>().subtract_health( amount / 2 ); 
   }
};

代わりに(そしておそらく私が何をするか)、コンポーネントを少し大きくして、特定のタイプの派生元である一般的な「エンティティ」基本クラスを用意することもできます。これにはヘルスとアーマーの両方の機能が含まれ、そこから特定のエンティティを実装できます。


あなたの解決策は私の問題に完全に対処していません。既存のコンポーネントを調整せずにArmorコンポーネントを追加したいと思います。できました。2つの鎧コンポーネントを追加するだけで、適切に機能します(ダメージを25%に減らします)。つまり、一般的なオーバーライドソリューションを使用して、オーバーライドをオーバーライドできます。
deft_code

3
私はここでテトラッドに少し同意します。私はあなたがあなたのシステムをあまりに複雑で細かくしていると思います。コンポーネントXをオーバーライドする場合は、関連するエンティティでコンポーネントXを見つけ、必要なことを行うコンポーネントYと交換します。

1
@deft_codeこれより前のコンポーネントに関するあなたの発言と他の投稿に基づいています。キャラクターに適用するバフを作成(デ)できるコンポーネントタイプの「CharacterEffects」(パワーアップを使用しないでください)を導入したいと思います。この場合も、関連する効果をスキャンして適用されるかどうかを確認するためにヘルスコンポーネントを更新する必要がありますが、ヘルスから継承せず、ヘルスを置き換えないで、新しいコンポーネントタイプが必要になります。または、コンポーネントシステムにスタックを作成して、実行/解決の順序が保証されるようにします。
ジェームズ

2

あなたの考えは正しい場所にありますが、私はコンポーネントシステムを必要のない詳細レベルにしようとしていると思います。あなたが必要なのは、健康をまっすぐにする場合、健康と呼ばれるコンポーネントを作ると思います。ただし、鎧も含まれているヘルスコンポーネントが必要な場合は、一般的なヘルスのAPIに適合するCHealthAndArmorコンポーネントにして、他のコンポーネントと同じように使用できるようにします。ただし、鎧レベルを変更する方法も提供します完全にロード時間の値である場合、必要はありません。それは、その特定のヘルスコンポーネントの内部動作のみです。

正直なところ、(これと他の投稿から)懸念しているのは、コンポーネントを細かくレベル分けすることです。別のコンポーネント内でコンポーネントを継承またはカプセル化していることに気付いた場合は、何をしているのかを再検討する必要があります。一般に、コンポーネントベースのアーキテクチャでは、このような継承を回避しようとしています。

お役に立てれば


1
HealthAndArmorコンポーネントを作成すると、コンポーネントベースのアーキテクチャの目的が損なわれます。
ミッチェル

@ミッチェルそれはすべて本当にゲームに依存します。鎧が古いドゥーム/クエイクスタイルのゲームのような余分な健康に過ぎない場合、これらのものを分割すると完全に無駄になります。コンポーネントは環境とゲームに一致する必要があります。
ジェームズ

それらを分割すると将来簡単に変更できるようになるので、「鎧と健康」から「健康」に変更するには、HealthAndArmorを削除してHealthを追加するのではなく、鎧を削除するだけで済みます。ささいなことのように思えるかもしれませんが、プロトタイピングをたくさんしていると、それは本当に迷惑になることがあります。しかし、それはエンティティマネージャーにも依存すると思います。
ミッチェル

@ミッチェル私たちはその時に意見を異にする必要があると思います..私は常に、問題のゲーム/アプリケーションに合わせて調整するものとしてコンポーネントを見てきました。システムを使用/再利用するプロジェクトの数に応じて、時間の経過とともにそれらの素晴らしいコレクションを収集しますが、コンポーネントシステムの全体のポイントは、単純で特定の小さなオブジェクトからより複雑なオブジェクトを作成することです。
James


0

イベント駆動型コンポーネントシステムを使用していて、同じ問題がありました。基本的に、私はコンポーネント関数を他のコンポーネントイベントにサブスクライブします。1つのイベントで、x個の関数をサブスクライブすることができ、イベントはサブスクライブされた順序で発生します。イベントは、動的クラスであり、参照によって渡される1つのパラメーターも提供します。これはすべて、StatsコンポーネントとHealthコンポーネントを作成できることを意味します。私は両方のハートメッセージをサブスクライブしますが、Statsが最初にサブスクライブされるため、Statコンポーネントの鎧ステータスを使用し、パラメーターオブジェクト内にあるダメージ値を変更します。ヘルスコンポーネントはサブスクリプションリストの次であり、それが呼び出されるまでに、ダメージ値はすでに減少しています。

コンポーネントを扱うイベントの方法を十分に話すことができません。実際には、コンポーネントが互いにイベントを知らないコンポーネントを介して機能をつなぎ合わせることができます。ユーザーは、イベントサブスクリプションを設定して機能を構築するだけです。このシステムから決して振り返ることはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.