CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-arcade

Arcade Game Development Library

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

physics-engines.mddocs/

Physics Engines

Multiple physics engine options from simple AABB collision to advanced 2D physics simulation, providing realistic movement, collision detection, and physics-based gameplay mechanics.

Capabilities

Simple Physics Engine

Basic AABB (Axis-Aligned Bounding Box) collision detection for straightforward movement and wall collision.

class PhysicsEngineSimple:
    """
    Simple physics engine using AABB collision detection.
    Prevents sprites from moving through solid walls and barriers.
    """
    def __init__(self, player_sprite: arcade.Sprite, walls: arcade.SpriteList):
        """
        Create a simple physics engine.

        Args:
            player_sprite: The sprite that will be moved and checked for collisions
            walls: SpriteList containing solid obstacles that block movement
        """
    
    player_sprite: arcade.Sprite
    walls: arcade.SpriteList
    
    def update(self) -> None:
        """
        Update physics simulation. Call this each frame to apply movement and handle collisions.
        Moves the player sprite based on its change_x and change_y values, then checks
        for collisions with walls and prevents movement through them.
        """
    
    def add_sprite(self, sprite: arcade.Sprite) -> None:
        """
        Add a wall sprite to the collision list.

        Args:
            sprite: Sprite to add as a collidable wall
        """
    
    def remove_sprite(self, sprite: arcade.Sprite) -> None:
        """
        Remove a wall sprite from the collision list.

        Args:
            sprite: Sprite to remove from walls
        """

Platformer Physics Engine

Advanced physics engine designed for platformer games with gravity, jumping, ladders, and moving platforms.

class PhysicsEnginePlatformer:
    """
    Physics engine designed for 2D platformer games with gravity, jumping mechanics,
    ladder climbing, and moving platform support.
    """
    def __init__(self, player_sprite: arcade.Sprite, platforms: arcade.SpriteList = None,
                 gravity_constant: float = 0.5, ladders: arcade.SpriteList = None,
                 walls: arcade.SpriteList = None):
        """
        Create a platformer physics engine.

        Args:
            player_sprite: The player character sprite
            platforms: SpriteList of platforms the player can stand on
            gravity_constant: Strength of gravity (pixels per frame squared)
            ladders: SpriteList of ladder sprites the player can climb
            walls: SpriteList of wall sprites that block horizontal movement
        """
    
    player_sprite: arcade.Sprite
    platforms: arcade.SpriteList
    ladders: arcade.SpriteList
    walls: arcade.SpriteList
    gravity_constant: float
    
    def update(self) -> None:
        """
        Update physics simulation.
        Applies gravity, handles jumping, processes collisions with platforms/walls/ladders,
        and manages platformer-specific movement mechanics.
        """
    
    def enable_multi_jump(self, allowed_jumps: int) -> None:
        """
        Enable multi-jumping (double jump, triple jump, etc.).

        Args:
            allowed_jumps: Total number of jumps allowed (including initial jump)
        """
    
    def disable_multi_jump(self) -> None:
        """Disable multi-jumping, allowing only single jumps."""
    
    def increment_jump_count(self) -> None:
        """Increment the current jump count (call when player jumps)."""
    
    def can_jump(self, y_distance: float = 5) -> bool:
        """
        Check if the player can jump.

        Args:
            y_distance: Distance to check below player for ground

        Returns:
            True if player can jump, False otherwise
        """
    
    def is_on_ladder(self) -> bool:
        """
        Check if the player is currently on a ladder.

        Returns:
            True if player is on a ladder, False otherwise
        """
    
    def add_sprite(self, sprite: arcade.Sprite, sprite_type: str) -> None:
        """
        Add a sprite to the physics engine.

        Args:
            sprite: Sprite to add
            sprite_type: Type of sprite ("platform", "wall", or "ladder")
        """
    
    def remove_sprite(self, sprite: arcade.Sprite) -> None:
        """
        Remove a sprite from all physics lists.

        Args:
            sprite: Sprite to remove
        """

Pymunk Physics Engine

Advanced 2D physics simulation using the Pymunk library for realistic physics behavior.

