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

Step2-2:Map Scanner を使用した事前データの準備

はじめに

この Step では、Map Scanner を使用するための事前準備と、Map Scanner を使用してマップのスキャンデータを作成する手順を確認します。

Map Scanner とは

Map Scanner は、ゲームのマップデータをオートスキャンし、Playable!の各ツールで使用するための事前データを作成するツールです。
レイキャストを取り付けたプレイヤーをマップ全体に移動させることでスキャンを実現します。

Collision Checker、Item Collector 及び General Agent を実行するには、事前に Map Scanner でマップスキャンを行なう必要があります。

注記

Step1 では「お試し用データ」としてマップスキャン済の事前データを導入しましたが、Step2 ではマップスキャンについてもご体験いただきます。

Map Scanner の設計

Map Scanner は、マップ全体を多数の小さな領域に分割してタスクを割り振るサーバープログラムと、サーバーから割り振られたタスクに従って実際にスキャンを行う複数並列のクライアントプログラムで構成されています。
複数のクライアントを並列で実行することでスキャンを効率的に行うことが可能です。

ヒント

より詳しい構成や技術仕様については、こちらをご覧ください。

事前準備

ダウンロードした Map Scanner には Alfort(サンプルゲーム)に対応した実装コードが含まれています。
そのため、Unreal Engine 5 の Third Person Template Project(以下、プロジェクトと呼称)に適用するために、いくつかのコード変更、設定変更が必要となります。

以下、\playable-map-scanner\Python ディレクトリ以下の実装コードについて変更を加えます。

プロジェクトに利用するための実装コードを準備する

Alfort(サンプルゲーム)に対応した実装コードを複製して、プロジェクトのための実装コードを準備します。
具体的には以下の手順を取ります。

  1. Pythonフォルダ内のcustomフォルダ以下にある alfort フォルダをコピーし、フォルダ名をプロジェクト名にリネームします。
    この Step では、プロジェクトの名称は「template」とします。
  2. Python/custom/init.py の 4 行目を以下のように変更し、コードの読み込み先を上記フォルダに変更します。
    - from .unity_demo.custom_client_thread import CustomComThread
    + from .template.custom_client_thread import CustomComThread
  3. Python\map_data フォルダ以下にある alfort フォルダをコピーし、フォルダ名を template にリネームします。

タイトル画面からゲームプレイ画面への遷移処理を削除する

Python/custom/template/custom_client_thread.py > CustomComThread クラス > title_menu_setting() にタイトル画面で行う操作コマンドが定義されています。
一方で、プロジェクトではゲームを起動すると直接ゲーム画面へ遷移するため、コード上で実行できない操作コマンドが含まれています。

そのため、下記の例に従って custom_client_thread.py の 30-31 行目をコメントアウトしてください。

    def title_menu_setting(self):
"""
タイトル画面を突破する
"""
+ # self.order_list.append(custom_order.SelectTitleMenu("ロード")) #コメントアウト
+ # self.order_list.append(custom_order.LoadMenu(self.save_data_name)) #コメントアウト
self.order_list.append(custom_order.WaitLoading())

デバッグ機能の不要な処理を削除する

Python/custom/template/custom_client_thread.py > CustomComThread クラス > set_debug_function() にデバッグ移動モードの挙動が定義されています。
一方で、プロジェクトでは該当するゲーム仕様が存在せず、実行できない操作コマンドが含まれています。

そのため、下記の例に従って custom_client_thread.py の 39-44 行目をコメントアウトしてください。

    def set_debug_function(self):
"""
デバッグ機能の設定
"""
self.order_list.append(custom_order.WaitSec(1))
+ # self.order_list.append(custom_order.OpenDebugMenu(True)) #コメントアウト
+ # self.order_list.append(custom_order.SetDebugMenu("Player無敵切り替え")) #コメントアウト
+ # self.order_list.append(custom_order.SetDebugMenu("位置情報の表示切り替え")) #コメントアウト
+ # self.order_list.append(custom_order.SetDebugMenu("全ての扉を開放する")) #コメントアウト
+ # self.order_list.append(custom_order.OpenDebugMenu(False)) #コメントアウト
+ # self.order_list.append(custom_order.WaitSec(5)) #コメントアウト
self.order_list.append(custom_order.SetDebugMove())
self.order_list.append(custom_order.SetDebugSpeed(self.scan_speed))

