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)
__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.
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 = 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.