Docs/Format/Interactive Actions
ℹ️
Two animation systems

MC3 has two complementary animation concepts:

  • Keyframe animation (the <actions> / <channel> / <keyframe> system) — timecoded curves exported to glTF. See Animation Reference.
  • Interactive state actions (this page) — named transforms and state-machine transitions triggered by in-game events (player interaction, collision, signals). These are stored in <actions> with a different schema and are intended for runtime game engines.

The two systems share the <actions> top-level element name but differ in internal structure — keyframe actions contain <channel> children; interactive actions contain type attributes on the action itself.

Object States

Any object can declare a set of named configurations using the <states> child element. Each <state> is a partial override of the object's attributes that the runtime applies when the object enters that state.

<box name="FrontDoor"
     size="1 2.1 0.08"
     pivot="-0.5 0 0"
     material="painted_wood"
     state="closed">
  <states>
    <state id="closed" rotation="0 0 0"/>
    <state id="open"   rotation="0 90 0"/>
  </states>
</box>

<states> element

Placed as a direct child of any object element. Contains one or more <state> children.

<state> attributes

AttributeTypeDescription
idstringUnique state name within this object. Referenced by set_state actions.
rotationvec3Overrides the object rotation when in this state.
positionvec3Overrides position.
scalevec3Overrides scale.
visiblebooleanOverrides visibility.
materialIDREFOverrides the material reference.

The state attribute on the object element (e.g., state="closed") sets the initial active state. When no state is set, the object's base attributes apply.

💡

For rotating doors and lids, combine <states> with the pivot attribute to set the rotation axis correctly. Example: a door with pivot="-0.5 0 0" rotates around its left edge.

Interactive Actions

Interactive actions describe what happens when an object state changes or a trigger fires. They are declared in the top-level <actions> element with a type attribute that determines the kind of transform or state change.

Action attributes (common)

AttributeTypeDescription
idstringUnique action identifier. Used in sequences and toggle definitions.
targetstringObject name to act on. Omit for sequence and parallel types.
typestringAction type (see table below).
durationfloatTransition duration in seconds. Default: 0 (instant).
easingstringlinear, ease_in, ease_out, ease_in_out. Default: linear.
relativebooleanWhether the transform is relative to current state. Default depends on type.

Action Types

rotate

Rotates the target object. Use with pivot on the target for door/lid motion.

<action id="open_door" target="FrontDoor" type="rotate"
        axis="y" angle="90" duration="0.6" easing="ease_out"/>
AttributeDescription
axisx, y, or z — local axis of rotation.
angleRotation angle in degrees.
rotationAlternative: Euler vec3 ("0 90 0").

translate

Moves the target object by an offset.

<action id="slide_drawer" target="Drawer" type="translate"
        offset="0 0 0.4" duration="0.4" easing="ease_in_out"/>

set_position

Sets the absolute world position of the target.

<action id="reset_chair" target="ChairA" type="set_position"
        position="1 0 2" duration="0.3"/>

scale

Changes the scale of the target.

<action id="grow_platform" target="Platform" type="scale"
        scale="2 1 2" duration="1.0"/>

set_visible

Shows or hides the target object.

<action id="hide_wall" target="SecretWall" type="set_visible" visible="false"/>

set_material

Swaps the material on the target.

<action id="turn_lamp_on" target="LampBulb" type="set_material"
        material="glowing_yellow"/>

set_state

Transitions the target object to a named state. The object must have a matching <states> block.

<action id="open_front_door" target="FrontDoor" type="set_state"
        state="open" duration="0.6" easing="ease_out"/>

<action id="close_front_door" target="FrontDoor" type="set_state"
        state="closed" duration="0.6" easing="ease_in"/>

toggle

Syntactic sugar that cycles through named states in order, wrapping after the last. Each invocation advances one step.

<action id="toggle_door" type="toggle">
  <state id="closed" target="FrontDoor" type="rotate" axis="y" angle="0"  duration="0.5"/>
  <state id="open"   target="FrontDoor" type="rotate" axis="y" angle="90" duration="0.5"/>
</action>

toggle and <states> are complementary: use <states> on the object to declare named configurations, and a toggle action to cycle through them. Direct set_state actions remain available for explicit targeting.

sequence

Runs child steps one after the other. Each step waits for the previous to complete before starting.

<action id="open_and_move" type="sequence">
  <step action="open_front_door"/>
  <step action="slide_drawer"/>
</action>

parallel

Runs all child steps simultaneously.

<action id="open_double_door" type="parallel">
  <step target="LeftDoor"  type="rotate" axis="y" angle="-90" duration="0.6"/>
  <step target="RightDoor" type="rotate" axis="y" angle="90"  duration="0.6"/>
</action>

Triggers

A <trigger> child element on an action describes the condition under which the action fires automatically in a game runtime. Without a trigger the action is manual — it must be explicitly invoked by game code.

<action id="open_front_door" target="FrontDoor" type="set_state"
        state="open" duration="0.6">
  <trigger type="interact" prompt="Open door"/>
</action>

Trigger attributes

AttributeTypeDescription
typestringTrigger type (see below).
promptstringUI hint text displayed to the player (used with interact).
signalstringNamed signal identifier (used with on_signal).

Trigger types

TypeWhen it fires
manualNever automatically — only when game code calls it explicitly. Default when no trigger is present.
interactWhen the player presses an interaction key while looking at or near the target object.
on_loadImmediately when the scene is loaded into the runtime.
on_collisionWhen a physics body collides with the target.
on_enter_areaWhen an entity enters an <area> volume.
on_signalWhen game code broadcasts the named signal (see signal attribute).

