HANDのページ

戦術SLGプログラミング(CardWirthで)

今回のコラムは、制限された環境で戦術SLGプログラミングを勉強し実装してみた備忘録のようなものである。 したがって、CardWirthシナリオ制作にとって有益な情報は全くない。この点をご容赦いただきたい。

本コラムは、現在制作(中断)中のCardWirth追加シナリオ『六角平野の戦い』についてのコラムである。ゲーム部分は最後まで完成しており、バランス調整もほぼ完了しているので、CardWirthをすでにインストールしている方はぜひとも遊んでみてほしい。

かなり前の制作状況になるが、ニコニコ動画にて動画も公開している。

敵はなかなかそれっぽい動きをしてくれていると、個人的には思う。

シミュレーションゲーム『六角平野の戦い』の仕様

CardWirthというゲームエンジンで戦術シミュレーションゲームをプログラミングしたという話を今からしたい。

ゲームエンジンとしてCardWirthは、大量のデータを扱うにあたって貧弱と言わざるを得ない。 盤面を小さく、しかし面白いものにするために『六角平野の戦い』では仕様を以下のように策定した。

ユニットは槍兵→騎兵→弓兵→槍兵の三すくみをベースに設定してある。 ほかにも多数のユニットはいるが基本的には以下の通り。

小規模の盤面に対してやや不釣り合いに、弓兵が射程2マス、砲兵が射程3マスと 設定しているのが工夫の一つで、近接ユニットと遠距離ユニットによる同時攻撃を仕掛けて 敵を攻めつぶすというのが、このシミュレーションゲームの基本的な力学である。

盤面が小さくても面白いシミュレーションゲームにするために、 攻撃偏重でやや大味な作戦級のウォーゲームを採用し、 個々のマス目の複雑度を上げるために地形効果・ZOC・ユニットの相性を採用した。 ちなみにHEX制を採用したのにもちゃんと理由があって、各カードの座標をずらしてクリックしやすくする、移動をより直感的にしてストレスを軽減するという意味がある。

結果として、戦略のゲームというよりは、パズルゲームに近いものとなった。 ひとくちに「CardWirthでSLGを作る」というが、作るべき仕様は割と真剣に取捨選択している。

思考ルーチン

移動範囲の計算や、思考ルーチンの実装について、CardWirthでどう実装したかについての工夫は数多くあるし、どこかでまとめてはおきたいが、ここでは触れない。 基本的には既存の高速化テクニックを用いるか、ループを全部展開するなど、ごり押しによる実装を行っている。

ここではそれよりも、簡単な計算でユニットをそれっぽく動かすために思考ルーチンをどう設計したかについて重点的に述べる。

思考ルーチンに何をさせるか

戦術シミュレーションゲームにおいて思考ルーチンはゲームコンテンツである。 プレイヤーの相手となるためには、プレイヤーの行動に対してきちんとレスポンスを返す、一貫性のある戦略(らしきもの)を持たせることが重要である。 そういうわけで、思考ルーチンには以下の要求を課すことになる。

上記の要求をすべて満たすのは工夫がいる。 明確な集中攻撃を実現するためには、集中攻撃目標を設定することと、それに向かうという挙動をプログラムしなければならない。 その上で、障害物・悪地形・ZOCを迂回するためには、マス目の価値を算出する必要がある。 特に、最後の「指揮官の防衛」を実装はなかなか厄介である。 例えば、複数のプレイヤー軍ユニットが迂回しつつ敵の指揮官に迫るというような状況では、 敵軍ユニットはプレイヤー軍ユニットを阻む位置に陣取る必要がある。

そしてその上で、遠距離ユニットならば敵から一定距離をとるような、そのユニットに固有の挙動が欲しいし、ステージごとに敵の作戦も柔軟に変えたい。

思考ルーチンのおおまかな仕様

少ない計算量で上記の要求を満たすため、 移動に関する思考では、 敵軍の状況や地形などから算出する「盤面の重みテーブル」と、 個々のユニットの移動に関する評価値算出を分離している。

これはゲームAI -基礎編- 『知識表現と影響マップ』 で紹介された考え方をもとにしている。

攻撃に関する思考は実に単純に組んでいて、攻撃範囲内の敵対ユニットから

確実に倒せるユニットを攻撃 > 指揮官を攻撃 > 最もダメージを与えられる敵を攻撃

の優先度で選んで攻撃している。回避率の概念が無いので、これでほぼ最適な攻撃ができる。

全体の仕様を簡単にまとめると以下の通り。

  1. 盤面全体の重みテーブルを計算 (ターン開始時に1回だけ実行)
  2. 行動済みでないユニットについて以下を行う (ユニットの行動順は固定):
    1. 移動可能な各マスについて、重みテーブルから評価値を算出 (ユニットごとに変える)
    2. 評価値が最も高かったマスへ移動
    3. 攻撃範囲内の敵対ユニットの優先度を算出 (いなければスキップ)
    4. 優先度が最も高かったユニットを攻撃
  3. 行動可能なユニットがいなくなれば終了

