or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

camera-system.mdcore-graphics.mdgui-framework.mdindex.mdmath-utilities.mdphysics-engines.mdsound-system.mdspecialized-features.mdsprite-system.mdtexture-management.mdwindow-management.md

physics-engines.mddocs/

0

# Physics Engines

1

2

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

3

4

## Capabilities

5

6

### Simple Physics Engine

7

8

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

9

10

```python { .api }

11

class PhysicsEngineSimple:

12

"""

13

Simple physics engine using AABB collision detection.

14

Prevents sprites from moving through solid walls and barriers.

15

"""

16

def __init__(self, player_sprite: arcade.Sprite, walls: arcade.SpriteList):

17

"""

18

Create a simple physics engine.

19

20

Args:

21

player_sprite: The sprite that will be moved and checked for collisions

22

walls: SpriteList containing solid obstacles that block movement

23

"""

24

25

player_sprite: arcade.Sprite

26

walls: arcade.SpriteList

27

28

def update(self) -> None:

29

"""

30

Update physics simulation. Call this each frame to apply movement and handle collisions.

31

Moves the player sprite based on its change_x and change_y values, then checks

32

for collisions with walls and prevents movement through them.

33

"""

34

35

def add_sprite(self, sprite: arcade.Sprite) -> None:

36

"""

37

Add a wall sprite to the collision list.

38

39

Args:

40

sprite: Sprite to add as a collidable wall

41

"""

42

43

def remove_sprite(self, sprite: arcade.Sprite) -> None:

44

"""

45

Remove a wall sprite from the collision list.

46

47

Args:

48

sprite: Sprite to remove from walls

49

"""

50

```

51

52

### Platformer Physics Engine

53

54

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

55

56

```python { .api }

57

class PhysicsEnginePlatformer:

58

"""

59

Physics engine designed for 2D platformer games with gravity, jumping mechanics,

60

ladder climbing, and moving platform support.

61

"""

62

def __init__(self, player_sprite: arcade.Sprite, platforms: arcade.SpriteList = None,

63

gravity_constant: float = 0.5, ladders: arcade.SpriteList = None,

64

walls: arcade.SpriteList = None):

65

"""

66

Create a platformer physics engine.

67

68

Args:

69

player_sprite: The player character sprite

70

platforms: SpriteList of platforms the player can stand on

71

gravity_constant: Strength of gravity (pixels per frame squared)

72

ladders: SpriteList of ladder sprites the player can climb

73

walls: SpriteList of wall sprites that block horizontal movement

74

"""

75

76

player_sprite: arcade.Sprite

77

platforms: arcade.SpriteList

78

ladders: arcade.SpriteList

79

walls: arcade.SpriteList

80

gravity_constant: float

81

82

def update(self) -> None:

83

"""

84

Update physics simulation.

85

Applies gravity, handles jumping, processes collisions with platforms/walls/ladders,

86

and manages platformer-specific movement mechanics.

87

"""

88

89

def enable_multi_jump(self, allowed_jumps: int) -> None:

90

"""

91

Enable multi-jumping (double jump, triple jump, etc.).

92

93

Args:

94

allowed_jumps: Total number of jumps allowed (including initial jump)

95

"""

96

97

def disable_multi_jump(self) -> None:

98

"""Disable multi-jumping, allowing only single jumps."""

99

100

def increment_jump_count(self) -> None:

101

"""Increment the current jump count (call when player jumps)."""

102

103

def can_jump(self, y_distance: float = 5) -> bool:

104

"""

105

Check if the player can jump.

106

107

Args:

108

y_distance: Distance to check below player for ground

109

110

Returns:

111

True if player can jump, False otherwise

112

"""

113

114

def is_on_ladder(self) -> bool:

115

"""

116

Check if the player is currently on a ladder.

117

118

Returns:

119

True if player is on a ladder, False otherwise

120

"""

121

122

def add_sprite(self, sprite: arcade.Sprite, sprite_type: str) -> None:

123

"""

124

Add a sprite to the physics engine.

125

126

Args:

127

sprite: Sprite to add

128

sprite_type: Type of sprite ("platform", "wall", or "ladder")

129

"""

130

131

def remove_sprite(self, sprite: arcade.Sprite) -> None:

132

"""

133

Remove a sprite from all physics lists.

134

135

Args:

136

sprite: Sprite to remove

137

"""

138

```

