Behavior
Attach a Python class to an Entity and implement the lifecycle or event callbacks it needs.
IRON GRID / OPTIONAL SCRIPTING
An optional scripting language for experimentation, learning, and simple behaviors. Start new gameplay work with C#, or explore the supported Python API below.
Reviewed against the current public Python SDK. Check published release notes for your installed version. Iron Grid uses a right-handed Z-up world: ground movement uses X/Y and height uses Z. Define MoveHorizontal and MoveVertical in Project Settings → Input before using the movement example. Python Transform.position is local; use Transforms.world_position for world coordinates. Python’s bound vector components write through to the entity.
THE SCRIPTING MODEL
Attach a Python class to an Entity and implement the lifecycle or event callbacks it needs.
Group the transforms, rendering, physics, audio, and UI Modules your gameplay needs.
Work with the Entity hierarchy and project definitions such as tags and layers.
START WITH A SMALL BEHAVIOR
Frame-rate independent movement with named axes.
from IronGrid import Behavior, Input, Vector3
class PlayerMover(Behavior):
speed = 5.0
def update(self, dt):
move = Vector3(Input.get_axis("MoveHorizontal"),
Input.get_axis("MoveVertical"), 0)
self.transform.position += move * self.speed * dtInstantiate an Archetype and keep its root Entity.
from IronGrid import Behavior, Archetypes
class Spawner(Behavior):
def start(self):
made = Archetypes.instantiate(self,
"Assets/Archetypes/Crate.archetype",
position=(0, 0, 2))
self.crate = made[0] if made else NoneUse callbacks instead of polling every frame.
from IronGrid import Behavior, Tags
class Pickup(Behavior):
def on_trigger_3d(self, event):
if Tags.has(self, event.other_entity_id, "Player"):
print("Collected")The base class for scripts attached through a Script Binding. Override only the lifecycle and event callbacks your script needs. Player controllers, interactables, game rules, UI presenters, and any stateful logic owned by an Entity.
from IronGrid import Behavior, Input, KeyCode
class DoorController(Behavior):
speed = 2.0
def update(self, dt):
if Input.get_key_down(KeyCode.E):
self.transform.position.y += self.speed * dtclass Behavior(entity=None, engine=None, _data=None)Base type for an attached gameplay script; the runtime supplies its Entity, Stage, and exposed field values.
soft_show(default=None) · soft_hide(default=None)Controls whether a field appears in Properties while retaining its Python default.
awake()Called once when the script instance is created, before start().
start()Called once before the first gameplay update.
update(dt: float)Called every rendered frame; dt is elapsed time in seconds.
fixed_update(dt: float)Called on the fixed simulation step; use for physics-affecting logic.
late_update(dt: float)Called after normal updates; useful for cameras and dependent transforms.
on_enable() · on_disable()Called when the Script Binding enters or leaves the enabled state.
on_destroy()Called before the script instance is removed.
on_collision_2d/3d(event) · on_trigger_2d/3d(event)Event-driven collision and trigger delivery for the attached Entity.
on_key_down/up(event) · on_button_down/up(event) · on_mouse_down/up(event)Edge-triggered keyboard, action, and mouse events.
on_ui_click(event) · on_ui_value_changed(event) · on_ui_text_changed(event) · on_ui_focus_changed(event)Events from interactive UI modules.
on_animator_state_changed(event)Called when an Animator changes state.
self.transform -> Transform | None · self.entity_ref -> Entity | NoneCurated wrappers bound to the attached Entity.
find_entity(name: str) -> Entity | NoneFinds the first exact-name match in the active Stage.
get_module(type) · add_module(value) · remove_module(type)Convenient module access scoped to the attached Entity.
Read the active Stage, resolve its camera, change hierarchy, or request a Stage transition. Level flow, camera-aware systems, and hierarchy changes that span multiple entities.
from IronGrid import Behavior, Stage
class ExitPortal(Behavior):
next_stage = "Assets/Stages/Level02.igstage"
def on_trigger_3d(self, event):
Stage.load_stage(self, self.next_stage)Stage.current(context)Returns the active Stage for a Behavior, Entity, or Stage context.
Stage.name(context) -> strReturns the active Stage name.
Stage.active_camera(context) -> int | NoneReturns the active camera Entity id.
Stage.load(context, name_or_path) -> bool · Stage.load_stage(...) -> boolRequests a transition to another Stage.
Stage.set_parent(context, child, parent=None, *, keep_world=True) -> boolReparents an Entity and optionally preserves world placement.
Create, query, rename, enable, destroy, and traverse Entities. Spawning projectiles, locating named anchors, querying populations, and organizing runtime hierarchies.
from IronGrid import Behavior, Entities
class Spawner(Behavior):
def start(self):
self.light = Entities.create(self, name="Warning Light",
parent=self.entity, position=(0, 0, 2.5))Entities.get(context, entity=None) -> Entity | NoneWraps an Entity id or Entity-like value.
Entities.create(context, name='Entity', *, parent=None, position=(0,0,0), rotation=(0,0,0), scale=(1,1,1), enabled=True, guid=None) -> Entity | NoneCreates one Entity with optional hierarchy and local transform values.
Entities.create_many(context, specs) -> list[Entity]Creates Entities from specification dictionaries.
Entities.destroy(context, entity=None) -> bool · destroy_many(context, entities) -> intDestroys one or many Entities.
Entities.all(context) -> list[Entity] · Entities.ids(context) -> list[int]Enumerates the active Stage.
Entities.with_module(context, *types) -> list[Entity]Returns Entities that contain every requested module type.
Entities.ids_with_module(context, *types) -> list[int]Id-only module query.
find_by_name(context, name) · find_all_by_name(context, name) · find_by_prefix(context, prefix)Exact-name and prefix lookup.
Entities.name(context, entity=None) -> str · set_name(..., value='Entity') -> boolReads or changes an Entity name.
Entities.enabled(context, entity=None) -> bool · set_enabled(..., value=True) -> boolReads or changes enabled state.
Entities.children(context, entity=None) -> list[Entity] · parent(...) -> Entity | NoneTraverses immediate hierarchy relationships.
Entities.set_parent(context, child, parent_entity=None, *, keep_world=True) -> boolReparents an Entity.
Entity.eid · Entity.transform · get_module · add_module · remove_module · get_child · get_childrenObject-oriented wrapper for one Entity.
Read and write local transforms, including efficient batch operations. Single-Entity movement, crowds, pickups, procedural layouts, and synchronized motion.
from IronGrid import Behavior, Transforms
class Bob(Behavior):
def update(self, dt):
Transforms.translate(self, delta=(0, 0, 0.15 * dt))Transforms.get(context, entity=None) -> Transform | NoneReturns one bound Transform.
position(...) -> Vector3 · set_position(..., value=(0,0,0)) -> boolReads or writes local position.
positions(context, entities=None) -> dict[int, Vector3] · set_positions(context, values) -> intBatch local position access.
translate(..., delta=(0,0,0)) -> bool · translate_many(context, values) -> intOffsets one or many local positions.
rotation(...) -> Vector3 · set_rotation(..., value=(0,0,0)) -> boolReads or writes local Euler rotation in degrees.
rotations(context, entities=None) -> dict[int, Vector3] · set_rotations(context, values) -> intBatch local rotation access.
scale(...) -> Vector3 · set_scale(..., value=(1,1,1)) -> boolReads or writes local scale.
scales(context, entities=None) -> dict[int, Vector3] · set_scales(context, values) -> intBatch local scale access.
world_position(context, entity=None) -> Vector3 · world_matrix(...) -> list[float]Resolved world-space position and matrix.
name · enabled · position · local_position · rotation · scaleMutable properties on one bound Transform.
set_rotation_deg(x,y,z) · set_rotation_quat(x,y,z,w)Explicit Euler-degree and quaternion setters.
Read, add, remove, test, and patch gameplay-facing modules. Changing rendering, physics, UI, camera, light, or gameplay data.
from IronGrid import Behavior, Modules
from IronGrid.modules import LightModule
class Pulse(Behavior):
def start(self):
Modules.patch(self, LightModule, {"intensity": 4.0})Modules.get(context, type, entity=None) · get_many(context, type, entities=None)Reads one module or a dictionary keyed by Entity id.
Modules.add(context, module, entity=None) · remove(context, type, entity=None) -> boolAdds or removes a module.
Modules.has(context, type, entity=None) · has_many(context, type, entities=None)Tests module presence.
Modules.patch(context, type, data, entity=None) · patch_many(context, type, values) -> intUpdates selected fields on one or many modules.
Transform · CameraModule · LightModule · EnvironmentModule · TagsModuleSpatial, camera, lighting, environment, and classification data.
MeshFilter · MeshRenderer · MaterialMesh, renderer, and material state.
Canvas · RectTransform · UIImage · UIPanel · UIText · Text3DLayout and visual UI modules.
UIButton · UIInputField · UICheckbox · UISlider · UIDropdown · UIProgressBarInput controls emit Behavior UI callbacks; UIProgressBar displays progress.
UILayoutContainer · UILayoutItemAuthored layout containers and child layout settings.
RigidBody2D · BoxCollider2D · CircleCollider2DPublic 2D body and collider types.
RigidBody · BoxCollider · SphereCollider · CapsuleCollider · CylinderCollider · ColliderSurfacePublic 3D body, shape, and surface types.
Poll keyboard, mouse, named actions, axes, cursor state, and relative motion. Named controls for players, raw keys for tools, and relative mouse mode for first-person camera look.
from IronGrid import Behavior, Input, KeyCode
class PlayerLook(Behavior):
def start(self):
Input.set_mouse_capture(True)
def update(self, dt):
dx, dy = Input.get_mouse_delta()
if Input.get_key_down(KeyCode.Escape):
Input.set_mouse_capture(False)get_key(key) · get_key_down(key) · get_key_up(key)Held, pressed-edge, and released-edge key state.
get_button(name) · get_button_down(name) · get_button_up(name)Held and edge state for named actions.
get_axis(name) -> float · set_axis(name, positive, negative)Reads or defines a named axis.
set_button(name, keys=None, mouse_buttons=None, gamepad_buttons=None)Defines a named cross-device action.
get_mouse_button(button) · get_mouse_button_down(button) · get_mouse_button_up(button)Held and edge states for MouseButton values.
get_mouse_position() · get_mouse_viewport_size() · get_mouse_delta()Viewport position, viewport dimensions, and per-frame relative motion.
set_mouse_capture(bool) · get_mouse_capture() · is_mouse_capture_requested()Requests and inspects gameplay mouse capture.
set_cursor_visible(bool) · get_cursor_visible() · set_cursor_locked(bool) · set_relative_mouse_mode(bool)Cursor visibility, locking, and relative input.
A · D · S · W · Q · E · F · G · R · Space · Escape · Enter · UpArrow · DownArrow · LeftArrow · RightArrowPublic Python key constants; other keys may be supplied as supported key codes.
Left · Middle · RightPublic Python mouse button constants.
2D and 3D queries, body control, collision events, and trigger events. Weapons, character clearance, areas of effect, impacts, and event-driven interactions.
from IronGrid import Behavior, Physics, Vector3
class Weapon(Behavior):
def fire(self, origin, direction):
hits = Physics.raycast_3d(self, origin, direction,
max_distance=120.0, include_triggers=False)
if hits:
impulse = Vector3(*direction) * 18
Physics.add_impulse_3d(self, hits[0].entity_id, impulse)raycast_3d(context, origin, direction, max_distance, *, collision_layer=None, collision_mask=None, include_triggers=False, all_hits=False, filter=None) · raycast_3d_many(context, rays)Single or batch ray queries. Hits expose entity_id, point, normal, distance, fraction, and is_trigger.
sphere_cast_3d(...) · box_cast_3d(...) · capsule_cast_3d(...)Sweeps a volume through space.
overlap_sphere_3d(...) · overlap_box_3d(...) · overlap_capsule_3d(...)Finds bodies inside a volume.
raycast_2d(context, origin, direction, max_distance, *, collision_layer=None, collision_mask=None, include_triggers=False, all_hits=False, filter=None) · raycast_2d_many(context, rays)Single or batch 2D ray queries.
overlap_circle_2d(...) · overlap_box_2d(...)Finds 2D bodies inside a shape.
collision_layer · collision_mask · include_triggers · all_hits · filterPass numeric layer indices and bit masks; use Layers.index and Layers.mask to convert project layer names.
set_linear_velocity_2d(..., velocity=(0,0)) · linear_velocity_2d(...)Writes or reads planar body velocity.
add_force_2d(..., force=(0,0)) · add_impulse_2d(..., impulse=(0,0))Continuous force and instantaneous impulse.
wake_body_2d(...) · teleport_body_2d(..., position, rotation_degrees=0, preserve_velocity=True)Wakes or explicitly teleports a 2D body.
add_force_3d(..., force=(0,0,0)) · add_impulse_3d(..., impulse=(0,0,0))Continuous force and instantaneous impulse.
wake_body_3d(context, entity=None) -> boolWakes a sleeping 3D body.
collisions_2d/3d(context) · triggers_2d/3d(context)Returns current physics event rows with self_entity_id and other_entity_id.
collisions_for(context, entity=None, dimension=3) · triggers_for(...)Filters current events to one Entity.
Tags classify gameplay meaning; layers define physics filtering. Semantic queries such as Enemy or Pickup, plus precise collision rules.
from IronGrid import Behavior, Tags, Layers
class Hazard(Behavior):
def start(self):
Layers.set_entity_layer(self, self.entity, "Hazards")
def on_trigger_3d(self, event):
if Tags.has(self, event.other_entity_id, "Player"):
print("Damage player")Tags.primary(...) · Tags.set_primary(..., value='Untagged')Reads or writes the single primary tag.
Tags.get(...) · Tags.set(..., values=()) · Tags.add(..., *values) · Tags.remove(..., *values)Reads or changes all tags.
Tags.has(..., value) · Tags.entities(context, value) · Tags.ids(context, value)Tests or queries by tag.
Tags.defined(context=None) · Tags.define(context, *values)Reads or extends project tag definitions.
Layers.names(context=None) · Layers.define(context, name, index=None)Reads or defines named collision layers.
Layers.index(context, layer) · bit(...) · name(...) · mask(context, *layers)Converts names, indices, bits, and masks.
entity_layer(...) · entity_layer_name(...) · set_entity_layer(..., layer='Default')Reads or assigns an Entity layer.
entity_collision_mask(...) · set_entity_collision_mask(..., layers_or_mask=None)Reads or assigns layers an Entity may collide with.
Instantiate a serialized authored Element with optional root overrides. Enemies, projectiles, pickups, effects, and other reusable authored hierarchies.
from IronGrid import Behavior, Archetypes
class WaveSpawner(Behavior):
def start(self):
spawned = Archetypes.instantiate(self,
"Assets/Archetypes/Drone.archetype",
position=(8, -4, 1.5), name="Drone 01")Archetypes.instantiate(context, path, *, position=None, rotation=None, scale=None, name=None) -> list[Entity]Instantiates one Element and returns the spawned Entities, with the root first.
Archetypes.instantiate_many(context, paths, **overrides) -> list[Entity]Instantiates multiple Element paths with shared root overrides.
Frame timing plus Stage-driven one-shot and repeating callbacks. Frame-rate independent motion, cooldowns, delayed actions, and repeating gameplay events.
from IronGrid import Behavior, Time, Timers
class AutoClose(Behavior):
def start(self):
self.handle = Timers.after(self, 2.5, self.close)
def update(self, dt):
self.transform.rotation.z += 45 * Time.delta_time
def close(self): print("Closing")Time.delta_time · Time.elapsed_time · Time.fixed_delta_timeRendered-frame delta, accumulated time, and fixed simulation step in seconds.
Timers.after(context, seconds, callback) -> TimerHandle | NoneSchedules a one-shot callback.
Timers.every(context, seconds, callback) -> TimerHandle | NoneSchedules a repeating callback.
Timers.cancel(handle) · handle.cancel()Cancels a timer safely.
handle.active -> boolWhether a TimerHandle is still active.
Control clips and Animator state through the public SDK. Direct clip playback and parameter-driven character state machines.
from IronGrid import Animation, Behavior
class CharacterMotion(Behavior):
def start(self): Animation.play_state(self, self.entity, "Idle")
def set_running(self, speed):
Animation.set_parameter(self, self.entity, "speed", speed)get_animation(context, entity) · require_animation(context, entity) · get_animator(context, entity) · require_animator(context, entity)Optional getters and required getters that report a missing module.
play(context, entity, clip_key=None, *, loop=None, speed=None, restart=False) · pause(context, entity) · stop(context, entity)Controls direct clip playback.
set_time(context, entity, seconds) · set_speed(context, entity, speed) · set_loop(context, entity, enabled)Seeks and configures playback.
play_state(context, entity, state) · set_parameter(context, entity, name, value)Changes Animator state or parameters. Pass the attached Behavior as context, as in the example.
Play Entity AudioSources, fire one-shots, and control source or bus state. Authored looping or spatial sources, transient effects, and mixer-style bus controls.
from IronGrid import Audio, Behavior
class ImpactSound(Behavior):
def on_collision_3d(self, event):
# Configure an AudioSource on this entity before Play.
Audio.play_source(self.entity, volume_scale=0.85)play_source(entity, volume_scale=1.0) · play_one_shot(asset_guid, volume=1.0, bus_name='SFX')Plays an authored source or a manifest-declared cooked audio asset by GUID. A project-relative source path is not a shipping one-shot identifier.
stop_source(entity) · pause_source(entity) · resume_source(entity)Controls source playback.
set_source_volume(entity, volume) · set_source_pitch(entity, pitch)Changes source gain or pitch.
set_bus_volume(bus_name, volume) · set_bus_mute(bus_name, muted)Controls a named audio bus.
Audio.stop_all() -> boolStops all active playback.
Log information and inspect supported script health telemetry. Development logs, health panels, smoke checks, and locating slow or failing scripts.
from IronGrid import Behavior, Debug, Diagnostics
class Probe(Behavior):
def update(self, dt):
if Diagnostics.exception_count(self):
Debug.error(Diagnostics.recent_exceptions(self, 5))Debug.log(*parts) · warning(*parts) · error(*parts)Writes normal, warning, or error log lines.
Diagnostics.script_snapshot(context) -> dictReturns the supported high-level script health snapshot.
Diagnostics.exception_count(context) -> intReturns the recorded script exception count.
Diagnostics.slow_scripts(context, limit=20) -> list[dict]Returns the slowest measured scripts.
Diagnostics.recent_exceptions(context, limit=20) -> list[dict]Returns recent public exception details.
Small ergonomic vector types used throughout the scripting SDK. Arithmetic, readable field access, and values passed to Transform and Physics helpers.
from IronGrid import Vector3
forward = Vector3(0, 1, 0)
velocity = forward * 6.5
next_position = Vector3(2, 1, 4) + velocity * 0.016Vector2(x=0.0, y=0.0)Mutable two-element value type.
Vector3(x=0.0, y=0.0, z=0.0)Mutable three-element value type.
a + b · a - b · a * b · a / b · in-place formsElement-wise arithmetic; scalar values are broadcast.
vector.copy() · tuple(vector)Copies or unpacks vector values.
MODULE OVERVIEW
Transform, CameraModule, LightModule, EnvironmentModule, TagsModule
MeshFilter, MeshRenderer, Material
Canvas, RectTransform, UIImage, UIPanel, UIText, Text3D, UIButton, UIInputField, UICheckbox, UISlider, UIDropdown, UIProgressBar, UILayoutContainer, UILayoutItem
AudioClipRef
RigidBody2D, BoxCollider2D, CircleCollider2D
RigidBody, BoxCollider, SphereCollider, CapsuleCollider, CylinderCollider, ColliderSurface