メインコンテンツまでスキップ
バージョン: 1.0.0

新規ゲームへの対応手順

下準備

  1. コリジョンチェックの実行結果を可視化するためのツール内で表示するゲーム内マップ画像を用意します。 テストを行いたいマップ単位で別々の画像を用意してください。
  2. 画像が用意できたらPython/res/以下に配置してください。 また、テストを行いたい各マップのコード名 (以降マップ ID と呼びます) を決めておき、 画像のファイル名も {マップID}.jpg/png/etc となるようにしてください。 以降、このマップ ID を Collision Checker を含めた各ツールのコード内で使用することになります。 また、このマップ ID は Map Scanner でも扱う文字列であり、既に Map Scanner 側で決められている場合はそちらに合わせてください。
  3. 事前準備 を参照し、手順 1~5 を済ませます。

コード変更箇所

新規ゲームに対応するために、コードを変更しなければならない箇所を以下に示します。 テストを実行するまでに必要な手順から考えて、以下で記載されているファイル順に作業していくことが望ましいです。

例えば、map_data.pyまでの変更作業を行うと、コリジョンファイルを出力するまでの作業を先に行うことができます。 これは、途中途中で正しく変更できているかを確認するためのチェックを繰り返せるようになることも意味します。

配布されているファイルにはサンプルゲーム(開発コードネーム: Alfort)におけるサンプルコードが含まれています。 こちらを参照しながら作業を進めてください。

result_viewer.py

Collision Checker の実行結果を GUI 並びにゲーム内に表示するためのコードです。

  • VERSION_TEXT
    • import 文の少し下で定義されているグローバル変数です。GUI 内のバージョン情報欄に表示する文字列を記載してください。
  • MAPNAME_LIST
    • import 文の少し下で定義されているグローバル変数で、コリジョンチェックの対象となるマップのゲーム内名称をリストにしたものです。 リストの要素に適切なマップ名称を追加してください。ここで追加した値が GUI 上の「マップ選択」欄に表示されます。
  • copy_savefile
    • 指定したマップ用のセーブデータを Python/data/testdata フォルダからゲームのセーブデータ用ディレクトリにコピーする関数です。 ゲームに応じて適切なパスを指定してください。 Unreal Engine であれば WindowsNoEditor/{ゲームのコード名}/Saved/SaveGames/ 以下にあると思われます。
      また、ゲームによってセーブデータの仕様が異なる場合があるため、適切な処理を行ってください。 例えば、サンプルゲームではセーブデータのロードには「ファイル名が可変であるセーブデータ本体」と「ファイル名が不変である SharedData.sav」の 2 種類のファイルが必要となります。

make_colfile.py

Collision Checker における衝突目標点(目標点)の配置場所などが記述された「コリジョンファイル」を生成するコードです。

  • get_thres_min_z
    • 目標点を配置してもよい z 座標の下限を返す関数です。この値はテストエリア毎に適切な値を用意してください。 どうしても本来行けないはずの地下空間 (コリジョン内部) に目標点が配置されてしまう場合の対策として記述しています。 ですが、現状はスキャンデータを使った目標点配置アルゴリズムの精度が十分であり、そのような場所に目標点が配置されることがありません。 そのため実際にはこの値は十分に小さな値を返すだけの関数にしておけばよいです。
  • main
    • map_list (List[str]) 内にテスト対象となるマップ ID を記述してください。
    • 実行パラメータ --dist のデフォルト値を各ゲームに適した値に変更しておくと、コマンド実行時の手間が省けて便利です。 最適値は、値を変えながら何度もコリジョンファイルを生成し、目標点配置が密すぎず疎にもなりすぎない値を探して決定してください。
      目標点間隔が密すぎるとテストが入念に行われるメリットがありますが、その分テスト時間が長くなります。 また、スキャンデータのボクセルサイズよりも小さな値に設定しても、アルゴリズム的にそれよりも狭い間隔で配置できないので意味がありません。

map_data/map_data.py

Map Scanner で作成したスキャンデータのロードと、同データに対して行われる様々な処理に関するコードです。

  • class MapDataService > CELL_SIZE
    • 必ずスキャンデータのボクセル一辺の長さと一致するようにしてください。 ボクセルのサイズを変更しなかった場合は、何もする必要はありません。

