コンポーネントベースのシステムでパワーアップを行う


29

コンポーネントベースの設計に頭を悩ませ始めたところです。これを行う「正しい」方法が何であるかはわかりません。

これがシナリオです。プレイヤーはシールドを装備できます。シールドはプレイヤーの周りにバブルとして描かれ、個別の衝突形状を持ち、プレイヤーがエリア効果から受けるダメージを減らします。

コンポーネントベースのゲームでは、このようなシールドはどのように設計されていますか?

混乱するのは、シールドに明らかに3つのコンポーネントが関連付けられていることです。

  • ダメージ低減/フィルタリング
  • スプライト
  • コライダー。

さらに悪いことに、さまざまなシールドのバリエーションがさらに多くの動作をする可能性があり、そのすべてがコンポーネントになります。

  • プレイヤーの最大ヘルスを向上させます
  • 健康回復
  • 発射体のたわみ

  1. 私はこれを考え直していますか?シールドは単にスーパーコンポーネントである必要がありますか?
    これは間違った答えだと思います。それがあなたがこれが行く方法であると思うならば、説明してください。

  2. シールドは、プレイヤーの位置を追跡する独自のエンティティである必要がありますか?
    そのため、損傷フィルタリングの実装が難しくなる場合があります。また、アタッチされたコンポーネントとエンティティの間の線がぼやけます。

  3. シールドは他のコンポーネントを収容するコンポーネントですか?
    私はこのようなものを見たことも聞いたこともないが、たぶんそれは一般的であり、私はまだ十分に深くない。

  4. シールドは、プレーヤーに追加されるコンポーネントのセットである必要がありますか?
    おそらく他のコンポーネントを管理するための追加のコンポーネントがあります。たとえば、それらをすべてグループとして削除できます。(誤ってダメージ軽減コンポーネントを残します。今ではそれが楽しいでしょう)。

  5. コンポーネントの経験が豊富な人にとって明らかな何か他のものはありますか?


タイトルをより具体的にする自由を取りました。
テトラッド

回答:


11

シールドは、プレイヤーの位置を追跡する独自のエンティティである必要がありますか?そのため、損傷フィルタリングの実装が難しくなる場合があります。また、アタッチされたコンポーネントとエンティティの間の線がぼやけます。

編集:私は、独立したエンティティには十分な「自律的な行動」がないと思います。この特定のケースでは、シールドはターゲットに追従し、ターゲットに対して機能し、ターゲットよりも長持ちしません。「シールドオブジェクト」の概念には何の問題もないことに同意する傾向がありますが、この場合はコンポーネントにうまく適合する動作を扱っています。しかし、私は純粋に論理的なエンティティの擁護者でもあります(変換コンポーネントやレンダリングコンポーネントを見つけることができる本格的なエンティティシステムとは対照的です)。

シールドは他のコンポーネントを収容するコンポーネントですか?私はこのようなものを見たことも聞いたこともないが、たぶんそれは一般的であり、私はまだ十分に深くない。

別の視点で見てください。コンポーネントを追加すると他のコンポーネントも追加され、削除すると追加のコンポーネントも削除されます。

シールドは、プレーヤーに追加されるコンポーネントのセットである必要がありますか?おそらく他のコンポーネントを管理するための追加コンポーネントがあります。たとえば、すべてをグループとして削除できます。(誤ってダメージ軽減コンポーネントを残します。今ではそれが楽しいでしょう)。

これは解決策になる可能性があり、再利用を促進しますが、エラーが発生しやすくなります(たとえば、あなたが言及した問題の場合)。必ずしも悪いわけではありません。あなたは試行錯誤のある新しい呪文の組み合わせを見つけるかもしれません:)

コンポーネントの経験が豊富な人にとって明らかな何か他のものはありますか?

少し詳しく説明します。

エンティティに追加された場合でも、一部のコンポーネントがどのように優先されるべきかに気付いたと思います(これは他の質問にも答えます)。

また、メッセージベースの通信を使用していると仮定します(説明のため、現時点ではメソッド呼び出しの単なる抽象化です)。

シールドコンポーネントが「インストール」されるたびに、シールドコンポーネントメッセージハンドラーは特定の(より高い)順序でチェーンされます。

Handler Stage    Handler Level     Handler Priority
In               Pre               System High
Out              Invariant         High
                 Post              AboveNormal
                                   Normal
                                   BelowNormal
                                   Low
                                   System Low

In - incoming messages
Out - outgoing messages
Index = ((int)Level | (int)Priority)

「stats」コンポーネントは、In / Invariant / Normalインデックスに「damage」メッセージハンドラーをインストールします。「損傷」メッセージを受信するたびに、HPを「価値」量だけ減らします。

かなり標準的な振る舞い(いくつかの自然な損傷抵抗および/または人種特性を入れます)。

シールドコンポーネントは、In / Pre / Highインデックスに「損傷」メッセージハンドラをインストールします。

