acron.blueprints¶
Blueprint runtime: the contract between Studio-generated code and the game.
Generated blueprint modules contain exactly:
from acron.blueprints import blueprint, game
@blueprint class WorldBlueprint:
def blueprint_start(self): … def blueprint_update(self): … def blueprint_input(self, key): …
and the project’s main.py drives them from a plain ShowBase:
import acron.blueprints as bp from direct.showbase.ShowBase import ShowBase
app = ShowBase() bp.load() # import every module in <project>/blueprints/ bp.game.player = player # publish shared objects on the context bp.start() # instantiate + blueprint_start() bp.attach(app) # per-frame update() + key presses → input() app.run()
Generated code sees the world through acron.stock (the acron.actor
object model plus stock actors and input/camera/time globals).
@blueprint is the whole registration story — an ejected file (plain Python, no graph) keeps working because it still carries the decorator.
Dispatch is error-isolated: a hook that raises gets its annotated traceback printed to stderr (the game keeps running), and after 3 consecutive failures that hook is disabled for that instance with one loud message — one broken blueprint cannot freeze every other one. A successful call resets the count. Set ACRON_BLUEPRINTS_STRICT=1 to re-raise on the first error instead (tests, CI). ACRON_BP_BREAK_TIMEOUT (float seconds) bounds how long a Breakpoint node may hold the game waiting for Studio’s continue.
Global Functions
- action_matches(action, key)¶
True when
keyis bound toactionin input.json. An unmapped action falls back to matching the key literally, so graphs run before any input.json exists.
- attach(app=None)¶
Drive the lifecycle from a ShowBase: a per-frame task calls update() (skipped while
acron.stock.application.paused); the actor world steps from the same task; and every key/mouse button press reaches input(key) with Panda’s button name (‘space’, ‘w’, ‘arrow_up’, ‘mouse1’).appdefaults to thebasebuiltin. Calling it twice is a no-op; reset() detaches.
- axis(name)¶
The current value of axis
namein [-1.0, 1.0], from held keys (posbindings pull +1,neg-1). 0.0 when unmapped or the engine input state isn’t available (headless tests).
- bind_event(name, handler)¶
Subscribe
handlerto dispatchername(duplicate binds of the same handler are collapsed).
- blueprint(cls)¶
Class decorator: register a blueprint class for start()/update()/input().
- classes()¶
- declare_enum(name, members)¶
Create (or fetch) the blueprint enum
name.membersis a space/comma-separated string or a list of member names. Idempotent for an identical member list; redeclaring with different members is a hard error — two graphs disagreeing about an enum is a bug, not a merge.
- declare_struct(name, **fields)¶
Create (or fetch) the blueprint struct
name.Each keyword argument is a field with its default value. Instances take any subset of the fields as keyword arguments; equality is field-wise. Idempotent for an identical field list; a different field list is a hard error (same contract as declare_enum).
- default_ui_font()¶
Path of a UI font for the Create UI node, so an RmlUi overlay renders text without the project shipping one: $ACRON_UI_FONT, else the wheel’s bundled face (acron/fonts/Lato-Regular.ttf), else a common system font. Raises FileNotFoundError when none exists.
- delay(seconds, fn)¶
Run
fn()once,secondsfrom now (measured across update() calls). The Delay node’s continuation callback.
- despawn(instance)¶
Remove a live instance from dispatch, calling its blueprint_destroy() (if defined; error-isolated) first. Despawning something that is not a live instance is a hard error, not a silent no-op.
- detach()¶
Undo attach(): remove the update task and the key event hook.
- fire_event(name, *args)¶
Fire dispatcher
name: every bound handler runs (bind order), error-isolated — one raising handler cannot stop the others (ACRON_BLUEPRINTS_STRICT=1 re-raises).
- get_class(name)¶
Look up a class by name (SpawnClass dynamic path): registered blueprint classes first, then the acron.stock namespace (Actor, Sky, …).
- get_enum(name)¶
The blueprint enum
name, loading the sibling graph that declares it if needed.
- get_instance(name)¶
The FIRST live instance of the blueprint class named
name(in start()/spawn() order — with several spawned instances of one class, iterate instances() to reach the others).This is how one graph calls another graph’s functions (cross-graph Call Graph Function node):
get_instance('WorldBlueprint').spawn_wave(3). Instances exist only afterbp.start().
- get_struct(name)¶
The blueprint struct
name, loading the sibling graph that declares it if needed.
- implements(obj, interface)¶
True when
obj’s blueprint class declaresinterfacein its graph’s implements list (compile-time-checked marker).
- input(key)¶
Dispatch blueprint_input(key) on every live instance (error-isolated).
- instances()¶
- interface_call(target, function_name, *args)¶
Call
target.function_name(*args)with UE interface-message semantics: a target that doesn’t implement the function is silently skipped (returns None). A None target is skipped too.
- load(directory=None)¶
Import every *.py module in the project’s blueprints/ directory.
Importing runs the @blueprint decorators, which is all registration takes.
directorydefaults to $ACRON_PROJECT_ROOT/blueprints, else ./blueprints. The _ejected/ archive subdirectory is never scanned.
- load_input_map(directory=None)¶
(Re)load blueprints/input.json into the action/axis map. Called by load(); call directly after editing the file at runtime.
- node_for_traceback(exc)¶
Map an exception’s traceback to the graph node it crashed in.
Returns
"[nID] Title (file.py:line)"for the innermost frame inside a*_generated.pyfile, or None when the crash wasn’t in generated code.
- parent_class(name)¶
The @blueprint class
name, for graph inheritance (class BossBlueprint(parent_class('EnemyBlueprint')):). Loads the sibling module that defines it if it hasn’t imported yet.
- reset()¶
Forget all registered classes and instances (tests, hot restart).
Every live instance gets its blueprint_destroy() (if defined; error-isolated) first, and the modules load() imported are purged from sys.modules so the next load() re-executes them — making
reset(); load(); start()a full hot restart.
- spawn(cls_or_name, /, **kwargs)¶
Create a live blueprint instance at runtime (SpawnClass dynamic path).
cls_or_nameis a class, or a name resolved via get_class (registered blueprints first, then acron.stock). The instance is constructed with no arguments; every keyword argument is set as an attribute on it BEFORE its blueprint_start() runs (spawn('Enemy', position=p)setsinst.position = p). REGISTERED blueprint classes join update()/input() dispatch; stage/engine classes are constructed and returned unregistered — the engine owns their lifecycle, and registering them here would retain destroyed entities in the dispatch list forever.
- start()¶
Instantiate every registered class and call its blueprint_start().
Idempotent per class (exact type — a live instance of the class itself, not of a subclass, is what skips it). The skip check is a SNAPSHOT taken before any class starts: an instance spawned from inside another class’s blueprint_start() never suppresses the auto-instance, so the resulting population is independent of registration (= file) order. Failures are isolated: a class whose constructor or blueprint_start() raises cannot stop the other blueprints from starting (ACRON_BLUEPRINTS_STRICT=1 re-raises).
- start_timeline(owner, duration=1.0, on_tick=None, on_finished=None, loop=False)¶
Start a timeline owned by
owner: every update() callson_tick(alpha)with alpha running 0→1 overdurationseconds, thenon_finished()fires (or the timeline wraps whenloop).
- stop_timelines(owner)¶
Cancel every running timeline owned by
owner(no on_finished).
- trace_tail(n=20)¶
The ids of the last
ngraph nodes entered ([‘n3’, ‘n7’, …]).Empty unless the project’s blueprints were compiled with
--debug. Use after a crash to see where execution was: the last entry is the node that was running (or the last one that completed) when things went wrong.
- unbind_event(name, handler=None)¶
Unsubscribe
handlerfrom dispatchername(None = all handlers). Unbinding something never bound is a no-op.
- unwatch_collisions(owner, actor=None)¶
Stop collision watches owned by
owner(all of them, or just the one onentity).
- update()¶
Dispatch blueprint_update() on every live instance (error-isolated), advance latent work (Delay/Timeline), and poll collision watches.
- watch_collisions(owner, actor)¶
Start dispatching
owner.blueprint_collision_enter/exit(actor, other)for contact changes onactor(needs a collider). Watching the same (owner, actor) twice is collapsed.