139

140

### Pymunk Physics Engine

141

142

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

143

144

```python { .api }

145

class PymunkPhysicsEngine:

146

"""

147

Advanced 2D physics engine using Pymunk for realistic physics simulation

148

including rigid body dynamics, joints, constraints, and collision callbacks.

149

"""

150

def __init__(self, gravity: tuple[float, float] = (0, -981), damping: float = 1.0):

151

"""

152

Create a Pymunk physics engine.

153

154

Args:

155

gravity: Gravity vector (x, y) in pixels per second squared

156

damping: Global damping factor (1.0 = no damping, lower = more damping)

157

"""

158

159

gravity: tuple[float, float]

160

damping: float

161

space: object # pymunk.Space

162

163

def add_sprite(self, sprite: arcade.Sprite, mass: float = 1,

164

moment: float = None, body_type: int = None,

165

collision_type: str = "default",

166

friction: float = 0.2, elasticity: float = 0.0) -> None:

167

"""

168

Add a sprite to the physics simulation.

169

170

Args:

171

sprite: Sprite to add physics body to

172

mass: Mass of the body (affects physics interactions)

173

moment: Moment of inertia (None = auto-calculate from shape)

174

body_type: Pymunk body type (DYNAMIC, KINEMATIC, STATIC)

175

collision_type: String identifier for collision callbacks

176

friction: Surface friction (0.0 = no friction, 1.0+ = high friction)

177

elasticity: Bounciness (0.0 = no bounce, 1.0 = perfect bounce)

178

"""

179

180

def add_sprite_list(self, sprite_list: arcade.SpriteList, mass: float = 1,

181

moment: float = None, body_type: int = None,

182

collision_type: str = "default",

183

friction: float = 0.2, elasticity: float = 0.0) -> None:

184

"""

185

Add all sprites in a sprite list to physics simulation.

186

187

Args:

188

sprite_list: SpriteList to add

189

mass: Mass for each sprite

190

moment: Moment of inertia for each sprite

191

body_type: Pymunk body type for each sprite

192

collision_type: Collision type for each sprite

193

friction: Friction for each sprite

194

elasticity: Elasticity for each sprite

195

"""

196

197

def remove_sprite(self, sprite: arcade.Sprite) -> None:

198

"""

199

Remove a sprite from the physics simulation.

200

201

Args:

202

sprite: Sprite to remove

203

"""

204

205

def add_collision_handler(self, first_type: str, second_type: str,

206

begin_handler: callable = None,

207

pre_solve_handler: callable = None,

208

post_solve_handler: callable = None,

209

separate_handler: callable = None) -> None:

210

"""

211

Add collision event handlers for specific collision types.

212

213

Args:

214

first_type: First collision type identifier

215

second_type: Second collision type identifier

216

begin_handler: Called when collision begins

217

pre_solve_handler: Called before collision resolution

218

post_solve_handler: Called after collision resolution

219

separate_handler: Called when objects separate

220

"""

221

222

def step(self, delta_time: float = 1/60, resync_sprites: bool = True) -> None:

223

"""

224

Step the physics simulation forward in time.

225

226

Args:

227

delta_time: Time step in seconds

228

resync_sprites: Whether to update sprite positions from physics bodies

229

"""

230

231

def resync_sprites(self) -> None:

232

"""Update all sprite positions and rotations from their physics bodies."""

233

234

def apply_force(self, sprite: arcade.Sprite, force: tuple[float, float],

235

point: tuple[float, float] = None) -> None:

236

"""

237

Apply a force to a sprite's physics body.

238

239

Args:

240

sprite: Target sprite

241

force: Force vector (x, y) in Newtons

242

point: Point to apply force at (None = center of mass)

243

"""

244

245

def apply_impulse(self, sprite: arcade.Sprite, impulse: tuple[float, float],

246

point: tuple[float, float] = None) -> None:

247

"""

248

Apply an impulse to a sprite's physics body.

249

250

Args:

251

sprite: Target sprite

252

impulse: Impulse vector (x, y) in Newton-seconds

253

point: Point to apply impulse at (None = center of mass)

254

"""

255

256

def set_velocity(self, sprite: arcade.Sprite, velocity: tuple[float, float]) -> None:

257

"""

258

Set the velocity of a sprite's physics body.

259

260

Args:

261

sprite: Target sprite

262

velocity: Velocity vector (x, y) in pixels per second

263

"""

264

265

def get_velocity(self, sprite: arcade.Sprite) -> tuple[float, float]:

266

"""

267

Get the velocity of a sprite's physics body.

268

269

Args:

270

sprite: Target sprite

271

272

Returns:

273

Velocity vector (x, y) in pixels per second

274

"""

275

276

def set_position(self, sprite: arcade.Sprite, position: tuple[float, float]) -> None:

277

"""

278

Set the position of a sprite's physics body.

279

280

Args:

281

sprite: Target sprite

282

position: Position (x, y) in pixels

283

"""

284

285

def get_sprites_from_space(self) -> list[arcade.Sprite]:

286

"""

287

Get all sprites currently in the physics space.

288

289

Returns:

290

List of sprites with physics bodies

291

"""

292

293

class PymunkPhysicsObject:

294

"""

295

Helper class for creating physics objects with specific properties.

296

"""

297

def __init__(self, shape: object, body: object):

298

"""

299

Create a physics object.

300

301

Args:

302

shape: Pymunk shape object

303

body: Pymunk body object

304

"""

305

306

shape: object # pymunk.Shape

307

body: object # pymunk.Body

308

309

class PymunkException(Exception):

310

"""Exception raised for Pymunk physics errors."""

311

pass

312

```

