Mukai Systems

Seeeduino XIAOでマクロパッドを作ってみた

Cookie Clickerというゲームがある。

興味がある方はここで無料で遊べるから是非やってみてほしい。

画面に現れるクッキーを1回クリックするごとに1枚クッキーを焼くことができる。焼いたクッキーはクッキーの生産施設購入費用に充てることができ、次第に一定時間毎に大量のクッキーが手に入るようになる仕組みをとっている。

-- Wikipedia: クッキークリッカー

要は、拡大生産系である。元祖放置ゲーでもあるらしい。

このゲームはただクッキーをクリックするだけではない。「ゴールデンクッキー」という、一定間隔で画面上にランダムで出現するクッキーが存在する。効果も「一定時間クリック強化」から「xxx枚クッキー獲得」などいくつかの候補からランダムで決定される。これが「究極のスキナー箱」と言わしめる所以であろう。それ以外にも、周回やミニゲームといった要素があり、随所に飽きさせない工夫がみられる。1

はっきり言って、すごい面白いわけではないと思うし、人にお勧めするかと言われたら絶対にしない。にもかかわらず、高評価をつけたくなる憎めないゲームなのである。実際に執筆現在Steamでの評価は沢山のクッキー中毒者によって「圧倒的に好評」となっている。これがどれほど凄いことか、AAA級のゲームを検索して比較してみてほしい。

とはいえ、しばらく遊んでいるとクリックするのが疲れてくる。我々プログラマにとって自動化こそが至高の喜びではないか。マイコンを使えば自動でクリックするデバイスを作成できそうな事がわかった。

方法

マイコンにスイッチを接続し、スイッチがONの場合にクリックイベントを発生させ続ける。ついでに、スイッチがOFFの場合にもマクロパッドとして利用できるようにする。

用意

今回使用したものは次のとおり。

上記のほか、はんだ、配線用ケーブルなど消耗品を使用した。全部込みでも税込み2500円未満だと思う。

実装

CircuitPythonのインストール

公式wikiに従い、CircuitPythonをインストールする。

インストールを行い、ここからマウスとキーボードのライブラリをダウンロードし、libに保存する。

手順に実行すると以下のようになる。

+ .fseventsd/
- lib/
 |+ adafruit_hid/
+ System Volume Information/
  .metadata_never_index
  .Trashes
  boot_out.txt
  code.py

code.pyにコードを書き込むとプログラムが実行される。

ハードウェア実装

今回のように実装するスイッチの数が高々6個の場合は、マイコンのpinとスイッチを1対1に対応させればよい。

マイコンのpinは有限だから、一般にスイッチの数が多くなるとそのようにはできなくなる。その場合キーマトリックス方式という配線を行う。この方式で実装すると、例えば16pinあれば8 x 8 = 64個のキーをNキーロールオーバー対応で扱うことができる。

購入したメカニカルスイッチのDIP化のキットがキーマトリックス方式に対応しているため、今回はそのように実装した。

商品の説明書実装例を参考にはんだ付けを行う。

ブレッドボードで動作を確認する。緑2 x 黄3で6個のスイッチがキーマトリックス方式で接続されているような雰囲気が伝わるだろうか。動作確認後、ユニバーサル基盤へはんだ付けを行う。

下が完成したものである。Seeeduino XIAOはUSB Type-CでPCと接続する。右下に見えるのがトグルスイッチで後述するようにマクロパッドの挙動を変更する。

ソフトウェア実装

最終的なcode.pyは次のようになった。

from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse
import board
import digitalio
import time
import usb_hid

sw = digitalio.DigitalInOut(board.D7)
sw.direction = digitalio.Direction.INPUT
sw.pull = digitalio.Pull.DOWN

row_pins = [board.D0, board.D1]
col_pins = [board.D4, board.D5, board.D6]
key_rows = []
key_cols = []

for pin in row_pins:
    row = digitalio.DigitalInOut(pin)
    row.direction = digitalio.Direction.INPUT
    row.pull = digitalio.Pull.DOWN
    key_rows.append(row)

for pin in col_pins:
    col = digitalio.DigitalInOut(pin)
    col.direction = digitalio.Direction.OUTPUT
    col.value = 0
    key_cols.append(col)

keycodes = [
        [Keycode.E, Keycode.D],
        [Keycode.W, Keycode.S],
        [Keycode.Q, Keycode.A]
        ]

states = [
            [False, False],
            [False, False],
            [False, False]
            ]

mouse = Mouse(usb_hid.devices)
keyboard = Keyboard(usb_hid.devices)

while True:
    time.sleep(0.001)
    if sw.value:
        key_cols[0].value = 1
        if not key_rows[0].value:
            mouse.click(Mouse.LEFT_BUTTON)
    else:
        for i in range(len(col_pins)):
            key_cols[i].value = 1
            for j in range(len(row_pins)):
                state = key_rows[j].value
                if not state == states[i][j]:
                    states[i][j] = state
                    if state:
                        keyboard.press(keycodes[i][j])
                    else:
                        keyboard.release(keycodes[i][j])
            key_cols[i].value = 0

末尾にあるwhile文が実質的なイベントハンドラである。swがトグルスイッチに対応し、ONかOFFかで挙動を変えている。

トグルスイッチがONの場合はクリックを行い続ける。ただし、右上のキーが押されている場合はクリックを行わない。この機能は、施設を購入したい場合などに一時的にクリックをやめて手動でクリックを行う用途などに使える。

トグルスイッチがOFFの場合はクリックは行わず、ただのマクロパッドとして振る舞う。それぞれのキーはq, w, e, a, s, dに対応させた。マイクラのような一般的なアクションゲームで移動ができる。

左側に見える大きいCookieをクリックするとクッキーが手に入る。duodecillion枚とは、すごいインフレである。

作成したクリックマシンを使用すると枚数がわからなくなるくらい入手できる。

おしまい。

Source code

More info

See also


[1] 画面に張り付いていては放置ゲーではないのではないか…。