
There is something magical about animation. Seeing each still image assembled on top of one another with ever so slight variations to create the illusion of movement. And then playing those animations at higher speeds to create a moving picture. I still remember my first game was just a square that could move, but the first time I made it move I knew I loved game making and programming.
Today, let’s keep our game making going and talk about how to animate sprites. We will use individual images for this tutorial, and in a later tutorial, I will show how to do sprite sheet animation.
By the end of this tutorial, you will understand how sprite animation works in Pygame and be able to animate sprites in a space shooter to show explosions and enemy ship actions.
CODE FOR THIS TUTORIAL CAN BE FOUND ON GITHUB.
Before we begin, the sprites used in this tutorial are from kisspng.com, or from a quick Google search for explosion sprite sheets.
What is Sprite Animation?
The sprites that we have been using in our space shooter, the ships, asteroids and others, are the 2D graphics that we can see and move around within the larger window. Before we only showed animation or movement by updating the location a specified number of pixels. For example, when we press the right arrow key, the ship is updated 8 pixels in the right direction. But this doesn’t really change the look of our ship as we move, only the location within the window.
If we want to show animation, what we need is a set of images (often using a spritesheet) and then a way to play those images on top of each other in rapid succession to give us the illusion that the object is really moving. Each individual image, or sprite, is a frame and we will need to display each frame in a continuous movement to create a loop.
In order to create the animation, we will need to create a loop to:
- Load the image,
- Update the image to each frame,
- Draw the frame to the window.
Loading images
We will use separate explosion animations for when the asteroids explodes and for when our ship explodes.
Let’s load the images for our types of explosions.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# asteroid explosion explosion_anim = {} explosion_anim['large'] = [] explosion_anim['small'] = [] explosion_anim['ship'] = [] for i in range(5): filename = 'explosion0{}.png'.format(i) img = pygame.image.load(path.join(img_dir, filename)).convert_alpha() # change the sizes of the explosion image_lg = pygame.transform.scale(img, (75, 75)) explosion_anim['large'].append(image_lg) image_sm = pygame.transform.scale(img, (45, 45)) explosion_anim['small'].append(image_sm) |
We have three different situations in which we will need to animate explosions (when an asteroid explodes (large), when an asteroid hits our ship (small), and when our ship or enemy ships explode (ship)). In line 2, we create a dictionary called explosion_anim, and in lines 3 – 5 create the keys. The keys will be used later when we are deciding what kind of explosion animation we need to display.
For the large and small explosions, there are 5 images that we need to cycle through. We locate the images based on their file names in line 7, then load them. Finally, we rescale the image sizes depending upon if they are large or not in lines 10 – 13.
The following code loads the explosion for the player’s and enemy ships. There are 10 images for the player explosion, so we need to load all 10 images using the for loop in line 1.
1 2 3 4 5 6 7 |
for i in range(10): filename = 'ship_explosion0{}.png'.format(i) img = pygame.image.load(path.join(img_dir, filename)).convert() img.set_colorkey(BLACK) # change the sizes of the explosion image_player = pygame.transform.scale(img, (100, 100)) explosion_anim['ship'].append(image_player) |
Explosion class and Updating images
Creating the Explosion class is similar to other classes we made in previous tutorials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Explosion(pygame.sprite.Sprite): '''create Explosion class''' def __init__(self, center, ex_type, explosion_anim): super().__init__() self.ex_type = ex_type self.explosion_anim = explosion_anim self.image = explosion_anim[self.ex_type][0] self.rect = self.image.get_rect() self.rect.center = center self.frame = 0 self.last_update = pygame.time.get_ticks() self.frame_rate = 100 def update(self): '''update explosions''' current_time = pygame.time.get_ticks() if current_time - self.last_update > self.frame_rate: self.last_update = current_time self.frame +=1 if self.frame == len(self.explosion_anim[self.ex_type]): self.kill() else: center = self.rect.center self.image = self.explosion_anim[self.ex_type][self.frame] self.rect = self.image.get_rect() self.rect.center = center |
Our Explosion class takes three arguments, the location of the explosion which is the center of the object that just exploded, the type of explosion, ex_type, and the list for the explosion animation. It’s important to note that we set the first frame to be the first image, and that we will cycle through the images by a specified frame_rate.
In the update function, we check the current game time, and if the current_time minus the last frame that was displayed is greater than our frame_rate, we update the frame number, check if it is the last frame in line 20, and if it’s not, continue to update the frames based upon the type of explosion and the image number.
Collision detection and Drawing frames
In the collision detection tutorial, our ships or asteroids would disappear when hit, but nothing else happened. In the collision detection loops we are going to create the explosions using our Explosion class and then add them to our all_active_sprites list to be updated and drawn in the window.
Below are the following bits of code needed to add to display the explosions.
Explosion when enemy ships are hit by player bullets. Notice in line 6 the ‘ship’ argument is passed to the Explosion class.
1 2 3 4 5 6 |
# check if a bullet hit an enemy ship enemy_hit = pygame.sprite.groupcollide(enemy_ships, bullets, True, pygame.sprite.collide_circle) # when asteroids are destroyed, spawn new asteroids for hit in enemy_hit: expl = Explosion(hit.rect.center, 'ship', explosion_anim) all_active_sprites.add(expl) |
Explosion when asteroids are hit by player bullets. ‘large’ argument in line 6.
1 2 3 4 5 6 |
# check if a bullet hit an asteroid asteroid_hit = pygame.sprite.groupcollide(asteroids, bullets, True, pygame.sprite.collide_circle) # when asteroids are destroyed, spawn new asteroids for hit in asteroid_hit: expl = Explosion(hit.rect.center, 'large', explosion_anim) all_active_sprites.add(expl) |
Explosion when asteroids hit the player’s ship.
1 2 3 4 5 6 |
# check for collisions between asteroids and player player_hit = pygame.sprite.spritecollide(player, asteroids, True) # if player is hit for hit in player_hit: expl = Explosion(hit.rect.center, 'small', explosion_anim) all_active_sprites.add(expl) |
Explosion when enemy ships hit the player’s ship.
1 2 3 4 5 6 |
# check for collisions between enemy ships and player player_hit_by_ship = pygame.sprite.spritecollide(player, enemy_ships, True) # if player is hit by enemy ship for hit in player_hit_by_ship: expl = Explosion(hit.rect.center, 'ship', explosion_anim) all_active_sprites.add(expl) |
Later, we will also add explosions if the player is hit by enemy lasers, but we will need to create a shield and health for the player first.
Boost Animation
One last animation we will discuss for our game is when the enemies perform their dive bomb attacks. Just having them speed off towards our player with out firing some sort of boosters looks strange. So we want to correct this by adding some animation when this occurs.
First, we create a boost_anim dictionary similar to our explosions, load the images, and rescale them.
1 2 3 4 5 6 7 8 9 |
# boost animation boost_anim = {} boost_anim['boost'] = [] for i in range(8): filename = 'boost0{}.png'.format(i) img = pygame.image.load(path.join(img_dir, filename)).convert_alpha() # change the sizes of the explosion boost_img = pygame.transform.scale(img, (50,50)) boost_anim['boost'].append(boost_img) |
We then create the Boost class, which is very similar to the Explosion class, except that the frame_rate value is 35.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Boost(pygame.sprite.Sprite): '''create Boost class''' def __init__(self, center, b_type, boost_anim): super().__init__() self.b_type = b_type self.boost_anim = boost_anim self.image = boost_anim[self.b_type][0] self.rect = self.image.get_rect() self.rect.center = center self.frame = 0 self.last_update = pygame.time.get_ticks() self.frame_rate = 35 def update(self): '''update boost animation''' current_time = pygame.time.get_ticks() if current_time - self.last_update > self.frame_rate: self.last_update = current_time self.frame += 1 if self.frame == len(self.boost_anim[self.b_type]): self.kill() else: center = self.rect.center self.image = self.boost_anim[self.b_type][self.frame] self.rect = self.image.get_rect() self.rect.midtop = center |
Update the EnemyShip class parameters to take the boost_anim argument. We want to use the update method of the EnemyShip class to check when the player dive bombs and then draw the boost animation when a ship does so.
1 2 3 4 5 |
class EnemyShip(pygame.sprite.Sprite): '''create EnemyShip class''' def __init__(self, enemy_image, bullet_image, sprites_list, bullet_list, boost_anim): super().__init__() ### other class parameters below this line ### |
Add Boost animation to EnemyShip class in divebomb method.
1 2 3 4 5 |
def divebomb(self): '''divebomb flight pattern''' boost = Boost(self.rect.center, 'boost', self.boost_anim) self.sprites.add(boost) self.rect.bottom += self.speedy |
And that’s it! We can finally see things blow up! Below is what our game looks like with explosions and the boost animation.

Summary
We looked at what sprite animation is and how to use Pygame to show animations in our game, showing explosions and a boost animation to make our game more realistic. Next time, we will add a game display to our window showing our health, score and lives.
I also really want to say thank you for taking the time to read and to learn from the tutorials I write. If you ever have any suggestions or questions, please let me know.