Sprite animation in pygame

By 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))
    group.draw(screen)
    pg.display.flip()

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):
        self.rect.move_ip(self.velocity)

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
pg.init()
clock = pg.time.Clock()

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

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

# 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))
    group.update()
    group.draw(screen)
    pg.display.flip()

    clock.tick(30)

# close pygame
pg.quit()

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.

See also

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

Join the PythonInformer Newsletter

Sign up using this form to receive an email when new content is added:

Popular tags

2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function latex len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template tex text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest