DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Arcade Library (Part 14): Using the Physics Engine

Using the Physics Engine

This time, we’ll introduce the physics engine.
By using it, you can apply movements based on physical laws to your sprites.

1. Creating a Physics Engine

First, create a physics engine using the arcade.PymunkPhysicsEngine() method.
The gravity argument represents gravity in the physics world.

# main.py (excerpt)
# Create a physics engine
self.physics = arcade.PymunkPhysicsEngine(damping=0.8, gravity=(0, -900))
Enter fullscreen mode Exit fullscreen mode

2. Adding the Player

Use the add_sprite() method to add a sprite to the physics engine.

# main.py (excerpt)
# Add the player
self.physics.add_sprite(
    self.player,
    friction=1.0,
    collision_type="player"
)
Enter fullscreen mode Exit fullscreen mode

3. Adding Coins and Blocks

Using the same approach, add coins and blocks.
For body_type, specify either DYNAMIC or STATIC as shown below.

Value Meaning
arcade.PymunkPhysicsEngine.DYNAMIC An object affected by physics (movable)
arcade.PymunkPhysicsEngine.STATIC An object not affected by physics (immovable)

Coins should be DYNAMIC, while blocks should be STATIC.

# main.py (excerpt)
# Add coins
for coin in self.coins:
    self.physics.add_sprite(
        coin,
        friction=1.0,
        collision_type="coin",
        body_type=arcade.PymunkPhysicsEngine.DYNAMIC
    )

# Add blocks
for block in self.blocks:
    self.physics.add_sprite(
        block,
        friction=1.0,
        collision_type="block",
        body_type=arcade.PymunkPhysicsEngine.STATIC
    )
Enter fullscreen mode Exit fullscreen mode

Use the following image for blocks (roof tiles are perfect for a ninja game!).

Image File name
block_01.png

4. Giving Velocity to the Player

In the on_key_press() method, we apply an instantaneous velocity to the player.

The first argument of self.physics.set_velocity() is the player sprite,
and the second argument is the velocity vector.

# main.py (excerpt)
PLAYER_JUMP_X = 60
PLAYER_JUMP_Y = 300

# ...

def on_key_press(self, key, key_modifiers):
    # Move (WASD)
    if key == arcade.key.W:
        self.physics.set_velocity(self.player, (0, PLAYER_JUMP_Y))
    if key == arcade.key.A:
        self.physics.set_velocity(self.player, (-PLAYER_JUMP_X, PLAYER_JUMP_Y))
    if key == arcade.key.S:
        pass
    if key == arcade.key.D:
        self.physics.set_velocity(self.player, (PLAYER_JUMP_X, PLAYER_JUMP_Y))
Enter fullscreen mode Exit fullscreen mode

5. Running the Physics Engine

Finally, step the physics engine inside the on_update() method.

# main.py (excerpt)
self.physics.step(delta_time)  # Advance the physics engine
Enter fullscreen mode Exit fullscreen mode

Complete Code

Below is the complete code implementing all the features above.
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

class Player(BaseSprite):

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

class Coin(BaseSprite):

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

class Block(BaseSprite):

    def __init__(self, filename, x, y):
        super().__init__(filename, x, y)
Enter fullscreen mode Exit fullscreen mode
# main.py (complete code)
import arcade
import random
import sprite

PLAYER_JUMP_X = 60
PLAYER_JUMP_Y = 300

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()      # Player camera
        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)

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

        # Blocks
        block_pad_x = 48
        block_total = 8
        block_x = self.w / 2 - block_pad_x * (block_total - 1) / 2
        block_y = 30
        self.blocks = arcade.SpriteList()
        for i in range(block_total):
            x = block_x + block_pad_x * i
            y = block_y
            block = sprite.Block("images/block/block_01.png", x, y)
            self.blocks.append(block)

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

        # Create physics engine
        self.physics = arcade.PymunkPhysicsEngine(damping=0.8, gravity=(0, -900))

        # Add player
        self.physics.add_sprite(
            self.player,
            friction=1.0,
            collision_type="player"
        )

        # Add coins
        for coin in self.coins:
            self.physics.add_sprite(
                coin,
                friction=1.0,
                collision_type="wall",
                body_type=arcade.PymunkPhysicsEngine.DYNAMIC
            )

        # Add blocks
        for block in self.blocks:
            self.physics.add_sprite(
                block,
                friction=1.0,
                collision_type="wall",
                body_type=arcade.PymunkPhysicsEngine.STATIC
            )

    def on_key_press(self, key, key_modifiers):
        # Move (WASD)
        if key == arcade.key.W:
            self.physics.set_velocity(self.player, (0, PLAYER_JUMP_Y))
        if key == arcade.key.A:
            self.physics.set_velocity(self.player, (-PLAYER_JUMP_X, PLAYER_JUMP_Y))
        if key == arcade.key.S:
            pass
        if key == arcade.key.D:
            self.physics.set_velocity(self.player, (PLAYER_JUMP_X, PLAYER_JUMP_Y))

    def on_update(self, delta_time):
        # Step physics engine
        self.physics.step(delta_time)

        # Camera follow
        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.blocks.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)