読者です 読者をやめる 読者になる 読者になる

人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習・Deep Learningに関する記事が多いです。



キーイベント

Pygame

ゲームの入力インタフェースとしてキーボードとマウスは欠かせません。マウスに関してはマウスイベント(2008/5/6)で紹介しました。ここでは、キーボードの入力を検出する方法を紹介します。 Pygameでキーボード入力を検出する方法は、マウスと同じく主に2つあります。1つめは、イベントハンドラでキーイベントを捕捉する方法、2つめは、pygame.keyモジュールの関数を使ってキー入力を検出する方法です。ゲームでは2つめの方法を使うのが一般的ですが、キーを押した、離したの瞬間を検出したい場合はイベントハンドラ方式が役立ちます。ここでは、

  • ESC キーを押すとスクリプトが終了する
  • 矢印キーを押すと画像が移動する

サンプルを作ります。

key_event.zip
f:id:aidiary:20100605095126p:plain

サンプルスクリプト1

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys
 
SCREEN_SIZE = (640, 480)
 
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption(u"キーイベント")
 
img = pygame.image.load("python.png").convert_alpha()
img_rect = img.get_rect()
img_rect.center = (320, 240)
 
vx = vy = 10  # キーを押したときの移動距離
 
while True:
    screen.fill((0,0,255))
    screen.blit(img, img_rect)
    pygame.display.update()
    
    for event in pygame.event.get():
        if event.type == QUIT: sys.exit()
        if event.type == KEYDOWN:  # キーを押したとき
            # ESCキーならスクリプトを終了
            if event.key == K_ESCAPE:
                sys.exit()
            # 矢印キーなら画像を移動
            if event.key == K_LEFT:
                img_rect.move_ip(-vx, 0)
            if event.key == K_RIGHT:
                img_rect.move_ip(vx, 0)
            if event.key == K_UP:
                img_rect.move_ip(0, -vy)
            if event.key == K_DOWN:
                img_rect.move_ip(0, vy)

サンプルスクリプト2

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys
 
SCREEN_SIZE = (640, 480)
 
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption(u"キーイベント2")
 
img = pygame.image.load("python.png").convert_alpha()
img_rect = img.get_rect()
img_rect.center = (320, 240)
 
vx = vy = 10  # キーを押したときの移動距離
 
while True:
    # 押されているキーをチェック
    pressed_keys = pygame.key.get_pressed()
    # 押されているキーに応じて画像を移動
    if pressed_keys[K_LEFT]:
        img_rect.move_ip(-vx, 0)
    if pressed_keys[K_RIGHT]:
        img_rect.move_ip(vx, 0)
    if pressed_keys[K_UP]:
        img_rect.move_ip(0, -vy)
    if pressed_keys[K_DOWN]:
        img_rect.move_ip(0, vy)
    
    screen.fill((0,0,255))
    screen.blit(img, img_rect)
    pygame.display.update()
    
    for event in pygame.event.get():
        if event.type == QUIT: sys.exit()
        if event.type == KEYDOWN:  # キーを押したとき
            # ESCキーならスクリプトを終了
            if event.key == K_ESCAPE:
                sys.exit()

イベントハンドラを使う場合

    for event in pygame.event.get():
        if event.type == QUIT: sys.exit()
        if event.type == KEYDOWN:  # キーを押したとき
            # ESCキーならスクリプトを終了
            if event.key == K_ESCAPE:
                sys.exit()
            # 矢印キーなら画像を移動
            if event.key == K_LEFT:
                img_rect.move_ip(-vx, 0)
            if event.key == K_RIGHT:
                img_rect.move_ip(vx, 0)
            if event.key == K_UP:
                img_rect.move_ip(0, -vy)
            if event.key == K_DOWN:
                img_rect.move_ip(0, vy)

1つめの方法は、イベントハンドラでキーイベントを検出する方法です。今までイベントハンドラではQUITイベントが来たときにスクリプトを終了していただけでしたが、他にもさまざまなイベントを捕捉できます。キーボードに関連するイベントは下の2つです。

イベント 発生するタイミング パラメータ
KEYDOWN キーが押されたとき key
KEYUP キーが離されたとき key

KEYDOWNは、キーが押されたときに発生します。「押された」がポイントでキーを押した瞬間に1回だけ発生します。1回KEYDOWNイベントが発生した後にキーを長押ししていても発生しません。1度離す必要があります。KEYUPは、キーを押した後、離したときに発生します。これも「離したとき」がポイントでキーを離した瞬間に1回だけ発生します。キーを押してない間中ずっと発生しているわけではありません。

どのイベントが発生したのかはEventオブジェクトのtypeで判断できます。上の例では、if文を使って typeがKEYDOWNの場合だけ処理してます。各Eventオブジェクトはtypeの他にkeyというパラメータを持っています。keyはどのキーが押されたか/離されたかを表す定数が格納されます。たとえば、ESCキーが押されたならKEY_ESC、↑キーが押されたならKEY_UPが入ってます。上のサンプルでは、ESCキーが押されたときスクリプトを終了し、矢印キーが押されたとき絵が移動するようにしています。絵を移動させる方法は画像の移動(2008/5/9)を参照してください。

ちなみにKEY_ESCAPE、KEY_UP、KEY_LEFTなどのどのキーが押された/離されたかを表す定数はpygame.localsで定義されてます。importしたので使えるわけです。どんな定数があるかはpygame.keyに一覧が載っています。数字キー、アルファベットキー、ファンクションキーなどもちゃんと定義されています。

サンプルスクリプト1を実行してみるとわかりますが、キーを押し続けても画像は移動し続けません。キーを押している間ずっと移動を続けるようにしたい場合は、pygame.keyモジュールを使います。

pygame.keyモジュールを使う場合

    # 押されているキーをチェック
    pressed_keys = pygame.key.get_pressed()
    # 押されているキーに応じて画像を移動
    if pressed_keys[K_LEFT]:
        img_rect.move_ip(-vx, 0)
    if pressed_keys[K_RIGHT]:
        img_rect.move_ip(vx, 0)
    if pressed_keys[K_UP]:
        img_rect.move_ip(0, -vy)
    if pressed_keys[K_DOWN]:
        img_rect.move_ip(0, vy)

2つめの方法は、pygame.keyの関数を使う方法です。pygame.key.get_pressed()関数を使うとどのキーが押されているかを知ることができます。この関数は、キーを押した/離したの瞬間の検出ではなく、関数を呼び出したときにどのキーが押されているかを検出する目的で使います。戻り値は、どのキーが押されているかを表すタプルです。たとえば、ESCキーが押されているか知りたい場合は、KEY_ESCの位置を調べます。サンプルでは、get_pressed()の戻り値であるタプルをpressed_keysに格納して、pressed_keys[KEY_ESC]を調べています。この値が0のとき押されていない、1のとき押されていることを意味します。

使い分け

2つのスクリプトの実行結果を比べてみるとわかりますが、イベントハンドラを使った場合では、キーを押した瞬間に1度だけ移動し、キーを押し続けても移動しません。一方、pygame.keyを使った場合はキーを押しっぱなしにすると移動を続けます。

キーを押した/離したの瞬間を検出したい場合はイベントハンドラ、キーが押された状態かを検出したい場合はpygame.keyを使うとよいと思います。たとえば、RPGでコマンドウィンドウのメニューを選択したい、タイピングゲームを作りたい場合はイベントハンドラ方式がよさそうです。一方、シューティングゲームでキーを押している間ミサイルを連射したい、RPGでキーを押しっぱなしで移動させたい場合は pygame.key方式がよさそうです。