# draw the green background DISPLAYSURF.fill(GRASSCOLOR) # draw all the grass objects on the screen for gObj in grassObjs: gRect = pygame.Rect( (gObj['x'] - camerax, gObj['y'] - cameray, gObj['width'], gObj['height']) ) DISPLAYSURF.blit(GRASSIMAGES[gObj['grassImage']], gRect) # draw the other squirrels for sObj in squirrelObjs: sObj['rect'] = pygame.Rect( (sObj['x'] - camerax, sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']), sObj['width'], sObj['height']) ) DISPLAYSURF.blit(sObj['surface'], sObj['rect']) # draw the player squirrel flashIsOn = round(time.time(), 1) * 10 % 2 == 1 if not gameOverMode and not (invulnerableMode and flashIsOn): playerObj['rect'] = pygame.Rect( (playerObj['x'] - camerax, playerObj['y'] - cameray - getBounceAmount(playerObj['bounce'], BOUNCERATE, BOUNCEHEIGHT), playerObj['size'], playerObj['size']) ) DISPLAYSURF.blit(playerObj['surface'], playerObj['rect']) # draw the health meter drawHealthMeter(playerObj['health'])
Line 2  begins the code that starts drawing the contents of the display Surface object. First, line 2  draws a green color for the background. This will paint over all of the previous contents of the Surface so that we can start drawing the frame from scratch.
for loop on line 5  goes through all the grass objects in the
grassObjs list and creates a Rect object from the x, y, width, and height information stored in it. This Rect object is stored in a variable named
gRect. On line 10 ,
gRect is used in the
blit() method call to draw the grass image on the display Surface. Note that
gObj['grassImage'] only contains an integer that is an index to
GRASSIMAGES is a list of Surface objects that contain all the grass images. Surface objects take up much more memory than just a single integer, and all the grass objects with similar
gObj['grassImage'] values look identical. So it makes sense to only have each grass image stored once in
GRASSIMAGES and simply store integers in the grass objects themselves.
for loop that draws all the enemy squirrel game objects is similar to the previous
for loop, except that the Rect object it creates is saved in the
'rect' key’s value of the squirrel dictionary. The reason the code does this is because we will use this Rect object later to check if the enemy squirrels have collided with the player squirrel.
Note that the top parameter for the Rect constructor is not just
sObj['y'] - cameray but
sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']). The
getBounceAmount() function will return the number of pixels that the top value should be raised.
Also, there is no common list of Surface objects of the squirrel images, like there was with grass game objects and
GRASSIMAGES. Each enemy squirrel game object has its own Surface object stored in the
'surface' key. This is because the squirrel images can be scaled to different sizes.
After drawing the grass and enemy squirrels, the code will draw the player’s squirrel. However, there is one case where we would skip drawing the player’s squirrel. When the player collides with a larger enemy squirrel, the player takes damage and flashes for a little bit to indicate that the player is temporarily invulnerable. This flashing effect is done by drawing the player squirrel on some iterations through the game loop but not on others.
The player squirrel will be drawn on game loop iterations for a tenth of a second, and then not drawn on the game loop iterations for a tenth of second. This repeats over and over again as long as the player is invulnerable (which, in the code, means that the
invulnerableMode variable is set to
True). Our code will make the flashing last for two seconds, since
2 was stored in the
INVULNTIME constant variable on line 25.
To determine if the flash is on or not, line 22  grabs the current time from
time.time(). Let’s use the example where this function call returns
1323926893.622. This value is passed to
round(), which rounds it to one digit past the decimal point (since 1 is passed as
round()’s second parameter). This means
round() will return the value
This value is then multiplied by
10, to become
13239268936. Once we have it as an integer, we can do the "mod two" trick first discussed in the Memory Puzzle chapter to see if it is even or odd.
13239268936 % 2 evaluates to
0, which means that
flashIsOn will be set to
0 == 1 is
time.time() will keep returning values that will end up putting
1323926893.700, which is the next tenth second. This is why the
flashIsOn variable will constantly have
False for one tenth of a second, and then
True for the next one tenth of a second (no matter how many iterations happen in that tenth of a second).
There are three things that must be
True before we draw the player’s squirrel. The game must currently be going on (which happens while
False) and the player is not invulnerable and not flashing (which happens while
The code for drawing the player’s squirrel is almost identical to the code for drawing the enemy squirrels.
drawHealthMeter() function draws the indicator at the top left corner of the screen that tells the player how many times the player squirrel can be hit before dying. This function will be explained later in this chapter.