13. Pygame: animation, visualization and controls
13.7 Timing and the game-loop
Imagine we want to let a rocket take off on the screen. We could use the following code to do so:
import pygame pygame.init() reso = (600,500) screen = pygame.display.set_mode(reso) scrrect = screen.get_rect() black =(0,0,0) ship = pygame.image.load("rocket.jpg") shiprect = ship.get_rect() shiprect.centerx = 250 for y in range(500,-100,-2): shiprect.centery = y pygame.draw.rect(screen,black,scrrect)
screen.blit(ship,shiprect) pygame.display.flip() pygame.quit()
Now when we run this, it could turn out that our ship moves too fast or too slow. To fix this, we will then adjust the third argument of the range function in the for-loop, currently set to -2. However, on another computer the speed would again be different. Also, when another
application in the background needs the CPU for a short period, our rocket will hamper before continuing. This is because we have no control over the real speed of the rocket. For this we need more than controlling the position, we need to control the timing in our game as well. Or at least measure it and calculate the elapsed time (time step) since we last drew our ship, so that we can calculate the required new position with the speed and right time step based on the elapsed time since the last frame.
There are two principles we can use to make sure our simulated time runs in accordance with the real time:
Method I: Fixed time step
We use a constant time step similar to our previous numerical integration examples. We check whether the real time is equal to or larger than our next to be simulated time, if so, we make a time step dt.
This is how this could look when coded in Python: import pygame # initialize clock pygame.init() tsim = 0.0 tstart = 0.001*pygame.time.get_ticks() dt = 0.1 …… …… running = True while running:
trun = 0.001*pygame.time.get_ticks() – tstart
if trun+dt >= tsim: tsim = tsim + dt vx = vx+ax*dt vy = vy+ay*dt x = x + vx*dt y = y + vy*dt …… ……
The advantage of this method is that you know what the time step is. This also allows for more advanced numerical integration methods and guarantees the stability of your simulation. When using a variable time step (Method II) there is a risk of getting a too large time step, which can result in overshoots and even a runaway of your simulation. The disadvantage of this method, that it requires a time step, which is large enough to make sure the simulation can always keep up with the real time. If you make this time step too small, the simulation may lag behind or run at a variable speed, resulting in quirky speeds and movements
Method II: Variable time step
In this case we simply measure the time elapsed since the last time we update the simulation. This difference is our time step dt, which we then use to update everything including the time. It looks much simpler, and when the computer is fast enough it will also use this for higher update frequencies. The downside is that the reverse is also true: if the computer is too slow or
occasionally too slow, the simulation might cause problems because of a very large dt. It is possible to safeguard this and catch up later, but then the code gets more complex than we want to use for now. (It becomes basically a mix of the two methods.)
This is how the code looks in Python when using the method of the variable time step. The variable t0 here keeps the previous time when everything was updated.
import pygame # initialize clock pygame.init() t0 = 0.001*pygame.time.get_ticks() ……. …….
maxdt = 0.5 # time step limit to avoid jumps
running = True
while running:
t = 0.001*pygame.time.get_ticks()
dt = min(t-t0,maxdt) # set maximum limit to dt if dt>0.: t0 = t vx = vx+ax*dt vy = vy+ay*dt x = x + vx*dt y = y + vy*dt …… ……
One of these two mechanisms forms the basis of our loop which is executed while running. This is generally called the game-loop. In principles it runs indefinitely until a quit event is triggered. This quit event can be multiple things:
o Escape key is pressed
o Some conditions in our simulation are met (simulation ready in simulation, player dead in games, exception has occurred, boss enemy detected, etc)
o Quit event is given by windows (so the user has clicked the cross of the window). In our example so far, just setting running to False will make sure the loop ends:
running = True while running: ... ... if y<0.: running = False ...
while True: ... ... if y<0.: break ...
In general within a game loop we see the following elements: - check for time step
- get input from keyboard, mouse or any other source - update model with numerical integration
- draw new frame - check for quit events
Before the game loop our model is initialized, graphics are loaded and set-up and our simulation is initialized.
We have seen how to control time, how to numerically integrate and how to draw a frame. But we still need to know how to process input from keyboard and/or mouse.