Arcade Game Development Library
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Multiple physics engine options from simple AABB collision to advanced 2D physics simulation, providing realistic movement, collision detection, and physics-based gameplay mechanics.
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
"""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
"""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."""
passHelper 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)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()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()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