やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Unity Multiplayer Play Mode (MPPM) のVirtual PlayerをAnjinで自動プレイさせてみた

Unity 6以降で利用できる Multiplayer Play Mode (以下MPPM)パッケージを使うことで、Unityエディターに加え最大3つのVirtual Playerを起動してマルチプレイ対応ゲームの動作確認を簡単に行えるようになりました。

とはいえ、複数のVirtual Playerウィンドウを切り替え切り替え操作するのは煩わしいので、Unityエディター上で動作するオートパイロットフレームワークを使って操作を自動化してみました。 使用したのは、株式会社ディー・エヌ・エーが開発・OSSとして公開している Anjin(com.dena.anjin)パッケージです。

github.com

題材には、Unity社が公開しているNetcodeサンプルの2Dシューター GalacticKittens を使用しました。MPPMとAnjinを組み込んだforkを公開しています。

github.com

前提バージョン

  • Unity 6000.0.23f1
  • MPPMパッケージ v1.3.1
  • Anjinパッケージ v1.7.0 v1.8.0
  • Input Test Helperパッケージ v1.0.1

GalacticKittensの修正

本題に入る前に、GalacticKittensを若干修正しました。詳細は割愛します。

  1. Unity 6へのアップグレード
  2. TextMesh Proアップデートに伴ない、TMP Essensialsの更新(再インポート)
  3. Physics 2DのGravity.Yを0に変更

MPPMの導入

MPPMはパッケージマネージャからインストールできます。 "Unity Registry" で "multiplay" で検索すれば見つかります。

インストールしたら、Window > Multiplayer > Multiplayer Play Mode でウィンドウを開き、Virtual Playerを有効化できます。 下図では、Editorだけに host タグ、Virtual Playerに join タグを付けています。 タグについては後述します。

Virtual Playerを有効化(チェックボックスをonにした後、表示が "Active" に変わる)した時点で、対応するVirtual Playerウィンドウが開きます。この状態でエディター側で再生モードに入ると、各Virtual Playerでもゲームが再生されます。

ここまでで、手動でマルチプレイの動作確認ができるようになりました。

Anjinの導入

Anjinパッケージのインストール

Anjinパッケージのインストールは、openupm-cli を使用して次のコマンドで行なうのが簡単です。

openupm add com.dena.anjin

Package Managerウィンドウによるインストール方法は、リポジトリのREADMEを参照してください。

GalacticKittensの追加修正(InputをDI可能にする)

続いて、GalacticKittens側のいくつかのクラスに修正を加えます。 GalacticKittensはキー入力判定にInput Manager (legacy) を使用しており、このままではAnjinから入力を操作できません。 そこで、Input Test Helperパッケージを利用してスタブを注入できるようにします。

Input Test Helperパッケージのインストールは、同様に次のコマンドで行ないます。

openupm add com.nowsprinting.test-helper

今回、Inputを置き換えたのは次の3ファイルです。

  • Assets/Scripts/Managers/MenuManager.cs
  • Assets/Scripts/Player/PlayerShipMovement.cs
  • Assets/Scripts/Player/PlayerShipShootBullet.cs

いずれも、クラスに次のプロパティを追加するだけです。

public IInput Input { private get; set; } = new InputWrapper();

Input Test Helperパッケージについて詳しくは拙著『Unity Test Framework完全攻略ガイド 統合テスト編』第4章を参照してください。

www.nowsprinting.com

オートパイロットの設定

Anjinの設定は、Sceneごとに操作を担当するAgentを割り当てることで行ないます*1。 設定のルートは Assets/Autopilot/Settings/AutopilotSettings ファイルで、これをInspectorウィンドウで開いて編集します。

今回は次のように設定しました(図はScene Agent Mappingのみ抜粋)。

カスタムAgent MenuAgent を実装しました。 Menuには2つの状態があり、状態を見て振る舞いを変えています。

  1. "PRESS ANY KEY TO START"と表示されているとき:MenuManager の参照している Input にスタブを注入し、"ANY KEY" が押されている状態を返します。これで次の状態に遷移します。
  2. "HOST" か "JOIN" を選択:Virtual Playerに設定されているタグに応じて、"HOST" か "JOIN" ボタンをクリックします。タグは、CurrentPlayer.ReadOnlyTags() で取得できます。

CharacterSelection

ビルトインの UGUIPlaybackAgent を使用して、"READY" ボタンをクリックします。

キャラクターは4種類いますが、マルチプレイヤーがJoinしたときに異なるキャラクターが選択された状態になっているため、そのまま変更せず次に進みます。

Gameplay

カスタムAgent GameplayAgent を実装しました。 移動を行なう PlayerShipMovement と、弾を発射する PlayerShipShootBullet それぞれの参照している Input にスタブを注入し、ランダムな入力を与えています。

自動プレイの目的が攻略であるなら、ランダム入力でなく賢いAIを実装したり、ML-Agents機械学習エージェントを使います。

Defeat および Victory

ビルトインの UGUIPlaybackAgent を使用して、"MENU" ボタンをクリックします。

Bootstrap および Controls

いずれも勝手に次のsceneに遷移するため、Agentを割り当てていません。

オートパイロットの実行

MPPMウィンドウでVirtual Playersの設定が済んだ状態で(ひとつは host タグが必要です)、次の操作で実行できます。

  1. Assets/Autopilot/Settings/AutopilotSettings をInspectorウィンドウで開く
  2. 下のほうにある "Run" ボタンをクリック

これで再生モードに切り替わり、オートパイロット設定に従って自動プレイされます。 停止するには "Stop" ボタンをクリックします。

参考:Virtual PlayersでもAnjinが動作する仕組み

Anjinは起動状態を AutopilotState というScriptableObjectで永続化しています。"Run" ボタンをクリックされると、まず AutopilotState の状態が変わり、再生モードに入ります。 MPPMでは、再生モードに入るときにプロジェクトのファイルが共有されるため*2、Virtual Player側でも AutopilotState を見てオートパイロットが動き出します。

この仕組みのため、現状では次の制約があります*3

  • Editorは手動、Virtual Playerは自動、といった使い分けはできません
  • Playerによって別々のシナリオ(AutopilotSettings)を割り当てることはできません
  • 再生モード中にAutopilotSettingsの "Run" ボタンをクリックしたときは、Editorのみオートパイロットが起動します

[11/18追記] AutopilotStateのファイル更新がされたりされなかったりするようで、Anjin v1.8から明示的に AssetDatabase.SaveAssetIfDirty する修正を入れます。 またv1.8ではSlackReporterのメッセージを入力できるようになりますが、ここにタグを出力するプレースホルダも追加します。

参考:出力ファイル

各Virtual PlayerはMacOSの場合、プロジェクトルート/Library/VP/の下にユニークな名前のディレクトリが作られます。 Anjinの FileLogger などの出力ファイルパスを相対パスで指定した場合、このVirtual Playerのパスが起点となります。 つまりVirtual Playerごと個別のパスに出力されます。

ただし、UGUIPlaybackAgent が内部で使用しているAutomated QAパッケージは、スクリーンショットApplication.PersistentDataPath に出力します。これは変更できません。 従って、MPPM環境下で UGUIPlaybackAgent を使用すると、出力ファイル重複のエラーが出ます。

将来的にAnjinはAutomated QAパッケージ非依存になり、この制限事項は解消される予定です。

Play Mode Scenarios [11/18追記]

MPPMには、Virtual Playerでなくプレイヤービルド*4をローカルインスタンスとして起動するPlay Mode Scenarios機能もあります。 Play Mode Scenariosではタグでなく起動時引数を渡すことができ、インスタンスごとに別々のAutopilotSettingsを割り当てることも可能です。

ただし、Play Mode ScenariosはAutopilotSettingsの "Run" ボタンでは起動しないため、通常の再生モードを開始してから "Run" ボタンで(Editorのみ)オートパイロットを起動する必要があります。 また、タグはEditor側は1つしか選択できず、ローカルインスタンスには指定できません。 Virtual Playersとは使い勝手が異なるので注意してください。

Play Mode Scenariosについて詳しくは Test live instances locally and remotely | Unity Multiplayer を参照してください。

宣伝

冬コミ(C105)で、Anjinのあれこれを詰め込んだ『Anjin非公式ファンブック』を頒布予定です。夏から出す出す詐欺していましたが今度こそ間に合うはず。

表紙・挿絵は『江戸むらさき特急』の著者、ほりのぶゆき先生にお願いしました!

2日目 西う47b「いか小屋」でお待ちしております。 また、後日BOOTHでも頒布予定です。

*1:テストシナリオとしてゴールを設けることもできますが、基本は設定したタイムアウト時間まで動き続けます

*2:MacOSではシンボリックリンク

*3:AutopilotState を使わないか、Assets下に置かないようにできれば可用性は上がりそうなので、検討してみます

*4:Anjin v1.7で実験的機能としてプレイヤービルドに対応しています