アズールレーン赤城加賀自働周回マシーン
はじめに
最近めっちゃUSAの駆逐艦になめられてる
あかんこのままじゃ・・・どうしてこんなことに
こいつらを締めてくれる怖いお姉さんが我が艦隊に必要
怖いお姉さんといえば赤城
赤城さんが指導すればUSAの駆逐艦もおとなしくなるはず
赤城さんを入手するためには、建造じゃでないので3-4周回が必要
でもドロップ率が低すぎて全然でない
手動で周回するのはだるすぎて限界がある
このままじゃ艦隊の秩序、保てないよ・・・
そうだ、自動化しよう
自動化
自動操作の実行環境の準備
-
OSエミュレータの利用
OSエミュレータはOSをパソコン上でエミュレートするソフトウェア。エミュレータ上でソフトウェアをインストールして動かせば、エミュレータを起動しているパソコンからマウスのクリックで操作できます。
アンドロイドの場合、エミュレータは実機ほどの性能はでないとのこと
iOSの場合、エミュレータのできが良く実機と遜色ない性能で動作させることが可能とのこと
今回はアンドロイドでNoxというエミュレータの使用を想定していますが、他のエミュレータでも問題ないと思われます。 - 自動化フレームワークの利用
アンドロイドOSエミュレータ上での実行はパフォーマンス上問題があり遅延等の原因になるそうです。しかしリアルタイム性がそれほど要求されないゲームの操作には問題ないレベルと感じました。もしパフォーマンス上気になる場合はアンドロイドOS上で直接実行し、アンドロイドOS上の操作をプログラムから制御できる何らかのソフトウェアを入れるほうが良いでしょう。Appiumというツールを使うとできるとのことですが詳細はよくわかりません。企業での受け入れテスト等では使われていることもあるとのことですが、個人で使うには情報も少なくまた高機能な分敷居は高そうという印象です。ただしオープンソースで個人でも利用は問題なし。
結論としては非リアルタイムゲームなので(戦闘はオート前提)アンドロイドのエミュレータで性能面は充分。また今回の目的では大したことはしないので高機能なツールを使う必要もなし。
クリックの自動化
クリックできるといってもどうやってクリックを自動化すればいいの?
クリックの自動化はWin32 APIのPostMessageを操作対象のプログラムのMainWindowHandleへ渡してあげることで実現できます。PostMessageを.net frameworkから利用する場合、user32.dllをインポートしてあげる必要があります。
(.netではクリックをウインドウハンドルに渡す関数は実装されてないらしい?)
実際にはクリックしているというかクリックの命令を送っているので実行している間マウスが操作できないとかそういうことはありません。
画像の取得
クリックする対象の画像を取得します。
Win 32APIのGetClientRect, GetWindowRect, ClientToScreen関数でデスクトップ上のプログラムの位置と画像を取得できます。取得した画像をBitmapに入れます。ピクチャボックスにBitmap画像をいれれば表示されます。
(.netではウインドウハンドルからウインドウの画像を取得するやウィンドウのサイズや位置を取得する関数は実装されていないらしい??)
別にそんなことをしなくてもPrintscreenからの画像を取得すればいいのですが、ディスプレイが2枚ありそこからマッチング範囲を設定するのも手間なのでこうしました。後述するテンプレートマッチングのパフォーマンスはマッチング対象のスクリーンの大きさに影響されるので何らかの方法で対象範囲は絞っておいたほうが良いです。
画像のマッチング
取得した画像に対して、プログラムでクリックしたい場所を探してクリックします。
画像のマッチングにはOpenCVのテンプレートマッチングを使用します。
テンプレートマッチングはテンプレートとして用意した画像をマッチング対象の画像から探すというオーソドックスなマッチング手法です。多少画像が伸びていたりしてもマッチングできるらしいです。ただし、例えばテンプレートとして用意する画像が星型をしている場合星型の背景まで含めてマッチング対象とみなされてしまうのでマッチングがうまくいきません。星という形として認識しているわけではなく、あくまでテンプレート画像そのものがマッチング対象としてみなされるためです。そのため、マッチングするためにテンプレート画像は背景を含めないように注意する必要があります。
その他マッチングにおける注意点として、色深度(ビットの深さ)の設定があります。
テンプレート画像とスクリーン画像の色深度が異なっている場合、テンプレートマッチング関数がエラーとなってしまいます。スクリーン画像の色深度は32bitなのでそちらに合わせるようにしました。
キャプチャソフトを使用した場合、色深度が24bitなど32bit未満になっていることがあります。その場合はmspaintで開いて保存するとなぜか32bitになってます。
テンプレート画像とスクリーン画像をマッチングすると、マッチングした範囲が帰ってきます。マッチングした範囲の中央をクリックすることで、ある特定の画像が表示されたらそれをクリックするという処理が可能となります。
Cv2.MatchTemplate(screen, template, result, TemplateMatchModes.CCoeffNormed);
Cv2.Threshold(result, result, 0.8, 1.0, ThresholdTypes.Binary);
Cv2.MinMaxLoc(result, out OpenCvSharp.Point minPoint, out OpenCvSharp.Point maxPoint);
状態遷移の処理
プログラム上ではひたすらループを回しています。
GUIでそれをやると固まっているように見えてしまうため、別にTaskを作成してその中でループを回すようにしています。例えば戦闘中は戦闘中の状態として処理し、戦闘中状態ではひたすら画面の虚空をクリックし続けています。そうするといずれは勝利するので確認ボタンが検出され押されます。
そうすると画面が遷移します。画面の遷移にあわせて、マップにいることが確認できたらマップ状態に遷移し今度は敵艦を探してクリックします。
実際の処理
戦闘マップでの処理
戦闘マップでは敵艦を見つけたらクリックして移動します。
クリックして無事戦闘に入れればいいのですが、上手くいかない場合があります。
例えば移動中に迎撃艦隊に遭遇した場合、移動が止まってしまいます。
また、クリックした敵艦との間に敵艦が存在し敵艦を通過せずに移動ができない場合、移動が失敗します(クリックしても移動できない)。
そのような場合を検知してうまく移動させます。
戦闘中での処理
戦闘中での処理はシンプルでひたすら画面の左端あたりをクリックします。
そして確認がみえたらクリックするだけです。