デバッグ移動時に参照する値を変更する

前ステップで実装したデバッグカメラの情報を取得するために、Python/custom/template/custom_order.py の以下 3 箇所のコードを変更します。

class SetDebugSpeed 49 行目

    def execute(self, game_dict: dict) -> Tuple[bridge.PseudoPadInput, bool]:
pad_input, done = super().execute(game_dict)

if self.push_key:
pad_input.FaceButtonTop = False
pad_input.FaceButtonBottom = False
self.push_key = False
else:
self.push_key = True
- speed_now = game_dict["Alfort"]["DebugCamera"]["MoveSpeed"]
+ speed_now = game_dict["DebugCamera"]["MoveSpeed"] #フィールド名を変更する
if speed_now - self.target_speed < 0:
pad_input.FaceButtonTop = True
elif speed_now - self.target_speed > 0:
pad_input.FaceButtonBottom = True
else:
done = True

return pad_input, done

class SetDebugMove 72 行目および 83 行目

    def execute(self, game_dict: dict) -> Tuple[bridge.PseudoPadInput, bool]:
pad_input, done = super().execute(game_dict)
if self.start_frame is None:
- self.start_debugmove_state = game_dict["Alfort"]["DebugCamera"]["DebugCameraMode"]
+ self.start_debugmove_state = game_dict["DebugCamera"]["DebugCameraMode"]
self.start_frame = self.elapsed_frame
if self.elapsed_frame - self.start_frame > 20:
pad_input.LeftTriggerThreshold = True
pad_input.RightTriggerThreshold = True
if self.elapsed_frame - self.start_frame > 50:
pad_input.LeftThumb = True
if self.elapsed_frame - self.start_frame > 80:
pad_input.LeftTriggerThreshold = False
pad_input.RightTriggerThreshold = False
pad_input.LeftThumb = False
- if game_dict["Alfort"]["DebugCamera"]["DebugCameraMode"] != self.start_debugmove_state:
+ if game_dict["DebugCamera"]["DebugCameraMode"] != self.start_debugmove_state:
done = True
else:
self.start_frame = self.elapsed_frame
return pad_input, done

スキャン用パラメーターを設定する

事前準備した Python/map_data/template/_map_list.yaml を以下のように変更します。

ヒント

それぞれのパラメーターの詳細については、_map_list.yaml 設定ファイルおよびmap_param_calcをご覧ください。

engine_type: unreal_engine
env_path: ./WindowsNoEditor/ThirdPersonTemplate.exe

scan_info:
scan_speed: 4000 # スキャン速度 debug移動コードと速度変更コードと対応
grid_cell_size: 40 # スキャングリッドサイズ(生成するボクセルマップのボクセルのサイズ)
scan_window_size:
front_back: 200 # スキャンウィンドウの前後サイズ laycastがカバーする前後の範囲
top_bottom: 1200 # スキャンウィンドウの上下サイズ laycastがカバーする上下の範囲
scan_window_overlap_count: # スキャンの重複回数 laycatの配置方法とスキャン結果に基づいて調整
front_back: 2 # スキャンウィンドウの前後繰り返し回数
top_bottom: 2 # スキャンウィンドウの上下繰り返し回数
scan_window_overlap_offset: # スキャンの重複進行距離 laycatの配置方法とスキャン結果に基づいて調整
front_back: 110 # スキャンウィンドウを前後に繰り返した場合、ステップ距離をどのくらい保つか
top_bottom: 450 # スキャンウィンドウを上下に繰り返した場合、ステップ距離をどのくらい保つか

step_move_limits:
horizontal_move_limit: 400 # ジャンプ時 水平に最大距離
upward_move_limit: 180 # ジャンプ時 上に最大距離
downward_move_limit: 1200 # ジャンプ時 下に最大距離
player_height: 180 # キャラクターの身長

collision_check_params: # コリジョンチェック用パラメータ
reset_limit: 300 # 一回の衝突でこの時間を超えた場合リセット処理を実行する
move: # 通常移動による衝突
normal_overstep: 100 # 壁方向への移動時間(ステップ数: 基本的に1/30秒が1ステップとなります)
random_overstep: 50 # ランダム方向への移動時間
random_move_num: 5 # ランダム方向への移動回数
debug_move: #デバッグ移動
diff_thres: 0.11 #目的地に移動したとみなす距離
jump:
max_jump_num: 5 # 際の最大でジャンプする回数
jump_interval: 150 # ジャンプ操作の実行間隔(ステップ数)

