HeyCHのブログ

慢性疲労のへいちゃんです

【C#】画像認識自動クリックツール

最近、オトギフロンティアやミストトレインガールズ等のブラウザゲームをやっているんですが
この2つのゲームって事実上無限に継続できるコンテンツがあります。

このようなコンテンツを自動で実行する場合
Windows10標準の自動化ツール等でやろうとすると、
現状の画面がどのような状態なのかわからないので、難しかったりする。

しかし、ゲームのようなボタン1個でも凝っているようなGUIの場合
ボタンの画像を見るだけで、それがどのような状況であるか判別できる場合が多い。

そのため、今回、画面キャプチャからボタン等の画像を見つけ出し、そこを自動でクリックするというツールを作ることにした。
(結論から言うと、めちゃめちゃ使える。オトギで500万くらいしかなかった金が既に1億以上ある。※1回のバトルで5万もらえる※鍵もめちゃめちゃ増えた)

マウスの位置指定とクリック

        public const int MOUSEEVENTF_LEFTDOWN = 0x2;
        public const int MOUSEEVENTF_LEFTUP = 0x4;
        [DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void SetCursorPos(int X, int Y);
        [DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
        //例
        public static void MouseClick(Point p, bool isRepeat = false) {
            SetCursorPos(point.X, point.Y);
            Thread.Sleep(100);//待機(僕の環境では100ms位必要)

            mouse_event(APIs.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            mouse_event(APIs.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        }

画像認識(MatchTemplate)

画像認識にはOpenCvSharp4というライブラリを使用します。
NuGetから「OpenCvSharp4」「OpenCvSharp4.runtime.win」をインストールしましょう。
使い方は以下のような感じで、今回のツールではbasePathにスクリーンショットを、targetPathに設定で指定した画像を入れている感じです。
※TemplateMatchModes.CCoeffNormed以外では全く関係無い所がマッチするため不採用
※new Mat(string path)ではなく、BitmapConverter.ToMat(Bitmap bitmap)みたいなのも有るけど、使ったらエラーになったので不採用(1度保存する必要がある)

    public System.Drawing.Rectangle? GetMatch(string basePath, string targetPath, double threshold = 0.9){
        using(var baseMat = new Mat(basePath))
        using(var targetMat = new Mat(targetPath))
        using(var resultMat = new Mat()){
            Cv2.MatchTemplate(baseMat, targetMat, resultMat, TemplateMatchModes.CCoeffNormed);
            Cv2.Threshold(resultMat, resultMat, threshold, 1.0, ThresholdTypes.Tozero);

            OpenCvSharp.Point minloc, maxloc;
            double minval, maxval;
            Cv2.MinMaxLoc(resultMat, out minval, out maxval, out minloc, out maxloc);
            if(maxval>threshold)
                return new System.Drawing.Rectangle(maxloc.X, maxloc.Y, targetMat.Width, targetMat.Height);
            return null;
        }
    }