前回の続きのエントリーとなります。今回はコントローラーの部分を作っていこうと思います。 ゲームのアプリケーションとの連携になるので、キーボードやコントローラーなどの操作をpythonから操作するものを 調べて使ってみようと思います。
【参考】
micro:bitからの操作をBluetoothで受信して、そのデータに従って操作制御を行うという形になります。
Minecraft Pi Edition
の操作は、キーボードとマウスの組み合わせのみになるので、これらをpython上から制御できるライブラリが必要になります。
いろいろ探してみると、キーボードとマウスの両方が制御できるメジャーなパッケージであるPyAutoGUI
がヒットします。
機能的にRPAに便利そうな制御を使用できることから、書籍などでも紹介されるメジャーなライブラリです。その他の内容も含めて以下の書籍は超おすすめです。
デジタル書籍は以下から
こちらのPyAutoGUI
を中心に動作テストをしてみたいと思います。
PyAutoGUI
のインストール
PyAutoGUI
は以下を参照にします。
PyAutoGUI
はpython2系もpython3系でも使用可能ですが、今回はpython3系を使用するので以下のようにpip3
インストールを行います。
インストールと同時に依存性のあるパッケージも同時にインストールされます。
【インストールコマンド】
$ pip3 install PyAutoGUI
【ログ】
$ pip3 install PyAutoGUI Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting PyAutoGUI Downloading https://www.piwheels.org/simple/pyautogui/PyAutoGUI-0.9.48-py3-none-any.whl Collecting pymsgbox (from PyAutoGUI) Downloading https://www.piwheels.org/simple/pymsgbox/PyMsgBox-1.0.7-py3-none-any.whl Collecting mouseinfo (from PyAutoGUI) Downloading https://www.piwheels.org/simple/mouseinfo/MouseInfo-0.1.2-py3-none-any.whl Collecting PyTweening>=1.0.1 (from PyAutoGUI) Downloading https://www.piwheels.org/simple/pytweening/PyTweening-1.0.3-py3-none-any.whl Collecting pygetwindow>=0.0.5 (from PyAutoGUI) Downloading https://www.piwheels.org/simple/pygetwindow/PyGetWindow-0.0.8-py3-none-any.whl Collecting pyscreeze>=0.1.21 (from PyAutoGUI) Downloading https://www.piwheels.org/simple/pyscreeze/PyScreeze-0.1.26-py3-none-any.whl Collecting python3-Xlib; platform_system == "Linux" and python_version >= "3.0" (from PyAutoGUI) Downloading https://www.piwheels.org/simple/python3-xlib/python3_xlib-0.15-py3-none-any.whl (110kB) 100% |████████████████████████████████| 112kB 176kB/s Requirement already satisfied: Pillow>=5.2.0; python_version == "3.7" in /usr/lib/python3/dist-packages (from mouseinfo->PyAutoGUI) (5.4.1) Collecting pyperclip (from mouseinfo->PyAutoGUI) Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProtocolError('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))': /simple/pyperclip/ Downloading https://www.piwheels.org/simple/pyperclip/pyperclip-1.7.0-py3-none-any.whl Collecting pyrect (from pygetwindow>=0.0.5->PyAutoGUI) Downloading https://www.piwheels.org/simple/pyrect/PyRect-0.1.4-py2.py3-none-any.whl Installing collected packages: pymsgbox, python3-Xlib, pyperclip, mouseinfo, PyTweening, pyrect, pygetwindow, pyscreeze, PyAutoGUI Successfully installed PyAutoGUI-0.9.48 PyTweening-1.0.3 mouseinfo-0.1.2 pygetwindow-0.0.8 pymsgbox-1.0.7 pyperclip-1.7.0 pyrect-0.1.4 pyscreeze-0.1.26 python3-Xlib-0.15
こちらでインストールは完了です。このライブラリはXを前提としているのでX上のコンソールなどで動かさないと起動時に以下のようなエラーが発生するので注意が必要です。
Traceback (most recent call last): File "pgui.sample.py", line 1, in <module> import pyautogui File "/home/pi/.local/lib/python3.7/site-packages/pyautogui/__init__.py", line 94, in <module> import mouseinfo File "/home/pi/.local/lib/python3.7/site-packages/mouseinfo/__init__.py", line 149, in <module> _display = Display(os.environ['DISPLAY']) File "/usr/lib/python3.7/os.py", line 678, in __getitem__ raise KeyError(key) from None KeyError: 'DISPLAY'
また、コンソールなどでのキー入力を行う場合にはウエイトなどは必要はないのですが、長押しなどの操作がある場合にはウエイト(sleep)が必要になるので
import time
を追加することが多いかと思います。
PyAutoGUI
をつかってみる
今回必要になるのはキーボード操作とマウス操作の2系統になりますのでそれぞれを考えてみることにします。
キーボード入力
Minecraft
で使用するキーボード操作は以下のようなものになります。すべては網羅していませんが、こんなところかなと思います。
操作キー | アクション |
---|---|
w | 前へ |
s | 後ろへ |
a | 左へ |
d | 右へ |
SPACE | ジャンプ 2回押しで飛行へ、飛行中は高度上昇 |
SHIFT | しゃがみ(スニーク) 飛行中は高度降下 |
e | インベントリを開く(持ち物を開く) |
1 ~ 9 | 画面中央下部から、数字の位置に該当するアイテムを選択 |
q | 選択中のアイテムを投げる(投げるのは1つ) |
ESC | メニュー画面を開く |
これらを操作することになります。
PyAutoGUI
でキーボード操作は以下を使用します。
メソッド | 使用例 | 意味 |
---|---|---|
typewrite() | pyautogui.typewrite('Hello world!\n', interval=0.25) | Hello world!を出力(文字間に0.25秒のインターバル) |
keyDown() | pyautogui.keyDown('shift') | シフトキーを押しっぱなしにする(処理後も押されている状態へ |
keyUp() | pyautogui.keyUp('shift') | シフトキーを放す(処理後も押していない状態へ) |
press() | pyautogui.press('esc') | ESCキーを押す |
write() | pyautogui.write('Hello world!', interval=0.25) | Hello world!を出力(文字間に0.25秒のインターバル) |
hotkey() | pyautogui.hotkey('ctrl', 'c') | CTRL+Cを押す(Windowsであればコピー) |
また、PyAutoGUI
で使用可能なキー文字列(引数可能な)は抜粋すると以下のようになっています。
キー文字列 | 対象のキー |
---|---|
a, A, 1, @ など | 各キーの文字 |
enter, return, \n | Enter |
esc | Esc |
shiftleft, shiftright | 左右のShift |
altleft, altright | 左右のAlt |
ctrlleft, ctrlright | 左右のCtrl |
tab, \t | Tab |
backspace | バックスペース |
delete | デリート |
pageup, pagedown | ページアップ・ページダウン |
up, down, left, right | 上下左右 |
f1, f2, f3 など | ファンクションキー(F1, F2など) |
capslock | CapsLock |
printscreen | Print Screen |
winleft, winright | 左右のWindowsキー |
ここまでを抑えて処理を考えてみます。以下の処理では操作時のウインドウをアクティブにすることは行っていないので、 処理開始後にWaitを3秒入れてその間にマウスを動かしてアクティブにしてください。
#!/usr/bin/env python3 import pyautogui import time # このウエイト間にウインドウをアクティブにすること time.sleep(3) # 前進 pyautogui.keyDown('w') time.sleep(0.153846) pyautogui.keyUp('w') time.sleep(2) # 右へ pyautogui.keyDown('d') time.sleep(0.153846) pyautogui.keyUp('d') time.sleep(2) # ジャンプ pyautogui.keyDown('space') time.sleep(0.15) pyautogui.keyUp('space') time.sleep(2) # 持ち物をチェンジ pyautogui.keyDown('2') time.sleep(0.15) pyautogui.keyUp('2') time.sleep(2) # 処理が終わったらTABキーを押して、ウインドウを非アクティブ化する pyautogui.press('tab')
基本的には、keyDown()
とkeyUp()
の組み合わせしか使っていません。というのも、移動に関しては基本長押しができることもあるので、その後のtime.sleep()
でキーを押している調整しています。
サンプルコードのtime.sleep()
の引数に0.153846
という数値を入れていますが、この値にすることでだいたい一歩(1ブロック分)の移動ができるようです。(この時間は色々移動させて計算しました)特に一歩という単位にこだわりがないのであれば、おおよその値を入れておけばいいかなと思います。
ただ、長押しが関係ない操作の場合にはtypewrite()
を使っても大丈夫です。(例えばチャットでメッセージを送る場合やコマンドを送信する場合など)
Minecraft
で使用する操作としては
- typewrite()
- keyDown()
- keyUp()
- hotkey()
などを組み合わせることでコントロールできます。でも、ウエイトの時間の計算などはちょっと面倒ですね。
マウス入力
Minecraft
ではマウスの操作で視界方向を操作したり、クリックでブロックを壊したりすることができます。
PyAutoGUI
でもマウス操作を行うことができます。以下まとめておきます。
ちなみにマウスの座標系は画面の左上のピクセルを原点として以下の画像の用になっています。
Minecraftの座標系では水平面はx-z座標系になっているので、混乱しないように注意が必要です。
メソッド | 使用例 | 意味 |
---|---|---|
position() | currentMouseX, currentMouseY = pyautogui.position() | 現在のマウスカーソルの座標の取得 |
moveTo() | pyautogui.moveTo(100, 150) | マウスカーソルの移動。x=100、y=150の位置へ移動 |
moveRel() | pyautogui.moveRel(300, 400) | マウスの相対移動。現在のマウスカーソルの座標からx=300、y=400の位置へ移動 |
click() | pyautogui.click(200, 220) pyautogui.click(clicks=3, interval=0.5) |
マウスクリック(指定座標でのクリックも可能) 0.5秒間隔でのトリプルクリック |
doubleClick() | pyautogui.doubleClick() | マウスのダブルクリック |
dragTo() | pyautogui.dragTo(100, 200, 3, button='left') | マウスの左ボタンを押しながら3秒かけてx=100、y=200の位置へドラッグ |
dragRel() | pyautogui.dragRel(-50, 0, 3, button='left') | マウスの左ボタンを押しながら3秒かけて現在のマウスカーソルの座標からx=-50、y=0の位置へドラッグ |
mouseDown() | pyautogui.mouseDown(button='right') | マウスの右ボタンを押下(し続ける) |
mouseUp() | pyautogui.mouseUp(button='right') | マウスの右ボタンを放す |
vscroll() | pyautogui.vscroll(-10) | マウスによる垂直方向-10(ピクセル?)スクロール |
hscroll() | pyautogui.hscroll(20) | マウスによる水平方向20(ピクセル?)スクロール |
基本的にはマウスの移動とクリック、ボタンの長押しぐらいができれば操作できますが、マウスの移動はそこまで必要ではありません。
アクション | 内容 |
---|---|
マウス(カーソル)の移動 | 視点移動(カメラ操作) |
左クリック | ブロックを壊す(長押し)/ 攻撃する インベントリ(持ち物)内のアイテムをダブルクリックをすれば、同一アイテムをまとめられる |
右クリック | ブロックを配置する アイテムを使う(食べる・飲む) ドアの開閉 弓を放つ 乗り物に乗る 作業台・チェストなど特殊ブロックを使用など |
センターホイール | アイテム切り替え |
#!/usr/bin/env python3 import pyautogui import time time.sleep(3) #マウス現在位置取得 x,y = pyautogui.position() pyautogui.click() # 前進 pyautogui.keyDown('w') time.sleep(0.153846) pyautogui.keyUp('w') time.sleep(2) # ジャンプ pyautogui.keyDown('space') time.sleep(0.15) pyautogui.keyUp('space') time.sleep(2) #現在位置からの相対移動、かける時間(duration)を秒で指定は同じ # pyautogui.moveRel(0, 5, duration=1) # 壊す pyautogui.mouseDown(button='left') time.sleep(0.5) pyautogui.mouseUp(button='left') time.sleep(2) #マウスホイール(手にもつアイテムを1つ右へ) pyautogui.scroll(-1, x, y) time.sleep(0.3) # アイテムを使う pyautogui.mouseDown(button='right') time.sleep(0.3) pyautogui.mouseUp(button='right') time.sleep(2) pyautogui.press('tab')
【操作前:実行前は水平な視点にしていても…】
【操作後:実行後は視点が真下を向いている】
実際に実行して、マウスカーソルの移動を行うと、キャラクターの視点が真下を向いてしまい、最後にマウスの右クリックで少しでアイテムを使用する処理が視点が真下であるために使用できなくなってしまいます。これでは操作としては今一つの印象です。この操作がそもそも必要なのかに関しては検討の余地はあるかもしれませんが。
他の制御手段としてMinecraft PI Edition
を操作するライブラリであるMCPI( Minecraft: Pi edition API Python Library)
があります。そちらと併用することで何らかの解決手段はあるのかもしれません。ちなみにMCPI
のソースを追ってみたのですが、Minecraft Pi Edition
ではキャラクターの向きや方向を設定するAPIが無いようです。※MCPI
については【後編】で取り上げます。PyAutoGUI
ではマウスのボタンを操作するだけのほうが良さそうな感じです。
おわりに
PyAutoGUI
を使うことでMinecraftをPython上から動かすことができました。ただ、これではあまりにもコントロールが微妙な部分が多いので、Minecraft
を操作するライブラリであるMCPI( Minecraft: Pi edition API Python Library)
を組合わせてみようと思います。また、最後ということでmicro:bit
からのBluetooth
制御も行ってみたいと思います。
【参考】