Every time a "damage" message is received, deplete the shield energy and substract
the shield energy from the damage value, so that the damage down the message
handler pipeline is reduced.

damage -> stats
    stats
        stats.hp -= damage.value

damage -> shield -> stats
    shield
        if(shield.energy) {
            remove_me();
            return;
        }
        damage.value -= shield.energyquantum
        shield.energy -= shield.energyquantum;

     stats
        stats.hp -= damage.value

メッセージ処理パイプラインコンポーネントメッセージイベントハンドラーのどの部分がインストールされているかを判断する必要があるため、コンポーネントの相互作用を設計する際には慎重な計画が必要ですが、これは非常に柔軟です。

理にかなっていますか?詳細を追加できるかどうかを教えてください。

編集:複数のコンポーネントインスタンス(2つの装甲コンポーネント)について。1つのエンティティインスタンスのみで合計インスタンス数を追跡し(ただし、これによりコンポーネントごとの状態が強制終了されます)、メッセージイベントハンドラーを追加し続けるか、コンポーネントコンテナーが事前にコンポーネントタイプの重複を許可していることを確認できます。


最初の質問に理由なしで「いいえ」と回答しました。他の人を教えることは、意思決定の背後にある理由を理解するのを助けることです。IMO、RLでは力場があなた自身の身体とは別の「物理的実体」になるという事実は、それをコード内の別の実体にするのに十分です。このルートに行くのが悪い理由を示唆する正当な理由を提案できますか?
エンジニア

@ニック、私は決して誰かに何かを教えることを試みているのではなく、そのテーマについて知っていることを共有しています。しかし、その「いいえ」の背後にある根拠を追加して、その不愉快な下票を削除することを望みます:(
Raine

あなたの自律のポイントは理にかなっています。ただし、「この場合、動作を処理している」ことに注意してください。True-完全に独立した物理オブジェクト(シールド衝突形状)を含む動作。私にとって、1つのエンティティは1つの物理ボディ(またはジョイントなどで接続されたボディの複合セット)に結び付けられています。これをどのように調整しますか?私の側では、プレイヤーがたまたまシールドを使用した場合にのみアクティブになる「ダミー」物理フィクスチャを追加するのは不快に感じます。IMOは柔軟性がなく、すべてのエンティティにわたって保守が困難です。シールドベルトが死後もシールドを保持するゲーム(Dune)も検討してください。
エンジニア

@Nick、大部分のエンティティシステムは、論理コンポーネントとグラフィックス関連コンポーネントの両方を備えているため、この場合、シールドのエンティティを持つことは絶対に妥当です。純粋に論理的なエンティティシステムでは、「自律性」はオブジェクトの複雑さ、その依存関係、および存続期間の産物です。最後に、要件が重要です-エンティティシステムが何であるかについて実際のコンセンサスがないことを考えると、プロジェクトに合わせたソリューションの余地が十分にあります:)
Raine

@deft_code、答えを改善できるかどうか教えてください。
レーヌ

4

1)私はこれを考え直していますか?シールドは単にスーパーコンポーネントである必要がありますか?

おそらく、コードをどの程度再利用可能にするか、それが理にかなっているかどうかに依存します。

2)シールドは、プレイヤーの位置を追跡する独自のエンティティである必要がありますか?

この盾は、ある段階で独立して歩き回ることができるクリーチャーでない限り、そうではありません。

3)シールドは他のコンポーネントを収容するコンポーネントですか?

これは実体のように聞こえるので、答えはノーです。

4)シールドは、プレーヤーに追加されるコンポーネントのセットである必要がありますか?

それは可能性があります。

「損害軽減/フィルタリング」

  • コアシールドコンポーネントの機能。

「スプライト」

  • 別のSpriteComponentをキャラクターエンティティ(つまり、エンティティごとに特定のタイプの複数のコンポーネント)に追加できない理由はありますか?

「コライダー」

  • 別のものが必要ですか?これは物理エンジンに依存します。キャラクターエンティティのColliderComponentにメッセージを送信し、形状の変更を要求できますか?

「ブーストプレーヤーの最大の健康、健康の回復、発射物のたわみなど」

  • 他のアーティファクトはこれを行うことができるかもしれません(剣、ブーツ、指輪、呪文/ポーション/神社を訪れるなど)、これらはコンポーネントである必要があります。

3

物理的なエンティティとしてのシールドは、他の物理的なエンティティ、たとえばあなたの周りを旋回するドローンなどと違いはありません(実際、それ自体がシールドの一種である可能性があります!)。そのため、シールドを個別の論理エンティティにします(したがって、シールドは独自のコンポーネントを保持できます)。

シールドにいくつかのコンポーネントを与えます:衝突フォームを表す物理/空間コンポーネント、およびダメージが増加または減少するエンティティ(プレイヤーキャラクターなど)がエンティティに毎回適用されるエンティティへの参照を保持するDamageAffectorコンポーネントDamageAffectorを保持するとダメージを受けます。したがって、プレーヤーは「プロキシ」によってダメージを受けています。

