Sprite animation in pygame

Martin McBride, 2021-03-08
Tags game development sprite animation
Categories pygame

The previous sprite example simply drew a sprite in a fixed place on the screen.

For most games, we will want some of our sprites to move around, bounce off things, or even explode once in a while. How do we do that?

The game loop

Let's look again at the main loop of the game:

running = True
while running:

    # Check events
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    screen.fill((0, 0, 0))

This codes loops continuously, and each time through the loop it fills the screen with black (deleting the previous ball image), then draws the sprite again. The sprite appears to be stationary because we always redraw it in the same place.

To make the sprite move, we need to draw it in a slightly different place, each time through the loop. We can do this using the update method of the sprite.

Implementing an update method

Out sprite already has an update method, but at the moment it doesn't do anything. Here is an updated sprite with a simple update method:

class Ball(pg.sprite.Sprite):

    def __init__(self, pos):
        super(Ball, self).__init__()
        self.image = pg.image.load(os.path.join('resources', 'ball.png'))
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.velocity = [1, 1]

    def update(self):

In the __init__ method have created a velocity variable with value [1, 1]. This represents a velocity of 1 in the x direction, and 1 in the y direction.

In the update method, we move rect. This is the rectangle that controls the position of the sprite on the screen. The move_ip stands for move in-place, which means we move the rectangle relative to its previous position. In other words, each time we call the update function, we move the rectangle by the amount [1, 1] - that is 1 pixel in the x direction and 1 pixel in the y direction. In other words, the sprite moves diagonally, like this:

Remember, of course, that the y value measures how far the sprite is down from the top of the screen. So an increasing y value means the sprite is travelling downwards.

Modifying the game loop

We need to make some minor changes to the game loop to get our sprite to move:

# Initialise pygame
clock = pg.time.Clock()

screen = pg.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])

# Create sprites
ball = Ball((100, 200))
group = pg.sprite.RenderPlain()

# Main loop, run until window closed
running = True
while running:

    # Check events
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    screen.fill((0, 0, 0))


# close pygame

The first change we have made is to call group.update() just before we call group.draw(). When we update the group, it automatically calls the update method of every sprite in the group. In our case, of course, we only have one sprite in our group.

The other is that we have introduced a clock:

clock = pg.time.Clock()

Then, inside the loop, we call clock.tick(30). This function slows the main loop down, so it can only run 30 times a second.

Why would we want to do that? Well every time the loop executes, the ball moves along by 1 pixel. This means that the speed of the ball depends on how fast your computer is. If you have a very slow computer that can only run the loop 100 times a second, the ball will move 100 pixels every second. If you have a much faster computer that can run the loop 1000 times a second, the ball will move 10 times as fast. That might make the game fairly easy on the slow computer but completely impossible on the fast machine.

By including the clock.tick call in the main loop, we are ensuring that the loop will only run 30 times a second, no matter how fast your computer. The ball will always move 30 pixels per second.

If you run the complete program, you will see the ball move diagonally across the screen. After a few seconds it will disappear off the screen, never to be seen again, but fixing that is the next stage.

The source code is available on github as movingsprite.py.

If you found this article useful, you might be interested in the book Computer Graphics in Python or other books by the same author.


Popular tags

2d arrays abstract data type alignment and animation arc array arrays bezier curve built-in function callable object circle classes close closure cmyk colour comparison operator comprehension context context manager conversion creational pattern data types design pattern device space dictionary drawing duck typing efficiency else encryption enumerate fill filter font font style for loop function function composition function plot functools game development generativepy tutorial generator geometry gif gradient greyscale higher order function hsl html image image processing imagesurface immutable object index inner function input installing iter iterable iterator itertools l system lambda function len line linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map monad mutability named parameter numeric python numpy object open operator optional parameter or partial application path polygon positional parameter print pure function pycairo radial gradient range recipes rectangle recursion reduce rgb rotation scaling sector segment sequence singleton slice slicing sound spirograph sprite square str stream string stroke subpath symmetric encryption template text text metrics tinkerbell fractal transform translation transparency tuple turtle unpacking user space vectorisation webserver website while loop zip