feat: Implement Live Director System for interactive simulation control
- Added waypoint context updates to handle action queue statuses in `waypoint_context.gd`. - Introduced new scene file `manwalk.scene` with terrain and stickman setup. - Created architecture summary for the Live Director System detailing core state transitions, physics mechanics, UI elements, and action execution. - Specified class contracts and interfaces for the Live Director implementation in `phase_12_live_director.md`. - Defined state transitions, input handling, and queue editing mechanics for the Live Director feature.
This commit is contained in:
@@ -117,6 +117,9 @@ var _probe_bottom_y := 0.0
|
||||
var _probe_exclude: Array[RID] = []
|
||||
var _spawn_pose: Dictionary = {}
|
||||
var _spawn_limb_rotations: Dictionary = {}
|
||||
var _simulation_paused: bool = false
|
||||
var _paused_body_states: Dictionary = {}
|
||||
var _was_recovery_tween_running: bool = false
|
||||
|
||||
func configure(rig: StickmanRig, locomotion: StickmanLocomotion) -> void:
|
||||
_rig = rig
|
||||
@@ -150,9 +153,97 @@ func initialize(skeleton: Skeleton2D) -> void:
|
||||
## straight knee cross its angular limit during the step; correcting here keeps
|
||||
## that invalid intermediate pose from ever reaching the rendered frame.
|
||||
func _process(_delta: float) -> void:
|
||||
if _rig != null and _rig.state == StickmanRig.RigState.RAGDOLL:
|
||||
if not _simulation_paused and _rig != null and _rig.state == StickmanRig.RigState.RAGDOLL:
|
||||
_enforce_side_profile_leg_bends()
|
||||
|
||||
|
||||
func pause_simulation() -> void:
|
||||
if _simulation_paused:
|
||||
return
|
||||
_simulation_paused = true
|
||||
_paused_body_states.clear()
|
||||
_was_recovery_tween_running = _stand_up_tween != null and _stand_up_tween.is_valid() and _stand_up_tween.is_running()
|
||||
if _was_recovery_tween_running:
|
||||
_stand_up_tween.pause()
|
||||
for body: RigidBody2D in _bodies.values():
|
||||
if body == null or not is_instance_valid(body):
|
||||
continue
|
||||
var body_id: int = body.get_instance_id()
|
||||
_paused_body_states[body_id] = {"body": body, "transform": body.global_transform, "linear_velocity": body.linear_velocity, "angular_velocity": body.angular_velocity, "sleeping": body.sleeping, "freeze": body.freeze, "freeze_mode": body.freeze_mode}
|
||||
body.freeze_mode = RigidBody2D.FREEZE_MODE_KINEMATIC
|
||||
body.freeze = true
|
||||
|
||||
|
||||
func resume_simulation() -> void:
|
||||
if not _simulation_paused:
|
||||
return
|
||||
for body_id: int in _paused_body_states.keys():
|
||||
var snapshot: Dictionary = _paused_body_states[body_id]
|
||||
var body: RigidBody2D = snapshot.get("body") as RigidBody2D
|
||||
if body == null or not is_instance_valid(body) or body.is_queued_for_deletion():
|
||||
continue
|
||||
body.freeze_mode = int(snapshot.get("freeze_mode", RigidBody2D.FREEZE_MODE_STATIC))
|
||||
body.freeze = bool(snapshot.get("freeze", false))
|
||||
body.linear_velocity = snapshot.get("linear_velocity", Vector2.ZERO)
|
||||
body.angular_velocity = float(snapshot.get("angular_velocity", 0.0))
|
||||
body.sleeping = bool(snapshot.get("sleeping", false))
|
||||
_paused_body_states.clear()
|
||||
_simulation_paused = false
|
||||
if _was_recovery_tween_running and _stand_up_tween != null and _stand_up_tween.is_valid():
|
||||
_stand_up_tween.play()
|
||||
_was_recovery_tween_running = false
|
||||
|
||||
|
||||
func clear_pause_snapshot() -> void:
|
||||
_paused_body_states.clear()
|
||||
_simulation_paused = false
|
||||
_was_recovery_tween_running = false
|
||||
|
||||
|
||||
func get_body_nodes() -> Array[RigidBody2D]:
|
||||
var result: Array[RigidBody2D] = []
|
||||
for body: RigidBody2D in _bodies.values():
|
||||
if body != null and is_instance_valid(body):
|
||||
result.append(body)
|
||||
return result
|
||||
|
||||
|
||||
func get_body_rids() -> Array[RID]:
|
||||
var result: Array[RID] = []
|
||||
for body: RigidBody2D in get_body_nodes():
|
||||
var rid: RID = body.get_rid()
|
||||
if rid.is_valid():
|
||||
result.append(rid)
|
||||
return result
|
||||
|
||||
|
||||
func get_world_body_bounds() -> Rect2:
|
||||
var bounds := Rect2()
|
||||
var has_bounds: bool = false
|
||||
for body: RigidBody2D in get_body_nodes():
|
||||
var extent: float = float(body.get_meta("radius", LIMB_RADIUS)) + float(body.get_meta("half_height", 0.0))
|
||||
var body_rect := Rect2(body.global_position - Vector2(extent, extent), Vector2(extent * 2.0, extent * 2.0))
|
||||
if not has_bounds:
|
||||
bounds = body_rect
|
||||
has_bounds = true
|
||||
else:
|
||||
bounds = bounds.merge(body_rect)
|
||||
return bounds if has_bounds else Rect2()
|
||||
|
||||
|
||||
func interrupt_recovery_to_ragdoll() -> void:
|
||||
if _rig == null or _rig.state != StickmanRig.RigState.RECOVERING:
|
||||
return
|
||||
if _stand_up_tween != null and _stand_up_tween.is_valid():
|
||||
_stand_up_tween.kill()
|
||||
_stand_up_tween = null
|
||||
_stand_up_generation += 1
|
||||
if enter_ragdoll():
|
||||
state_transition_requested.emit(StickmanRig.RigState.RAGDOLL)
|
||||
if _simulation_paused:
|
||||
_simulation_paused = false
|
||||
pause_simulation()
|
||||
|
||||
func _resolve_limb_bones() -> void:
|
||||
_limb_bones.clear()
|
||||
for joint: String in LIMB_BONE_PATHS:
|
||||
@@ -186,7 +277,7 @@ func build() -> bool:
|
||||
var parent: Node = _rig.get_parent()
|
||||
if parent == null: parent = _rig.get_tree().current_scene
|
||||
if parent == null: return false
|
||||
_root = Node2D.new(); _root.name = CONTAINER_NAME; parent.add_child(_root); _bodies.clear()
|
||||
_root = Node2D.new(); _root.name = CONTAINER_NAME; _root.set_meta("stickman_ragdoll_container", true); _root.add_to_group("stickman_ragdoll_container"); _root.set_meta("stickman_rig", _rig); parent.add_child(_root); _bodies.clear()
|
||||
for entry: Dictionary in BODIES: _build_body(entry)
|
||||
_stabilize_side_profile_leg_bends()
|
||||
for entry: Dictionary in JOINTS: _build_joint(entry)
|
||||
|
||||
Reference in New Issue
Block a user