ArcadeでpygletのJoystickを使う

Arcadeでは、ゲームコントローラーを扱うための機能が用意されています。 一般的なゲームコントローラーであれば、arcade.get_controllers() を利用できます。

controllers = arcade.get_controllers()

しかし、コントローラーによっては、Arcadeが使用している pyglet によってHIDデバイスとして認識されていても、 標準的なコントローラーとしての「mapping」が登録されていない場合があります。 その場合、arcade.get_controllers() ではコントローラーを取得できないことがあります。

例えば、arcade.get_controllers()を実行すると

Warning: HIDDevice(name=4Axes 12Key GamePad)
has no controller mappings.

という警告が表示され、

controllers = arcade.get_controllers()
print(len(controllers))

の結果が 0 になる場合があります。このような場合は、Arcadeが内部で使用している pygletのJoystick API を直接利用する方法があります。

1. pygletを読み込む

Arcadeと一緒にpygletを読み込みます。

import arcade
import pyglet

Arcadeは内部でpygletを利用しているため、Arcadeのプログラムからpygletの入力機能を利用できます。

2. Joystickを取得する

pygletでは、get_joysticks() を使って接続されているJoystickを取得できます。

joysticks = pyglet.input.get_joysticks()
print("Joystick数:", len(joysticks))

例えば、Joystick数: 1と表示されれば、Joystickが1台認識されています。

3. Joystickを開く

取得したJoystickを使用するには、open() を呼び出します。

joysticks = pyglet.input.get_joysticks() 
if joysticks: 
    joystick = joysticks[0]
    joystick.open()

複数のJoystickを扱う場合は、リストの要素をそれぞれ開きます。

joysticks = pyglet.input.get_joysticks() 
for joystick in joysticks:
    joystick.open()

4. ボタン入力をcallbackで取得する

Joystickのボタンはイベントとして取得できます。

#joystick.event
def on_joybutton_press(joystick, button):
    print("ボタンを押しました:", button)
#joystick.event
def on_joybutton_release(joystick, button):
    print("ボタンを離しました:", button)

ボタンを押すと on_joybutton_press() が呼び出されます。 ボタンを離すと on_joybutton_release() が呼び出されます。 そのため、ボタンについては定期的に状態を調べるポーリング方式ではなく、イベント駆動方式で処理できます。

5. スティックや軸の入力をcallbackで取得する

Joystickの軸が動いたときは、on_joyaxis_motion() が呼び出されます。

#joystick.event
def on_joyaxis_motion(joystick, axis, value):
    print("軸:", axis)
    print("値:", value)

例えばスティックを動かすと、

軸: x
値: 0.52

のようなイベントが発生します。axis は軸の名前、value はその軸の現在の値です。 一般的には、

x y z rx ry rz

などの軸があります。ただし、Joystickによって軸の数や割り当ては異なるため、 実際の値を確認することが重要です。

6. ArcadeのWindowでJoystickイベントを受け取る

ArcadeのゲームでJoystickを利用する場合は、JoystickのイベントハンドラをArcadeのWindowに登録する方法が便利です。

import arcade
import pyglet
class GameWindow(arcade.Window):
    def __init__(self):
        super().__init__(800, 600, "Joystick Test")
        joysticks = pyglet.input.get_joysticks()
    
        if joysticks:
            self.joystick = joysticks[0]
            self.joystick.open()
            self.joystick.push_handlers(self)
    
    def on_joybutton_press(self, joystick, button):
        print("ボタンを押しました:", button)
    
    def on_joybutton_release(self, joystick, button):
        print("ボタンを離しました:", button)
    
    def on_joyaxis_motion(self, joystick, axis, value):
        print("軸:", axis, "値:", value)
    
    def on_draw(self):
        self.clear()

game = GameWindow()
arcade.run()

ポイントは、self.joystick.push_handlers(self)です。 これによって、Joystickから発生したイベントを GameWindow で受け取れるようになります。 そのため、def on_joybutton_press(self, joystick, button): や、def on_joyaxis_motion(self, joystick, axis, value): を GameWindow のメソッドとして定義できます。

7. スティックの値は変数に保存する

スティック入力の場合、callbackの中ですぐにゲーム処理を行うより、現在の入力状態を変数に保存する方法が便利です。例えば、

self.joystick_x = 0
self.joystick_y = 0

としておきます。 そしてcallbackで、

def on_joyaxis_motion(self, joystick, axis, value):

    if axis == "x":
        self.joystick_x = value

    elif axis == "y":
        self.joystick_y = value

とします。 これで、

スティックを動かす
        ↓
on_joyaxis_motion()
        ↓
joystick_x / joystick_y を更新

という処理になります。

8. on_update()でゲームに反映する

保存した値は、Arcadeの on_update() でゲームに利用できます。 例えば、スプライトをスティックで移動する場合、

def on_update(self, delta_time):

    self.player.change_x = self.joystick_x * 5
    self.player.change_y = self.joystick_y * 5

    self.player.update()

とできます。このように、入力を受け取る処理on_joyaxis_motion()と、ゲームを更新する処理 on_update()を分けると、プログラムを整理しやすくなります。

9. ボタンとスティックの役割

ゲームでは、ボタンとスティックでは扱い方が少し異なります。

ボタンは「押した」「離した」という瞬間的なイベントとして扱いやすい入力です。

def on_joybutton_press(self, joystick, button):
    if button == 0:
        print("ボタン0を押した")

例えば、

Aボタン → ジャンプ
Bボタン → 攻撃
START → ゲーム開始

などに利用できます。

スティックは「現在どの方向に、どの程度倒されているか」という状態として扱うことが多い入力です。 そのため、

on_joyaxis_motion()
        ↓
現在値を変数に保存
        ↓
on_update()
        ↓
キャラクターを移動

という構造にすると扱いやすくなります

10. Joystickの情報を確認する

Joystickによって、軸やボタンの構成が異なる場合があります。 そのため、最初にJoystickの情報を確認するプログラムを作っておくと便利です。

import pyglet
joysticks = pyglet.input.get_joysticks()
print("Joystick数:", len(joysticks)) 

for i, joystick in enumerate(joysticks): 
    print() print("Joystick", i) 
    print("名前:", joystick.name) 
    print("Joystick:", joystick)

さらに、実際にスティックやボタンを操作して、

def on_joybutton_press(self, joystick, button):
    print("ボタン:", button)

def on_joyaxis_motion(self, joystick, axis, value):
    print("軸:", axis, "値:", value)

で値を確認すると、そのJoystickの入力構成を調べることができます。

11. arcade.get_controllers() と pyglet.input.get_joysticks() の違い


API 用途
arcade.get_controllers() 標準的なControllerとして認識されたゲームコントローラ
pyglet.input.get_joysticks() より汎用的なJoystickデバイス

標準的なゲームコントローラーであれば、arcade.get_controllers()を利用するのが簡単です。しかし

HIDデバイスとして認識されている
 ↓ 
Controller mappingが存在しない
 ↓ 
arcade.get_controllers() が空

という場合には、pyglet.input.get_joysticks()を利用する方法があります。