目录
事件
一、概念
①、事件循环
②、事件队列
③、事件处理器
二、键盘事件
① 、先左右移动
②、添加按键事件——上下移动
③、重复按键
④、事件名和按键名
三、鼠标事件
①、让沙滩球随着鼠标位置移动
②、在鼠标按钮保持按下时才让鼠标控制起作用
四、定时器事件
大多数程序自始至终沿着一条可以预测的路径运行,可能中间穿插着循环和判断,还存在一种特殊程序——事件驱动程序(event-driven program),事件驱动程序基本“原地不动”,什么都不做,等待事件发生,一旦有事件发生,就会做出反应,完成该事件所涉及的一些程序。有点类似单片机的中断。
如:Windows 操作系统(或者其他 GUI)就是一个很好的例子。打开 Windows 计算机时,启动后它只是“原地不动”,不会启动任何程序,也不会看到鼠标光标在屏幕上移动。不过,如果开始移动或点击鼠标,就会有情况发生。鼠标光标会在屏幕上移动,“开始”菜单会弹出,或者会做其他事情。
为了让一个事件驱动程序“看到”有事件发生,必须“寻找”这些事件。程序必须不断地扫描计算机内存中用来指示事件发生的部分。
running = True
while running:
for event in pygame.event.get():
#添加事件
if event.type == pygame.QUIT:
running = False
pygame.quit()
只要移动或点击了鼠标或者按下了按键,就会发生事件。这些事件将会保存在事件队列 。
事件队列:事件循环会一直不断搜索内存某个部分,内存中储存事件部分;发生的所有事件的列表,这些事件按它们发生的顺序排列
程序需要找到用户什么时候按下按键或其他动作,以及知道怎么应对这些事件,处理这些事件。
程序中处理某个事件部分 ———— 事件处理器
在 Pygame 中,按下键盘上的某个键值这个事件是 KEYDOWN。
以小球为例:
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
在新位置上重画球之前要从原位置“擦除”动画精灵有两种方法:
- 在每个动画精灵的原位置上涂上背景颜色
- 直接重绘每一帧的整个背景
方式一:screen.fill()
方式二:screen = pygame.display.set_mode([640,480])
background = pygame.Surface(screen.get_size())background.fill([255, 255, 255]) #绘制白色
screen.blit(background, (0, 0))
pygame.event.get() :从事件队列得到所有事件的一个列表
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
#do something
if event.key == pygame.K_UP:
my_ball.rect.top = my_ball.rect.top - 10
elif event.key == pygame.K_DOWN:
my_ball.rect.top = my_ball.rect.top + 10
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
在长按按键时,球只会向按的方向移动一步,因为在设置的按键事件处理程序中只执行了处理一次。
在Pygame 中有一个设置,可以在按键一直按下时生成多个 KEYDOWN 事件。这称为按键重复(key repeat)
delay = 100 #在开始重复前等待多长时间
interval = 50 #定义按键要以多快速度重复,及各个按键事件之间要间隔多久
pygame.key.set_repeat(delay, interval)
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
delay = 100 #在开始重复前等待多长时间
interval = 50 #定义按键要以多快速度重复,及各个按键事件之间要间隔多久
pygame.key.set_repeat(delay, interval)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
#do something
if event.key == pygame.K_UP:
my_ball.rect.top = my_ball.rect.top - 10
elif event.key == pygame.K_DOWN:
my_ball.rect.top = my_ball.rect.top + 10
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
效果可以看见长按,小球还可以继续移动。
pygame有相当多的事件。在官网文档中提供所有事件的列表——http://www.pygame.org/docs/ref/event.html
最常用的 3 类鼠标事件如下:
例子用鼠标控制球:只要鼠标在 Pygame 窗口中移动,就让沙滩球随着鼠标位置移动。
使用球的 rect.center 属性。让球的中心跟着鼠标移动,只要鼠标移动,球就跟着移动。若没有鼠标事件(可能因为鼠标没有移动,或者鼠标光标落在 Pygame 窗口之外),球就会继续在左右两边反弹。
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
#do something
my_ball.rect.center = event.pos #鼠标的位置(x和y坐标)
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
#do something
my_ball.rect.center = event.pos
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
鼠标按钮保持按下时移动鼠标称为拖动(dragging),但在pygame没有直接可以鼠标拖动的事件,需要使用现有的事件来得到需要的效果。
操作:拖动意味着鼠标移动时鼠标按钮一直保持按下。可以利用 MOUSEBUTTONDOWN 事件得到鼠标按钮何时按下,另外利用 MOUSEBUTTONUP事件可以得到按钮何时松开(还原,不再按下),这样只需要跟踪按钮状态。
held_down = False #跟踪按钮状态
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
#do something
held_down = True
elif event.type == pygame.MOUSEBUTTONUP:
held_down = False
elif event.type == pygame.MOUSEMOTION:
if held_down:
my_ball.rect.center = event.pos
效果:在按下并拖动时球才会移动
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
running = True
held_down = False #跟踪按钮状态
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
#do something
held_down = True
elif event.type == pygame.MOUSEBUTTONUP:
held_down = False
elif event.type == pygame.MOUSEMOTION:
if held_down:
my_ball.rect.center = event.pos
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
定时器事件(timer event)。定时器会按固定的间隔生成事件,如同闹钟一样:设好闹钟,并把闹铃打开,每天它都会在固定的时刻响起来。
将pygame定时器设置为任意间隔,如果定时器到时间,能执行设置的用户事件(user event)
pygame 0 - NUMEVENTS 的定义事件,其中使用USERWVENT - NUMEVENTS 为用户自定义事件。下面代码可以知道共65535个事件, 32847 - 65535 为自定义事件, 0- 32847为pygame事件。
>>> import pygame
>>> pygame.USEREVENT
32847
>>> pygame.NUMEVENTS
65535
Pygame 中设置定时器:
pygame.time.set_timer(EVENT_NUMBER, interval)
#EVENT_NUMBER 是事件编号,interval 是定时器多长时间(单位是毫秒)到期并生成一个事件。
小球设置定时器事件:
pygame.time.set_timer(pygame.USEREVENT, 1000)
后面自定义事件在pygame.USEREVENT + 1
效果:让小球左右移动过程中,经过一定事件上移或下移,且左右两边反弹还会在上下边反弹。
import sys, pygame
from random import *
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
if self.rect.left <= screen.get_rect().left or \
self.rect.right >= screen.get_rect().right:
self.speed[0] = -self.speed[0]
self.rect = self.rect.move(self.speed)
my_ball = MyBallClass("beach_ball.png", [20, 20], [10, 0])
pygame.time.set_timer(pygame.USEREVENT, 1000)
direction = 1
running = True
held_down = False #跟踪按钮状态
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.USEREVENT:
my_ball.rect.centery = my_ball.rect.centery + (30 * direction)
if my_ball.rect.top <=0 or \
my_ball.rect.bottom >= screen.get_rect().bottom:
direction = -direction
clock.tick(30)
screen.blit(background, (0, 0))
my_ball.move()
screen.blit(my_ball.image,my_ball.rect)
pygame.display.flip()
pygame.quit()
参考:
父与子的编程之旅:与小卡特一起学Python