on_load example — auto-start a windmill

<action id="spin_windmill" target="WindmillBlades" type="rotate"
        axis="z" angle="360" duration="4.0">
  <trigger type="on_load"/>
</action>

on_signal example — open a gate on command

<action id="open_gate" target="CastleGate" type="translate"
        offset="0 3 0" duration="1.5">
  <trigger type="on_signal" signal="gate_opened"/>
</action>

Collision & Physics Attributes

The collision attribute on any object marks it for physics processing:

<box name="Wall" size="10 3 0.3" collision="static"/>
<box name="Crate" size="1 1 1" collision="dynamic"/>
<box name="Door"  size="1 2 0.1" collision="kinematic"/>
ValueDescription
noneNo collision — object is purely visual. Default.
staticImmovable solid. Use for floors, walls, terrain.
dynamicFull physics body — gravity, collisions, constraints.
kinematicMoved by actions/code, not by physics. Use for doors, elevators, platforms.
triggerDetects overlap events but does not block movement.

The extended <collision> child element allows additional physics properties:

<box name="Gate">
  <collision type="kinematic" shape="box" layer="environment">
    <mask>player npc</mask>
  </collision>
</box>

Area Objects

An <area> is an invisible logical volume used for trigger zones, checkpoints, or pathfinding regions. It has no visual geometry.

<area name="CheckpointZone" size="5 3 5"
      position="0 1.5 10" tags="trigger checkpoint"/>

Areas are typically paired with on_enter_area triggers on other actions:

<action id="unlock_door" target="ExitDoor" type="set_visible" visible="true">
  <trigger type="on_enter_area"/>
</action>

Complete Interactive Example

A front wall with a door hole cut out, a rotating door with open/close states, a glass window, and a movable chair:

<?xml version="1.0" encoding="UTF-8"?>
<mc3 version="0.3" model="InteractiveHouse" unit="meter">

  <textures>
    <texture id="brick_albedo" uri="textures/brick.png"
             wrap_u="repeat" wrap_v="repeat"/>
    <texture id="wood_albedo"  uri="textures/wood.png"
             wrap_u="repeat" wrap_v="repeat"/>
  </textures>

  <materials>
    <material id="brick" roughness="0.9">
      <base_color_texture>brick_albedo</base_color_texture>
    </material>
    <material id="painted_wood" roughness="0.6">
      <base_color_texture>wood_albedo</base_color_texture>
    </material>
    <material id="glass" roughness="0.05" alpha_mode="blend">
      <base_color>0.7 0.9 1.0 0.35</base_color>
    </material>
  </materials>

  <definitions>
    <definition id="SimpleChair">
      <group>
        <box name="Seat" size="1 0.15 1"    position="0 0.55 0"     material="painted_wood"/>
        <box name="Back" size="1 1 0.15"    position="0 1.1 0.45"   material="painted_wood"/>
        <box name="Leg1" size="0.1 0.55 0.1" position="-0.4 0.275 -0.4" material="painted_wood"/>
        <box name="Leg2" size="0.1 0.55 0.1" position=" 0.4 0.275 -0.4" material="painted_wood"/>
        <box name="Leg3" size="0.1 0.55 0.1" position="-0.4 0.275  0.4" material="painted_wood"/>
        <box name="Leg4" size="0.1 0.55 0.1" position=" 0.4 0.275  0.4" material="painted_wood"/>
      </group>
    </definition>
  </definitions>

  <objects>

    <!-- Front wall with door hole cut out via CSG difference -->
    <difference name="FrontWall">
      <box name="WallBase" size="8 3 0.3" position="0 1.5 -3" material="brick"/>
      <box name="DoorHole" size="1.1 2.2 0.5" position="0 1.1 -3"
           role="cutter" visible="false"/>
    </difference>

    <!-- Door — rotates around its left edge (pivot) -->
    <box name="FrontDoor" size="1 2.1 0.08"
         position="0 1.05 -3.08"
         pivot="-0.5 0 0"
         material="painted_wood"
         collision="kinematic"
         state="closed">
      <states>
        <state id="closed" rotation="0 0 0"/>
        <state id="open"   rotation="0 90 0"/>
      </states>
    </box>

    <!-- Glass window -->
    <box name="WindowGlass" size="1.2 1.0 0.05"
         position="2.2 1.7 -3.08" material="glass"/>

    <!-- Movable chair from definition -->
    <instance name="ChairA" definition="SimpleChair"
              position="1.5 0 1.5" rotation="0 20 0"
              collision="dynamic"/>

  </objects>

  <actions>

    <action id="open_front_door" target="FrontDoor"
            type="set_state" state="open"
            duration="0.6" easing="ease_out">
      <trigger type="interact" prompt="Open door"/>
    </action>

    <action id="close_front_door" target="FrontDoor"
            type="set_state" state="closed"
            duration="0.6" easing="ease_in">
      <trigger type="interact" prompt="Close door"/>
    </action>

    <action id="push_chair" target="ChairA"
            type="translate" offset="1.0 0 0"
            duration="0.4" easing="ease_in_out">
      <trigger type="on_signal" signal="push_chair"/>
    </action>

  </actions>

</mc3>

Best Practices for Interactive Objects

Relationship with Keyframe Animation

Interactive State ActionsKeyframe Animation
Schema<action type="rotate"/set_state/…><action name="…"><channel><keyframe>
Triggered byIn-game events (interact, collision, signal)Timeline playback, game code
Duration/easingPer actionPer keyframe interpolation curve
glTF exportNot exported (runtime-only concept)Exported as animations[]
Best forDoors, levers, triggers, state machinesCharacter clips, camera paths, continuous motion