313

314

### Physics Engine Utilities

315

316

Helper functions and constants for physics engine setup and configuration.

317

318

```python { .api }

319

# Pymunk body type constants

320

DYNAMIC: int = 0 # Objects affected by forces and collisions

321

KINEMATIC: int = 1 # Objects with infinite mass, position controlled manually

322

STATIC: int = 2 # Objects with infinite mass that never move

323

324

# Common physics materials

325

class PhysicsMaterial:

326

"""Predefined physics materials with realistic properties."""

327

328

# Material properties: (friction, elasticity)

329

STEEL: tuple[float, float] = (0.7, 0.2)

330

ICE: tuple[float, float] = (0.03, 0.1)

331

RUBBER: tuple[float, float] = (1.0, 0.9)

332

WOOD: tuple[float, float] = (0.4, 0.3)

333

CONCRETE: tuple[float, float] = (0.8, 0.1)

334

GLASS: tuple[float, float] = (0.2, 0.8)

335

```

336

337

## Usage Examples

338

339

### Simple Physics Engine Example

340

341

```python

342

import arcade

343

344

class SimplePhysicsGame(arcade.Window):

345

def __init__(self):

346

super().__init__(800, 600, "Simple Physics")

347

348

self.player_list = None

349

self.wall_list = None

350

self.physics_engine = None

351

352

def setup(self):

353

# Create sprite lists

354

self.player_list = arcade.SpriteList()

355

self.wall_list = arcade.SpriteList()

356

357

# Create player

358

self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png", 0.5)

359

self.player_sprite.center_x = 64

360

self.player_sprite.center_y = 96

361

self.player_list.append(self.player_sprite)

362

363

# Create walls

364

for x in range(0, 800, 64):

365

wall = arcade.Sprite(":resources:images/tiles/grassMid.png")

366

wall.center_x = x

367

wall.center_y = 32

368

self.wall_list.append(wall)

369

370

# Create physics engine

371

self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)

372

373

def on_draw(self):

374

self.clear()

375

self.wall_list.draw()

376

self.player_list.draw()

377

378

def on_update(self, delta_time):

379

self.physics_engine.update()

380

381

def on_key_press(self, key, modifiers):

382

if key == arcade.key.UP:

383

self.player_sprite.change_y = 5

384

elif key == arcade.key.DOWN:

385

self.player_sprite.change_y = -5

386

elif key == arcade.key.LEFT:

387

self.player_sprite.change_x = -5

388

elif key == arcade.key.RIGHT:

389

self.player_sprite.change_x = 5

390

391

def on_key_release(self, key, modifiers):

392

if key in (arcade.key.UP, arcade.key.DOWN):

393

self.player_sprite.change_y = 0

394

elif key in (arcade.key.LEFT, arcade.key.RIGHT):

395

self.player_sprite.change_x = 0

396

397

def main():

398

game = SimplePhysicsGame()

399

game.setup()

400

arcade.run()

401

402

if __name__ == "__main__":

403

main()

404

```

