ジョイスティックの値を表示する

マイクロソフト ゲーム コントローラー Xbox 360 Controller for Windows C8G-00003

マイクロソフト ゲーム コントローラー Xbox 360 Controller for Windows C8G-00003

screenshot
とある思惑があってマイクロソフト謹製ジョイパットを買ってしまいました。

ELECOM のそれを既に持っていたのですが、さすがコンシューマ機メーカー製、作りが全然違いますね。これはいいや。

というわけでとりあえず、Python + Pygame で値を表示するスクリプトです。

"""Joystick value viewer

(Python 3.1 + Pygame 1.9.1)

ジョイスティックの値一覧を表示します。
"""
import pygame
from pygame.locals import *

WIDTH, HEIGHT = 512, 512
COLOR = 255, 255, 255  # 文字色
OFF_COLOR = 128, 128, 153  # 非アクティブ時の文字色
BG_COLOR = 0, 0, 50  # 背景色
FONTSIZE = 32
FPS = 60

def blittext(surface, font, text, dest, color=COLOR):
    text_s = font.render(text, True, color)
    surface.blit(text_s, dest)
    x, y = dest
    return x + text_s.get_width(), y + font.get_linesize()


class Joy:

    def __init__(self, joy_no):
        self.no = joy_no
        j = pygame.joystick.Joystick(joy_no)
        j.init()
        self.j = j
        self.name = j.get_name()
        self.axes = [0] * j.get_numaxes()
        self.buttons = [False] * j.get_numbuttons()
        self.hats = [(0, 0)] * j.get_numhats()

    def event(self, e, frame_no=0):
        if e.type == JOYAXISMOTION and e.joy == self.no:
            self.axes[e.axis] = e.value
            print('{0:5d} axis {1:d}:{2: f}'.format(
                frame_no, e.axis, e.value))
        elif e.type == JOYHATMOTION and e.joy == self.no:
            self.hats[e.hat] = e.value
            print('{0:5d} hat {1:d}:{2:s}'.format(
                frame_no, e.hat, e.value))
        elif e.type == JOYBUTTONUP and e.joy == self.no:
            self.buttons[e.button] = False
            print('{0:5d} button {1:d} up'.format(
                frame_no, e.button))
        elif e.type == JOYBUTTONDOWN and e.joy == self.no:
            self.buttons[e.button] = True
            print('{0:5d} button {1:d} doun'.format(
                frame_no, e.button))

    def blittext(self, surface, font, dest, color=COLOR, off_color=OFF_COLOR):
        x, y = dest
        d, y = blittext(surface, font, self.name, (x, y))
        y += (font.get_linesize() // 4)
        bx, d = blittext(surface, font, 'buttons: ', (x, y))
        for i, b in enumerate(self.buttons):
            bx, d = blittext(surface, font, str(i), (bx, y),
                             (off_color, color)[b])
        y += font.get_linesize()
        d, y = blittext(
            surface, font,
            'hats: {0:s}'.format(
                ', '.join(['_RL'[vx] + '_UD'[vy] for vx, vy in self.hats])),
            (x, y))
        d, y = blittext(surface, font, 'axis:', (x, y))
        r = surface.get_rect()
        for i, a in enumerate(self.axes):
            pygame.draw.rect(
                surface, off_color,
                (r.centerx, y, int(WIDTH * 0.4 * a), font.get_ascent()))
            d, y = blittext(
                surface, font,
                '  {0:d}: {1:+f}'.format(i, a),
                (x, y))
        return x, y


def main():
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    font = pygame.font.Font(None, FONTSIZE)
    joysticks = [Joy(n) for n in range(pygame.joystick.get_count())]
    pygame.display.set_caption('Joystick value')
    clock = pygame.time.Clock()
    clock.tick(FPS)
    frame_no = 0
    while True:
        for e in pygame.event.get():
            if e.type == QUIT:
                return
            elif e.type == KEYDOWN and e.key == K_ESCAPE:
                return
            else:
                for j in joysticks:
                    j.event(e, frame_no)
        screen.fill(BG_COLOR)
        x, y = 5, 5
        for j in joysticks:
            x, y = j.blittext(screen, font, (x, y))
            y += font.get_linesize()
        clock.tick(FPS)
        pygame.display.flip()
        frame_no += 1


if  __name__ == '__main__':
    try:
        main()
    finally:
        pygame.quit()

# Public Domain. 好きに流用してください。

Microsoft 公式ということは、これがこれからの Windows での標準ということで、今までばらばらだったボタンの割り当てなんかにも基準ができるわけですからいいこと、なんでしょうね。ただ Pygame(というか SDL)だとまだ XInput に対応しておらず DirectInput 使っているみたいで左右トリガーの値を独立して取り扱えない、とかバイブレーション出力不能とか問題あり*1ですが、これもそのうちどうにか……なるのかな? まあ MIDI にも対応したし、そのうちこちらもどうにかなると期待だけはしておきます。

ついでに Pygame での割り振り

(1つ有る hat については常識通りなので省略)

button
No ゲームパッド
0 A
1 B
2 X
3 Y
4 LB(左ショルダー)
5 RB(右ショルダー)
6 BACK
7 START
8 左スティック押下
9 右スティック押下

Xbox ガイド ボタンは使用不能

axis
No ゲームパッド
0 左スティックの X 軸
1 左スティックの Y 軸
2 +: LT(左トリガー),
-: RT(右トリガー)
3 右スティックの Y 軸
4 右スティックの X 軸

左右トリガーが連動してしまっているという前述のそれの他、右と左のスティックで X軸、Y軸の順番が逆、というのも問題かも。(ELECOM のものでは右も左も若い番号の方が X軸)