こんにちは!私は Vladimir Popov、War Robots プロジェクトのクライアント開発者です。この記事の執筆時点では、War Robots は数年前から存在しており、この間に数十の新しいメカがゲームに登場しました。当然のことながら、ロボットのさまざまな能力は重要です。なぜなら、ロボットのさまざまな能力がなければ、ロボットの独自性が失われ、ゲームがより面白くなるからです。
この投稿では、ゲームプレイ アビリティ システムがどのように機能するか、そしてそれがどのように進化したかについて説明します。また、わかりやすくするために、あまり技術的な詳細は省き、簡単な言葉で説明します。
まず、プロジェクトの歴史を詳しく調べて、現在は使用されていない古い実装を見てみましょう。
以前は、アビリティは非常に単純な方法で設計されていました。つまり、アビリティはロボットに接続された 1 つのコンポーネントを持っていました。これは、プログラマーが能力がどのように機能するか、つまりその流れと他の能力との相互作用の両方を完全に説明した構造でした。すべてのロジックは 1 つのコンポーネント内に記述されており、ゲーム設計者はこれをロボットに接続し、必要に応じてパラメーターを構成するだけで済みます。能力の流れを変更することはできなかったということは言及する価値があります。ゲームデザイナーが変更できるのはパラメータとタイミングだけでした。
古い能力は、アクティブと非アクティブの 2 つの状態でのみ存在でき、各状態にアクションを割り当てることができました。
ロボット「ストーカー」が以前持っていた「ジャマー」能力の例を見てみましょう。それは次のように機能しました:
長い間、この機能は私たちにとって十分でしたが、時間が経つにつれて、ゲーム デザイナーとプログラマーの両方がこのアプローチに満足できなくなりました。コードが巨大になったため、プログラマーがこれらの機能をサポートするのが困難になりました。これには非常に長いレガシーチェーンが関係しており、あらゆる状況を説明する必要がありました。さらに、ゲーム デザイナーには柔軟性がありませんでした。能力に変更を加えるには、たとえ隣接する能力が同じ機能を持っていたとしても、プログラマーに変更を指示する必要がありました。
そこで、何かを変える必要があることに気づきました。そこで、各能力を複数の関連オブジェクトのセットとして表す新しいシステムを開発しました。機能は状態能力と状態コンポーネントに分割されました。
どのように機能するのでしょうか?すべての能力には主な目的があります。この中心的なオブジェクトは、他の能力オブジェクトを外の世界に接続し、またその逆も同様です。すべての主要な決定もそれによって行われます。
状態はいくつでも存在できます。基本的に、この反復の状態は古いバージョンのアクティブ/非アクティブ状態とあまり変わりませんが、現在は任意の数の状態が存在する可能性があり、その目的はより抽象的になっています。アビリティは一度に 1 つの状態しかアクティブにできないことに注意してください。
古いシステムと比較した主な革新はコンポーネントでした。コンポーネントは何らかのアクションを記述し、各状態は任意の数のコンポーネントを持つことができます。
新しい能力はどのように機能しますか?能力はいずれかの状態にのみ存在できます。メインオブジェクトはそれらを切り替える責任があります。状態にリンクするコンポーネントは、状態のアクティブ化/非アクティブ化に反応し、これに応じて、何らかのアクションの実行を開始したり、実行を停止したりできます。
すべてのオブジェクトがカスタマイズ可能になりました。ゲーム設計者は、状態とコンポーネントを自由に組み合わせることができるため、事前にインストールされたブロックから新しい機能を構成できます。現在、プログラマは、新しいコンポーネントまたは状態を作成するために画像を入力するだけで済みます。これにより、コードの作成がはるかに簡単になります。プログラマは小さなエンティティを操作し、いくつかの単純な要素を記述し、機能を自分で構築する必要はなくなりました。現在、ゲーム デザイナーがこれを行っています。
流れはこんな感じになりました。
以降、この手順が何度も繰り返される。使いやすくするために、状態はコンポーネント コンテナとして機能するだけでなく、いつ別の状態に切り替えるかを決定し、メイン オブジェクトに切り替えを要求します。時間が経つにつれて、これではまだ不十分であることが判明し、能力図は次のように変換されました。
主要なオブジェクト、状態、コンポーネントはそのまま残りますが、新しい要素も追加されました。
最初に目を引くのは、各状態とコンポーネントに条件を追加したことです。状態の場合、これらは状態を離れるための追加要件を定義します。コンポーネントの場合、コンポーネントがそのアクションを実行できるかどうかを決定します。
チャージ コンテナにはチャージが含まれており、チャージを再チャージし、必要に応じて再チャージを停止し、使用するステートにチャージを提供します。
タイマーは、複数の状態に共通の実行時間を設定する必要があるが、独自の実行時間が定義されていない場合に使用されます。
すべての能力オブジェクトはオプションであることに注意することが重要です。技術的には、機能が機能するには、メイン オブジェクトと 1 つの状態のみが必要です。
実際、プログラマーの関与なしに完全に構築される機能はそれほど多くありませんが、プログラマーは非常に小さなものだけを記述するだけで済むようになったため、開発全体が著しく安価になりました。たとえば、1 つの新しい状態や 2 つのコンポーネント – 残り再利用されます。
私たちの能力の構成要素を要約してみましょう。
メイン オブジェクトはステート マシンの機能を実行します。状態とコンポーネントに世界に関する情報を提供し、世界に能力に関する情報を提供します。メイン オブジェクトは、状態、コンポーネント、および機能のサービス部分 (料金と外部タイマー) の間のリンクとして機能します。
状態は、メイン オブジェクトからのアクティブ化および非アクティブ化コマンドをリッスンし、それに応じてコンポーネントをアクティブ化および非アクティブ化し、またメイン オブジェクトに別の状態に切り替えるように要求します。状態は、いつ次の状態に切り替える必要があるかを決定します。これを行うために、プレイヤーが能力ボタンをクリックしたかどうか、状態のアクティブ化から一定時間が経過したかどうかなどの内部条件と、状態に関連付けられた外部条件を使用します。
コンポーネントは状態からのアクティブ化および非アクティブ化コマンドをリッスンし、何らかのアクション (個別または長期) を実行します。アクションはまったく異なる場合があります。ダメージを与えたり、味方を回復したり、アニメーションをオンにしたりする場合があります。
条件は、目的の要素がどのような状態にあるかを確認し、これを状態またはコンポーネントに報告します。条件は複雑になる場合があります。条件が満たされない場合、状態は別の状態への遷移を要求しません。また、条件が満たされない場合、コンポーネントはアクションを実行しません。条件はオプションのエンティティです。すべての能力がそれらを持っているわけではありません。
電荷コンテナは電荷を保持し、再充電し、必要に応じて再充電を停止し、各状態に電荷を提供します。これは、プレイヤーが連続してn回を超えてそれを数回使用できるようにする必要がある場合に、マルチチャージ能力で使用されます。
タイマーは、複数の状態が共通の継続時間を持っているが、それぞれの状態がどれくらい続くかは不明な場合に使用されます。どの状態でもn秒間のタイマーを開始できます。関連するすべての状態はタイマー終了イベントをサブスクライブし、終了時に何かを実行します。
さて、能力図に戻りましょう。その機能はどのように変化しましたか?
州は追加の移行条件として料金を使用できます。このような遷移が発生すると、電荷の数が減少します。各州は共通のタイマーを使用することもできます。この場合、それらの実行の合計時間はタイマーによって決定され、各状態は個別にいつでも継続できます。
新しい能力 UI のために車輪を完全に再発明したわけではありません。
メインオブジェクトには独自の UI があります。これは、UI 内に常に存在する必要があり、現在アクティブな状態に依存しないいくつかの要素を定義します。
各状態には UI 内に独自のペアがあり、状態 UI はその状態がアクティブな場合にのみ表示されます。状態に関するデータを受信し、何らかの方法でそれを表示できます。たとえば、期間の状態の UI には通常、残り時間を表示するバーとテキストがあります。
ステートが能力を継続するための外部コマンドを待っている場合、その UI にはボタンが表示され、それを押すとコマンドがステートに送信されます。
具体的な例を使用して、能力がどのように機能するかを見ていきます。まずは「インクイジター」というロボットを見てみましょう。互いに続く 4 つの状態があります。状態の上に UI で表示されているのがわかります。そのうちの 2 つについては、それらに属するコンポーネントも表示されます。他の 2 つの州にはコンポーネントがありません。
アビリティの流れは次のとおりです。
すべては「WaitForClick」状態から始まります。現時点では、この能力は何も行いません。コマンドを待つだけです。
このようなコマンドを受信するとすぐに、メイン オブジェクトは状態を切り替えます。次のアクティブな状態は「WaitForGrounded」です。
この状態にはいくつかのコンポーネントがあるため、アクティブ化すると、ロボットはジャンプし、サウンドとアニメーションを再生します。特に、この状態がアクティブである間、ロボットはジャマー効果の影響を受け、ロボットを狙うことが禁止されます。
ロボットが着地すると、その能力は次の状態に移行します。
この状態には 3 つのコンポーネントがあります。すでにおなじみのサウンドとジャマー、そして半径n内のすべてのプレイヤーに対してカメラを振るシェイクです。
この状態にはDurationがあるため、 n秒間動作し、その後、能力は次の状態に移行します。
最後の状態にも期間が付いていますが、コンポーネントはなく、通常のクールダウン中です。
完了すると、能力は最初の状態に戻ります。
もう一つの例は「ファントム」です。 Inquisitor によく似ていますが、いくつかのニュアンスがあります。
まずは「WaitForClick」から始めます。
次に、Duration ではテレポートが設置され、メカのステータスが変更され、サウンドとアニメーションが再生されます。
この後:DurationOrClick では、メカのステータスが変更され、アニメーションと FX が再生されます。
クリックが行われると、別のデュレーションに移動し、メックがテレポートし、ステータスが変更され、アニメーション、FX、サウンドが再生されます。
この状態の後 (または、DurationOrClick の時間が経過した後)、Duration に進みます。
ここでの主な違いは、分岐のある状態が表示されることです。指定された時間が経過すると、DurationOrClick は状態 A に進み、プレイヤーが以前にアビリティ ボタンを押した場合は状態 B に進みます。
私たちのシステムは単純なものから非常に複雑なものに進化したように見えますが、この変化により、プログラマーとゲーム デザイナーの両方の作業が簡素化されました。現在、小さなコンポーネントを追加する場合は主にプログラマーの支援が必要ですが、後者のグループのチーム メンバーはより大きな自主性を獲得し、既存の状態やコンポーネントから新しい機能を独立して組み立てることができるようになりました。別のボーナスとして、同時に、プレイヤーはメカのより多様で複雑な能力の形で利益も受け取りました。