class PymunkPhysicsEngine:
    """
    Advanced 2D physics engine using Pymunk for realistic physics simulation
    including rigid body dynamics, joints, constraints, and collision callbacks.
    """
    def __init__(self, gravity: tuple[float, float] = (0, -981), damping: float = 1.0):
        """
        Create a Pymunk physics engine.

        Args:
            gravity: Gravity vector (x, y) in pixels per second squared
            damping: Global damping factor (1.0 = no damping, lower = more damping)
        """
    
    gravity: tuple[float, float]
    damping: float
    space: object  # pymunk.Space
    
    def add_sprite(self, sprite: arcade.Sprite, mass: float = 1, 
                   moment: float = None, body_type: int = None,
                   collision_type: str = "default", 
                   friction: float = 0.2, elasticity: float = 0.0) -> None:
        """
        Add a sprite to the physics simulation.

        Args:
            sprite: Sprite to add physics body to
            mass: Mass of the body (affects physics interactions)
            moment: Moment of inertia (None = auto-calculate from shape)
            body_type: Pymunk body type (DYNAMIC, KINEMATIC, STATIC)
            collision_type: String identifier for collision callbacks
            friction: Surface friction (0.0 = no friction, 1.0+ = high friction)
            elasticity: Bounciness (0.0 = no bounce, 1.0 = perfect bounce)
        """
    
    def add_sprite_list(self, sprite_list: arcade.SpriteList, mass: float = 1,
                        moment: float = None, body_type: int = None,
                        collision_type: str = "default",
                        friction: float = 0.2, elasticity: float = 0.0) -> None:
        """
        Add all sprites in a sprite list to physics simulation.

        Args:
            sprite_list: SpriteList to add
            mass: Mass for each sprite
            moment: Moment of inertia for each sprite
            body_type: Pymunk body type for each sprite
            collision_type: Collision type for each sprite
            friction: Friction for each sprite
            elasticity: Elasticity for each sprite
        """
    
    def remove_sprite(self, sprite: arcade.Sprite) -> None:
        """
        Remove a sprite from the physics simulation.

        Args:
            sprite: Sprite to remove
        """
    
    def add_collision_handler(self, first_type: str, second_type: str,
                             begin_handler: callable = None,
                             pre_solve_handler: callable = None,
                             post_solve_handler: callable = None,
                             separate_handler: callable = None) -> None:
        """
        Add collision event handlers for specific collision types.

        Args:
            first_type: First collision type identifier
            second_type: Second collision type identifier
            begin_handler: Called when collision begins
            pre_solve_handler: Called before collision resolution
            post_solve_handler: Called after collision resolution
            separate_handler: Called when objects separate
        """
    
    def step(self, delta_time: float = 1/60, resync_sprites: bool = True) -> None:
        """
        Step the physics simulation forward in time.

        Args:
            delta_time: Time step in seconds
            resync_sprites: Whether to update sprite positions from physics bodies
        """
    
    def resync_sprites(self) -> None:
        """Update all sprite positions and rotations from their physics bodies."""
    
    def apply_force(self, sprite: arcade.Sprite, force: tuple[float, float], 
                   point: tuple[float, float] = None) -> None:
        """
        Apply a force to a sprite's physics body.

        Args:
            sprite: Target sprite
            force: Force vector (x, y) in Newtons
            point: Point to apply force at (None = center of mass)
        """
    
    def apply_impulse(self, sprite: arcade.Sprite, impulse: tuple[float, float],
                     point: tuple[float, float] = None) -> None:
        """
        Apply an impulse to a sprite's physics body.

        Args:
            sprite: Target sprite
            impulse: Impulse vector (x, y) in Newton-seconds
            point: Point to apply impulse at (None = center of mass)
        """
    
    def set_velocity(self, sprite: arcade.Sprite, velocity: tuple[float, float]) -> None:
        """
        Set the velocity of a sprite's physics body.

        Args:
            sprite: Target sprite
            velocity: Velocity vector (x, y) in pixels per second
        """
    
    def get_velocity(self, sprite: arcade.Sprite) -> tuple[float, float]:
        """
        Get the velocity of a sprite's physics body.

        Args:
            sprite: Target sprite

        Returns:
            Velocity vector (x, y) in pixels per second
        """
    
    def set_position(self, sprite: arcade.Sprite, position: tuple[float, float]) -> None:
        """
        Set the position of a sprite's physics body.

        Args:
            sprite: Target sprite
            position: Position (x, y) in pixels
        """
    
    def get_sprites_from_space(self) -> list[arcade.Sprite]:
        """
        Get all sprites currently in the physics space.

        Returns:
            List of sprites with physics bodies
        """

class PymunkPhysicsObject:
    """
    Helper class for creating physics objects with specific properties.
    """
    def __init__(self, shape: object, body: object):
        """
        Create a physics object.

        Args:
            shape: Pymunk shape object
            body: Pymunk body object
        """
    
    shape: object  # pymunk.Shape
    body: object   # pymunk.Body

class PymunkException(Exception):
    """Exception raised for Pymunk physics errors."""
    pass

Physics Engine Utilities

Helper functions and constants for physics engine setup and configuration.

# Pymunk body type constants
DYNAMIC: int = 0    # Objects affected by forces and collisions
KINEMATIC: int = 1  # Objects with infinite mass, position controlled manually
STATIC: int = 2     # Objects with infinite mass that never move

