DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Arcade Library (Part 8): Adding Animation

Let’s Add Animation

In this chapter, we’ll build a mechanism that makes sprites look animated by switching images rapidly.

1. Add Ninja Images

Add the following images to the ninja folder inside the images directory.

Image File name Image File name Image File name
 front_01.png  front_02.png  front_03.png
 front_04.png  front_05.png

The folder structure should look like this:

# Folder structure
working_folder/
 ├ main.py
 ├ sprite.py
 └ images/
    ├ bg_temple.png
    └ ninja/      # Folder for ninja images
       ├ front_01.png
       ├ front_02.png
       ├ front_03.png
       ├ front_04.png
       └ front_05.png
Enter fullscreen mode Exit fullscreen mode

2. Add Coin Images

Just like before, add the following images to the coin folder inside the images directory.

Image File name Image File name Image File name
 coin_01.png  coin_02.png  coin_03.png
 coin_04.png  coin_05.png

The folder structure should look like this:

# Folder structure
working_folder/
 ├ main.py
 ├ sprite.py
 └ images/
    ├ bg_temple.png
    ├ ninja/
    └ coin/       # Folder for coin images
       ├ coin_01.png
       ├ coin_02.png
       ├ coin_03.png
       ├ coin_04.png
       └ coin_05.png
Enter fullscreen mode Exit fullscreen mode

3. Load Ninja Images

Next, in the constructor of the Player class in sprite.py,
load five ninja images into the anim list.

# sprite.py (added to Player)
def __init__(self, filename, x, y):
    super().__init__(filename, x, y)

    # Load 5 ninja images
    self.anim.append(arcade.load_texture("images/ninja/front_01.png"))
    self.anim.append(arcade.load_texture("images/ninja/front_02.png"))
    self.anim.append(arcade.load_texture("images/ninja/front_03.png"))
    self.anim.append(arcade.load_texture("images/ninja/front_04.png"))
    self.anim.append(arcade.load_texture("images/ninja/front_05.png"))
Enter fullscreen mode Exit fullscreen mode

4. Load Coin Images

Similarly, in the constructor of the Coin class in sprite.py,
load five coin images into the anim list.

# sprite.py (added to Coin)
def __init__(self, filename, x, y):
    super().__init__(filename, x, y)

    # Load 5 coin images
    self.anim.append(arcade.load_texture("images/coin/coin_01.png"))
    self.anim.append(arcade.load_texture("images/coin/coin_02.png"))
    self.anim.append(arcade.load_texture("images/coin/coin_03.png"))
    self.anim.append(arcade.load_texture("images/coin/coin_04.png"))
    self.anim.append(arcade.load_texture("images/coin/coin_05.png"))
Enter fullscreen mode Exit fullscreen mode

5. Add Variables to the Base Class

In the base class BaseSprite in sprite.py,
add four new variables.

These variables will be used by the update_animation() method,
which we’ll implement next.

# sprite.py (added to BaseSprite)
def __init__(self, filename, x, y):
    super().__init__(filename)
    # Position
    self.center_x = x
    self.center_y = y
    # Velocity
    self.vx = 0
    self.vy = 0
    # Animation
    self.anim_counter = 0     # counter
    self.anim_interval = 4    # count every 4 frames
    self.anim_index = 0       # frame index
    self.anim = []            # list of textures
Enter fullscreen mode Exit fullscreen mode

6. Add an Animation Method

In the same BaseSprite class, add a new method called update_animation().

# sprite.py (added to BaseSprite)
def update_animation(self):
    """ Animation """
    if not self.anim:
        return
    self.anim_counter += 1          # increment counter
    if self.anim_counter < self.anim_interval:
        return                      # switch every 4 frames
    self.anim_counter = 0           # reset counter
    self.anim_index += 1            # next frame
    if len(self.anim) <= self.anim_index:
        self.anim_index = 0         # loop back to start
    self.texture = self.anim[self.anim_index]  # set texture
Enter fullscreen mode Exit fullscreen mode

This code switches the images stored in anim
one by one every 4 frames.

If you decrease this value, the animation becomes faster.
If you increase it, the animation becomes slower.

(Definitely try tweaking it!)

7. Call It from the Update Method

Finally, call update_animation() from inside the update() method.

# sprite.py (added to BaseSprite)
def update(self, delta_time):
    """ Update """
    self.center_x += self.vx * delta_time
    self.center_y += self.vy * delta_time
    # Animation
    self.update_animation()
Enter fullscreen mode Exit fullscreen mode

Complete Code

