Pygame Animations using a Spritesheet

In this tutorial, we will learn how to animate a character in Python using the Pygame library and a spritesheet. Instead of loading lots of separate images to animate our main sprite/character, we will use one single image containing multiple frames — a much more efficient and professional approach used in real games!

Our learning objectives are as follows:

  • Understand what a spritesheet is
  • Load and display images in Pygame
  • Extract frames from a spritesheet
  • Animate a walking character

What’s a Spritesheet?

A spritesheet is a single image that contains multiple frames to be used to create a frame-based animation.

For example: a walking character might have 6 frames showing different leg and arm positions.

Instead of loading 6 separate images, we:

  • Load one image (called a spritesheet)
  • “Cut” it into smaller pieces (frames)
  • Display these frames in sequence

Here is a spritesheet to create an animated walking ninja girl. Our animation will contain 9 frames:

When we animated these frames in sequence we will get the following animation:

Let’s now investigate the Python code to complete this animation with a 2D game implemented using the Pygame library.

Python Tutorial

Step 1: Setup the game (using Pygame)

You will need to make sure that the Pygame library is already installed on your computer.

The you will create your main python file and use the following code to initialise your game:

import pygame
import sys

pygame.init()

# Create game window
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Spritesheet Animation")

Step 2: Loading the Spritesheet

You will need to download the spritesheet below and save it in the same folder as where your Python file is saved.

Now add the following line of code to load the spritesheet file:

spritesheet = pygame.image.load("character_spritesheet.png").convert_alpha()

Step 3: Extracting Frames from the Spritesheet

We will use the following function to extract the different frames from the spritesheet: (Add this function at the top of your code)

def load_frames(sheet, x, y, frame_width, frame_height, num_frames):
    frames = []

    for i in range(num_frames):
        frame = sheet.subsurface(
            pygame.Rect(x + i * frame_width, y, frame_width, frame_height)
        )
        frames.append(frame)

    return frames

After loading the spritesheet, we will call our function. For instance to extract the top row of frames (running animation) we will start extraxcting 9 frames from position x=0 and y=0, each frame being 120 pixels wide and 160 pixels high.

Here the code we will use to extract these 9 frames:

frames = load_frames(spritesheet, 0, 0, 120, 160, 9)

We will also use two animation variables as follows:

current_frame = 0
animation_speed = 0.15

Step 4: Animating our character

Now let’s add some code to the main program loop of our game:

clock = pygame.time.Clock()
carryOn = True
while carryOn:
   screen.fill((30, 30, 30))

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

   # Update animation
   current_frame += animation_speed
   if current_frame >= len(frames):
      current_frame = 0

   # Draw current frame
   screen.blit(frames[int(current_frame)], (100, 300))

   pygame.display.flip()
   clock.tick(60)

pygame.quit()
sys.exit()

Your Turn

Use the above code and spritesheet to reproduce the animation of the ninja-girl running.

The tweak the code provided to make a jumping ninja girl animation.

Finally, create one more animation of the ninja girl attacking!

Using Object Oriented Programming

Within a Pygame project, it is very likely that you will define a separate class for the main character of your game. So let’s rework the code provided above to create a NinjaGirl class. We will then respond to keyboards input to change the state of our player (e.g. from idle to running or attacking). We will also use some code to flip our character so that we can have them looking/running right or left.

When trying the code provided below use the following keyboard inputs:

  • Right arrow to run towards the right
  • Left arrow to run towards the left
  • Space bar to attack
main.pyninja_girl.py

Python Code: Main.py file

# Ninja Girl Animation using Pygame - www.101computing.net/pygame-animations-using-a-spritesheet/

import pygame
import sys
from ninja_girl import NinjaGirl

IDLE = 0
RUN = 1
JUMP = 2
ATTACK = 3
DEAD = 4

pygame.init()

# Create game window
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Spritesheet Animation")

# Create sprite
player = NinjaGirl(100, 300)
all_sprites = pygame.sprite.Group(player)

clock = pygame.time.Clock()

running = True

while running:
   screen.fill((30, 30, 30))

   # Events
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         running = False

   keys = pygame.key.get_pressed()

    # State logic
   if keys[pygame.K_RIGHT]:
       player.set_state(RUN)
       player.rect.x += 3
       player.facing_right = True

   elif keys[pygame.K_LEFT]:
       player.set_state(RUN)
       player.rect.x -= 3
       player.facing_right = False
   elif keys[pygame.K_SPACE]:
       player.set_state(ATTACK)
   else:
       player.set_state(IDLE)


   # Update + draw
   all_sprites.update()
   all_sprites.draw(screen)

   pygame.display.flip()
   clock.tick(60)

pygame.quit()
sys.exit()

Python Code: ninja_girl.py file

import pygame

IDLE = 0
RUN = 1
JUMP = 2
ATTACK = 3
DEAD = 4

class NinjaGirl(pygame.sprite.Sprite):
   #This class represents a ball. It derives from the "Sprite" class in Pygame.

   def __init__(self, x, y):
      super().__init__()

      # State
      self.state = IDLE
      self.facing_right = True

      # Animation
      self.animations = {}
      self.current_frame = 0
      self.animation_speed = 0.15

      # Load spritesheets
      self.load_animations()

      # Starting image
      self.image = self.animations[self.state][0]
      self.rect = self.image.get_rect()

      # Position
      self.rect.x = x
      self.rect.y = y


   def load_frames(self, filename, x, y, frame_width, frame_height, num_frames):
      sheet = pygame.image.load(filename).convert_alpha()
      frames = []

      for i in range(num_frames):
         frame = sheet.subsurface(
            pygame.Rect(x + i * frame_width, y, frame_width, frame_height)
         )
         frames.append(frame)

      return frames


   def load_animations(self):
      self.animations[IDLE] = self.load_frames("spritesheet-ninja-girl.png", 960, 350, 120, 160, 1)
      self.animations[RUN] = self.load_frames("spritesheet-ninja-girl.png", 0, 0, 120, 160, 9)
      self.animations[JUMP]  = self.load_frames("spritesheet-ninja-girl.png", 0, 160, 120, 160, 9)
      self.animations[ATTACK]  = self.load_frames("spritesheet-ninja-girl.png", 0, 350, 200, 160, 4)
      self.animations[DEAD] = self.load_frames("spritesheet-ninja-girl.png", 800, 350, 200, 160, 1)


   def update(self):
      frames = self.animations[self.state]

      self.current_frame += self.animation_speed
      if self.current_frame >= len(frames):
         self.current_frame = 0

      frame = frames[int(self.current_frame)]

      # Flip image if facing left
      if not self.facing_right:
         frame = pygame.transform.flip(frame, True, False)

      self.image = frame


   def set_state(self, new_state):
      if self.state != new_state:
         self.state = new_state
         self.current_frame = 0  # Reset animation



unlock-access

Solution...

The solution for this challenge is available to full members!
Find out how to become a member:
➤ Members' Area

Did you like this challenge?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 5

No votes so far! Be the first to rate this post.

As you found this challenge interesting...

Follow us on social media!

Tagged with: