Animated sprite from several images

I was looking for a good tutorial on creating simple sprite animation from multiple images in Python using Pygame. I still did not find what I was looking for.

My question is simple: how to make an animated sprite from several images (for example: make several explosion images with dimensions 20x20px the same, animated)

Any good ideas?

+7
source share
3 answers

You can try changing your sprite so that it changes its image for another inside update . Thus, when the sprite is displayed, it will look animated.

Edit

Here is a quick example:

 import pygame import sys def load_image(name): image = pygame.image.load(name) return image class TestSprite(pygame.sprite.Sprite): def __init__(self): super(TestSprite, self).__init__() self.images = [] self.images.append(load_image('image1.png')) self.images.append(load_image('image2.png')) # assuming both images are 64x64 pixels self.index = 0 self.image = self.images[self.index] self.rect = pygame.Rect(5, 5, 64, 64) def update(self): '''This method iterates through the elements inside self.images and displays the next one each tick. For a slower animation, you may want to consider using a timer of some sort so it updates slower.''' self.index += 1 if self.index >= len(self.images): self.index = 0 self.image = self.images[self.index] def main(): pygame.init() screen = pygame.display.set_mode((250, 250)) my_sprite = TestSprite() my_group = pygame.sprite.Group(my_sprite) while True: event = pygame.event.poll() if event.type == pygame.QUIT: pygame.quit() sys.exit(0) # Calling the 'my_group.update' function calls the 'update' function of all # its member sprites. Calling the 'my_group.draw' function uses the 'image' # and 'rect' attributes of its member sprites to draw the sprite. my_group.update() my_group.draw(screen) pygame.display.flip() if __name__ == '__main__': main() 

It is assumed that you have two images named image1.png and image2.png inside the same folder where the code is located.

+13
source

There are two types of animation: frame - dependent and time-dependent . Both work in a similar way.


To the main cycle

  • Upload all images to a list.
  • Create three variables:
    • index , which tracks the current index of the image list.
    • current_time or current_frame , which tracks the current time or current frame since the last index switch.
    • animation_time or animation_frames , which determine how many seconds or frames must elapse before switching the image.

During the main cycle

  • The increment of current_time by the number of seconds elapsed since its last increase, or the increase of current_frame by 1.
  • Check if current_time >= animation_time or current_frame >= animation_frame . If true, continue with 3-5.
  • Reset current_time = 0 or current_frame = 0 .
  • Index increase, unless it is equal to or greater than the number of images. In this case, reset index = 0 .
  • Resize the sprite accordingly.

Full working example

 import os import pygame pygame.init() SIZE = WIDTH, HEIGHT = 720, 480 BACKGROUND_COLOR = pygame.Color('black') FPS = 60 screen = pygame.display.set_mode(SIZE) clock = pygame.time.Clock() def load_images(path): """ Loads all images in directory. The directory must only contain images. Args: path: The relative or absolute path to the directory to load images from. Returns: List of images. """ images = [] for file_name in os.listdir(path): image = pygame.image.load(path + os.sep + file_name).convert() images.append(image) return images class AnimatedSprite(pygame.sprite.Sprite): def __init__(self, position, images): """ Animated sprite object. Args: position: x, y coordinate on the screen to place the AnimatedSprite. images: Images to use in the animation. """ super(AnimatedSprite, self).__init__() size = (32, 32) # This should match the size of the images. self.rect = pygame.Rect(position, size) self.images = images self.images_right = images self.images_left = [pygame.transform.flip(image, True, False) for image in images] # Flipping every image. self.index = 0 self.image = images[self.index] # 'image' is the current image of the animation. self.velocity = pygame.math.Vector2(0, 0) self.animation_time = 0.1 self.current_time = 0 self.animation_frames = 6 self.current_frame = 0 def update_time_dependent(self, dt): """ Updates the image of Sprite approximately every 0.1 second. Args: dt: Time elapsed between each frame. """ if self.velocity.x > 0: # Use the right images if sprite is moving right. self.images = self.images_right elif self.velocity.x < 0: self.images = self.images_left self.current_time += dt if self.current_time >= self.animation_time: self.current_time = 0 self.index = (self.index + 1) % len(self.images) self.image = self.images[self.index] self.rect.move_ip(*self.velocity) def update_frame_dependent(self): """ Updates the image of Sprite every 6 frame (approximately every 0.1 second if frame rate is 60). """ if self.velocity.x > 0: # Use the right images if sprite is moving right. self.images = self.images_right elif self.velocity.x < 0: self.images = self.images_left self.current_frame += 1 if self.current_frame >= self.animation_frames: self.current_frame = 0 self.index = (self.index + 1) % len(self.images) self.image = self.images[self.index] self.rect.move_ip(*self.velocity) def update(self, dt): """This is the method that being called when 'all_sprites.update(dt)' is called.""" # Switch between the two update methods by commenting/uncommenting. self.update_time_dependent(dt) # self.update_frame_dependent() def main(): images = load_images(path='temp') # Make sure to provide the relative or full path to the images directory. player = AnimatedSprite(position=(100, 100), images=images) all_sprites = pygame.sprite.Group(player) # Creates a sprite group and adds 'player' to it. running = True while running: dt = clock.tick(FPS) / 1000 # Amount of seconds between each loop. for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: player.velocity.x = 4 elif event.key == pygame.K_LEFT: player.velocity.x = -4 elif event.key == pygame.K_DOWN: player.velocity.y = 4 elif event.key == pygame.K_UP: player.velocity.y = -4 elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT: player.velocity.x = 0 elif event.key == pygame.K_DOWN or event.key == pygame.K_UP: player.velocity.y = 0 all_sprites.update(dt) # Calls the 'update' method on all sprites in the list (currently just the player). screen.fill(BACKGROUND_COLOR) all_sprites.draw(screen) pygame.display.update() if __name__ == '__main__': main() 

When to choose which

Time-dependent animation allows you to play the animation at the same speed, regardless of how slow / fast the frame rate or how slow / fast your computer is. This allows your program to freely change the frame rate without affecting the animation, and it will also be consistent, even if the computer can not cope with the frame rate. If the program lags behind, the animation will catch up to the state, it should have been as if there was no delay.

Although it may happen that the animation loop does not synchronize with the frame rate, which makes the animation loop irregular. For example, let's say that we update frames every 0.05 seconds and the image of the animation switch every 0.075 seconds, then the cycle will be:

  • Frame 1; 0.00 seconds image 1
  • Frame 2; 0.05 seconds; image 1
  • Frame 3; 0.10 seconds; image 2
  • Frame 4; 0.15 s; image 1
  • Frame 5; 0.20 s; image 1
  • Box 6; 0.25 s; image 2

And so on...

Frame-dependent may look smoother if your computer can process frame rates sequentially. If a lag occurs, it will stop in its current state and restart when the lag stops, which makes the lag more noticeable. This alternative is a little easier to implement, since you just need to increment the current_frame with 1 per call instead of dealing with delta time ( dt ) and passing it to each object.

Sprites

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

Result

enter image description here

+9
source

You should have all your sprite animations on one big "canvas", so for 3-bit 20x20 help frames you will have 60x20 images. Now you can get the correct frames by loading the image area.

Inside the sprite class, most likely, in the update method, you should have something like this (hard-coded for simplicity, I prefer a separate class to be responsible for selecting the correct animation frame). self.f = 0 on __init__ .

 def update(self): images = [[0, 0], [20, 0], [40, 0]] self.f += 1 if self.f < len(images) else 0 self.image = your_function_to_get_image_by_coordinates(images[i]) 
+2
source

All Articles