svrun_client.py

テスト実行時のクライアントエージェントsvrun_client_logic.pyを複数立ち上げるためのコードです。

  • main
    • 実行パラメータ--envpathのデフォルト値をゲームに合わせて適切に変更してください。

svrun_client_logic.py

テスト実行時のクライアントエージェントの挙動を司るコードです。

  • class CollisionTestAgent > step
    • ある目標点に対するテストを行っている最中で奈落落ちが発生した場合、その目標点に対するテストを直ちに終了し、次の目標点に移動してテストを再開する処理が走ります。 「どのような状態の時に奈落落ちが発生したと判断するか」をゲームの奈落落ち仕様に合わせて適切に記述してください。 より具体的には、変数 drop (bool) が「奈落落ちが発生したか?」を表す変数であり、この値をどのような時にTrueにするかを記述します。
      備考

      サンプルゲームには奈落落ちという概念が存在しないため、エリアごとに z 座標の閾値 (標高が最も低い地点より少し下側に設定) を決めておき、 衝突行動中に z 座標がこれを下回ったら drop=True としています。 この判定は self.status == "Check"、つまり衝突行動中にしか行われないため、 デバッグ移動時に z 座標が閾値を下回る場合は考慮しなくてもよいです。

  • class CollisionTestUtility > init_game
    • ゲームの起動、及びリセット時に行われる処理です。 テスト開始前に行うべき操作や設定 (タイトル/ロード画面の突破、無敵化などの各種デバッグオプション設定) を自動操作で実現するために、 クラス DebugLauncher で定義した関数群を適切な順序で組み合わせて登録してください。 クラス DebugLauncher に関する説明は後述の alfort/debug_launcher.py の項を参照してください。
  • class CollisionTestUtility > copy_savefile
    • result_viewer.pycopy_savefile と同様の変更を行ってください。 ただし、一部で関数の引数や参照する変数が異なるため、適切に改変してください。

alfort/debug_launcher.py

ゲーム固有(ここではサンプルゲーム固有)のタイトル画面/ゲーム内メニュー/デバッグメニューの操作アルゴリズムが記述されているコードです。

主にテスト開始前の設定に必要な操作や、テスト中に必要な操作のうちアウトゲーム的な部分に相当します。 ここで必要とされるコードの変更は、ほとんどがゲームによって全く異なると考えられ、決まった手順を書くことはできません。 ゲームの仕様を調べ、それに合うように適切なルール化を行ってください。

  • # 命令群 とコメントが書かれている部分以降
    • 各種メニュー操作を細かな単位に分解したものが定義されています。ゲームに合わせて適切なものを用意してください。例として Alfort では以下のものが用意されています。
      • 特定の項目 (引数と一致する文字列) を選択する
      • メニューの開閉を行う
      • 指定したボタンを 1 回押す など
  • # 命令群 とコメントが書かれている部分以前
    • 上記で定義した細かな単位の操作を組み合わせ、特定のことを行うのに必要な一連の操作を定義してください。 各操作に対応する関数には、ゲームと非同期で操作を行う XXXX_async と、ゲームと同期して操作を行う XXXX_sync が存在します。 既存のコードを参考にして両方用意してください。例として Alfort では以下のものが用意されています。
      • セーブデータをロードするために必要な一連のパッド操作
      • 特定のデバッグ機能を ON/OFF するために必要な一連のパッド操作 など
  • class DebugLauncher > step
    • self.level = xxxx の部分で、ゲームから毎フレーム送信される情報を参照し、現在ゲームプレー画面 or タイトル画面などの非ゲームプレー画面にいるのかを判定しています。 この判定ルールをゲームに応じて適切に設定してください。 基本的にはゲームから現在どのレベルにいるのかという情報が送信されるはずなので、それを元に場合分けすれば大丈夫です。
      備考

      class Order 及び Order を継承しているクラスの多くは Playthrough Tester、Map Scanner と共通して使えるため、 他のツールで既に変更対応が済んでいればコピペするだけでよいです。 ただし、他のツールでは必要のない操作が Collision Checker では必要となる、ということも考えられるので注意してください。 また、これらのクラスは svrun_client_logic.py > CollisionTestUtility > init_game をはじめとした同コード内の各所や result_viewer.py > Main_Model > run_exe から DebugLauncher.instance.XXXX を経由する形で呼び出されています。

