ゲームAI -基礎編- 『知識表現と影響マップ』


みなさん、こんにちは!
突然ですが…皆さんには、ひいきにしている
ゲームのキャラクターはいらっしゃいますでしょうか。

手ごわいボス敵や頼れるパートナー、愛嬌のある動きをするモンスター達は
一体どのような仕組みで動いているのでしょう?

今回の記事ではそんなゲームの中のキャラクター達を
魅力的に動かす仕組み、AIについて御紹介したいと思います。

改めまして本記事を担当させて頂きます、Cygamesエンジニアの佐藤です。
これまでコンシューマ機でのゲームAI開発に携わり、
ゲームならではのキャラクター表現の楽しさを追いかけてきました。

このブログを通じて、皆さんのゲームのキャラクターを
より表情豊かに魅力的なものにする方法について、皆さんと一緒に考えていければ幸いです。
今回はゲームのAIをデザインするにあたって重要となる、
「知識表現を定義する」というステップと、
知識表現の一つである影響マップについて御紹介したいと思います。

知識表現とは?

ゲームの中のAIは人間と同じような
認知・思考の能力を持ちません。(少なくとも今はまだ)

そのため、ゲームAIをデザインするにあたっては、
ゲームの状況をAIが把握するのに適したデータ構造・仕組みを考える必要があります。

AIが主観的に認識している世界のことを、生物学の概念にならって「AIの環世界」、
環世界を構成するAIにとっての知識の表現方法を「AIの知識表現」と呼びます。

ゲームAIを動かすにあたっては、
各ゲームのコンセプトに基づいて求められるAIの特性を踏まえた上で、
AIの知識表現をどのように定義するかが、重要になってきます。

影響マップとは

影響マップは、AIの知識表現の一つで、
敵の脅威度や戦略的な重要度など、AIの行動の指標となる評価を
空間上に直接マッピングしたものです。

影響マップがあると、AIのポジショニングを考えるのに非常に便利!!
敵の射線を考慮して有利なポジションを確保しながら、
目的地に向かうような、高度な挙動が可能になります。

ワークケース

では実際に簡単なワークケースに沿って、AIの要件定義の流れと、影響マップの適用効果について、見ていきたいと思います。

ゲームルール

下記のような簡単なルールの4人対戦のアクションゲームのAIを考えてみましょう。

【ルール1】
マップ上にポップするりんご(アイテム)を自分の家(スタート地点)に持ち帰ると得点。
制限時間内に一番多くの得点を集めたプレイヤー(女の子)の勝ち。

【ルール2】
お化けに見つかって捕まると減点。スタート地点からやり直し。
他のプレイヤーに体当たりされると、一定時間硬直。

【ルール3】
一度にたくさんのりんごを家に持ち帰ると高得点。
ただし、一度に運ぶりんごが多いほど、移動の速度は遅くなる。

プレイヤーの対戦相手として、他のプレイヤーをAIで動かすことを考えた場合、
どのように設計するのが良いでしょうか…

NGケース

まずは考えやすいように、最も単純な行動ルーチンとして、
最短経路でりんごに向かって、りんごを拾い続けるAIを考えてみます。

動きが簡単に読めるので、お化けや他のプレイヤーの格好の餌食になりそうですね…。

周囲の状況にまるで気を配っていないように見えるので機械的に映りますし、
なにより「プレイヤーに無関心な相手」というのは、対戦していて面白くなさそうです。

AIにどうさせたい?

対人戦でプレイヤーがどのような行動をとるか、
どのようにして他のプレイヤーに干渉するかを軸に考えてみましょう。

お化けの死角や、周囲のプレイヤーの動向に気を配りつつ、
他のプレイヤーを牽制しながらアイテムを狙いに行ったり…
お化けを他のプレイヤーに押し付けたり…

といった動きをAIがとっているように見えれば、
先ほどよりも”らしい”対戦相手になりそうですね!

状況の目まぐるしく変化するタイプのゲームですので、
AIにも機敏な判断が求められそうです。

全てを条件分岐だけで組むのは大変そう…

