There are several approaches, like keeping track of the system time or using a Clock
and counting ticks.
But the simplest way is to use the event queue and creating an event every x ms, using pygame.time.set_timer()
:
pygame.time.set_timer()
repeatedly create an event on the event queue
set_timer(eventid, milliseconds) -> None
Set an event type to appear on the event queue every given number of milliseconds. The first event will not appear until the amount of time has passed.
Every event type can have a separate timer attached to it. It is best to use the value between pygame.USEREVENT and pygame.NUMEVENTS.
To disable the timer for an event, set the milliseconds argument to 0.
Here's a small, running example where the snake moves every 250 ms:
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()
Since pygame 2.0.1, it's quite easy to schedule things since pygame.time.set_timer
now allows custom events to be fired once.
Here's a simple example of a way to run a function X seconds in the future once:
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()
Minimal example: repl.it@queueseven/Simple-Scheduler