405

406

### Platformer Physics Engine Example

407

408

```python

409

import arcade

410

411

class PlatformerGame(arcade.Window):

412

def __init__(self):

413

super().__init__(1000, 650, "Platformer Physics")

414

415

self.player_list = None

416

self.platform_list = None

417

self.ladder_list = None

418

self.physics_engine = None

419

420

# Player movement constants

421

self.PLAYER_MOVEMENT_SPEED = 10

422

self.GRAVITY = 1

423

self.PLAYER_JUMP_SPEED = 20

424

425

def setup(self):

426

# Create sprite lists

427

self.player_list = arcade.SpriteList()

428

self.platform_list = arcade.SpriteList()

429

self.ladder_list = arcade.SpriteList()

430

431

# Create player

432

self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png", 0.4)

433

self.player_sprite.center_x = 64

434

self.player_sprite.center_y = 128

435

self.player_list.append(self.player_sprite)

436

437

# Create platforms

438

for x in range(0, 1000, 64):

439

platform = arcade.Sprite(":resources:images/tiles/grassMid.png")

440

platform.center_x = x

441

platform.center_y = 32

442

self.platform_list.append(platform)

443

444

# Create floating platforms

445

for x in range(200, 600, 200):

446

platform = arcade.Sprite(":resources:images/tiles/grassMid.png")

447

platform.center_x = x

448

platform.center_y = 200

449

self.platform_list.append(platform)

450

451

# Create ladder

452

for y in range(100, 300, 64):

453

ladder = arcade.Sprite(":resources:images/tiles/ladderMid.png")

454

ladder.center_x = 500

455

ladder.center_y = y

456

self.ladder_list.append(ladder)

457

458

# Create physics engine with gravity

459

self.physics_engine = arcade.PhysicsEnginePlatformer(

460

self.player_sprite,

461

platforms=self.platform_list,

462

gravity_constant=self.GRAVITY,

463

ladders=self.ladder_list

464

)

465

466

# Enable double jumping

467

self.physics_engine.enable_multi_jump(2)

468

469

def on_draw(self):

470

self.clear()

471

self.platform_list.draw()

472

self.ladder_list.draw()

473

self.player_list.draw()

474

475

# Draw debug info

476

on_ladder = self.physics_engine.is_on_ladder()

477

can_jump = self.physics_engine.can_jump()

478

arcade.draw_text(f"On ladder: {on_ladder}", 10, 580, arcade.color.WHITE, 16)

479

arcade.draw_text(f"Can jump: {can_jump}", 10, 560, arcade.color.WHITE, 16)

480

481

def on_update(self, delta_time):

482

self.physics_engine.update()

483

484

# Scroll camera to follow player

485

self.center_camera_to_player()

486

487

def on_key_press(self, key, modifiers):

488

if key == arcade.key.UP:

489

# Jump if on ground, or climb ladder

490

if self.physics_engine.is_on_ladder():

491

self.player_sprite.change_y = self.PLAYER_MOVEMENT_SPEED

492

elif self.physics_engine.can_jump():

493

self.player_sprite.change_y = self.PLAYER_JUMP_SPEED

494

self.physics_engine.increment_jump_count()

495

496

elif key == arcade.key.DOWN:

497

if self.physics_engine.is_on_ladder():

498

self.player_sprite.change_y = -self.PLAYER_MOVEMENT_SPEED

499

500

elif key == arcade.key.LEFT:

501

self.player_sprite.change_x = -self.PLAYER_MOVEMENT_SPEED

502

503

elif key == arcade.key.RIGHT:

504

self.player_sprite.change_x = self.PLAYER_MOVEMENT_SPEED

505

506

def on_key_release(self, key, modifiers):

507

if key in (arcade.key.UP, arcade.key.DOWN):

508

if self.physics_engine.is_on_ladder():

509

self.player_sprite.change_y = 0

510

elif key in (arcade.key.LEFT, arcade.key.RIGHT):

511

self.player_sprite.change_x = 0

512

513

def main():

514

game = PlatformerGame()

515

game.setup()

516

arcade.run()

517

518

if __name__ == "__main__":

519

main()

520

```