map_params:
template:
name: template
drop_threshold: -1000 # このマップにおける落下判定しきい値(コリジョンチェックで使用)
save_data: none
x:
end: 3400
start: -400
y:
end: 3900
start: -400
z:
end: 1000
start: 0
plot:
coef_x: 0.38291417583308407
coef_y: -0.3758863280011576
bias_x: 121.13033928136372
bias_y: 1479.8354607679594
search_start:
x: 900
y: 1110
z: 100

Map Scanner の実行

ツールを起動する

Anaconda Prompt を起動後、以下のコマンドを実行して Map Scanner 用の仮想環境に切り替えてください。

$ conda activate mapscanner  # 仮想環境の切り替え
$ cd {playable-map-scanner のディレクトリ}/Python # ディレクトリの移動

以下のコマンドでサーバープログラム、クライアントプログラムをそれぞれ起動します。
上述のとおり複数のクライアントサーバーを用いて並列稼働させることを想定していますが、それぞれのプログラムを同一のコンピューターで実行することも可能です。

$ python map_scanner_server.py #サーバープログラムの起動
$ python map_scanner_client.py #クライアントプログラムの起動

ツールが起動すると以下のウィンドウが表示されます。

サーバープログラム

クライアントプログラム

サーバーとクライアントを接続する

まず、map_scanner_servermap_scanner_client の IP アドレスを map_scanner_server を実行しているコンピューターの IP アドレスに設定し、ポート番号を統一してください。
同一のコンピューターで実行する場合は、設定の変更は必要ありません。

map_scanner_server 側でスキャンしたいマップを選択し、[サーバーを起動する] をクリックしてください。
操作後、以下のような画面が表示されます。

次に、 map_scanner_client[ゲームファイルを選択] をクリックし、ゲームの実行ファイル(.exe)を指定してください。
その後、[サーバー接続] ボタンをクリックし、サーバーに接続してください。 成功した場合は以下のような画面が表示されます。

スキャンを実行する

map_scanner_client 側で同時実行するゲームの数を指定します。
実行したいゲームの数に合わせて [スレッドを増やす] ボタンをクリックすることで数を増やすことができます。
ゲームが起動すると自動的にサーバーからタスクを受け取り、スキャンを実行していきます。

並列数が多いほどスキャンは短時間で完了しますが、より多くのマシンパワーを必要とします。 同時実行数は少なめの数(CPU コア数の半分程度)から始め、CPU および GPU の負荷率を監視しながら調整することをおすすめします。

map_scanner_server側の「残タスク」が 0 になり、すべてのスレッドでスキャン中のタスクが None になればスキャンは完了です。
スキャンが完了したら [データを保存する] ボタンをクリックして結果を保存し、サーバー側、クライアント側ともにツールをウィンドウの「×」ボタンから閉じてください。

スキャン結果は {ツールのルートディレクトリ}/map_data 以下に保存されます。

ヒント

スキャン実行中に [データを保存する] ボタンをクリックすることで、スキャンを中断しこれまでの結果を保存することができます。
詳細はこちらをご覧ください。

スキャン結果を用いた探索の事前確認を行う

最後に、スキャンによって得られた立体地形データを使って経路探索の事前計算を行います。

まず、以下のコマンドを Anaconda Prompt で実行し、 map_scanner_searcher を起動します。

$ python map_scanner_searcher.py

ツールが起動すると以下のウィンドウが表示されます。

スキャンしたマップを選択し、 [search map] ボタンをクリックします。
事前計算が行われますので、「スキャンデータを探索中…」のダイアログが消えるまでしばらくお待ち下さい。

探索が完了すると、スキャン対象としたマップフォルダ以下に、下記 3 ファイルの探索データが保存されます。

  • _is_close.npy
  • _search_map.npy
  • graph_data.bin

次のステップ

おつかれさまでした!

次の Step では Collision Checker を利用し、コリジョンチェックの実行方法について学びましょう。
チュートリアル Step2-3 へ