2. pygame事件

pygame之事件

什么是事件

和事件关联的动词,是“发生”,因此当咱们在关注事件的时候,咱们其实就是在关注当前正在发生什么……
举一个很是扯淡的例子,在咱们口语表达中,有这样一种嫌弃:“你事情怎么这么多的?”。这是一种南方的口语表达,可能还不是很是明显,换成北方的表达方式,就很直接了:“你怎么这么事儿?”
这个“事儿”咱们就能够理解为,操做。咱们的程序是会一直一直运行下去的,直到我关闭窗口的操做产生了一个QUIT事件。事件随时可能发生,并且量也可能会很大,pygame的作法是把一系列的事情存放在一个队列里,逐个处理。python

事件检索

一般,在我目前写到的程序中,我都使用pygame.event.get()来处理全部的事件。若是咱们使用pygame.event.wait(),pygame就会等到发生下一个事件才继续下去。
在一些动态游戏中,游戏每每是要动态运做的,而另一个方法pygame.event.poll()就好一些,一旦调用,他会根据如今的情形返回一个真实的事件,或者一个“什么都没有”。app

下表为一个经常使用事件集:post

事件 产生途径 参数
QUIT 用户按下关闭按钮 none
ATIVEEVENT pygame被激活或者隐藏 gain,state
KEYDOWN 键盘被按下 unicode,key,mod
KEYUP 键盘被放开 key,mod
MOUSEMOTION 鼠标移动 pos,rel,buttons
MOUSEBUTTONDOWN 鼠标按下 pos,button
MOUSEBUTTONUP 鼠标放开 pos,button
JOYAXISMOTION 游戏手柄(joystick or pad)移动 joy,axis,value
JOYBALLMOTION 游戏手柄(joystick ball)移动 joy,axis,value
JOYHATMOTION 游戏手柄(joystick)移动 joy,axis,value
JOYBUTTONDOWN 游戏手柄按下 joy,button
JOYBUTTONUP 游戏手柄放开 joy,button
VIDEORESIZE pygame窗口缩放 size,w,h
VIDEOEXPOSE pygame窗口部分公开(expose) none
USEREVENT 触发用户事件 code

一个用来测试查看事件的小脚本

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen_size = (640, 480)
screen = pygame.display.set_mode(screen_size, 0, 32)

font = pygame.font.SysFont("arial", 17)
font_height = font.get_linesize()
event_text = []

