pygameでマウス・キーボードを使う

Python
この記事は約17分で読めます。

今回は、pygameでマウスやキーボードを使う『イベント処理』をいろいろ試してみます。

イベント処理おさらい

pygameでイベントを処理する基本パターンは、以下の通りでした。

while True:                                             # メッセージループ
    for event in pygame.event.get():                    # メッセージを取得
        if event.type == pygame.locals.QUIT:            # ×ボタンが押されたとき
            pygame.quit()
            sys.exit()
    pygame.time.wait(100)

for event in pygame.event.get(): で発生したイベントを1つずつ変数event に取得し、 event.type を参照してイベントの種類毎に処理を振り分けるのでした。

マウスのイベント

イベントの種類

pygameで扱えるマウスイベントは3つあります。

pygame.locals.MOUSEBUTTONDOWN

マウスボタンが押されたときに発生。
(Java の MouseListener の mousePressed に相当)

pygame.locals.MOUSEBUTTONUP

マウスボタンが離されたときに発生。
(Java の MouseListener の mouseReleased に相当)

pygame.locals.MOUSEMOTION

マウスカーソルが移動したときに発生。
(Java の MouseMotionListener の mouseMoved に相当)

Java で使える mouseClicked(マウスボタンがクリックされた)や mouseEnterd(特定領域にマウスカーソルが入った)、mouseExited(特定領域からマウスカーソルが出た)に相当するものは pygame にはないようです。

個別のイベントにはなっていませんが、

MOUSEBUTTONDOWN の後、MOUSEMOTION があってから MOUSEBUTTONUP があればドラッグ
MOUSEBUTTONDOWN と MOUSEBUTTONUP の間に MOUSEMOTION がなければ(または時間間隔が短ければ)クリック

などと判別することはできるでしょう。

マウスイベント関係のプロパティ

マウスイベントでは、変数eventから2つの値を取得することができます。

event.pos

イベントが発生したときのマウスカーソルの座標を表します。座標はウィンドウのクライアント領域の左上隅を (0, 0) とし、x軸は右方向が正・y軸は下方向が正となる値です。

座標値は (x座標, y座標) で表されていて、

x,y = event.pos

のような受け取り方が可能です。

event.button

MOUSEBUTTONDOWN、MOUSEBUTTONUPの場合、どのボタンが押された/離されたかを表しています。値は整数値で、以下の意味です。

1 左ボタン
2 中ボタン
3 右ボタン
4 ホイールを奥に向かって回す
5 ホイールを手前に向かって回す

最近の多くのマウスのようにホイールが中ボタンを兼ねている場合、押しこむと3、奥に回すと4、手前に回すと5です。

ホイールの回転量を直接取得するプロパティやメソッドはないようですが、一定角度回るたびにMOUSEBUTTONDOWN/MOUSEBUTTONUPイベントが発生するようなので、イベント発生回数をカウントすればホイール回転量を計測することができるでしょう。

サンプルプログラム

では、簡単なプログラムを作って実験してみます。

import pygame
import pygame.locals
import sys

pygame.init()
pygame.display.set_mode((640,480))                      # ウィンドウサイズ(width. height) のタプルで指定
pygame.display.set_caption("mouse event test")          # ウィンドウのタイトルを指定
surface = pygame.display.get_surface()                  # サーフェイス(描画のための土台)を求める
surface.fill((0,128,0))                                 # 背景色を (R, G, B) のタプルで指定

while(True):                                            # メッセージループ
    pygame.display.update()                             # 表示の更新(これがないとクライアント領域が表示されない)
    for event in pygame.event.get():                    # メッセージを所得
        if event.type == pygame.locals.QUIT:            # ×ボタンが押されたとき
            pygame.quit()
            sys.exit()
        if event.type == pygame.locals.MOUSEBUTTONDOWN: # マウスボタンが押されたとき
            x,y = event.pos                             # マウスカーソルの座標を取得
            b   = event.button                          # ボタンの種類を取得
            print("MOUSE BUTTON",b,"DOWN",x,y)
        if event.type == pygame.locals.MOUSEBUTTONUP:   # マウスボタンが離されたとき
            x,y = event.pos
            b   = event.button
            print("MOUSE BUTTON",b,"UP",x,y)
        if event.type == pygame.locals.MOUSEMOTION:     # マウスカーソルが移動したとき
            x,y = event.pos
            print("MOUSE MOTION",x,y)
        
    pygame.time.wait(100)

実行するとウィンドウが表示されますが、結果はコンソールに表示されます。ウィンドウ上でマウスカーソルを動かしたり、ボタンを押したり離したりしてみましょう。

MOUSE MOTION 423 126
MOUSE MOTION 422 126
MOUSE BUTTON 1 DOWN 422 126
MOUSE BUTTON 1 UP 422 126
MOUSE MOTION 420 127
MOUSE MOTION 417 128
MOUSE MOTION 414 129
MOUSE BUTTON 3 DOWN 414 129
MOUSE MOTION 390 142
MOUSE MOTION 389 143
MOUSE BUTTON 3 UP 389 143

キーボードのイベント

イベントの種類

pygame で扱えるキーボードイベントは2つあります。

pygame.locals.KEYDOWN

キーボードのキーが押されたときに発生。
(Java の keyListener の keyPressed に相当)

デフォルトでは、キーを押しっぱなしにしても KEYDOWN イベントは一度しか発生しません。これはOSのキーリピートの設定とは関係がなく、変更する場合は pygame.key.set_repeat() メソッドを使用します。

pygame.locals.KEYUP

キーボードのキーが話されたときに発生。
(Java の keyListener の keyReleased に相当)

キーイベントのプロパティ

キーイベントでは、変数eventから以下の値を取得することができます。

event.key

どのキーが押されたかを取得できます。物理的なキーごとに異なるコードが割り当てられているので、例えばフルキーの『1』とテンキーの『1』では値が異なります。
また逆に、『a』キーを単独で押しても、ShiftやCAPSを併用しても(つまり入力されるのが『a』『A』のいずれであっても)同じ値となります。
Shiftキー、Ctrlキー、矢印キーなど、文字が入力されないキーでも判別できます。

キーのコードにはpygameモジュールで定数が定義されています。主要なもの(ゲームでよく使われるもの、次項のevent.unicodeで判別できないもの)を揚げておきます。

pygame定数キー
K_0
~K_9
フルキーの数字0~9キー
K_KP_0
~K_KP_9
テンキーの数字0~9キー
K_a
~K_z
アルファベットa~zキー
K_UP上矢印キー
K_DOWN下矢印キー
K_LEFT左矢印キー
K_RIGHT右矢印キー
K_LALT、K_RALTそれぞれ左・右のALTキー
K_LCTRL、K_RCTRLそれぞれ左・右のCTRLキー
K_LSHIFT、K_RSHIFTそれぞれ左・右のSHIFTキー
K_SPACEスペースキー
K_RETURNエンターキー
event.unicode

そのキーが押された時に入力される文字を取得できます。よってevent.keyとは異なり、『a』というキーを単独で押すと『a』、Shiftキーと同時に押すと『A』が得られます。
また、Shiftキー、Ctrlキーなど、文字を入力しないキーを単独で押した場合は、KEYDOWNイベントは発生しますが event.unicode の中身は空です。

event.mod

修飾キーが押されているかどうかの情報を取得できます。左右のShiftキーやCtrlキーなど、各キーにそれぞれ1ビットの桁を割り当てた整数値で表します。値は以下の通りです。pygame定数名はキーを判別するために定義されたものです。event.modと下記定数のAND演算を行い、0でなければキーが押されていると判別できます。

NumLock と CapsLock のような、押すたびにセット/リセットの状態が切り替わるタイプのキーの場合、物理的にキーが押されていなくても『セットされた状態』ならば該当するビットがセットされます。

pygame定数名キー
0pygame.KMOD_NONE修飾キーがなにも押されていない
1pygame.KMOD_LSHIFT左 Shift キー
2pygame.KMOD_RSHIFT右 Shift キー
3pygame.KMOD_SHIFT左右の Shift キーのいずれか(1+2)
64pygame.KMOD_LCTRL左 Ctrl キー
128pygame.KMOD_RCTRL右 Ctrl キー
192pygame.KMOD_CTRL左右の Ctrl キーのいずれか(64+128)
256pygame.KMOD_LALT左 Alt キー
512pygame.KMOD_RALT 右 Alt キー
768pygame.KMOD_ALT左右の Alt キーのいずれか(256+512)
1024pygame.KMOD_LMETA左メタキー(左Windowsキー)
2048pygame.KMOD_RMETA右メタキー(右Windowsキー)
3072pygame.KMOD_META左右のメタキーのいずれか(1024+2048)
4096pygame.KMOD_NUMNUM Lock キー
8192pygame.KMOD_CAPSCAPS Lock キー
16384pygame.KMOD_MODE(現在の日本語キーボードには該当なし)
event.scancode

これも物理的なキーごとに異なるコードを得られます。event.key ではコードが割り当てられていない、日本語キーボードの『変換』『無変換』『半角全角』『カタカナひらがなローマ字』も判別できます。ただし event.keyのコードのような判りやすいpygame定数は定義されていないようです。

キーイベントに関連するメソッド

キーリピート

pygameでは、OSのキーリピート設定とは無関係に、キーをずっと押し続けた場合に KEYDOWNイベントの発生をリピートさせることができます。キーリピートの設定をするには、pygame.key.set_repeat() メソッドを使用します。

pygame.key.set_repeat(delay, interval)

キーを押し続けた場合、delay だけ待ってから interval 毎にKEYDOWNイベントが発生します。いずれも単位は ms です。引数を省略するとキーリピートが無効になります。なお、OSでのキーリピートの設定には関係なく、pygame.init() した直後はキーリピートが無効になっています。

サンプルプログラム