上記の仕様では、 計算量を節約するために意図的な穴をあけてある。 何かといえば、ユニットの行動順を固定していること、 重みテーブル計算をターンの開始時にしか行わないため、 味方ユニットの移動や攻撃による状況変化を無視している点である。

盤面の重みテーブル(影響マップ)

(ターン開始時に)盤面の各マスに数値による重みづけを行う。 直感的には、敵味方の影響度や距離の概念を重みとして各マスに設定し、 各ユニットの行動時に、重みテーブルから各マスの評価値を算出する。

算出方法の異なる3つのテーブルを使用している。

重みテーブルの算出に当たって、基本的には、悪地形・ZOC・敵ユニットの有無を参照する。

テーブル計算アルゴリズム

ここでは最も重要な「(2) 味方ユニット(特に指揮官)を中心とする歩行距離テーブル」を見てみよう。

まず、悪地形・ZOC・敵ユニットを「弱い障害物」とみなし、その移動コストを設定する。 ここでは例えば、以下のように設定したとして話を進める。

これらのパラメータをもとに、各マスの移動コストを決めることができる。 これは実際の移動コストでなく、敵ユニットを考慮した、主観的な移動コストである。 いくつか例を見てみよう。

以下のようなマップを一つ考えてみよう。 この概略図では、○が自軍ユニット、□が敵ユニット。黒色が障害物(通行不可)、 水色が水辺(悪地形)、網掛けがZOCである。

マップ概略

このマップについて、移動コストを計算するとこのような感じになる。

各マスの重み

それから敵ユニットの重みを設定し、テーブル計算を行う。

テーブル

例えば図のように、敵ユニットの重みを14と設定し、その敵がいるマスの重みを14と定める。 このように設定した重みを周囲に伝播させることで、重みづけテーブルを作る (頂点をの高さを決めて、それを均すともとれる)。

テーブル

まずは14とセットした周囲のマス目の重みを計算する。 移動コストの分だけ重みを減らし、そのマスの重みを確定する。 このような操作を重みが大きいマスから順番に行い、重みづけされていないマスが無くなれば終了。

プログラミングになれた人向けに言うと、 経路長に限界をもうけたダイクストラ法を、経路長と頂点数に関する2重ループを用いて書き直し、 CardWirthというゲームエンジンに最適化している。

テーブル

手続きをくりかえし、計算を終えたテーブル。 このテーブルを使うと、主観的な「最適ルート」を求めることができる。 移動距離だけで考えると、水辺を通ってもかかる時間は同じだが、 水辺を迂回したほうが良いという考慮をそこに織り込んでいる。

テーブル

同様にして、敵が複数の場合でも同様の処理でテーブルの作成が可能で、 別の例を考えてみよう。

テーブル

ここで、☆が指揮官ユニット、□が防衛ユニットとする。 この図の状況で、司令官ユニットの重みが20、防衛ユニットが14であるとする。 先ほどと同様に、テーブルを計算してみよう。

テーブル

各マスの移動コストを算出(移動コスト1は省略)。

テーブル

ユニットがいるマスの重みを設定。 この重みは状況に応じて可変である。

テーブル

テーブル全体の重みづけを行う:ユニットがいるマスの重みを均す。

テーブル

こうして得られたテーブルによって決まる「最適ルート」は、 守備ユニットを迂回して指揮官ユニットに迫る形になっている。

パラメータやユニットのいるマスの重みを変更することで、 この「最適ルート」についても、 守備ユニットを無理やり通過する形にできたり、 守備兵を優先して撃破するなど柔軟に変更することができる。


細かい部分は異なるが、テーブル3つの重みづけは、いずれも上にあげた方法で行われる。

「指揮官の防衛」を実現するために、実際の思考ルーチンでは、最初に「(1) 味方ユニット(特に指揮官)を中心とする歩行距離テーブル」を算出し、「(2) 敵ユニットを中心とする歩行距離テーブル」を計算する際に、指揮官に近い敵ユニットの重みを大きくしている。

ユニットごとの評価値算出

場合によっては「最適ルート」が2つ以上あったりするし、 遠距離ユニットが近接ユニットに距離をとる、という挙動もさせたい。 すでに述べたように、盤面の重みテーブルとユニットごとの評価値算出の処理を分離し、 各ユニットの個性や詳細な判断は後者で行うことにする。

各ユニットの移動において、各マスにおける以下のような情報を取得して、 「マス目の評価値」を計算し、評価値が最も高いマスへ移動する。

「(0) 思考中のユニット自身の情報」を参照したうえで、(1)~(4)の地形情報の計算方式を変更している。具体的にどのような判断が織り込まれているかを例示する。

こうすることで、特別な場合分けをすることなしに、ごく自然な形で 指揮官は敵から離れようとするし、 槍兵・弓兵は固まって行動しやすくなり、 遠距離ユニットは射程ギリギリの距離に陣取るようになる。 そしてどのユニットも有利な地形を優先する。

さらに、パラメータを調整することで柔軟に変化をつけることも可能。

参考サイト