シールドエンティティの位置を、ティックごとにプレイヤーの位置に設定します。(これを行う再利用可能なコンポーネントクラスを作成します。1回書き込み、何度も使用します。)

シールドエンティティを作成する必要があります。パワーアップの収集。Emitterと呼ばれる一般的な概念を使用します。これは、新しいエンティティを生成するエンティティコンポーネントの一種です(通常は、参照するEntityFactoryを使用して)。エミッターを見つける場所はあなた次第です。電源投入時にそれを配置し、電源投入が収集されるとトリガーします。


シールドは、プレイヤーの位置を追跡する独自のエンティティである必要がありますか?そのため、損傷フィルタリングの実装が難しくなる場合があります。また、アタッチされたコンポーネントとエンティティの間の線がぼやけます。

論理的なサブコンポーネント(空間、AI、武器スロット、入力処理など)と物理的なサブコンポーネントの間には細かい境界線があります。どちらの側に立つかを決定する必要があります。これにより、所有するエンティティシステムの種類が明確に定義されます。私にとって、エンティティのPhysicsサブコンポーネントは、物理階層関係(身体の手足-シーングラフノードなど)を処理しますが、上記のロジックコントローラーは通常、エンティティコンポーネントによって表されるものではなく、エンティティコンポーネントによって表されます個々の物理的な「備品」。


3

シールドは他のコンポーネントを収容するコンポーネントですか?

他のコンポーネントを収容していないかもしれませんが、サブコンポーネントの寿命を制御します。そのため、いくつかの大まかな擬似コードでは、クライアントコードがこの「シールド」コンポーネントを追加します。

class Shield : Component
{
    void Start() // happens when the component is added
    {
        sprite = entity.add_component<Sprite>( "shield" );
        collider = entity.add_component<Collider>( whatever );
        //etc
    }

    void OnDestroy() // when the component is removed
    {
        entity.remove_component( sprite );
        entity.remove_component( collider );
    }

    void Update() // every tick
    {
        if( ShouldRemoveSelf() ) // probably time based or something
            entity.remove_component( this );
    }
}

thisあなたの答えが何を意味するのか明確ではありません。されたthisシールドコンポーネントを参照するか、盾を使っているエンティティ、その親を意味するのですか?混乱は私のせいかもしれません。 「コンポーネントベース」はちょっとあいまいです。コンポーネントベースのエンティティの私のバージョンでは、エンティティは、独自の最小限の機能(オブジェクト名、タグ、メッセージングなど)を備えた単なるコンポーネントコンテナです。
deft_code

私がgameObject何かを使用した場合、混乱が少なくなります。これは、現在のゲームオブジェクト/エンティティ/コンポーネントの所有物への参照です。
テトラッド

0

コンポーネントシステムでスクリプトが許可されている場合、シールドコンポーネントは、「エフェクト」パラメータのスクリプトを呼び出すだけのほぼスーパーコンポーネントになります。そのようにして、シールドの単一コンポーネントのシンプルさを維持し、エンティティ定義によってシールドに供給されるカスタムスクリプトファイルに実際に行うすべてのロジックをオフロードします。

私は、Moveableコンポーネントに対して同様のことを行います。キーリアクションスクリプト(エンジンのスクリプトのサブクラス)のフィールドをカウントします。このスクリプトは、入力メッセージに続くメソッドを定義します。そのため、tempalte定義ファイルでこのようなことを簡単に行うことができます

camera template
    moveable
    {
        keyreaction = "scriptforcameramoves"

    }  

player template
    moveable
    {
        keyreaction = "scriptfroplayerkeypress"

    }  

その後、メッセージの登録中に私の可動コンポーネントで私はスクリプトDoメソッドを登録します(C#のコード)

Owner.RegisterHandler<InputStateInformation>(MessageType.InputUpdate, kScript.Do);

もちろん、これは、RegisterHandlerが取る関数のパターンに従うDoメソッドに依存しています。この場合、その(IComponent sender、ref type引数)

だから私の「スクリプト」(私の場合はC#だけrunimeコンパイル)も定義します

 public class CameraMoveScript : KeyReactionScript
{
 public override void Do(IComponent pSender, ref InputStateInformation inputState)
 {
    //code here
 }
}

と私の基本クラスKeyReactionScript

public class KeyReactionScript : Script
{
      public virtual void Do(IComponent pSender, ref InputStateInformation inputState);
}

その後、入力コンポーネントがタイプMessageTypes.InputUpdateのメッセージを送信したとき

 InputStateInformation myInputState = InputSystem.GetInputState();
 SendMessage<InputStateInformation>(MessageTypes.InputUpdate, ref myInputState);

そのメッセージとデータ型にバインドされたスクリプト内のメソッドが起動され、すべてのロジックを処理します。

コードは私のエンジンにかなり固有のものですが、ロジックはどのような場合でも機能するはずです。コンポーネントの構造をシンプルかつ柔軟に保つために、多くのタイプでこれを行います。

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