ではキーイベントを利用した簡単なプログラムを作って実験してみましょう。このプログラムは、矢印キーを使って白い円を上下左右に動かすものです。pygame.key.set_repeat() を利用して、キーをずっと押し続ければ連続して白い円が動き続けます。

import pygame
import pygame.locals
import sys

pygame.init()
pygame.display.set_mode((640,480))                      # ウィンドウサイズ(width. height) のタプルで指定
pygame.display.set_caption("key event test")            # ウィンドウのタイトルを指定
surface = pygame.display.get_surface()                  # サーフェイス(描画のための土台)を求める

x=320
y=240
pygame.key.set_repeat(100,100)                          # 100ms間隔でキーリピートするように設定

while(True):                                            # メッセージループ
    for event in pygame.event.get():                    # メッセージを所得
        if event.type == pygame.locals.QUIT:            # ×ボタンが押されたとき
            pygame.quit()
            sys.exit()
        if event.type == pygame.locals.KEYDOWN:         # キーが押された時
            if event.key == pygame.K_UP:                # ↑ キーが押された時
                y -= 10
                if y<=0:
                    y=470
            if event.key == pygame.K_DOWN:              # ↓ キーが押された時
                y += 10
                if y>=480:
                    y=10
            if event.key == pygame.K_LEFT:              # ← キーが押された時
                x -= 10
                if x<=0:
                    x=630
            if event.key == pygame.K_RIGHT:             # → キーが押された時
                x += 10
                if x>=640:
                    x=10
    surface.fill((0,128,0))                             # 画面を塗りつぶす
    pygame.draw.circle(surface,(255,255,255),(x,y),20)  # (x,y)の位置に白い円を描く
    pygame.display.update()                             # 画面の更新
    pygame.time.wait(100)

イベントに依らないキー入力

pygameでは、KEYDOWNイベントに依らずにキーが押されているかどうかを判別する方法が用意されています。

キー入力のメソッド

一般キーの押された状態を取得する

キーが押されているかどうかを判別するには、pygame.key.get_pressed() メソッドを使用します。キーイベントの発生とは無関係に、このメソッドが実行された瞬間の状態を返します。返却値は真偽値のリストで、キーイベントの event.key のキーコードを添字とする要素が、そのキーが押されているかどうかを表します。

たとえば以下のコードでは、↑矢印キーが押されているかを判別できます。

keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
    print("↑が押されています")
修飾キーの押された状態を取得する

Shiftキーなどの修飾キーが押されているかどうかを判別するには、pygame.key.get_mods() メソッドを使用します。キーイベントの発生とは無関係に、このメソッドが実行された瞬間の状態を返します。返却値は整数値で、KEYDOWN イベントでの event.mod の値と意味は同じです。

サンプルプログラム

キーイベントと同じく矢印キーで白い円が動くプログラムですが、get_pressed() メソッドでは複数のキーの状態を同時に取得できるため、たとえば↑と→を同時に押したときに斜め右上に移動ができます。

import pygame
import pygame.locals
import sys

pygame.init()
pygame.display.set_mode((640,480))                      # ウィンドウサイズ(width. height) のタプルで指定
pygame.display.set_caption("key event test")            # ウィンドウのタイトルを指定
surface = pygame.display.get_surface()                  # サーフェイス(描画のための土台)を求める

x=320
y=240
pygame.key.set_repeat(100,100)                          # 100ms間隔でキーリピートするように設定

while(True):                                            # メッセージループ
    for event in pygame.event.get():                    # メッセージを所得
        if event.type == pygame.locals.QUIT:            # ×ボタンが押されたとき
            pygame.quit()
            sys.exit()
    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP]:                # ↑ キーが押された時
        y -= 10
        if y<=0:
            y=470
    if keys[pygame.K_DOWN]:              # ↓ キーが押された時
        y += 10
        if y>=480:
            y=10
    if keys[pygame.K_LEFT]:              # ← キーが押された時
        x -= 10
        if x<=0:
            x=630
    if keys[pygame.K_RIGHT]:             # → キーが押された時
        x += 10
        if x>=640:
            x=10
    surface.fill((0,128,0))                             # 画面を塗りつぶす
    pygame.draw.circle(surface,(255,255,255),(x,y),20)  # (x,y)の位置に白い円を描く
    pygame.display.update()                             # 画面の更新
    pygame.time.wait(100)

注意点

pygame.key.get_pressed() メソッドは、実行された瞬間のキーの状態を取得するものです。よって、 pygame.key.get_pressed() が実行されてから次に pygame.key.get_pressed() が実行されるまでの間に押して離されたキーについては、押されたことを知ることができません。

また、pygame.key.get_pressed() が実行されてから次に pygame.key.get_pressed() が実行されるまでの間に複数のキーが押された場合、その押された順を知ることができません。

KEYDOWNイベントならばこれらを知ることができます。

プログラムの操作法によって使い分けるようにするのが良さそうです。

というわけで。

図形描画、マウス&キー入力と来て、いろいろ作れそうな気がしてきました。

次回は簡単なゲームでも作ってみようかな…。

コメント

タイトルとURLをコピーしました