我正在学习 Python 和 Pygame,我要做的第一件事是制作一个简单的贪吃蛇游戏。我试图让蛇每 0.25 秒移动一次。这是我的代码循环部分:
while True:
check_for_quit()
clear_screen()
draw_snake()
draw_food()
check_for_direction_change()
move_snake() #How do I make it so that this loop runs at normal speed, but move_snake() only executes once every 0.25 seconds?
pygame.display.update()
我希望所有其他函数都能正常运行,但 move_snake() 仅每 0.25 秒发生一次。我查了一下并找到了一些答案,但对于制作第一个 Python 脚本的人来说,它们似乎都太复杂了。
是否有可能真正获得我的代码应该是什么样子的示例,而不仅仅是告诉我需要使用哪个函数?谢谢!
有多种方法,例如跟踪系统时间或使用
Clock
和计数刻度。
但最简单的方法是使用事件队列并每 x 毫秒创建一个事件,使用
pygame.time.set_timer()
:
pygame.time.set_timer()
在事件队列上重复创建事件
set_timer(eventid, milliseconds) -> None
设置事件类型以每隔给定的毫秒数出现在事件队列中。在经过一定时间后,第一个事件才会出现。
每个事件类型都可以附加一个单独的计时器。最好使用 pygame.USEREVENT 和 pygame.NUMEVENTS 之间的值。
要禁用事件的计时器,请将毫秒参数设置为 0。
这是一个小型的运行示例,其中蛇每 250 毫秒移动一次:
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
player, dir, size = pygame.Rect(100,100,20,20), (0, 0), 20
MOVEEVENT, t, trail = pygame.USEREVENT+1, 250, []
pygame.time.set_timer(MOVEEVENT, t)
while True:
keys = pygame.key.get_pressed()
if keys[pygame.K_w]: dir = 0, -1
if keys[pygame.K_a]: dir = -1, 0
if keys[pygame.K_s]: dir = 0, 1
if keys[pygame.K_d]: dir = 1, 0
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
if e.type == MOVEEVENT: # is called every 't' milliseconds
trail.append(player.inflate((-10, -10)))
trail = trail[-5:]
player.move_ip(*[v*size for v in dir])
screen.fill((0,120,0))
for t in trail:
pygame.draw.rect(screen, (255,0,0), t)
pygame.draw.rect(screen, (255,0,0), player)
pygame.display.flip()
从 pygame 2.0.1 开始,安排事情非常容易,因为
pygame.time.set_timer
现在允许自定义事件被触发一次。
这是一个在未来 X 秒运行函数一次的简单示例:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
ACTION = pygame.event.custom_type() # our custom event that contains an action
clock = pygame.time.Clock()
def main():
shield = 0
# function to activate the shield
# note that shield is NOT just a boolean flag,
# but an integer. Everytime we activate the shield,
# we increment it by 1. If we want to deactivate the
# shield later, we can test if the deactivate event
# is the latest one. If not, then nothing should happen.
def activate_shield():
nonlocal shield
shield += 1
return shield
# function that creates the function that is triggered
# after some time. It's nested so we can pass and capture
# the id variable to check if the event is actually the
# last deactivate event. If so, we reset shield to 0.
def remove_shield(id):
def action():
nonlocal shield
if shield == id:
shield = 0
return action
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT: return
if e.type == pygame.KEYDOWN:
# activate the shield
id = activate_shield()
# deactivate it 2000ms in the future
# pygame will post this event into the event queue at the right time
pygame.time.set_timer(pygame.event.Event(ACTION, action=remove_shield(id)), 2000, 1)
if e.type == ACTION:
# if there's an ACTION event, invoke its action!!!
e.action()
screen.fill('black')
# is the shield active?
if shield:
pygame.draw.circle(screen, 'red', (320, 220), 35, 4)
# our green little guy
pygame.draw.rect(screen, 'green', (300, 200, 40, 40))
pygame.display.flip()
clock.tick(60)
main()
使用 Pygame 的 Clock 模块 来记录时间。具体来说,
tick
类的方法Clock
将向您报告自上次调用tick
以来的毫秒数。因此,您可以在游戏循环中每次迭代的开始(或结束)时调用 tick
一次,并将其返回值存储在名为 dt
的变量中。然后使用 dt
更新与时间相关的游戏状态变量。
time_elapsed_since_last_action = 0
clock = pygame.time.Clock()
while True: # game loop
# the following method returns the time since its last call in milliseconds
# it is good practice to store it in a variable called 'dt'
dt = clock.tick()
time_elapsed_since_last_action += dt
# dt is measured in milliseconds, therefore 250 ms = 0.25 seconds
if time_elapsed_since_last_action > 250:
snake.action() # move the snake here
time_elapsed_since_last_action = 0 # reset it to 0 so you can count again
如果你想在 Pygame 中随时间控制某些东西,你有两个选择:
pygame.time.get_ticks()
测量时间并实现根据时间控制对象的逻辑。
pygame.time.set_timer()
在事件队列中重复创建 USEREVENT
。事件发生时更改对象状态。
以下问题答案中的示例展示了如何使用它:
选项 1 的最小示例:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 200))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 80, 20, 20)
time_interval = 500 # 500 milliseconds == 0.5 seconds
next_step_time = 0
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > next_step_time:
next_step_time += time_interval
rect.x = (rect.x + 20) % window.get_width()
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
选项 2 的最小示例:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 200))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 80, 20, 20)
time_interval = 500 # 500 milliseconds == 0.5 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == timer_event:
rect.x = (rect.x + 20) % window.get_width()
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()