1. 条件分岐の数が膨大
「お化けが3マスに以内にいたら…」
「三方を他のプレイヤーに囲まれていたら…」などと考えていくと、きりがなさそうです…
自然な動きに調整するのは大変そうですし、あまり現実的ではありません。

2. 意思決定の境界となる条件がプレイヤーにばれやすくなる
ゲームコンセプトによっては良い方向に働きますが、
何度も同じステージで繰り返し対戦することが前提となるボンバーマンライクなゲームの場合、
AIのルーチンが見切りやすいと似たようなゲーム展開を導きやすくなるため、
プレイヤーの飽きが早くなってしまいます。

3. 調整の度に、ロジックの修正が必要
ロジックとスクリプトに強いプランナーさんの手が、いつでも空いているとは限りません。
スクリプト自体の学習コストや整備コストを考えると、
ロジックを直接修正しなければならない状況は最小限にとどめたいところです。

必要なAIの特性

以上の点を踏まえると

  • 状況の変化が流動的⇒イテレーションの高速な意思決定が必要
    ※長期の状況予測に基づいて行動計画をたてるようなアルゴリズムは不向き
  • 場の状況の変化を汲み取った行動・経路移動
  • 調整が容易ととなるよう、可能な部分はパラメータ駆動化

あたりがあると良いでしょうか。
上記三点を満たす方向で、必要な実装を考えてみます。

ポジショニングをベースに考えてみる

ここでAIの知識表現として、影響マップを利用すると場の状況の評価を簡易化できるため、
アルゴリズムを単純化しつつ、前述の要件を満たすことができます。

下記のような指針でAIを動かしてみましょう。

一手先に、今より有利なポジションを確保するように動けば、
二手先以降に、より有利な選択が取れる可能性が高くなるはず。

AIの評価指標に基づいて影響マップを作成し、
近傍で最も評価の高いセルを割り出す。

評価の高いセルへの移動を短期的に繰り返す。

意思決定の間隔が短くなるため、状況の変化に強くなりますし、
短期的な状況予測を行いながら動いているように見えるため、
プレイヤーから見て、一貫性のある”意図”をもった行動が発生しやすくなります。

AIの評価指標の洗い出し

影響マップを作成するためには、
AIの行動判断の指標となる要素を洗い出す必要があります。

今回のケースでは

  1. お化けの脅威度(どれだけ発見されやすいか)
  2. アイテムの狙いやすさ
  3. 他のプレイヤーの狙いやすさ・脅威度
  4. 家への帰還のしやすさ

の4つが指標になりそうです。
評価の指標は必要最小限となるよう切り出しましょう。

AIのコントロールとメンテナンスが容易になります。

お化けの脅威度をマッピングする

では実際にお化けの脅威度をマッピングしてみましょう。
地点ごとのお化けから見つかりにくさを、

  • お化けからの最短距離
  • お化けから見て視線が通るか
  • お化けの索敵範囲

の三点で評価します。

脅威度マップの適用効果

具体的な作成方法は後述するとして、まず適用効果を見てみましょう。

前述の指標を元に作成した脅威度のマップに
先ほどのアルゴリズムを適用したのが、下図の動きです。

画面隅に追い詰められても、敵からの遮蔽を確保するようにしながら、
するりと抜け出すように逃走する動きになっています。

敵の反対方向に距離をとるような単純なアルゴリズムに比べて、
少し賢そうに見えますね。

脅威度マップの作成手順

1. お化けからの距離を評価

各セルごとのお化けからの最短距離を算出し、
0.0~1.0の範囲に正規化したもの(距離マップ)を作成します。

このようなケースでは、ダイクストラ法を利用すると、
一度の探索で全てのセルの最短距離を求めることができるので効果的です。

2. 視線の遮蔽を評価

お化けから見て、視線の通らないセルの評価を低減します。

