Translations of this page:

Step 011 - Keys, Rotating and Zoom

Code Discussion

Pressed Keys

rotated and zoomed pygame snake Surfaces can not only moved around, but also rotated and -unlike beer mats- zoomed. The next source code examples introduce a new method of keyboard control.

Instead of checking a queued event with pygame.event.get() the function pygame.key.get_pressed() delivers the actual state of the complete keyboard. This state is represented by a tuple of 0/1 values for each key. A value of 0 signals an unpressed, a value of 1 signals a pressed State of a key. You can access the value for a specific key with the K_<A_SPECIFIC_KEY> values defined in pygame.constants. For example

pressedkeys = pygame.key.get_pressed()
if pressedkeys[pygame.K_x]:
    do_something()

checks if the key “x” is pressed. This method is ideal for constant movement, where a sprite moves or rotate as long as a specific key is pressed.

In the source code example bleow, the cursor keys are used to move the snake surface, while the keys W and S zoom/shrink the snake and the keys A and D rotate the snake.

It is possible to press several keys together, like left and right cursor, and the program will move the correctly (not at all in this case):

    dx, dy  = 0, 0   # no cursor key, no movement
    if pressedkeys[pygame.K_LEFT]:
        dx -= speed
    if pressedkeys[pygame.K_RIGHT]:
        dx += speed

Note that this method of keyboard control is less precise than the if pygame.event.type… - method because there is no guarantee that a fast key-pressing will be noticed by pygame. Depending on how fast the computer can calculate each cycle of the main-loop, there could be a chance that you press and release a key just between 2 main loop cycles and pygame would not notice. However, if your program runs around 30 frames per second, you would need lightning fast fingers to become not noticed by pygame.

Also note that this keyboard control method is not ideal for pre-defined movement like stones on a board game. While you can control with the time-based movement the speed of a surface, it lay in the skill of the user and his dexterity in pressing and releasing a key to control how long a surface moves (and where its movements end exactly).

troublesome subsurface

Also note that the cleaning of the old surface (using the subsurface method) is done inside a try…except block. If the subsurface is no longer inside the surface, pygame would raise an error. This can happen when you zoom or rotate the snake outside the screen.

   # only blit the part of the background where the snake was (cleanrect)
    try:
        #if the subsurface is outside the screen pygame would raise an error
        #this can happen when using rotozoom, therfore check inside try..except
        #Surface.subsurface(Rect): return Surface
        dirtyrect = background.subsurface((round(snakex,0), 
                round(snakey,0), snake.get_width(), snake.get_height()))
 
        screen.blit(dirtyrect, (round(snakex,0), round(snakey,0))) 
    except:
        screen.blit(background,(0,0)) # blit the whole background (slow but secure)

do not get lost in space !

Also note that in this code example there is no checking if the snake is inside the screen whatsoever. You can try to move the snake outside of the right , move it down, left and up and reappear from the left.

Zooming around the Center

the pygame.transform.rotozoom command would rotate around the position (0,0) of a Surface - the topleft corner.

To create a more pleasing rotation aroundthe center effect, we need some tricks: First, the original snake surface is copied into snake_original right after creation, to have always a not-manipulated image:

snake_original = snake.copy()      # store a unmodified copy of the snake surface

snake_original = snake

would not work because both snake and snake_original would be just pointers to the same python object (the manipulated snake). If you need a copy of an object, use the .copy() method

Always the original_snake surface is zoomed and rotated with the current zoom and angle values by the pygame.transform.rotozoom command.

But how to avoid a rotation around the topleft corner?

For that, before pygame.transform.rotozoom does its work, the current rectangle of the snake surface is stored into the variable oldrect by the surface.get_rect() command. Pygame rects have several useful properties, like pre-defined constants for center, centerx, centery, width, height etc.

After rotating and now having a usually resized snake surface (pygame always calculate a rectangle around the visible surface, thus a rotated snake fits in a bigger rectangle than a non-rotated snake) the rectangle of the new surface is stored into the variable newrect. This is also done with the surface.get_rect() command. Now the code example simply blits the new surface so that its new center lays on the same spot as the center of the old rectangle - rotated around the center.

    if turnfactor != 0 or zoomfactor !=1.0:
        angle += turnfactor * turnspeed * seconds # time-based turning
        zoom *= zoomfactor 
        # the surface shrinks and zooms and moves by rotating
        oldrect = snake.get_rect() # store current surface rect
        snake = pygame.transform.rotozoom(snake_original, angle, zoom)
        newrect = snake.get_rect() # store new surface rect
        # put new surface rect center on same spot as old surface rect center
        snakex += oldrect.centerx - newrect.width / 2
        snakey += oldrect.centery - newrect.height / 2

Documentation

Source Code on Github

To run this example you need:

file in folder download
011_rotozoom.py pygame Download the whole Archive with all files from Github:
https://github.com/horstjens/ThePythonGameBook/archives/master
background640x480_a.jpg
background150.jpg by Horst JENS
pygame/data
snake.gif
pygame snake from www.pygame.org
pygame/data

View/Edit/Download the file directly in Github: https://github.com/horstjens/ThePythonGameBook/blob/master/pygame/011_rotozoom.py

click reload in your browser if you see no code here:

Comment this Page


en/pygame/step011.txt · Last modified: 2014/01/09 11:07 (external edit)