Bouncing Balls¶
Properties, velocity, and screen-edge collision.
▶ Run in browserTags: getting_started
Spawn colourful balls that bounce off screen edges. Demonstrates the
Property descriptor for editor-visible values and basic frame-by-frame
movement with on_process().
What You Will Learn¶
Property – Declare editor-visible properties with validation ranges
on_process(dt) – Per-frame update callback with delta time
position – Move nodes by updating
self.positioneach frameadd_child() – Build a scene tree dynamically in
on_ready()draw_circle() – Render filled circles
How It Works¶
Ball declares radius and speed as Property descriptors with value
ranges. In __init__, a random velocity vector is created. Each frame,
on_process(dt) advances the ball’s position and reflects velocity when the
ball hits a screen edge.
BouncingBalls is the root node that spawns 8 Ball children in on_ready(),
placing them at random positions. The parent also draws a title and ball
count via draw_text().
Source¶
1#!/usr/bin/env python3
2"""Bouncing Balls: Properties, velocity, and screen-edge collision.
3
4Spawn colourful balls that bounce off screen edges. Demonstrates the
5`Property` descriptor for editor-visible values and basic frame-by-frame
6movement with `on_process()`.
7
8# /// simvx
9# web = { root = "BouncingBalls" }
10# ///
11
12## What You Will Learn
13
14- **Property** -- Declare editor-visible properties with validation ranges
15- **on_process(dt)** -- Per-frame update callback with delta time
16- **position** -- Move nodes by updating `self.position` each frame
17- **add_child()** -- Build a scene tree dynamically in `on_ready()`
18- **draw_circle()** -- Render filled circles
19
20## How It Works
21
22`Ball` declares `radius` and `speed` as `Property` descriptors with value
23ranges. In `__init__`, a random velocity vector is created. Each frame,
24`on_process(dt)` advances the ball's position and reflects velocity when the
25ball hits a screen edge.
26
27`BouncingBalls` is the root node that spawns 8 `Ball` children in `on_ready()`,
28placing them at random positions. The parent also draws a title and ball
29count via `draw_text()`.
30"""
31
32import math
33import random
34
35from simvx.core import Node2D, Property, Vec2
36from simvx.graphics import App
37
38WIDTH, HEIGHT = 800, 600
39
40
41class Ball(Node2D):
42 radius = Property(12.0, range=(4, 40))
43 speed = Property(200.0, range=(50, 500))
44
45 def __init__(self, **kwargs):
46 super().__init__(**kwargs)
47 angle = random.uniform(0, math.tau)
48 self.velocity = Vec2(math.cos(angle), math.sin(angle)) * self.speed
49 self.colour = (
50 random.uniform(0.4, 1.0),
51 random.uniform(0.4, 1.0),
52 random.uniform(0.4, 1.0),
53 1.0,
54 )
55
56 def on_process(self, dt: float):
57 self.position += self.velocity * dt
58
59 # Bounce off walls
60 if self.position.x < self.radius or self.position.x > WIDTH - self.radius:
61 self.velocity.x *= -1
62 self.position.x = max(self.radius, min(WIDTH - self.radius, self.position.x))
63 if self.position.y < self.radius or self.position.y > HEIGHT - self.radius:
64 self.velocity.y *= -1
65 self.position.y = max(self.radius, min(HEIGHT - self.radius, self.position.y))
66
67 def on_draw(self, renderer):
68 renderer.draw_circle(self.position, self.radius, colour=self.colour)
69
70
71class BouncingBalls(Node2D):
72 def on_ready(self):
73 for i in range(8):
74 self.add_child(
75 Ball(
76 name=f"Ball{i}",
77 position=Vec2(random.uniform(50, WIDTH - 50), random.uniform(50, HEIGHT - 50)),
78 )
79 )
80
81 def on_draw(self, renderer):
82 renderer.draw_text("Bouncing Balls", (10, 10), scale=2, colour=(1.0, 1.0, 1.0))
83 renderer.draw_text(f"{len(self.children)} balls", (10, 35), scale=1, colour=(0.71, 0.71, 0.71))
84
85
86if __name__ == "__main__":
87 App(title="Bouncing Balls", width=WIDTH, height=HEIGHT).run(BouncingBalls())