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

人工知能に関する断創録

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



マウスイベント

Pygame

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

mouse_event.zip

f:id:aidiary:20100605093033p:plain f:id:aidiary:20100605093034p: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"マウスイベント")
 
backImg = pygame.image.load("moriyama.jpg").convert()
pythonImg = pygame.image.load("python.png").convert_alpha()
 
cur_pos = (0,0)    # 蛇の位置
pythons_pos = []   # コピーした蛇の位置リスト
 
while True:
    screen.blit(backImg, (0,0))
    
    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()
        # マウスクリックで蛇をコピー
        if event.type == MOUSEBUTTONDOWN and event.button == 1:
            x, y = event.pos
            x -= pythonImg.get_width() / 2
            y -= pythonImg.get_height() / 2
            pythons_pos.append((x,y))  # 蛇の位置を追加
        # マウス移動で蛇を移動
        if event.type == MOUSEMOTION:
            x, y = event.pos
            x -= pythonImg.get_width() / 2
            y -= pythonImg.get_height() / 2
            cur_pos = (x,y)
    
    # 蛇を表示
    screen.blit(pythonImg, cur_pos)
    for i, j in pythons_pos:
        screen.blit(pythonImg, (i,j))
    
    pygame.display.update()

サンプルスクリプト(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")
 
backImg = pygame.image.load("moriyama.jpg").convert()
pythonImg = pygame.image.load("python.png").convert_alpha()
 
cur_pos = (0,0)    # 蛇の位置
pythons_pos = []   # コピーした蛇の位置リスト
 
while True:
    screen.blit(backImg, (0,0))
    
    # マウスクリックで蛇をコピー
    mouse_pressed = pygame.mouse.get_pressed()
    if mouse_pressed[0]:  # 左クリック
        x, y = pygame.mouse.get_pos()
        x -= pythonImg.get_width() / 2
        y -= pythonImg.get_height() / 2
        pythons_pos.append((x,y))  # 蛇の位置を追加
    
    # マウス移動で蛇を移動
    x, y = pygame.mouse.get_pos()
    x -= pythonImg.get_width() / 2
    y -= pythonImg.get_height() / 2
    cur_pos = (x,y)
    
    # 蛇を表示
    screen.blit(pythonImg, cur_pos)
    for i, j in pythons_pos:
        screen.blit(pythonImg, (i,j))
    pygame.display.update()
    
    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()

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

    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()
        # マウスクリックで蛇をコピー
        if event.type == MOUSEBUTTONDOWN and event.button == 1:
            x, y = event.pos
            x -= pythonImg.get_width() / 2
            y -= pythonImg.get_height() / 2
            pythons_pos.append((x,y))  # 蛇の位置を追加
        # マウス移動で蛇を移動
        if event.type == MOUSEMOTION:
            x, y = event.pos
            x -= pythonImg.get_width() / 2
            y -= pythonImg.get_height() / 2
            cur_pos = (x,y)

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

イベント 発生するタイミング パラメータ
MOUSEBUTTONDOWN マウスボタンが押されたとき pos, button
MOUSEBUTTONUP マウスボタンが離されたとき pos, button
MOUSEMOTION マウスカーソルを移動したとき pos, rel, buttons

MOUSEBUTTONDOWNは、マウスボタンが押されたときに発生します。「押された」がポイントでボタンを押した瞬間に1回だけ発生します。 1回MOUSEBUTTONDOWNイベントが発生した後にボタンを長押ししていても発生しません。1度離す必要があります。MOUSEBUTTONUPは、マウスボタンを押した後、離したときに発生します。これも「離したとき」がポイントでボタンを離した瞬間に1回だけ発生します。ボタンを押してない間中ずっと発生しているわけではありません。MOUSEMOTIONは、マウスカーソルを移動したときに発生します。これはカーソルが少しでも移動すると発生します。イベント発生を画面に表示してみるとわかりますが、MOUSEMOTIONは大量に発生してます。

どのイベントが発生したのかはEventオブジェクトのtypeで判断できます。上の例では、if文を使って typeがQUIT、MOUSEBUTTONDOWN、MOUSEMOTIONの場合だけ処理してます。ちなみにQUIT、 MOUSEBUTTONDOWN、MOUSEMOTIONなどの定数はpygame.localsで定義されてます。importしたので使えるわけです。

各Eventオブジェクトはtypeの他にいくつかパラメータを持っています。マウス関係イベントのパラメータは上の表にまとめてあります。pos はマウスイベントが発生した座標です。buttonはどのマウスボタンを押した/離したかを表しています。左クリックのとき1、中クリックのとき2、右クリックのとき3のようです。MOUSEMOTIONのrelは、マウスカーソルの移動距離を表すタプルです。例えば、(10,0)のときX軸方向に10ピクセル移動したことを意味します。buttonsは、どのボタンを押しながら移動したかを表すタプルです。(1,0,0)なら左ボタンを押しながら移動、(0,0,1)なら右ボタンを押しながら移動です。マウスドラッグを検出するのに使えますね。

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

    # マウスクリックで蛇をコピー
    mouse_pressed = pygame.mouse.get_pressed()
    if mouse_pressed[0]:  # 左クリック
        x, y = pygame.mouse.get_pos()
        x -= pythonImg.get_width() / 2
        y -= pythonImg.get_height() / 2
        pythons_pos.append((x,y))  # 蛇の位置を追加
    
    # マウス移動で蛇を移動
    x, y = pygame.mouse.get_pos()
    x -= pythonImg.get_width() / 2
    y -= pythonImg.get_height() / 2
    cur_pos = (x,y)

2つめの方法は、pygame.mouseの関数を使う方法です。pygame.mouse.get_pressed()関数を使うとマウスのどのボタンが押されているかを知ることができます。この関数は、マウスボタンを押した/離したの瞬間の検出ではなく、関数を呼び出したときにボタンが押されているかを検出する目的で使います。戻り値は、どのボタンが押されているかを表すタプルです。たとえば、(1,0,0)なら左ボタン、(0,1,0) なら中ボタン、(0,0,1)なら右ボタンが押されていることを意味します。マウスの移動は、マウスカーソルがある位置を知ることで代用できます。pygame.mouse.get_pos()でマウスの座標をタプル(x,y)で返します。

使い分け

2つのスクリプトの実行結果を比べてみるとわかりますが、イベントハンドラを使った場合では、マウスを押した瞬間に1度だけ蛇がコピーされ、マウスボタンを押したまま移動しても蛇はコピーされません。一方、pygame.mouseを使った場合はマウスを押しながら移動した場合にたくさんコピーされてしまいます。

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