Below is the complete code with all features implemented so far.
You can copy and run it as-is.

# sprite.py (complete code)
import arcade
import math

class BaseSprite(arcade.Sprite):

    def __init__(self, filename, x, y):
        super().__init__(filename)
        # Position
        self.center_x = x
        self.center_y = y
        # Velocity
        self.vx = 0
        self.vy = 0
        # Animation
        self.anim_counter = 0     # counter
        self.anim_interval = 4    # count every 4 frames
        self.anim_index = 0       # frame index
        self.anim = []            # list of textures

    def update(self, delta_time):
        """ Update """
        self.center_x += self.vx * delta_time
        self.center_y += self.vy * delta_time
        # Animation
        self.update_animation()

    def move(self, spd, deg):
        """ Move Sprite """
        rad = deg * math.pi / 180
        self.vx = spd * math.cos(rad)
        self.vy = spd * math.sin(rad)

    def stop(self):
        """ Stop Sprite """
        self.vx = 0
        self.vy = 0

    def update_animation(self):
        """ Animation """
        if not self.anim:
            return
        self.anim_counter += 1
        if self.anim_counter < self.anim_interval:
            return
        self.anim_counter = 0
        self.anim_index += 1
        if len(self.anim) <= self.anim_index:
            self.anim_index = 0
        self.texture = self.anim[self.anim_index]

class Player(BaseSprite):

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

        # Load 5 ninja images
        self.anim.append(arcade.load_texture("images/ninja/front_01.png"))
        self.anim.append(arcade.load_texture("images/ninja/front_02.png"))
        self.anim.append(arcade.load_texture("images/ninja/front_03.png"))
        self.anim.append(arcade.load_texture("images/ninja/front_04.png"))
        self.anim.append(arcade.load_texture("images/ninja/front_05.png"))

class Coin(BaseSprite):

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

        # Load 5 coin images
        self.anim.append(arcade.load_texture("images/coin/coin_01.png"))
        self.anim.append(arcade.load_texture("images/coin/coin_02.png"))
        self.anim.append(arcade.load_texture("images/coin/coin_03.png"))
        self.anim.append(arcade.load_texture("images/coin/coin_04.png"))
        self.anim.append(arcade.load_texture("images/coin/coin_05.png"))
Enter fullscreen mode Exit fullscreen mode
# main.py (complete code)
import arcade
import sprite
import random

class GameView(arcade.View):

    def __init__(self, window):
        super().__init__()
        self.window = window
        self.w = self.window.width
        self.h = self.window.height

        # Background color
        self.background_color = arcade.color.PAYNE_GREY

        # Background sprites
        self.backgrounds = arcade.SpriteList()
        bkg = arcade.Sprite("images/bg_temple.png")
        bkg.center_x = self.w / 2
        bkg.center_y = self.h / 2
        self.backgrounds.append(bkg)

        # Player sprites
        self.players = arcade.SpriteList()
        self.player = sprite.Player(
            "images/ninja/front_01.png",
            x=self.w / 2,
            y=self.h / 2
        )
        self.players.append(self.player)

        # Coin sprites
        self.coins = arcade.SpriteList()
        for i in range(10):
            x = random.random() * self.w
            y = random.random() * self.h
            coin = sprite.Coin(
                "images/coin/coin_01.png",
                x=x,
                y=y
            )
            self.coins.append(coin)

    def on_key_press(self, key, key_modifiers):
        # Move (WASD)
        if key == arcade.key.W: self.player.move(90, 90)
        if key == arcade.key.A: self.player.move(90, 180)
        if key == arcade.key.S: self.player.move(90, 270)
        if key == arcade.key.D: self.player.move(90, 0)

    def on_key_release(self, key, key_modifiers):
        self.player.stop()

    def on_update(self, delta_time):
        self.players.update(delta_time)
        self.coins.update(delta_time)

        # Player x coin list
        hit_coins = arcade.check_for_collision_with_list(
            self.player,
            self.coins
        )
        for coin in hit_coins:
            coin.remove_from_sprite_lists()

    def on_draw(self):
        self.clear()  # Clear
        self.backgrounds.draw()
        self.players.draw()
        self.coins.draw()

def main():
    """ main process """
    window = arcade.Window(480, 320, "Hello, Arcade!!")
    game = GameView(window)
    window.show_view(game)
    arcade.run()

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

When you run the program, it will look like this:

Coming Up Next...

Thank you for reading.
The next chapter is titled “Let’s Display the Score.”

Stay tuned 👍

Top comments (0)