おそらく、手続き型レベル生成についてはご存知でしょう。この投稿では、手続き型ミッション生成について取り上げます。ローグライク ゲーム向けの従来の機械学習とリカレント ニューラル ネットワークを使用してミッションを生成する全体像を説明します。
皆さん、こんにちは!私の名前は Lev Kobelev です。MY.GAMES のゲーム デザイナーです。この記事では、従来の ML とシンプルなニューラル ネットワークを使用した経験を共有し、手続き型ミッション生成を選択した理由と方法を説明します。また、Zombie State でのプロセスの実装についても詳しく説明します。
免責事項: この記事は情報提供/娯楽目的のみを目的としており、特定のソリューションを使用する場合は、特定のリソースの利用規約を慎重に確認し、法務担当者に相談することをお勧めします。
☝🏻 まず、用語について説明します。この文脈では、「アリーナ」、「レベル」、「ロケーション」は同義語であり、「エリア」、「ゾーン」、「スポーンエリア」も同様です。
さて、「ミッション」を定義しましょう。ミッションとは、特定のルールに従って敵が特定の場所に出現する、あらかじめ決められた順序のことです。前述のように、Zombie State では場所が生成されるため、「段階的な」体験は作成されません。つまり、あらかじめ決められたポイントに敵を配置するわけではありません。実際、そのようなポイントは存在しません。私たちの場合、敵はプレイヤーまたは特定の壁の近くのどこかに出現します。さらに、ゲーム内のすべてのアリーナは長方形であるため、どのミッションもどのアリーナでもプレイできます。
「スポーン」という用語を紹介しましょう。スポーンとは、指定されたゾーン内のポイントに、事前に決められたパラメータに従って同じ種類の敵が複数出現することです。1 つのポイントに 1 体の敵。エリア内に十分なポイントがない場合は、特別なルールに従ってエリアが拡張されます。ゾーンはスポーンがトリガーされたときにのみ決定されることも理解しておくことが重要です。エリアはスポーン パラメータによって決定されます。以下では、プレイヤーの近くと壁の近くの 2 つの例を考えてみましょう。
最初のタイプのスポーンは、プレイヤーの近くです。プレイヤーの近くでの出現は、セクターによって指定されます。セクターは、外部と内部の 2 つの半径 (R と r)、セクターの幅 (β)、プレイヤーに対する回転角度 (α)、および敵の出現の望ましい可視性 (または不可視性) によって表されます。セクター内には、敵に必要な数のポイントがあり、そこから敵が出現します。
2 つ目のタイプのスポーンは壁の近くです。レベルが生成されると、各サイドにタグ (基本方向) が付けられます。出口のある壁は常に北にあります。壁の近くに敵が出現するかどうかは、タグ、タグからの距離 (o)、長さ (a)、ゾーンの幅 (b)、および敵の出現の望ましい可視性 (または不可視性) によって指定されます。ゾーンの中心は、プレイヤーの現在の位置を基準にして決定されます。
スポーンはウェーブで出現します。ウェーブとはスポーンが出現する方法、つまりスポーン間の遅延のことです。一度にすべての敵をプレイヤーにぶつけたくないからです。ウェーブはミッションに結合され、特定のロジックに従って次々に開始されます。たとえば、2 番目のウェーブは最初のウェーブの 20 秒後 (またはウェーブ内のゾンビの 90% 以上が殺された場合) に開始できます。つまり、ミッション全体を大きなボックスと見なすことができ、そのボックス内には中サイズのボックス (ウェーブ) があり、ウェーブ内にはさらに小さなボックス (スポーン) があります。
そのため、実際のミッションに取り組む前に、いくつかのルールをすでに定義しています。
ある時点では、約 100 個のミッションが準備できていましたが、しばらくすると、さらに多くのミッションが必要になりました。他のデザイナーと私は、さらに 100 個のミッションを作成するために多くの時間と労力を費やしたくなかったので、ミッションを生成するための迅速で安価な方法を探し始めました。
すべてのジェネレーターは、一連のルールに従って動作し、手動で作成したミッションも、特定の推奨事項に従って実行されました。そこで、ミッション内のパターンに関する仮説を立て、そのパターンがジェネレーターのルールとして機能します。
✍🏻 本文中に出てくる用語の一部:
クラスタリングとは、特定のコレクションを重複しないサブセット (クラスター) に分割するタスクです。これにより、類似のオブジェクトは同じクラスターに属し、異なるクラスターのオブジェクトは大幅に異なるようになります。
カテゴリ特徴は、有限集合から値を取得し、数値表現を持たないデータです。たとえば、スポーン ウォール タグ (北、南など) などです。
カテゴリ特徴のコーディングは、事前に指定されたいくつかの規則に従って、カテゴリ特徴を数値表現に変換する手順です。たとえば、北 → 0、南 → 1 などです。
正規化とは、数値特性を前処理して、範囲の違いに関する情報を失うことなく、共通のスケールに合わせる方法です。たとえば、オブジェクトの類似性を計算するために使用できます。前述のように、オブジェクトの類似性はクラスタリングの問題で重要な役割を果たします。
これらすべてのパターンを手動で検索するのは非常に時間がかかるため、クラスタリングを使用することにしました。ここで役立つのが機械学習です。機械学習はこのタスクをうまく処理します。
クラスタリングは N 次元空間で機能し、ML は特に数値で機能します。したがって、すべてのスポーンはベクトルになります。
つまり、例えば「北側の壁のくぼみ 2 メートル、幅 10、長さ 5 の領域にゾンビ シューター 10 体をスポーンする」というスポーンは、ベクトル [0.5, 0.25, 0.2, 0.8, …, 0.5] になります (←これらの数字は抽象的です)。
さらに、特定の敵を抽象型にマッピングすることで、敵セットの威力が軽減されました。まず、このようなマッピングにより、新しい敵を特定のクラスターに割り当てることが容易になりました。これにより、最適なパターンの数を減らすことも可能になり、結果として生成精度が向上しました。これについては後ほど詳しく説明します。
クラスタリング アルゴリズムには、K-Means、DBSCAN、スペクトル、階層など、さまざまなものがあります。これらはすべて異なるアイデアに基づいていますが、目的は同じです。つまり、データ内のクラスターを見つけることです。以下では、選択したアルゴリズムに応じて、同じデータに対してクラスターを見つけるさまざまな方法を示します。
K-Means アルゴリズムは、スポーンの場合に最も優れたパフォーマンスを発揮しました。
ここで、このアルゴリズムについて何も知らない人のために少し余談します (この記事はゲーム開発に関するものであり、ML の基礎に関するものではないため、厳密な数学的推論はありません)。K 平均法は、各特徴から割り当てられたクラスターの平均値までの二乗距離の合計を最小化することにより、データを K 個のクラスターに繰り返し分割します。平均は、クラスター内二乗距離の合計で表されます。
この方法については、次の点を理解することが重要です。
2 番目のポイントをもう少し詳しく見てみましょう。
エルボー法は、最適なクラスター数を選択するためによく使用されます。考え方は非常に単純です。アルゴリズムを実行し、1 から N まで K をすべて試します。ここで、N は適切な数です。このケースでは 10 でした。これ以上のクラスターを見つけるのは不可能でした。次に、各クラスター内の距離の二乗の合計 (WSS または SS と呼ばれるスコア) を計算します。これをすべてグラフに表示し、y 軸の値が大幅に変化しなくなるポイントを選択します。
説明のために、よく知られているデータセットである
肘が見えない場合は、シルエット法を使用できますが、この記事の範囲外です。
上記および下記の計算はすべて、ML およびデータ分析用の標準ライブラリ (pandas、numpy、seaborn、sklearn) を使用して Python で実行されました。この記事の主な目的は技術的な詳細を説明することではなく、機能を説明することであるため、コードは公開していません。
最適なクラスター数を取得したら、各クラスターを詳細に調査する必要があります。クラスターに含まれるスポーンと、それらが取る値を確認する必要があります。今後の生成で使用するために、各クラスターに独自の設定を作成しましょう。パラメーターは次のとおりです。
クラスター設定について考えてみましょう。これは言葉で説明すると、「単純な敵がプレイヤーの近くのどこか、短い距離、おそらくは目に見える場所に出現する」というものです。
クラスター 1 テーブル
敵 | タイプ | r | Rデルタ | 回転 | 幅 | 可視性 |
---|---|---|---|---|---|---|
ゾンビ_コモン_3_5=4、ゾンビ_ヘビー=1 | プレーヤー | 10-12 | 1-2 | 0-30 | 30-45 | 表示=9、非表示=1 |
ここに 2 つの便利なトリックがあります:
これは各クラスターで実行されましたが、クラスターの数は 10 未満だったので、それほど時間はかかりませんでした。
このトピックについてはまだ少ししか触れていませんが、学ぶべき興味深い内容はまだたくさんあります。ここでは、データの処理、クラスタリング、結果の分析のプロセスについてわかりやすく説明した記事をいくつか紹介します。
スポーン パターンに加えて、生成時にこのパラメータを使用するために、ミッション内の敵の総体力とその完了予定時間の依存性を調査することにしました。
手動ミッションを作成する過程では、チャプターの調整されたペース、つまり短い、長い、短い、また短い、といった一連のミッションを構築することが課題でした。プレイヤーの予想 DPS とその時間がわかっている場合、ミッション内の敵の総体力をどのように取得できるでしょうか?
💡 線形回帰は、線形依存関数を使用して、ある変数の別の変数または他の複数の変数への依存性を再構築する方法です。以下の例では、1 つの変数からの線形回帰のみを検討します: f(x) = wx + b。
以下の用語を紹介します。
つまり、HP = DPS * アクション時間 + 自由時間です。マニュアルの章を作成するときに、各ミッションの予想時間を記録しました。次に、アクション時間を見つける必要があります。
予想されるミッション時間がわかっている場合は、アクション時間を計算し、それを予想される時間から引いて自由時間を得ることができます。自由時間 = ミッション時間 - アクション時間 = ミッション時間 - HP * DPS。この数値をミッション内の平均敵数で割ると、敵 1 体あたりの自由時間が得られます。したがって、残っているのは、予想されるミッション時間から敵 1 体あたりの自由時間への線形回帰を構築するだけです。
さらに、ミッション時間からのアクション時間の割合の回帰を構築します。
計算例を見て、なぜこれらの回帰が使用されるのかを見てみましょう。
ここで疑問が湧きます。なぜ敵の自由時間を知る必要があるのでしょうか? 前述のように、スポーンは時間順に並べられています。したがって、i 番目のスポーンの時間は、(i-1) 番目のスポーンのアクション時間とその中の自由時間の合計として計算できます。
ここで別の疑問が浮かびます。なぜ行動時間と自由時間の割合は一定ではないのでしょうか?
このゲームでは、ミッションの難易度はミッションの期間と関係があります。つまり、短いミッションは簡単で、長いミッションは難しくなります。難易度パラメータの 1 つは、敵ごとの自由時間です。上のグラフには複数の直線があり、それらの傾き係数 (w) は同じですが、オフセット (b) が異なります。したがって、難易度を変更するには、オフセットを変更するだけで十分です。b を増やすとゲームは簡単になり、減らすと難しくなり、負の数も許可されます。これらのオプションは、章ごとに難易度を変更するのに役立ちます。
私は、すべてのデザイナーが回帰の問題を詳しく調べるべきだと考えています。回帰は他のプロジェクトを分解する際に役立つことが多いからです。
これで、ジェネレータのルールを見つけることができたので、生成プロセスに進むことができます。
抽象的に考えると、どのミッションも数字のシーケンスとして表すことができ、各数字は特定のスポーン クラスターを反映します。たとえば、ミッション: 1、2、1、1、2、3、3、2、1、3。つまり、新しいミッションを生成するタスクは、新しい数字のシーケンスを生成することになります。生成後は、クラスター設定に従って各数字を個別に「拡張」するだけです。
シーケンスを生成する単純な方法を考えると、特定のスポーンが他のスポーンに続く統計的確率を計算できます。たとえば、次の図が得られます。
図の上部は、それが導くクラスター、つまり頂点であり、エッジの重みは、次のクラスターの確率です。
このようなグラフをたどっていくと、シーケンスを生成できます。ただし、このアプローチにはいくつかの欠点があります。たとえば、メモリが不足している (現在の状態しか認識できない) ことや、統計的にその状態に戻る確率が高い場合に、1 つの状態に「とどまる」可能性があることなどが挙げられます。
✍🏻 このグラフをプロセスとして考えると、単純なマルコフ連鎖が得られます。
ニューラル ネットワーク、特にリカレント ネットワークに目を向けてみましょう。リカレント ネットワークには、基本的なアプローチの欠点がありません。これらのネットワークは、自然言語処理タスクにおける文字や単語などのシーケンスをモデル化するのに優れています。簡単に言うと、ネットワークは、シーケンスの前の要素に基づいて次の要素を予測するようにトレーニングされます。
これらのネットワークがどのように機能するかについては、非常に大きなトピックであるため、この記事では説明しません。代わりに、トレーニングに必要なものを見てみましょう。
N=2、L=3、C=5 の簡単な例です。シーケンス 1、2、3、4、1 を取り、その中に長さ L+1 のサブシーケンス [1、2、3、4]、[2、3、4、1] を探します。シーケンスを L 文字の入力と回答 (ターゲット) - (L+1) 番目の文字* に分割します。* たとえば、[1、2、3、4] → [1、2、3] および [4] です。回答をワンホット ベクトル [4] → [0、0、0、0、1] にエンコードします。
次に、Tensorflow または PyTorch を使用して、Python で簡単なニューラル ネットワークをスケッチします。以下のリンクを使用して、これがどのように行われるかを確認できます。残っているのは、上記のデータでトレーニング プロセスを開始し、待機することだけです。その後、本番環境に移行できます。
機械学習モデルには、精度などの特定の指標があります。精度は、正しく与えられた回答の割合を示します。ただし、データにクラスの不均衡がある可能性があるため、注意して見る必要があります。クラス不均衡がまったくない場合 (またはほとんどない場合)、ランダムよりも正確に回答を予測する場合、つまり精度 > 1/C の場合、モデルは適切に機能していると言えます。1 に近い場合は、非常にうまく機能しています。
私たちの場合、モデルは良好な精度を示しました。これらの結果の理由の 1 つは、敵をそのタイプとバランスにマッピングすることで達成されたクラスターの数が少ないことです。
興味のある方のために、RNN に関するその他の資料を以下に示します。
訓練されたモデルは簡単に
モデルを操作するために、Unity にカスタム ウィンドウが作成され、ゲーム デザイナーはここで必要なすべてのミッション パラメータを設定できます。
設定を入力したら、あとはボタンを押して、必要に応じて編集できるファイルを取得するだけです。はい、ゲーム中ではなく事前にミッションを生成して、微調整できるようにしたかったのです。
生成プロセスを見てみましょう。
したがって、これはミッションの作成を数倍高速化するのに役立つ優れたツールです。さらに、数秒で既成のソリューションを入手できるようになったため、一部のデザイナーはいわゆる「ライターズ ブロック」の恐怖を克服するのにも役立ちました。
この記事では、ミッション生成の例を使用して、従来の機械学習手法とニューラル ネットワークがゲーム開発にどのように役立つかを説明しようとしました。最近では生成 AI への大きなトレンドがありますが、機械学習の他の分野も忘れないでください。それらにも多くの可能性があります。
この記事を読んでいただきありがとうございます。生成された場所でのミッションへのアプローチとミッションの生成の両方について理解していただけたら幸いです。新しいことを学ぶことを恐れず、自分自身を成長させ、良いゲームを作りましょう。
イラスト:shabbyrtist