API cheat sheet¶
The common API on one page. Each section links to the guide that explains it. Everything below
imports from simvx.core, except App, which is in simvx.graphics.
Node lifecycle (Node system)¶
class Player(Node3D):
def on_enter_tree(self): ... # every time the node joins a tree
def on_ready(self): ... # once, after its children are ready
def on_update(self, dt): ... # every frame
def on_fixed_update(self, dt): ... # fixed timestep, 60 Hz by default
def on_input(self, event): ... # every input event that reaches the node
def on_draw(self, renderer): ... # 2D drawing, in the node's own space
def on_exit_tree(self): ...
Decorators add more handlers under any method name (Patterns):
@on_update
def animate(self, dt): ...
@on_input("jump") # an action's press
def jump(self, event):
return True # claim the event: no later handler runs
@on_input(Key.CTRL + Key.S) # a key with a modifier held
def save(self, event): ...
Properties and signals¶
class Rank(StrEnum): # from enum import StrEnum
GRUNT = "grunt"
ELITE = "elite"
BOSS = "boss"
class Enemy(Node):
speed = Property(5.0, range=(0, 20), hint="px/sec")
rank = Property(Rank.GRUNT) # a closed set; also accepts the plain string
hp = Property(100, on_change="_on_hp", persist=True)
died = Signal() # no arguments
damaged = Signal(int) # one int
hit_by = Signal(int, str) # an int and a str
def _on_hp(self): # the hook takes no arguments: read the property
if self.hp <= 0:
self.died.emit() # fire the signal
enemy = Enemy(speed=8.0, rank="elite") # Properties are constructor keywords
conn = enemy.died.connect(on_died) # returns a Connection
enemy.damaged.connect(show_damage, once=True)
conn.disconnect()
Property takes more options than these; see Node system.
The tree¶
child = self.add_child(Node2D(name="HUD")) # returns the child
self.parent # the parent node
self.tree # the SceneTree
self.app # the App, from on_enter_tree() on
self["HUD/Score"] # a descendant by path
self.find(Camera3D) # the first descendant of a type
self.find_all(Light3D) # every descendant of a type
self.find("HUD", direct=True) # a direct child by name
self.add_to_group("enemies")
self.tree.group("enemies")
self.destroy() # removed at the end of the frame
Input (Input)¶
class Game(Node):
input_context = {
"jump": [Key.SPACE, Controller.A], # bool
"throttle": [Axis1D(low=Key.S, high=Key.W), Controller.RIGHT_TRIGGER], # float
"move": [Axis2D.WASD, Axis2D.ARROWS, Controller.LEFT_STICK], # Vec2
}
Input.value("move") # the action's typed value
Input.pressed("jump")
Input.just_pressed("jump")
Input.just_released("jump")
Input.get_action_strength("throttle") # the magnitude, as a float
Coroutines¶
from simvx.core import next_frame, parallel, tween, wait, wait_signal, wait_until
def cutscene(self):
yield from wait(1.0)
yield from tween(self.camera, "position", target, 2.0)
yield from wait_signal(self.boss.died)
yield from parallel(
tween(a, "position", pa, 1.0),
tween(b, "position", pb, 1.0),
)
handle = self.start_coroutine(self.cutscene())
Scenes and singletons (Patterns)¶
self.tree.change_scene(GameOverScreen(score=self.score))
self.tree.add_singleton("Jukebox", Jukebox()) # survives change_scene
self.tree.singletons["Jukebox"]
Rendering (WorldEnvironment, App)¶
self.add_child(WorldEnvironment(bloom_enabled=True, bloom_threshold=0.8,
tonemap_mode="aces", ssao_enabled=True))
App(title="Game", physics_fps=120).run(root) # defaults: 60 Hz physics, vsync on
# Headless, for tests: returns the captured frames as RGBA arrays
frames = App(visible=False).run_headless(root, frames=10, capture_frames=[0, 9])
Maths¶
Vec2 and Vec3 are float32 NumPy arrays, and Quat holds rotations. Angles are radians:
use math.radians() when you think in degrees. Matrices are row-major. A tuple converts at every
boundary: node.position = (100, 200) stores a Vec2.