Skip to main content
Engineering LibreTexts

9.21: Drawing the Background, Grass, Squirrels, and Health Meter

  • Page ID
    14639
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

            # 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 [182] begins the code that starts drawing the contents of the display Surface object. First, line 2 [182] 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.

    The for loop on line 5 [185] 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 [190], 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. 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.

    The 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 [202] 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 1323926893.6.

    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 False, since 0 == 1 is False.

    In fact, time.time() will keep returning values that will end up putting False into flashIsOn until 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 gameOverMode is False) and the player is not invulnerable and not flashing (which happens while invulnerableMode and flashIsOn are False).

    The code for drawing the player’s squirrel is almost identical to the code for drawing the enemy squirrels.

    The 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.


    This page titled 9.21: Drawing the Background, Grass, Squirrels, and Health Meter is shared under a CC BY-NC-SA 3.0 license and was authored, remixed, and/or curated by Al Sweigart via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.