DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Arcade Library (Part 13): Using the Camera

Getting Started with 2D Games Using Arcade Library (Part x): Using the Camera

This time, we’ll use a camera to make it follow the player.
By using a camera, you can take advantage of a much wider game world.

1. Creating Two Cameras

A camera object defines the visible area of the game world.

In the constructor of GameView in main.py, we prepare two camera objects:

  • self.camera to follow the player and other world objects
  • self.camera_gui to keep GUI elements fixed on the screen
# main.py (excerpt)
# Camera
self.camera = arcade.Camera2D()      # Camera for the player
self.camera_gui = arcade.Camera2D()  # Fixed camera for GUI
Enter fullscreen mode Exit fullscreen mode

2. Making the Camera Follow the Player

Next, in the on_update() method, we implement logic that makes the camera follow the player’s position.
By adjusting the last value (0.1), you can control how smoothly and quickly the camera moves.

# main.py (excerpt)
# Make the player camera follow the player position
self.camera.position = arcade.math.lerp_2d(
    self.camera.position,
    self.player.position,
    0.1
)
Enter fullscreen mode Exit fullscreen mode

3. Activating the Camera

Finally, in the on_draw() method, we decide which camera to use for drawing.

  • Drawing after self.camera.use() will follow the player
  • Drawing after self.camera_gui.use() will stay fixed on the screen
# main.py (excerpt)
def on_draw(self):
    self.clear()  # Clear

    self.camera.use()  # Use the player camera
    self.players.draw()
    self.coins.draw()

    self.camera_gui.use()  # Use the fixed camera
    self.msg_info.draw()
Enter fullscreen mode Exit fullscreen mode

Complete Code

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

# sprite.py (complete code)
import arcade
import random
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
        self.anim_interval = 4
        self.anim_index = 0
        self.anim_key = ""
        self.anim_pause = True
        self.anims = {}

    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, tag=""):
        """Move sprite"""
        rad = deg * math.pi / 180
        self.vx = spd * math.cos(rad)
        self.vy = spd * math.sin(rad)
        if tag:
            self.change_animation(tag)

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

    def update_animation(self):
        """Update animation"""
        if self.anim_key not in self.anims:
            return
        if self.anim_pause:
            return
        self.anim_counter += 1
        if self.anim_counter < self.anim_interval:
            return
        self.anim_counter = 0
        self.anim_index += 1
        anim = self.anims[self.anim_key]
        if len(anim) <= self.anim_index:
            self.anim_index = 0
        self.texture = anim[self.anim_index]

    def load_animation(self, key, filename, num):
        """Load animation"""
        anim = []
        for i in range(num):
            path = filename.format(i + 1)
            anim.append(arcade.load_texture(path))
        self.anims[key] = anim

    def change_animation(self, key):
        """Change animation"""
        if key not in self.anims:
            return
        self.anim_counter = 0
        self.anim_index = 0
        self.anim_key = key
        self.texture = self.anims[key][0]
        self.start_animation()

    def start_animation(self):
        """Start animation"""
        self.anim_pause = False

    def stop_animation(self):
        """Stop animation"""
        self.anim_pause = True


class Player(BaseSprite):

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

        # Animation
        self.load_animation("front", "images/ninja/front_{:02d}.png", 5)
        self.load_animation("left", "images/ninja/left_{:02d}.png", 5)
        self.load_animation("right", "images/ninja/right_{:02d}.png", 5)
        self.load_animation("back", "images/ninja/back_{:02d}.png", 5)
        self.change_animation("front")


class Coin(BaseSprite):

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

        # Animation
        self.load_animation("coin", "images/coin/coin_{:02d}.png", 5)
        self.change_animation("coin")
Enter fullscreen mode Exit fullscreen mode
# main.py (complete code)
import arcade
import random
import sprite

PLAYER_SPEED = 90

class GameView(arcade.View):

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

        self.background_color = arcade.color.PAYNE_GREY

        # Camera
        self.camera = arcade.Camera2D()      # Camera for the player
        self.camera_gui = arcade.Camera2D()  # Fixed camera

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

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

        # Info text (GUI)
        self.msg_info = arcade.Text(
            "GAME",
            self.w / 2,
            self.h - 20,
            arcade.color.WHITE,
            12,
            anchor_x="center",
            anchor_y="top"
        )

    def on_key_press(self, key, modifiers):
        # Move (WASD)
        if key == arcade.key.W:
            self.player.move(PLAYER_SPEED, 90, "back")
        if key == arcade.key.A:
            self.player.move(PLAYER_SPEED, 180, "left")
        if key == arcade.key.S:
            self.player.move(PLAYER_SPEED, 270, "front")
        if key == arcade.key.D:
            self.player.move(PLAYER_SPEED, 0, "right")

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

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

        # Make the player camera follow the player
        self.camera.position = arcade.math.lerp_2d(
            self.camera.position,
            self.player.position,
            0.1
        )

    def on_draw(self):
        self.clear()

        self.camera.use()
        self.players.draw()
        self.coins.draw()

        self.camera_gui.use()
        self.msg_info.draw()


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


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

The result looks like this:

Closing Thoughts

Thank you for reading!
I hope this series becomes a starting point for your own game development. ޱ(ఠ皿ఠ)ว👍

Top comments (0)