# Common physics materials
class PhysicsMaterial:
    """Predefined physics materials with realistic properties."""
    
    # Material properties: (friction, elasticity)
    STEEL: tuple[float, float] = (0.7, 0.2)
    ICE: tuple[float, float] = (0.03, 0.1)
    RUBBER: tuple[float, float] = (1.0, 0.9)
    WOOD: tuple[float, float] = (0.4, 0.3)
    CONCRETE: tuple[float, float] = (0.8, 0.1)
    GLASS: tuple[float, float] = (0.2, 0.8)

Usage Examples

Simple Physics Engine Example

import arcade

class SimplePhysicsGame(arcade.Window):
    def __init__(self):
        super().__init__(800, 600, "Simple Physics")
        
        self.player_list = None
        self.wall_list = None
        self.physics_engine = None
        
    def setup(self):
        # Create sprite lists
        self.player_list = arcade.SpriteList()
        self.wall_list = arcade.SpriteList()
        
        # Create player
        self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png", 0.5)
        self.player_sprite.center_x = 64
        self.player_sprite.center_y = 96
        self.player_list.append(self.player_sprite)
        
        # Create walls
        for x in range(0, 800, 64):
            wall = arcade.Sprite(":resources:images/tiles/grassMid.png")
            wall.center_x = x
            wall.center_y = 32
            self.wall_list.append(wall)
        
        # Create physics engine
        self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)
    
    def on_draw(self):
        self.clear()
        self.wall_list.draw()
        self.player_list.draw()
    
    def on_update(self, delta_time):
        self.physics_engine.update()
    
    def on_key_press(self, key, modifiers):
        if key == arcade.key.UP:
            self.player_sprite.change_y = 5
        elif key == arcade.key.DOWN:
            self.player_sprite.change_y = -5
        elif key == arcade.key.LEFT:
            self.player_sprite.change_x = -5
        elif key == arcade.key.RIGHT:
            self.player_sprite.change_x = 5
    
    def on_key_release(self, key, modifiers):
        if key in (arcade.key.UP, arcade.key.DOWN):
            self.player_sprite.change_y = 0
        elif key in (arcade.key.LEFT, arcade.key.RIGHT):
            self.player_sprite.change_x = 0

def main():
    game = SimplePhysicsGame()
    game.setup()
    arcade.run()

if __name__ == "__main__":
    main()

Platformer Physics Engine Example

import arcade

class PlatformerGame(arcade.Window):
    def __init__(self):
        super().__init__(1000, 650, "Platformer Physics")
        
        self.player_list = None
        self.platform_list = None
        self.ladder_list = None
        self.physics_engine = None
        
        # Player movement constants
        self.PLAYER_MOVEMENT_SPEED = 10
        self.GRAVITY = 1
        self.PLAYER_JUMP_SPEED = 20
        
    def setup(self):
        # Create sprite lists
        self.player_list = arcade.SpriteList()
        self.platform_list = arcade.SpriteList()
        self.ladder_list = arcade.SpriteList()
        
        # Create player
        self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png", 0.4)
        self.player_sprite.center_x = 64
        self.player_sprite.center_y = 128
        self.player_list.append(self.player_sprite)
        
        # Create platforms
        for x in range(0, 1000, 64):
            platform = arcade.Sprite(":resources:images/tiles/grassMid.png")
            platform.center_x = x
            platform.center_y = 32
            self.platform_list.append(platform)
        
        # Create floating platforms
        for x in range(200, 600, 200):
            platform = arcade.Sprite(":resources:images/tiles/grassMid.png")
            platform.center_x = x
            platform.center_y = 200
            self.platform_list.append(platform)
        
        # Create ladder
        for y in range(100, 300, 64):
            ladder = arcade.Sprite(":resources:images/tiles/ladderMid.png")
            ladder.center_x = 500
            ladder.center_y = y
            self.ladder_list.append(ladder)
        
        # Create physics engine with gravity
        self.physics_engine = arcade.PhysicsEnginePlatformer(
            self.player_sprite, 
            platforms=self.platform_list,
            gravity_constant=self.GRAVITY,
            ladders=self.ladder_list
        )
        
        # Enable double jumping
        self.physics_engine.enable_multi_jump(2)
    
    def on_draw(self):
        self.clear()
        self.platform_list.draw()
        self.ladder_list.draw()
        self.player_list.draw()
        
        # Draw debug info
        on_ladder = self.physics_engine.is_on_ladder()
        can_jump = self.physics_engine.can_jump()
        arcade.draw_text(f"On ladder: {on_ladder}", 10, 580, arcade.color.WHITE, 16)
        arcade.draw_text(f"Can jump: {can_jump}", 10, 560, arcade.color.WHITE, 16)
    
    def on_update(self, delta_time):
        self.physics_engine.update()
        
        # Scroll camera to follow player
        self.center_camera_to_player()
    
    def on_key_press(self, key, modifiers):
        if key == arcade.key.UP:
            # Jump if on ground, or climb ladder
            if self.physics_engine.is_on_ladder():
                self.player_sprite.change_y = self.PLAYER_MOVEMENT_SPEED
            elif self.physics_engine.can_jump():
                self.player_sprite.change_y = self.PLAYER_JUMP_SPEED
                self.physics_engine.increment_jump_count()
        
        elif key == arcade.key.DOWN:
            if self.physics_engine.is_on_ladder():
                self.player_sprite.change_y = -self.PLAYER_MOVEMENT_SPEED
        
        elif key == arcade.key.LEFT:
            self.player_sprite.change_x = -self.PLAYER_MOVEMENT_SPEED
        
        elif key == arcade.key.RIGHT:
            self.player_sprite.change_x = self.PLAYER_MOVEMENT_SPEED
    
    def on_key_release(self, key, modifiers):
        if key in (arcade.key.UP, arcade.key.DOWN):
            if self.physics_engine.is_on_ladder():
                self.player_sprite.change_y = 0
        elif key in (arcade.key.LEFT, arcade.key.RIGHT):
            self.player_sprite.change_x = 0

