スプラトゥーンの戦況をモニタに出力するプログラムを書きました
・敵の生存秒数を数値/ゲージで表示、デス回数のカウント表示
・味方、敵のデス時にSEで通知
というシロモノプレイヤーの人数状況をプログラムに補助させるというシロモノです
【仕組み】
まず、スプラトゥーン2ではゲーム中、画面上に「イカアイコン」なる
敵、味方合計8人のブキの種類、生存してるか、SPが溜まっているかどうか
という3つの情報が表示されています。
プレイヤーはこの情報から立ち回りを判断するわけですが
しかしキャラコントロールしながら「常に」その情報にまで目を配らせることは至難なので
そこから分かる情報をプログラムに保持させウィンドウに表示させようという発想です。
キャプチャボードからのゲーム画面のピクセルデータからOpenCVで画像認識を行い、イカアイコン情報を取得します。
OpenCVの関数で直接キャプチャーボードの映像を取ることも可能ですが、
録画ソフトと並列できなくなるので(プレイ動画の録画もしたい)、
OBSSutdioというソフトで録画しながらゲーム画面のフルスクリーン表示をさせ、それをスクリーンキャプチャでプログラムに取得させるという
手段を取りました。
WINAPIで1/60秒毎にでスクリーンキャプチャ行い、OBSstudioで表示された画像データを取得します。
あとは、ブキアイコンの認識をどのような方法で行うか?です
このゲーム、戦況に応じて「イカアイコン」のサイズと位置がアニメーションで変化してしまうのです。
これがopencvで画像認識する上で、とにかく邪魔なんです。常に位置を固定してくれてれば処理も楽なんですけどね。
それらの問題を「ラベリング」と「テンプレートマッチング」で解決しました。
テンプレートマッチングについてですが
用意するテンプレート画像はデスを表す画像の1枚だけにしました。
このゲームのブキは約127種類以上存在し、それぞれのアイコン画像を用意などやってられません。
またOpenCVのテンプレートマッチングではテンプレートにアルファ値が使えません。(=矩形領域の検出しかできない)
「デス状態のイカアイコン」と「それ以外のイカアイコン」という扱いで処理していきます。
◆解説
イカアイコンの並びの下部にあるボーダーラインを利用します。
まず、画面上部のイカアイコンを切り取り、さらにそれを味方アイコン領域、敵アイコン領域、下部のボーダーラインの3つに切り取ります。
↓
切り取った味方/敵のイカアイコン画像から、cv::inRange()関数でデス状態を表すグレーのバツマークの色のピクセルのみを抽出
↓
ここでテンプレートマッチング
とりあえずこれでマッチ回数から8人中何人デスしているか検出できました。
しかしこれでは誰がデスなのかはわからない状態です。
それと、マッチしたそのxy座標(緑のラインで囲われた部分の中心)を取得しておきます。
↓
ここで、最初に切り取った3つのうちのボーダーライン部分画像を利用することになります
まず、ラベリングの前処理をします。
デス状態を表すグレーの罰点印の色をcv::inRange()でマスクし黒(0,0,0)にします。
もう一度cv::inRange()でボーダ部分の黒検出し黒とそれ以外の色分けたマスクゲット。
↓
ラベリングを実行します。幅と高さのしきい値を決めた上で
白ピクセルを連続している領域のバウンディングボックス(位置となるxy座標、幅、高さ)を取得します。
結果が緑色で囲われた領域です。見事に生存しているプレイヤーのイカアイコンのみのバウンディングボックスが取得でき、
テンプレートマッチングで取得した罰点印のバウンディングボックスと合わせて、
プレイヤー8人のイカアイコン位置が取得できました。
↓
あとは、8人分のバウンディングボックスのx位置座標で昇順ソートを掛けます。
ソートの順序に対応するようにプレイヤー情報クラスを配列変数に格納すれば、
これで誰が生存していてデスしているのかが把握できます。
あとは以上の処理を1/60秒毎に繰り返し、前フレームのアイコン状態と比較してデスプレイヤーが増えた時に効果音を鳴らす処理するだけです。
WINAPIでは複数の効果音を同時に再生できないらしいので、HSPでウィンドウメッセージ受け取ったら効果音鳴らすアプリを数十行で書いて、
C++側ではデス検知したときはそのHSPで作ったアプリにsendmessageでユーザーアプリを送るようにしました。
ガチマで何度か使ってみた所、デス通知よりも敵が復活した時に効果音通知のほうが実践では有用かも
githubにソースコード載せました
https://github.com/kazumax75/opencv_base_GUI/