組み合わせることができる多くの異なる攻撃タイプをどのように設計できますか?


17

トップダウンの2Dゲームを作成しています。さまざまな種類の攻撃が必要です。The Binding of Isaacが機能するように、攻撃を非常に柔軟で組み合わせ可能にしたいと思います。ゲーム内のすべてのグッズのリストを次に示します。良い例を見つけるために、スプーンベンダーのアイテムを見てみましょう。

スプーンベンダーは、Isaacにホーミング涙を放つ能力を与えます。

「相乗効果」セクションを見ると、興味深いが直感的な効果を得るために他の収集品と組み合わせることができることがわかります。たとえば、Inner Eyeと組み合わせると、「Isaacが複数のホーミングショットを一度に発射できるようになります」。これは理にかなっています

アイザックにトリプルショットを与える

このようなものを設計するのに適したアーキテクチャは何ですか?これがブルートフォースソリューションです。

if not spoon bender and not the inner eye then ...
if spoon bender and not the inner eye then ...
if not spoon bender and the inner eye then ...
if spoon bender and the inner eye then ...

しかし、それは非常に早く手に負えなくなります。このようなシステムを設計するより良い方法は何ですか?


個人的には、装備されているすべてのアイテムをリストに保持し、オブジェクトからオブジェクトに変換するオブジェクトを取得する一般的なインターフェイスを実装するようにします。「アイテムごとに計画された攻撃を変更する」ため、1つのアイテムが発射物の量を複製し、色合いを追加してダメージを変更できます(したがって、1つのアイテムが赤いボルトを作成し、別のアイテムが黄色になった場合、両方が攻撃を変更した後、オレンジ色の攻撃になります) 。また、計画された攻撃をどのように変更するかを決定するパラメーターを持つ汎用アイテムクラスを1つだけ持つこともできます。
ベンジャミンデンジャージョンソン

2
私がいることを指摘したいと思います星のカービィ64が、それはなんとかですので、ブルートフォースアプローチを取り、能力のすべての可能な組み合わせに対して異なる効果をハードプログラム。
ケビン-モニカの復活14

回答:


16

完全に組み合わせをコーディングする必要はありません。代わりに、各アイテムが提供するプロパティに集中できます。たとえば、アイテムAが設定しますProjectile=Fireball,Targetting=HomingアイテムBセットFireMode=ArcShot,Count=3ArcShotロジックの発送を担当してCountProjectile弧内の項目を。

これらの2つのアイテムは、これらの(または他の)プロパティを自由に変更する他のアイテムと組み合わせることができます。新しいタイプの発射体ArcShotを追加すると、自動的にで動作します。新しい発射モードを追加すると、発射Fireball体で自動的に動作します。同様に、Targetting発射物のFireMode作成中に発射物のコントローラを設定するプロパティは、発射物を作成するため、理にかなった任意の組み合わせで簡単かつ簡単に組み合わせることができます。

プロパティの依存関係などのセットもあります。たとえばArcShot、プロバイダを持っている必要がありますProjectile(これはデフォルトである可能性があります)。優先度を設定して、2つのアクティブなアイテムが両方ともProjectileコードを提供する場合、どちらを使用するかを認識できるようにします。または、ユーザーが使用する発射物のタイプを選択できるUIを提供するか、プレイヤーに不要な優先度の高いアイテムの装備を解除するよう要求するか、最新のアイテムを使用するなどを行うことができます。 、たとえば、両方がただ修正する2つのアイテムProjectileを同時に装備することはできません。

一般に、可能であれば、ゲーム内のオブジェクトなどに関しては、手続き型アプローチ(大きなif-else混乱)よりも、あらゆる種類のデータ駆動型アプローチ(または宣言型)をお勧めします。単純なデータで構成可能な高レベルの汎用ロジックは、特殊なケースのルールのハードコードされたリストよりもはるかに望ましいです。


小さいですが、使用している例のプロパティが正しくないようです。「スプーンベンダー」はホーミングを追加し、「インナーアイ」はトリプルショットを追加します。どちらもアークを追加せず、両方とも涙です。例で任意のプロパティを使用してデザインを抽象化する場合、誤解を招くような名前が付けられていなければ読みやすくなります。私はこれよりも「アイテムA」と「アイテムB」を好むでしょう。
ダニエルカプラン

1
@tieTYT:名前を一般化し、発射物の種類と発射モードに加えてターゲットモードを含めるように例を拡張しました。BoIを数分以上プレイしたことがないので、他の人のように内部化された名前はありません。:)
ショーンミドルディッチ

トリッキーな部分は、プロパティのカテゴリを識別することです。例:ターゲティングが1つ、FireModeが1つ、Countが3番目の場合もあればそうでない場合もあります。3つの発射体が火の玉であり、5つは手rena弾である可能性があります。
ダニエルカプラン

