2
4

オートモード欲しい!!

昨今は多くの素晴らしい同人ゲームが存在しますが、そのすべてにオートモードが実装されているわけではありません。オートモードがないと、片手間にナニかをするときに非常に不便ですよね...

自分でオートモード作らないか?

とは言え、製作者の方にオートモード作ってもらうよう呼びかけたり、プログラムの中身を見て自分でオートモードを実装するのは無理があります。なので、ユーザーサイドでオートモードを再現してみました!使ってみたところ、割といい感じに使えましたので、喜びと方法を共有します!

コードの解説と使用方法

オートクリックができるモジュールはpyautoguiやpynputなど多くありますが、色々試してみた結果、Windows APIを直接呼び出すことができる、ctypes とwintypesのみが有効だと判明しました。このモジュールと音声認識のモジュールをつかったコードになります。
コードの動き方としては、5秒ごとに判定を行い、音声が認識されていない間はクリックを行わず、音声が認識されていなかったら、クリックを行う、みたいな感じになっています。(めっちゃガバガバなので、時間があればアップグレードします)
というわけですが、BGMと声を分けることはできていないので、使うときはBGMを切ることが必要です。(強化学習とかでどれがBGMでどれが人の声かを聞き分けることができたら最高なんすけどね。誰か作ってください)(あと、文字の量に応じて待機の時間を延ばしたりすることができたらもっといいんすけどね)

コードの中身

コードはこんな感じになっています。アプリケーションのウィンドウタイトル指定とクリック位置の指定を忘れずに!pip installも忘れずに!

pip install pygetwindow SpeechRecognition pyaudio pypiwin32

automode.py
import ctypes
import time
from ctypes import wintypes
import pygetwindow as gw
import speech_recognition as sr

# 定数の定義
MOUSEEVENTF_MOVE = 0x0001
MOUSEEVENTF_ABSOLUTE = 0x8000
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004

# 間隔を秒数で指定(例:5秒ごとにクリックを送信)
interval = 5

# アプリケーションのウィンドウタイトルを指定
app_title = "abcdefg"

# クリック位置を指定(ウィンドウの左上を基準とした相対座標)
click_x = 500
click_y = 500

# スクリーンの幅と高さを取得
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)

# 座標をスクリーン座標に変換
def convert_coords(x, y):
    return int(x * 65535 / screen_width), int(y * 65535 / screen_height)

# マウスイベントを送信する関数
def mouse_event(event, x, y, data, extra_info):
    x, y = convert_coords(x, y)
    ctypes.windll.user32.mouse_event(event, x, y, data, extra_info)

# 音声認識をセットアップ
recognizer = sr.Recognizer()
microphone = sr.Microphone()

def is_speech_recognized():
    with microphone as source:
        recognizer.adjust_for_ambient_noise(source)
        print("Listening for speech...")
        try:
            audio = recognizer.listen(source, timeout=interval*2, phrase_time_limit=interval)
            recognizer.recognize_google(audio)
            print("Speech detected!")
            return True
        except sr.UnknownValueError:
            print("No speech detected.")
            return False
        except sr.RequestError as e:
            print(f"Could not request results from Google Speech Recognition service; {e}")
            return False
        except sr.WaitTimeoutError:
            print("Listening timed out while waiting for phrase to start.")
            return False

try:
    while True:
        if not is_speech_recognized():
            # ウィンドウをアクティブにする
            window = gw.getWindowsWithTitle(app_title)
            if window:
                win = window[0]
                win.activate()

                # 少し待つ(ウィンドウがアクティブになるのを待つ)
                time.sleep(1)

                # マウスの位置を移動
                mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, click_x, click_y, 0, 0)

                # マウスの左ボタンを押す
                mouse_event(MOUSEEVENTF_LEFTDOWN, click_x, click_y, 0, 0)
                time.sleep(0.05)  # 少し待つ
                # マウスの左ボタンを離す
                mouse_event(MOUSEEVENTF_LEFTUP, click_x, click_y, 0, 0)

        # 指定した間隔だけ待機
        time.sleep(interval)
except KeyboardInterrupt:
    print("プログラムが中断されました。")

ちなみに、現在起動しているウィンドウのタイトル名はこのコードを実行すると得られます。

getwindow.py
import pygetwindow as gw

# 現在起動しているすべてのウィンドウを取得
windows = gw.getAllTitles()

# 各ウィンドウのタイトルを表示
for i, title in enumerate(windows):
    if title:  # 空のタイトルをスキップ
        print(f"{i+1}: {title}")

最後に

試作品みたいな感じなので、そのまま使うってよりかは、好みによって色々と修正する必要があると思います。っていうか、かなり調べたのですが、同人ゲーのオートモードのユーザーサイドの実装がみつからないことにはかなり驚きました。私もこのコードを使いつつアップグレードしていく予定なので、同人ゲーが好きな同士はぜひ(音声認識の強化とか)ノウハウを共有してください🥺。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4