Platformer¶
CharacterBody2D with gravity, jump, and platforms.
▶ Run in browserTags: game character-body gravity input-actions
Demonstrates: CharacterBody2D, CollisionShape2D, move_and_slide, is_on_floor, gravity, input actions, platform collision.
Controls: A/D or Left/Right = move, Space = jump.
Source¶
1#!/usr/bin/env python3
2"""Platformer: CharacterBody2D with gravity, jump, and platforms.
3
4# /// simvx
5# tags = ["game", "character-body", "gravity", "input-actions"]
6# web = { root = "PlatformerDemo" }
7# ///
8
9Demonstrates: CharacterBody2D, CollisionShape2D, move_and_slide, is_on_floor,
10 gravity, input actions, platform collision.
11
12Controls: A/D or Left/Right = move, Space = jump.
13"""
14
15from simvx.core import CharacterBody2D, Input, InputMap, Key, Node2D, Property, Vec2
16from simvx.graphics import App
17
18WIDTH, HEIGHT = 800, 600
19GRAVITY = 800.0
20
21
22class Platform(CharacterBody2D):
23 """Static platform that other bodies collide with."""
24
25 w = Property(120.0, range=(20, 400))
26 h = Property(16.0, range=(8, 60))
27
28 def __init__(self, w: float = 120, h: float = 16, **kwargs):
29 super().__init__(collision=Vec2(w / 2, h / 2), **kwargs)
30 self.w = w
31 self.h = h
32
33 def on_draw(self, renderer):
34 x = self.position.x - self.w / 2
35 y = self.position.y - self.h / 2
36 renderer.draw_rect((x, y), (self.w, self.h), colour=(0.39, 0.71, 0.31, 1.0), filled=True)
37
38
39class Player(CharacterBody2D):
40 speed = Property(250.0, range=(100, 500))
41 jump_force = Property(450.0, range=(200, 800))
42
43 def __init__(self, **kwargs):
44 super().__init__(collision=12, **kwargs)
45 self.can_jump = True
46
47 def on_process(self, dt: float):
48 # Horizontal movement
49 self.velocity.x = (Input.get_strength("right") - Input.get_strength("left")) * self.speed
50
51 # Gravity
52 self.velocity.y += GRAVITY * dt
53
54 # Jump
55 if Input.is_action_just_pressed("jump") and self.is_on_floor():
56 self.velocity.y = -self.jump_force
57
58 self.move_and_slide(dt)
59
60 # Reset if fallen off screen
61 if self.position.y > HEIGHT + 50:
62 self.position = Vec2(WIDTH / 2, 100)
63 self.velocity = Vec2()
64
65 def on_draw(self, renderer):
66 renderer.draw_rect((self.position.x - 10, self.position.y - 14), (20, 28), colour=(0.24, 0.55, 1.0, 1.0), filled=True)
67 # Eyes
68 renderer.draw_rect((self.position.x - 5, self.position.y - 10), (4, 4), colour=(1.0, 1.0, 1.0, 1.0), filled=True)
69 renderer.draw_rect((self.position.x + 1, self.position.y - 10), (4, 4), colour=(1.0, 1.0, 1.0, 1.0), filled=True)
70
71
72class PlatformerDemo(Node2D):
73 def on_ready(self):
74 InputMap.add_action("left", [Key.A, Key.LEFT])
75 InputMap.add_action("right", [Key.D, Key.RIGHT])
76 InputMap.add_action("jump", [Key.SPACE])
77
78 self.add_child(Player(name="Player", position=Vec2(WIDTH / 2, 100)))
79
80 # Ground
81 self.add_child(Platform(name="Ground", w=WIDTH, h=20, position=Vec2(WIDTH / 2, HEIGHT - 10)))
82
83 # Platforms
84 platforms = [
85 (200, 450, 150),
86 (400, 350, 120),
87 (600, 450, 150),
88 (300, 250, 100),
89 (500, 250, 100),
90 (150, 150, 130),
91 (650, 150, 130),
92 ]
93 for i, (x, y, w) in enumerate(platforms):
94 self.add_child(Platform(name=f"Plat{i}", w=w, position=Vec2(x, y)))
95
96 def on_draw(self, renderer):
97 renderer.draw_text("Platformer Demo", (10, 10), scale=2, colour=(1.0, 1.0, 1.0))
98 renderer.draw_text("A/D: move SPACE: jump", (10, 35), scale=1, colour=(0.71, 0.71, 0.71))
99
100
101if __name__ == "__main__":
102 App(title="Platformer Demo", width=WIDTH, height=HEIGHT).run(PlatformerDemo())