while 1:
    event = pygame.event.wait()
    event_text.append(str(event))
    event_text = event_text[(-screen_size[1]//font_height):]
    # 这个切片操做保证了event_text里面只保留一个屏幕的文字

    if event.type == QUIT:
        exit()
    screen.fill((0, 0, 0))
    # screen.blit()
    y = screen_size[1]-font_height
    # 找一个合适的起笔位置,最下面开始可是要留一行空

    for text in reversed(event_text):
        screen.blit(font.render(text,True,(0,255,0)),(0,y))
        y -= font_height

    pygame.display.update()

这个程序很是适合分步去了解各个操做在pygame内的响应效果,可是有一个小小的弊端就是,他和通常的pygame.event.get()不一样,它只有在新的事件发生的时候才会有反馈到屏幕上,这就让咱们形成某种错觉,就是pygame只能知道咱们某瞬间的状态,若是这个状态不改变,pygame就没法察觉。
显然这是不对的,例如咱们按下某个键的时候,咱们能够用for语句搭配pygame.event.get去进行一个轮询,这个轮询会不断的检测事件,pygame始终能觉察到咱们的操做和状态。
下面的程序就是一个典型的例子。在长按方向键的时候,pygame并不会移动一次就结束,而是会一直移动,直到咱们松开按键返回原始状态。测试

background_image_filename = 'sushiplate.jpg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            #键盘有按下?
            if event.key == K_LEFT:
                #按下的是左方向键的话,把x坐标减一
                move_x = -1
            elif event.key == K_RIGHT:
                #右方向键则加一
                move_x = 1
            elif event.key == K_UP:
                #相似了
                move_y = -1
            elif event.key == K_DOWN:
                move_y = 1
        elif event.type == KEYUP:
            #若是用户放开了键盘,图就不要动了
            move_x = 0
            move_y = 0

    #计算出新的坐标
    x+= move_x
    y+= move_y

    screen.fill((0,0,0))
    screen.blit(background, (x,y))
    #在新的位置上画图
    pygame.display.update()

举个经常使用的栗子

经过上面的动做检测脚本咱们能够发现,出现频率最高的,就是鼠标事件和键盘事件。因此在这里咱们特别地来讨论一下这两个事件的典型用法。code

处理鼠标事件

MOUSEMOTION事件会在鼠标动做的时候发生,他有三个参数:队列

  • buttons:一个含有三个数字的元组,三个值分别表示左键、中键、右键。1表示按下,0表示空放状态。
  • pos:position,表示位置
  • rel:表明当前坐标距离上次鼠标事件的距离,表示形式也是一个二维元组。

MOUSEBUTTONDOWNMOUSEBUTTONUP游戏

  • button:这个参数,跟上面比,少了一个s,这个表明了那个按键被操做
  • pos:仍是位置

处理键盘事件

KEYDOWNKEYUP的参数描述:事件

  • key:按下或者放开的键值,是一个数字,估计地球上不多有人能够彻底记得住,因此pygame中你可使用K_xxx来表示,好比字母a就是K_a,还有K_SPACE和K_RETURN等。
  • mod:包含了组合键信息,若是mod&KMOD_CTRL的值为真,表示用户同时按下了ctrl键。相似的还有KMOD_SHIFT,KMOD_ALT
  • unicode:表明了按下键的unicode值

案例在上面已经放了,就是经过方向键移动图片的小脚本。可是这个脚本有一个小小的bug:“在快速切换方向时,有时候会出现没法持续移动的状况”,这种bug的缘由其实跟pygame.event.get()机制有关。event.get只能监听接收一个事件,在咱们快速操做的时候,好比咱们按下左键,忽然按下上键而且松开左键,这种时候bug就会出现。
咱们把上述动做分解一下:图片

  • KEYDOWN-------K_LEFT
  • KEYDOWN-------K_UP
  • KEYUP-----------K_LEFT

显而易见,最后的终结操做并非KEYDOWN,而是KEYUP,在咱们的代码中KEYUP会把移动位给归零。这时候即使K_UP仍是在一个KEYDOWN的状态,可是他再也不被event.get到了,因此天然,这个图像就停下来了。ip

改良版:

background_image_filename = 'sushiplate.jpg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

move={K_LEFT:0,K_RIGHT:0,K_UP:0,K_DOWN:0}


while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            #键盘有按下?
            if event.key in move.keys():
                #按下的是左方向键的话,把x坐标减一
                move[event.key]=1

        elif event.type == KEYUP:
            if event.key in move.keys():
                move[event.key]=0
    #计算出新的坐标
    x-= move[K_LEFT]
    x+= move[K_RIGHT]
    y-= move[K_UP]
    y+= move[K_DOWN]


    screen.fill((0,0,0))
    screen.blit(background, (x,y))
    #在新的位置上画图
    pygame.display.update()

事件过滤

并非全部的事件都是须要处理的,好比在游戏场景切换的时候,你按什么都没有用。咱们应该有一个方法来过滤掉一些咱们不感兴趣的事件(固然咱们能够不处理,不给他们设置响应的事件,可是最好的方法仍是让他们根本不进入咱们的事件队列)。
咱们使用pygame.event.set_blocked(事件名)来完成。若是有好多事件须要过滤,能够传递一个列表pygame.event.set_blocked([list]),若是是设置参数为None,那么全部的事件就被打开了。
与之相对的,咱们使用pygame.event.set_allowed()来设定容许的事件

产生事件

一般玩家作什么,pygame产生对应的事件就能够了,不过有的时候咱们须要模拟出一些事件来,好比录像回放的时候,咱们就要把用户的操做再现一遍。或者说咱们在作外挂的时候,咱们就要把一些用户的操做自动完成。

my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=u' ')
#你也能够像下面这样写,看起来比较清晰(但字变多了……)
my_event = pygame.event.Event(KEYDOWN, {"key":K_SPACE, "mod":0, "unicode":u' '})
pygame.event.post(my_event)

有时候咱们甚至能够产生一个彻底自定义的全新事件。范例代码以下,可是我本身并无敲成功,由于我不知道它的这个USEREVENT在哪里何时如何定义的

CATONKEYBOARD = USEREVENT+1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pgame.event.post(my_event)

#而后得到它
for event in pygame.event.get():
    if event.type == CATONKEYBOARD:
        print event.message
相关文章
相关标签/搜索