- Introduced `stickman_prop_interactor.gd` to manage prop interactions. - Created `phase_11_prop_interactions.md` detailing the design and implementation of prop pickup, drop, and throw mechanics. - Defined interaction states, action lifecycles, and event triggers for prop handling. - Specified constraints for throw actions, including target positioning and velocity calculations. - Established acceptance criteria for prop interactions and lifecycle management.
14 KiB
Architecture Specification: Interactive Prop System & Event Engine
This specification details the technical architecture for bringing interactive prop capabilities—including auto-approach, IK socket binding, parabolic throwing, ragdoll disarming, and "When..." rule triggers—into the Sandbox Stage.
1. System Overview & Layer Interactions
Prop interactions bridge five core subsystems across the runtime stack:
[ Director UI / Target Selector ]
│
▼
[ StickmanActionRunner ] ──(Auto-Approach)──► [ StickmanLocomotion ]
│
▼
[ StickmanRig IK ] ──(Socket Binding)──► [ PropBlock Physics ]
│
▼
[ StageEventRegistry ] ──("When..." Rules)──► [ Reactive Actions ]
-
Director UI & Target Selection: Captures spatial throw targets and enqueues prop interaction commands.
-
Action Runner & Locomotion: Evaluates spatial reach; automatically inserts
walk_tosub-goals when props are out of arm's reach. -
IK & Bone Socketing: Extends arm IK targets toward prop
gripanchors and managesRemoteTransform2Dbinding. -
Physics Lifecycle: Unfreezes/freezes
PropBlockbodies and inherits velocity during release, throw, or ragdoll disarm transitions. -
Event Registry: Emits and evaluates edge-triggered prop rules within the stage's "When → Then" engine.
2. Dynamic Action Queue & Auto-Approach (walk_to)
To ensure smooth director interaction, the action queue automatically resolves spatial gaps between the stickman and target props.
Action Payload Schemas
-
pick_up_prop:{ "type": "pick_up_prop", "target_prop_id": String, "hand": "auto" | "left" | "right" } -
drop_prop:{ "type": "drop_prop" } -
throw_prop:{ "type": "throw_prop", "target_position": Vector2, "force": float }
Smart Approach Execution
When StickmanActionRunner receives a pick_up_prop payload:
┌──────────────────────────────────────────────┐
│ pick_up_prop Action Start │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ Distance = |rig.position - prop.position| │
└──────────────────────┬───────────────────────┘
│
Is Distance > Reach Radius (e.g. 40px)?
┌───────────────────────┴───────────────────────┐
│ │
[ YES ] [ NO ]
│ │
┌─────────────────▼─────────────────┐ ┌─────────────────▼─────────────────┐
│ 1. Pause pick_up_prop sequence │ │ 1. Skip locomotion step │
│ 2. Prepend walk_to(prop_offset) │ │ 2. Trigger arm IK reach animation │
│ 3. Resume pickup on arrival │ └───────────────────────────────────┘
└───────────────────────────────────┘
-
Distance Check: Evaluates global horizontal/spatial distance between the stickman root and prop
originpivot. -
Sub-Goal Injection: If outside reach radius,
StickmanActionRunnerpauses the pickup sequence and internally issues awalk_tocommand to a stand point adjacent to the prop. -
Sequence Resume: Upon receiving
StickmanLocomotion.arrived_at_waypoint, the runner resumes the pickup sequence and triggers arm IK extension.
3. Pickup, Drop, and Throw Pipelines
A. Prop Pickup Sequence & IK Socketing
-
IK Reach Phase (150 ms):
TwoBoneIKtargets for the selected arm interpolate toward the world position of the prop'sgripanchor.Edit: Torso position should also move up/down based on grip anchor. The stickman should look like it is bending or extending legs to make pickup look more realistic.
-
Socket Attachment:
-
Set
PropBlock.freeze = true(kinematic mode) to suspend environmental physics forces. -
Instantiate or activate a
RemoteTransform2Dnode attached to the lower arm / wrist joint, targeting thePropBlock. -
Apply anchor local offsets (
x, y, rotation_deg, z_index_offset) to align the prop grip directly within the hand geometry.
- Holding Stance Latch: Lock upper limb IK targets into a carried stance during subsequent walk cycles.
B. Prop Drop Sequence
-
Unbinding: Clear
RemoteTransform2Dtarget and reparentPropBlockback to the stage world root. -
Physics Activation: Set
PropBlock.freeze = falseto re-enable rigid body simulation, gravity, and collision shapes. -
Inertia Inheritance: Set
PropBlock.linear_velocityequal to the stickman's current movement velocity (_last_grounded_walk_velocity_x) so dropped items retain forward momentum. -
Arm Recovery: Blend wrist IK targets smoothly back to standard locomotion/idle rest positions.
C. Prop Throw Sequence & Trajectory Selection
Director Target UI Flow
-
Targeting Mode: Selecting "Throw Prop" in Director/Play mode activates an aim reticle cursor and dynamic arc preview.
EDIT: When the user clicks the stickman in DIRECT mode, 3 new additions should appear in the action context menu
1. Pick up 2. Drop 3. Throw [Give these proper emoji]- If Drop is added to the queue, and the action is run when the stickman holds nothing, the action is skipped (marked as done)
- If Throw is added to the queue, and the action is run when the stickman holds nothing, the action is skipped (marked as done)
- These 3 actions should also be selectable when handling a 'When' event
-
Trajectory Math: Given hand release point
P_0, target landing pointP_t, base speedv_0, and gravityg:
\theta = \arctan\left(\frac{v_0^2 \pm \sqrt{v_0^4 - g(g \cdot x^2 + 2 y \cdot v_0^2)}}{g \cdot x}\right)
Calculates exact initial launch velocity components V_{\text{launch}} = (v_0 \cos\theta, v_0 \sin\theta).
Keyframe Throw Animation Arc
[ Windup: 100ms ] ──► [ Release Frame: 150ms ] ──► [ Follow-Through: 50ms ]
- Torso tilts -15° - Arm sweeps to angle - Arm extends along arc
- Arm pulls backward - Prop unfreezes + impulse - Blends back to idle
- Windup (100 ms): Torso rotates backward opposite the launch vector while the arm pulls back.
- Release Frame (150 ms):
-
Arm sweeps rapidly along the calculated launch angle
\theta. EDIT: The arm should go overhead - not an underhand throw. -
Unbind
RemoteTransform2Dand reparent prop to world root. -
Set
PropBlock.freeze = falseand executeapply_central_impulse(V_launch). -
Apply rotational torque proportional to the distance between hand grip and prop center-of-mass.
- Follow-Through (50 ms): Arm extends toward launch vector, then smoothly dampens back to default idle/walking pose.
4. Physics & State Machine Lifecycle (Ragdoll Disarm Handoff)
When StickmanRig.set_ragdoll(true) is executed (via impact, command, or keypress), any held prop must be immediately disarmed before kinematic nodes are hidden and ragdoll bodies are built.
┌──────────────────────────────────────────────┐
│ StickmanRig.set_ragdoll(true) │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 1. Pre-Ragdoll Intercept │
│ Query if held_prop != null │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 2. Execute Disarm Handoff │
│ - Clear RemoteTransform2D binding │
│ - Reparent PropBlock to world root │
│ - Set prop.freeze = false │
│ - Apply cached momentum (_track_momentum) │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 3. Build Physics Ragdoll │
│ Hide animated body, build RigidBody2D │
│ network at current solved bone positions │
└──────────────────────────────────────────────┘
-
Pre-Ragdoll Intercept: Prior to clearing collision proxies or hiding body meshes, check for an active
held_propbinding. -
Physics Unbind:
-
Detach the
RemoteTransform2Dsocket. -
Reparent prop to the world root and set
PropBlock.freeze = false. -
Transfer cached rig momentum (
_track_momentum()) directly toPropBlock.linear_velocityandangular_velocityso the prop tumbles naturally alongside the falling figure.
- Ragdoll Construction: Proceed with standard
_build_ragdoll()processing at current solved bone transforms.
5. Event System Integration ("When..." Triggers)
Three new edge-triggered prop event types are added to StageEventRegistry (sandbox_stage.gd) to support the director's "When → Then" mental model.
Prop Trigger Specifications
| Trigger Type | Source Object | Target Object | Firing Condition |
|---|---|---|---|
prop_picked_up |
Stickman / Prop | Prop / Stickman | Emitted as soon as IK arm reach completes and RemoteTransform2D socket locks. |
|
| prop_dropped | Stickman / Prop | Prop / Stickman | Emitted when a stickman drops a prop manually or via ragdoll disarm.
|
| prop_thrown | Stickman / Prop | Prop / Stickman | Emitted at the exact release frame of a throw animation arc.
|
Signal Routing & Evaluation
-
StickmanActionRunneremitsprop_action_executed(action_type, stickman, prop)upon completing or releasing an anchor attachment. -
StageEventRegistrymatches active stage rules whererule.trigger.typeequalsprop_picked_up,prop_dropped, orprop_thrown. -
Matching rules trigger reactive actions on target stickmen or props (e.g., When Stickman A picks up Sword X
\rightarrowStickman B ragdolls).
6. Implementation Roadmap for Architect Agent
[ Phase 1: Action Queue & Locomotion ]
├─ Add pick_up_prop, drop_prop, and throw_prop to StickmanActionRunner
└─ Implement distance evaluation & auto-enqueueing of walk_to sub-goals
[ Phase 2: Socket Attachment & IK Handshake ]
├─ Implement RemoteTransform2D socketing to wrist joint with grip offsets
└─ Implement holding stance arm pose blending
[ Phase 3: Throw Mechanics & Trajectory UI ]
├─ Add trajectory solver & reticle target selector to Director UI
└─ Build throw animation timeline (windup -> release impulse -> follow-through)
[ Phase 4: State Machine Integration & Disarm ]
├─ Intercept held props during set_ragdoll(true) transitions
└─ Transfer cached linear/angular momentum to disarmed prop physics body
[ Phase 5: Event System Expansion ]
├─ Register prop_picked_up, prop_dropped, prop_thrown triggers in StageEventRegistry
└─ Wire action signals to trigger "When -> Then" rule evaluations
7. Design Questions / Rules
- When creating a Throw action, the user must create an arc defining the throw. However, when the scene is playing, the stickman might be in a whole different position for when the throw must happen. Should we define a new arc and make a best effort to throw the prop towards the (x,y) location selected or should we use the same arc, but translate the throw endpoint relevant to the stickman's current position.
- The stickman profile direction should change to whichever direction the prop will be thrown.
Architectural Acceptance Criteria Checklist
-
Auto-Approach: Requesting pickup of a distant prop automatically causes the stickman to walk to the prop before reaching out.
-
Grip Alignment: Hand attaches precisely to the prop's authored
gripanchor, respecting local position, rotation, and Z-index offsets. -
Trajectory Accuracy: Thrown props follow the calculated parabolic arc toward the director's targeted ground point.
-
Ragdoll Disarm: Entering ragdoll mode immediately detaches held props and applies rig momentum so items tumble realistically.
-
Event Rule Engine: Rules configured with
When prop_picked_up,When prop_dropped, orWhen prop_thrownexecute downstream actions reliably.