Morikatron Engineer Blog

モリカトロン開発者ブログ

CEDEC2020の補足 ー格闘ゲーム編その2ー

どうも、モリカトロンのプログラミングおじさん、岡島です。
馬淵の記事で予告された通り、今回は僕から

用意した環境ではUE4の描画を切ることが出来ず(その辺りの話は岡島さんが書いてくれると思います)

の部分について補足説明をします。

UE4とPythonを通信させて学習環境を構築することについてはCEDiLにアップロードした資料の 付録2-1 に 簡単なまとめをしておりますので、そちらもご覧ください。

cedil.cesa.or.jp

最初に: 格闘ゲームAIの研究に使っていたマシンスペック

僕も馬淵も

  • CPU: AMD Ryzen 9 3900X
  • RAM: 80GB(ただし諸事情でシングルチャネル)
  • GPU: NVIDIA GeForceRTX 2070 Super (VRAM 8GB)

という、ゲーミングPCを学習に使っていました。 以降の話は、全てこのマシンを前提に話していきます。

格闘ゲームAIの分散学習の様子

Atari2600のゲームを現代のPCで実行すると超高速で実行可能だけど、現代のゲームではそうもいきません。
ってことは最初から分かっていたので、当初から格闘ゲームAIの学習を高速化するために、分散学習(これは馬淵が実装)を行うようにし、PC のスペックが許す限りゲームを並列動作させるようにしています。 その風景がこちら。

f:id:morika-okajima:20200910170337p:plain

非常ににぎやかですね。

上記のゲーミングPCで、最大で12のゲームを並列に動かせています。

もっとたくさんのゲームを並列で動かしたい

別に我々の格闘ゲームAIはゲームの画面を見て学習するわけではありません。僕たちもちゃんと動いているのなら画面を見る必要はありません。
そういう見る必要のないゲーム画面を描画するなら、その分大量に並列実行させ、学習時間の短縮をしたいところです。

ということで、Unreal Engine 4の起動オプションを使って描画をOFFに出来ないか試してみました。

先に結果を言うと、今回のゲームではうまくいきませんでした。

UE4 のゲームを画面なしで起動する方法

UE4 でヘッドレスモード…画面なしでプログラムを起動するを探してみると、-NullRHI オプションをつけて起動すると良いという情報が見つかりました。

historia.co.jp

公式でこのオプションを解説しているドキュメントを探せませんでしたが、どうやら自動テストのための機能としてこういうものが用意されているようです。

早速 -NullRHI オプションを有効にしてみると、描画の処理が行われないのでゲームのプロセスをより多く…具体的には32~64個程度 (4倍以上!) を 並列実行させることが出来、Python 側から通信で様子を見る限り、キャラクタの操作もできているようでした。

しかし、これを利用して学習をすると、なぜかうまく学習できずAIが強くなりません…

学習がうまくいかない理由の調査

ゲームが進行しないというような問題が起きているなら調査しやすいのですが、ゲームはパッと見(画面は見えないんですが)ちゃんと動いています。

調べてみると、描画を切るとなぜか通常攻撃を空振りする頻度が、通常よりも多くなっていました。 キャラクタの位置情報を考えると確実にヒットするはずの攻撃が、-NullRHI オプションが有効な場合にはなぜかヒットせず、これが学習に大きな悪影響を与えているようです。

攻撃がヒットしない…つまり衝突判定に何か違いがあるのでしょうか・・・

-NullRHI だとアクターのモーションが再生されない?

衝突判定に違いがあるという事で、キャラクタの体にセットしたコリジョンの座標を調べてみたところ、攻撃しても位置が変化しないことが分かりました。

キャラクタがパンチをしないので、パンチが当たらないのです。

検証

-NullRHI だとアクターのモーションが再生されないという予想を検証するために、サードパーソンのテンプレートプロジェクトを使って以下のような検証をしました。

  1. グレイマンはこのように止まっている状態でも停止時のモーションが再生されて微妙に動いています。 f:id:morika-okajima:20200910171433g:plain

  2. このグレイマンの右手にCubeをくっつけます。
    f:id:morika-okajima:20200910171805j:plain

  3. そして右手につけたCubeの位置を周期的にデバッグ出力します。
    f:id:morika-okajima:20200910172017p:plain

これで実行すると、グレイマンの右手に付いたCubeはモーションに合わせて微妙に位置が変化していくはずです。 f:id:morika-okajima:20200910173235p:plain

検証結果

上記を試してみた結果(Cube の座標)はこうなりました。

f:id:morika-okajima:20200910172318p:plain f:id:morika-okajima:20200910172329p:plain f:id:morika-okajima:20200910172340p:plain

-NullRHI オプションが有効だと座標が動かない=モーションが再生されていないですね。

これだけだと、グレイマンがどういうポーズの状態かは分かりませんが、とにかくモーションが再生されていないようです。

今回使用している格闘ゲームではキャラクタの体につけたコリジョンで衝突判定を行っているので、これは致命的・・・

これはバグ?

最初はエンジン側のバグだと思ったのですが、RHI(Render Hardware Interface)は各プラットホームのグラフィックスAPI…DirectXやOpenGL、Vulcanといったライブラリの上に位置する抽象化レイヤーのようです。

docs.unrealengine.com

スケルタルアニメーションはよほどの事情がない限り、高速なGPUで行うでしょうから、RHIがないモードでこういう挙動なのは当たり前と言えます。
-NullRHI オプションについての公式のドキュメントを見つけられていませんが、多分そういう事なんじゃないかと思います。

機械学習をすることだけを考えると、ゲームの挙動が全く変わらないことが保証され、かつ描画をしないモードがあると大変便利なのですが…

おわりに

そういう事情で、現状ではゲームの描画を切るのを諦めて最初に見せたスクリーンショットのようにたくさん起動したゲームプロセス全てで描画を行うようになっています。

欲を言えばもっとたくさんのゲームを並列実行したい=画面描画を OFF にしたいところですが、-NullRHI オプションが有効でも正しく動作するようにゲームの実装をごっそり変えるか、ゲームエンジンを改造するというハイコストな選択肢しか思い浮かびません。
ハイコストな対応をするのは大変なので、今後継続的に低コストで出来て効果のありそうな方法を考えて試していこうと思います。

おまけ

自分たちの CEDEC 2020 の発表後、フロムソフトウェアさんのセッション「ゲームAI研究をしたい! ゲームを持たない研究開発部署は、発売済みタイトルから実験場を生み出した!!」を受講したところ、画面描画を OFF にして ARMORED CORE VERDICT DAY が128並列で動いてました。

cedil.cesa.or.jp

フロムソフトウェアさんの事例で描画を OFF にしてもうまくいっている理由は、ARMORED CORE VERDICT DAY は様々なパーツを組み合わせて構築したロボットを操るゲームなので、スケルタルアニメーションではなく個々のパーツを(CPUで)回転させることでロボットを動かしているため 我々と同じような問題が起きないからではないかと思います。

こういったキャラクタのモーションをGPUで処理していないゲームでは、描画に関係する処理をすべて省略しても当たり判定には影響がありませんし、UE4の -NullRHI オプションでも問題なさそうですね。

これ以外にも学習を高速化するためにいろいろな最適化をしたからこそ128ものゲームを同時に動かせているのでしょう。そういう努力が大切なんだと痛感させられました。僕も頑張らねば…