521

522

### Pymunk Physics Engine Example

523

524

```python

525

import arcade

526

527

class PymunkExample(arcade.Window):

528

def __init__(self):

529

super().__init__(800, 600, "Pymunk Physics")

530

531

self.sprite_list = None

532

self.physics_engine = None

533

534

def setup(self):

535

self.sprite_list = arcade.SpriteList()

536

537

# Create Pymunk physics engine with gravity

538

self.physics_engine = arcade.PymunkPhysicsEngine(gravity=(0, -900))

539

540

# Create ground

541

for x in range(0, 800, 64):

542

sprite = arcade.SpriteSolidColor(64, 32, arcade.color.BROWN)

543

sprite.center_x = x

544

sprite.center_y = 16

545

self.sprite_list.append(sprite)

546

547

# Add to physics as static (non-moving) body

548

self.physics_engine.add_sprite(sprite, body_type=arcade.STATIC, friction=0.6)

549

550

# Create dynamic boxes that fall

551

for i in range(5):

552

sprite = arcade.SpriteSolidColor(32, 32, arcade.color.RED)

553

sprite.center_x = 100 + i * 100

554

sprite.center_y = 300

555

self.sprite_list.append(sprite)

556

557

# Add as dynamic body with physics properties

558

self.physics_engine.add_sprite(sprite,

559

mass=1.0,

560

friction=0.4,

561

elasticity=0.3,

562

collision_type="box")

563

564

# Create a ball

565

ball = arcade.SpriteCircle(20, arcade.color.BLUE)

566

ball.center_x = 400

567

ball.center_y = 400

568

self.sprite_list.append(ball)

569

570

# Add ball with high bounce

571

self.physics_engine.add_sprite(ball,

572

mass=0.5,

573

friction=0.3,

574

elasticity=0.8,

575

collision_type="ball")

576

577

# Add collision handler

578

def handle_collision(arbiter, space, data):

579

print("Box and ball collided!")

580

return True

581

582

self.physics_engine.add_collision_handler("box", "ball", begin_handler=handle_collision)

583

584

def on_draw(self):

585

self.clear()

586

self.sprite_list.draw()

587

588

# Draw physics debug info

589

arcade.draw_text("Click to add falling boxes", 10, 570, arcade.color.WHITE, 16)

590

591

def on_update(self, delta_time):

592

# Step physics simulation

593

self.physics_engine.step(delta_time)

594

595

def on_mouse_press(self, x, y, button, modifiers):

596

# Add a new falling box at mouse position

597

sprite = arcade.SpriteSolidColor(32, 32, arcade.color.GREEN)

598

sprite.center_x = x

599

sprite.center_y = y

600

self.sprite_list.append(sprite)

601

602

# Add random velocity for fun

603

self.physics_engine.add_sprite(sprite, mass=1.0, friction=0.4, elasticity=0.2)

604

self.physics_engine.set_velocity(sprite, (0, 100))

605

606

def main():

607

game = PymunkExample()

608

game.setup()

609

arcade.run()

610

611

if __name__ == "__main__":

612

main()

613

```