@tieTYT:絶対に。もちろん、システムをさらに拡張して、特別な組み合わせやロジックを許可することもできますが、それはルールではなく例外であるべきです。コーナーケースではなく、一般的なケース用に最適化します。
ショーンミドルディッチ

ここでは、手続き型プログラミングに関するしている間違った理由についての素晴らしいビデオだyoutu.be/QM1iUe6IofMは、また、あなたが記述データ駆動型の方法は、基本的にあなたが持っているどのように多くの特殊なケースのシナリオを減らすために何もしない、個々のデータセットに存在する場合/他のブロックをマップするにはあなたがそれらをすべてプログラムしなければならないので、プログラム
ルネサンス

8

OOP言語を使用している場合、これはDecoratorパターンを採用するのに適した場所のように聞こえます。攻撃の発生方法を変更する場合は、適切な拡張機能で攻撃を飾り付けてください。

粗c ++の例:

class AttackBehaviour
{
    /* other code */
    virtual void Attack(double angle);
};

class TearAttack: public AttackBehaviour
{
    /* other code */
    void Attack(double angle);
};

class TripleAttack: public AttackBehaviour
{
    /* other code */
    AttackBehaviour* baseAttackBehaviour;
    void Attack(double angle);
};

void TripleAttack::Attack(angle)
{
    baseAttackBehaviour->Attack(angle-30);
    baseAttackBehaviour->Attack(angle);
    baseAttackBehaviour->Attack(angle+30);
}

この方法は、非常に多くの攻撃があり、それらすべてをほぼ同じ方法で動作させる必要がある場合に最適です。モディファイアを使用した攻撃の方法を大幅に変更する場合(モディファイアを使用した新しいアニメーションなど)、この方法は適していません。


アニメーションを変更したい場合、なぜこれが私に合わないのですか?また、より機能的な言語で作業している場合はどうなりますか?彼らに合ったデザインパターンを知っていますか?
ダニエルカプラン

モディファイヤは常にAttack集約するオブジェクトのメソッドを呼び出すことになるため、より厳格です。TripleAttackクラスが知っているべきではないTearAttackクラス。これが本当なら、else-ifブロックと同じくらい頭痛になります。つまり、ティアアニメーションはTearAttackBehaviourオブジェクト内に存在する必要があります。このオブジェクトは、TripleAttackオブジェクトによって装飾されていることを認識しません(認識すべきではありません)。その結果、3つの涙アニメーション独立しているため独立して進行します。
NauticalMile

これを言葉で説明するのに苦労しています。他の誰かがそれを突き刺したい場合は、ゲストにしてください。
NauticalMile

これをより機能的な言語で実装することに関しては、しばらく考えて、準備ができたら答えを修正します。
NauticalMile

1

Binding of Isaacのファンとして、私もこのようなことをどのようにすればよいのか疑問に思っています。ゲーム内のシステムは、エフェクトの組み合わせから発生する行動に対して十分に堅牢です(私の頭に浮かぶのは、ミラー、スプーンベンダー、およびいくつかのレンジブースターの結果、Isaac、Magnetoスタイルの周りに渦巻く、ホーミング涙の壁をもたらすことです)。それらの膨大な数は、「if」ブロックを非実用的にします。

私の結論は、Isaacと彼の涙は、大規模なComponent-Entity Frameworkの中心にある2つのエンティティであるということです。エンティティにはいくつかの基本的な統計(移動速度、ライフ、範囲、ダメージ、スプライトなど)があり、各コンポーネントは統計修飾子と動詞をもたらします。

コードでは、Isaacと彼の涙にはそれぞれ、インターフェースの事柄を含むリストがあります。Isaacには、IsaacMutatorインターフェースにサブスクライブするもののリストと、涙のtearMutatorがあります。IsaacMutatorには、Isaacsのヘルス、速度、範囲、外観、および特別な動詞を変更する機能があります。TearMutatorも同様です。ゲームループごとに1回、IsaacはすべてのIsaacMutatorsをループし、すべての生きている涙もループします。英語の例で言えば、次のようになります。

Isaac has IsaacMutators:
--spoonbender which gives no stat change and: Tears are made homing
--MeatEater which give +1 health, +1 damage and: nothing
--MagicMirror which gives no stat change and: Tears are made reflecting

Tears have tearMutators:
--(depends on MeatEater) +1 damage and: nothing
--(depends on MagicMirror) no stat change and: +1 vector towards isaac
--(depnds on spoonbender) no stat change and: +1 vector towards enemytype

等々。タイプは付加的なものであるため、心のコンテンツを積み重ねたり、追加したり、削除したりできます。


-5

あなたのやり方が一番うまくいくと思います。これらの種類のアイテムはそれぞれ条件を提供し、一緒に使用すると異なる条件が生成されるため、3つの可能な条件すべてを定義する必要があります。

また、両方の項目が存在するときに新しいタイプの定義を作成することでそれを実行することもできますが、これは実際に畳み込みに追加されます。

if spoon bender and the inner eye then new spoon bender inner eye

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