Drawings movement¶
The animations we have seen so far are based on showing a different image we prepared in advance in each frame. Now we are also going to move the images that are shown, so that the same image appears in different places in the window, that is, it moves.
Let’s look at an example first:

import pygame as pg, petljapg
(width, height) = (400, 300)
canvas = petljapg.open_window(width, height, "Car")
car_image = pg.image.load("car.png")
(car_width, car_height) = (car_image.get_width(), car_image.get_height()) # car image size
fps = 50 # number of frames per second
dt = 1 / fps # duration of one frame in seconds
car_v = 100 # car speed (pixels per second)
(car_x, car_y) = (0, height - car_height) # car position (lower left corner initially)
def new_frame():
global car_x # we will only change x coordinate of the car
car_x += car_v * dt # move car to the right
if car_x > width: # if it went out of the canvas
car_x = - car_width # bring it back to the beginning
canvas.fill(pg.Color("skyblue")) # paint background to sky-blue
canvas.blit(car_image, (car_x, car_y)) # displaying car image
petljapg.frame_loop(fps, new_frame)
(PyGame__anim_car_oneway_eng)
As before, we have a new_frame function that shows an image in each frame. What is new in this example is that the position of that image changes from frame to frame.
The image is shown so that its upper left corner appears at the point (car_x, car_y). In order for the car to move to the right, in each frame we increase the x coordinate of the image. We only keep in mind that when the car goes too far to the right, we return the car so that its right end aligns with the left edge of the window. In this way, the car begins to gradually reappear on the left.
Similarly, we can also move drawings we draw ourselves (not just ready-made images). In doing so, we can move the image or drawing in any direction. Here is one example:
import pygame as pg, petljapg
(width, height) = (800, 400)
canvas = petljapg.open_window(width, height, "billiards")
(cx, cy) = (width // 2, height // 2) # position of the ball center (initially at canvas center)
(dx, dy) = (3, 2) # ball displacement (initially 3 pixels to the right and 2 downwards per frame)
r = 15 # ball radius
def new_frame():
global cx, cy, dx, dy # there variables will be changed
# moving the ball
cx += dx
cy += dy
if cx - r < 0 or cx + r > width: # if the ball penetrated trough the left or right edge
dx = -dx # change the x-direction (if it was going rightwards, now will go leftwards and vice versa)
if cy - r < 0 or cy + r > height: # if the ball penetrated trough the upper of lower edge
dy = -dy # change the y-direction (if it was going downwards, now will go upwards and vice versa)
canvas.fill(pg.Color("darkgreen"))
pg.draw.circle(canvas, pg.Color("white"), (cx, cy), r)
petljapg.frame_loop(100, new_frame)
(PyGame__anim_billiards_eng)
Notice how we check if the ball touches the edge of the screen. The far right of the ball has an x coordinate equal to cx+r. If this value were equal to the width of the window, it would mean that the ball touches the right edge of the window, and if cx+r>width, it means that the ball has already partially passed the right edge of the window. In this case, with the dx=−dx command, starting from the next frame a value opposite to the one so far will be added to the x coordinate of the ball, that is, the ball will henceforward be moving 3 pixels to the left. This will look like the ball bounced off the right edge of the window.
Notice another detail: instead of cx+r>width we could have also used cx+r>=width and the program would work almost the same. However, since the ball does not move by one pixel, it would not be valid if we used the condition cx+r==width, because then the ball could skip the position we are checking and go through the edge of the window.
We thoroughly analyzed the case of the right edge of the window, and the same reasoning was applied to other edges when the program was being written. The overall effect of the two if commands is that the ball bounces off each edge of the window.
Check if you understand this by answering the following questions.
Drawings movement - questions¶
Q-73: To what side does an image move by adding a negative value to its x coordinate?
Q-74: Let the dimensions of a given image be im_width and im_height, and its upper left corner (x, y). How do we check if the image has completely gone through the top edge of the window and no part of it is visible?
Q-75: Let width be the width of the window, im_width the width of the image, (x, y) the upper left corner of the image and dx the value by which the x coordinate of the image will be changed later. What commands will cause the image to begin to appear entering the window through the right edge?
Task - a car going left-right¶
Try reworking the first program so that the car moves alternately to one side and to the other, as in the example (“Play Task” button). The program already contains commands to form a tuple of two images. The image of the car facing right is loaded, while the image of the car facing the other side is obtained using the pg.transform.flip function, which transforms the given image into one symmetrical to it.
import pygame as pg, petljapg
(width, height) = (400, 300)
canvas = petljapg.open_window(width, height, "Car")
car_rightwards_image = pg.image.load("car.png")
# creating flipped image (symmetric with respect to the vertical axis)
car_leftwards_image = pg.transform.flip(car_rightwards_image, True, False)
car_images = (car_rightwards_image, car_leftwards_image)
fps = 50
def new_frame():
pass
petljapg.frame_loop(fps, new_frame)
(PyGame__anim_car_right_left_eng)