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