■均一格子と直線の交差判定の高速化■
垂直軸と直線との交差点間の距離 tx、水平軸と直線との交差点間の距離 tyは常に一定です。
最初の交点までの距離が求まっていれば、次に辿るべきセルは単純な比較と加算で割り出せます。

    int curCell_X = *; // 現在のセルの水平軸インデックス( 初期セルのインデックスを代入 )
    int curCell_Y = *; // 現在のセルの垂直軸インデックス( 初期セルのインデックスを代入 )
    int dir_X = 1; //直線の水平軸方向の進行方向
    int dir_Y = 1; //直線の垂直軸方向の進行方向

    float next_X = *: //次の垂直軸との交差点までの、始点からの距離
    float next_Y = *; //次の水平軸との交差点までの、始点からの距離

    float dist = *; //始点から終点までの距離
    float t = 0; //始点から現在の交点までの距離

    int tx = *; //水平軸と直線との交差点間の距離
    int ty = *; //垂直軸と直線との交差点間の距離

    while( t < dist ){
        if( next_X <= next_Y ){ //水平軸との交点の方が近い
          curCell_X += dir_X; t = next_X; next_X +=tx; //水平軸方向に交点を辿る
        }else{ //垂直軸との交点の方が近い
          curCell_Y += dir_Y; t = next_Y; next_Y +=ty; //垂直軸方向に交点を辿る
        }

        //curCell_X、curCell_Yが示すセルの状態を見る
    }

 

3. 索敵範囲を反映

お化けの正面視野の領域の値を上昇させます。

最後に再度値を正規化した後、反転させて完成!

アイテムの狙いやすさをマッピングする

アイテムの狙いやすさについてもマッピングしてみましょう。

各アイテムごとに距離マップを作成し、
各セルごとに評価値のMaxを取る形で合成します。
評価値は0.0~1.0の間になるよう正規化しておきましょう。

先ほどのアルゴリズムを適用してみます。

アイテムを素早く回収して回る動きになりました。

影響マップを合成する

お化けの脅威度マップと、アイテムの狙いやすさマップを積和合成してみましょう。
どのような動きになるでしょうか?

この際、正規化されたマップを係数の和が1.0になるように積和合成することで、
出来上がるマップも必ず正規化されたものになります。

各マップの係数がどの程度、結果に影響を与えるかの基準が統一されるため、
レベルコントロールが容易になります。

障害物を利用して敵を躱しながら、アイテムを回収してまわる動きになりました!

このように複数の評価指標を合成したマップを利用することで、
単純な実装で、少し高度なAI表現を行うことができます。

他のプレイヤーの脅威度や家への帰還のしやすさについても、
同様に影響マップを作成してやれば、より面白い動きがとれそうですね!

マップ合成時の係数を動的に変更

影響マップの合成時に、各マップごとの係数(どの評価指標を重視するか)を
変化させることで、AIの行動(性格)を変化させることができます。

※逃走を重視する臆病なAI、アイテムの回収を重視する攻撃的なAI…

またゲームの状況にあわせて、動的に係数を変更することで、
臨機応変な対応を行うAIの表現を容易に実現することができます。

影響マップの適用効果のまとめ

今回のワークケースにおいて、
影響マップを利用することによって生まれたメリットをまとめます。

  • プレイヤーから見て”意図”が感じられ、かつ場の状況に応じた
    非パターン的なファジーなAIの動きを演出できる
  • パラメータ駆動なので、調整しやすい
  • 実装がシンプル

影響マップの高速化

処理の高速化についても、軽く見ておきましょう。

  • 評価値をマップの各セルごとに割り出すため、並列処理に向きます。
  • 事前計算可能な要素は、あらかじめ静的にマップに焼き込んでおくのも手です。
    障害物の位置などは不動であるため、距離・遮蔽の計算を適用したマップなどは事前作成できます。

ゲームAIの知識表現と影響マップ、いかがだったでしょうか?

ほんの少しの工夫と発想の転換の積み重ねで、
キャラクターの動きはぐっと面白くなりますよ。

ゲームAIのデザインに正解はありません。
そのゲームのコンセプト、構造、キャラクター性、ユーザーの目線を十二分に理解しようと努めること。
その上で、そのゲームの特性に合わせたAIのデザインを行うことが重要だと思っています。

より多くの分野でのゲームAIの要件定義についてのノウハウを、
業界全体で共有できれば、より素晴らしいゲーム体験をユーザーに届けることができるのではないでしょうか。
皆さんのゲームAIについての御意見・アイディアについて、ぜひお伺いしたく思います。

今後ともゲームAIについての情報を発信していきたいと思いますので、よろしくお願いします!