alfort/state_machines.py

テスト中にプレイヤーキャラクターが取る行動のうち、ゲーム固有のものが記述されているコードです。

  • class DebugMoveState > step

    • デバッグ移動状態の時に毎フレーム呼ばれる関数です。 self.__case が 0 ~ 1 の時はデバッグメニューを操作して通常移動モードからデバッグ移動モードに切り替える操作、 3 ~ 5 の時はデバッグ移動が完了した後に行われる通常移動モードに戻す操作に相当します。 2 の時は実際にデバッグ移動を行う操作に相当します。この部分をゲームによって適切に書き換えてください。
      備考

      サンプルゲームのデバッグ移動はプレイヤーではなくカメラを移動させるもので、 ビュー ボタン (Unreal Engine の定義だと SpecialLeft) を押すことでカメラと同じ位置にプレイヤーをワープさせることができる仕様となっています。

  • class DebugMoveState > move

    • 実際にデバッグ移動を行う時の操作を定義する関数です。 どのボタン or スティックを使えば移動できるかはゲームによって異なるため、仕様に合わせて適切に記述してください。 この関数は上記の step 関数を経由して毎フレーム呼ばれるものであるため、 1 フレーム単位での操作方法を記述しなければならないことに注意してください。
  • class JumpState

    • ジャンプしながら移動するためのstateを提供するクラスです。 ジャンプしながら壁に衝突したい場合は# 「ジャンプしながら前進」のジャンプ部分とコメントが書かれた部分をゲームに合わせて変更してください。 変更点の例は以下のようなものが考えられます。

      • どのボタンを押せばジャンプできるか
      • 最後にジャンプしてから何フレーム後に再度ジャンプするか(着地完了してから再ジャンプしなければならないため)
      • 2 段ジャンプが使えるゲームの場合、1 段ジャンプをしてから何フレーム後に再度ジャンプボタンを押す (2 段ジャンプのタイミングによって最高到達点が変わるため)

      また、テスト時にジャンプ移動を適用するためには、svrun_client_logic.py > class DecideState > step を変更する必要があります。 この関数では「1 つの目標点に対するテスト」のフローを順番に登録しています。詳しくはソースコードのコメントを参照してください。

alfort/alfort_env_worker.py

サブスレッドで非同期にゲームと通信するための専用通信ラッパーです。

  • class AlfortEnvWorker > __init__, _init
    サンプルゲームではセーブデータをロードするために、DebugLauncherにセーブデータの拡張子なしファイル名を渡す必要があります。 他のゲームではこれが必要ないか、該当する値が存在しないためにエラーとなる可能性があります。 対象ゲームのロード周りの仕様を確認した上、不要なら引数の savename やクラス変数 self.savename を削除してください。 また、他ファイルでこのクラスインスタンスを生成している部分も同様に変更してください。
    備考

    上記の変更を行った場合、alfort/debug_launcher.pySelectSaveData関数の引数からも削除する必要もあります。 ただしゲームの仕様によっては SelectSaveData 関数が必要ない可能性も考えられます。

utils/assert_window_watcher.py

ゲームのウィンドウを常に監視し、ポップアップウィンドウが出現したら自動で閉じるためのコードです。

サンプルゲームでは現在は使用されていませんが、今後エラー状況の記録が必要になる場合を想定しサンプルとして実装してあります。 なお、以下で説明する変更対応を行っても動かない可能性が考えられるため、ゲーム毎に上手くカスタマイズしてください。

  • __set_foreground_handler
    • # ウィンドウのタイトル名が XXX から始まる とコメントにある通り、 if name.find('Alfort') >= 0: の部分で監視対象となるゲームのウィンドウ名の先頭文字列を指定してください。
  • get_window_handle
    • handle = win32gui.FindWindow(32770, "ASSERT") の部分で、第 2 引数を自動で閉じたいウィンドウの名前を指定してください。 また、第 1 引数に閉じたいウィンドウのウィンドウクラスを指定してください。
      備考

      ウィンドウ クラスについて - Win32 apps
      https://learn.microsoft.com/ja-jp/windows/win32/winmsg/about-window-classes

Unreal Engine 側の変更

こちらの資料に従って CollisionTest プラグインの導入を行ってください。