Implement collision bodies, physics events, and hit reactions for StickmanRig
- Added a new specification for collision bodies, detailing the implementation requirements and corrections needed in the StickmanRig, PropBlock, and SandboxStage scripts. - Created a comprehensive test suite for collision bodies, including tests for proxy geometry, physics alignment, reaction handling, ghost collision, and stage contact routing. - Ensured that the collision system correctly handles ragdoll transitions and maintains the integrity of existing event rules. - Introduced new methods in StickmanRig for managing collision bodies and applying reactions based on impact direction and speed. - Updated PropBlock to emit signals for collisions and separations, preserving the order of events for stickman and prop interactions.
This commit is contained in:
@@ -372,6 +372,19 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
|
||||
`state == RigState.RAGDOLL` (so `RECOVERING` reads as "Stickman"). Runtime diagnosis is
|
||||
gated behind `const DEBUG_RECOVERY := false` (off by default; `_recovery_dbg()` traces the
|
||||
stack + bone-following state — the temporary per-step debug prints have been stripped).
|
||||
- **Phase 4.5 collision bodies and reactions:** while ANIMATED, `_build_collision_bodies()` creates
|
||||
ten metadata-tagged `AnimatableBody2D` proxies (head, torso, and eight limbs), stored in
|
||||
`_collision_bodies`. Proxies are top-level relative to their `Body/*` visuals,
|
||||
`sync_to_physics = false`, layer/mask 1, and `_sync_collision_bodies()` copies each visual's
|
||||
global transform every physics frame so RemoteTransform2D/IK poses and whole-rig LEFT
|
||||
mirroring cannot leave stale shapes. Torso/limb capsules use fitted lengths in the hanging
|
||||
frame; the head circle is centered from mounted geometry and expands beyond its 90 px minimum
|
||||
when needed. `set_collision_bodies_enabled()` disables both layer and mask for ghosts/RAGDOLL
|
||||
and restores them for RECOVERING/ANIMATED. `apply_collision_reaction()` uses contact position
|
||||
plus local force quadrant to choose mirrored side/upward/downward flinch direction, applies a
|
||||
capped `REACTION_MAX_ANGLE` offset from a captured base rotation, and decays without cumulative
|
||||
full rotations. PLAY→DIRECT restoration also resets ANIMATED rigs' authored transform, facing,
|
||||
IK markers, and torso rotation. Null-guards + `push_warning` prefixed `StickmanRig:` throughout.
|
||||
- **Phase 3a director functionality:** adds navigation/walking, speech, an action queue, and a
|
||||
queue-runner state machine. Enums `RunnerState { IDLE, EXECUTING }` and `ActionPhase { NONE,
|
||||
WALKING, SPEAKING, WAITING, RAGDOLLING, RECOVERING }`. Constants `FOOT_OFFSET := (0, -385)`
|
||||
@@ -591,11 +604,12 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
|
||||
None: mass 1.0, friction 0.5, bounce 0.05) and recolor fill/outline for non-`NONE`. Unified
|
||||
live-update setters push geometry/color changes to all children (null-guarded for `@tool`
|
||||
editor safety); `_apply_shape()` enables exactly one collision node.
|
||||
- **Phase 4 collision signal:** new `signal collided(other: Node)`; `_ready()` sets
|
||||
`contact_monitor = true`, `max_contacts_reported = 8`, and connects the guarded
|
||||
`body_entered` signal → `_on_body_entered` → `collided.emit(body)`, so **prop-vs-prop**
|
||||
collisions are reported by the physics engine (the stage's geometric feet-point test covers
|
||||
stickman-vs-prop separately).
|
||||
- **Phase 4 / Phase 4.5 collision signals:** `collided(other, contact_normal, contact_position)`
|
||||
and `separated(other)`; `_ready()` sets `contact_monitor = true`,
|
||||
`max_contacts_reported = 32`, and shape-cast CCD. The guarded `body_entered` path reads a
|
||||
matching direct-state contact row as world-space normal/position, with a center-direction
|
||||
fallback when timing leaves no row. The physics signal covers prop-vs-prop and Phase 4.5
|
||||
stickman-prop contacts; the old geometric feet-point stickman test is removed.
|
||||
- `scripts/trigger_area.gd` — `class_name TriggerArea`, `extends Node2D`; a **placeable sensor**
|
||||
(Phase 4, **not used by the editor**). `@export size: Vector2` (default 96×96), `get_area_rect()
|
||||
-> Rect2` (centered local-space rectangle), and `_draw()` (translucent green fill +
|
||||
@@ -714,10 +728,10 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
|
||||
Array[Dictionary]` of `{id, trigger, actions}` rules (trigger types: `arrived_at_waypoint`,
|
||||
`action_finished`, `speech_finished`, `entered_area`, `collided`; action types reuse the Phase
|
||||
3a set: `walk_to`/`speak`/`wait`/`ragdoll`/`recover`). Rules persist across Play/Edit mode
|
||||
toggles but are **not** saved to disk (Phase 5). A geometric **event engine** runs in PLAY
|
||||
toggles but are **not** saved to disk (Phase 5). A geometric **area-entry engine** runs in PLAY
|
||||
(`_update_area_entry` tests each movable's visible world AABB against the current transformed
|
||||
`TriggerArea` world AABB; `_update_stickman_prop_collision` continues to test a stickman's feet
|
||||
point against prop AABBs and vice-versa),
|
||||
`TriggerArea` world AABB); Phase 4.5 routes stickman-prop `collided` events from `PropBlock`
|
||||
physics contacts instead of polling stickman feet points,
|
||||
with **edge-triggered** dicts (already-fired events) reset on mode entry. A **rule-builder UI
|
||||
state machine** (`RuleStep` enum: `IDLE`, `SELECT_TRIGGER`, `TRIGGER_TARGET`, `SELECT_ACTION`,
|
||||
`ACTION_TARGET`, `ACTION_POSITION`, `PARAMS`) is driven from a **"⚡ When..."** item in the Direct
|
||||
|
||||
Reference in New Issue
Block a user