def main():
    game = PlatformerGame()
    game.setup()
    arcade.run()

if __name__ == "__main__":
    main()

Pymunk Physics Engine Example

import arcade

class PymunkExample(arcade.Window):
    def __init__(self):
        super().__init__(800, 600, "Pymunk Physics")
        
        self.sprite_list = None
        self.physics_engine = None
        
    def setup(self):
        self.sprite_list = arcade.SpriteList()
        
        # Create Pymunk physics engine with gravity
        self.physics_engine = arcade.PymunkPhysicsEngine(gravity=(0, -900))
        
        # Create ground
        for x in range(0, 800, 64):
            sprite = arcade.SpriteSolidColor(64, 32, arcade.color.BROWN)
            sprite.center_x = x
            sprite.center_y = 16
            self.sprite_list.append(sprite)
            
            # Add to physics as static (non-moving) body
            self.physics_engine.add_sprite(sprite, body_type=arcade.STATIC, friction=0.6)
        
        # Create dynamic boxes that fall
        for i in range(5):
            sprite = arcade.SpriteSolidColor(32, 32, arcade.color.RED)
            sprite.center_x = 100 + i * 100
            sprite.center_y = 300
            self.sprite_list.append(sprite)
            
            # Add as dynamic body with physics properties
            self.physics_engine.add_sprite(sprite, 
                                         mass=1.0,
                                         friction=0.4,
                                         elasticity=0.3,
                                         collision_type="box")
        
        # Create a ball
        ball = arcade.SpriteCircle(20, arcade.color.BLUE)
        ball.center_x = 400
        ball.center_y = 400
        self.sprite_list.append(ball)
        
        # Add ball with high bounce
        self.physics_engine.add_sprite(ball,
                                     mass=0.5,
                                     friction=0.3,
                                     elasticity=0.8,
                                     collision_type="ball")
        
        # Add collision handler
        def handle_collision(arbiter, space, data):
            print("Box and ball collided!")
            return True
        
        self.physics_engine.add_collision_handler("box", "ball", begin_handler=handle_collision)
    
    def on_draw(self):
        self.clear()
        self.sprite_list.draw()
        
        # Draw physics debug info
        arcade.draw_text("Click to add falling boxes", 10, 570, arcade.color.WHITE, 16)
    
    def on_update(self, delta_time):
        # Step physics simulation
        self.physics_engine.step(delta_time)
    
    def on_mouse_press(self, x, y, button, modifiers):
        # Add a new falling box at mouse position
        sprite = arcade.SpriteSolidColor(32, 32, arcade.color.GREEN)
        sprite.center_x = x
        sprite.center_y = y
        self.sprite_list.append(sprite)
        
        # Add random velocity for fun
        self.physics_engine.add_sprite(sprite, mass=1.0, friction=0.4, elasticity=0.2)
        self.physics_engine.set_velocity(sprite, (0, 100))

def main():
    game = PymunkExample()
    game.setup()
    arcade.run()

if __name__ == "__main__":
    main()

Install with Tessl CLI

npx tessl i tessl/pypi-arcade

docs

camera-system.md

core-graphics.md

gui-framework.md

index.md

math-utilities.md

physics-engines.md

sound-system.md

specialized-features.md

sprite-system.md

texture-management.md

window-management.md

tile.json