ual1.glb (120 clips) + ual2.glb (134) replace the free-tier subsets; CharacterVisual loads both under explicit library names with lib/Clip exact addressing (bare names keep cross-library search). Gait table re-pointed; two new one-shot overlays in the gait machine: stance transitions (Sprint/Crouch Enter/Exit, destination-Enter priority, clip-length timer, movement never stalls) and turn-in-place (Turn90/180 L/R from shortest-arc sign, >=60/135 deg idle yaw jumps, retrigger-guarded). All clip names verified by dumping the imported GLBs. 70 gait tests + live smoke green (both libraries load with exact counts). S9 live-tune list: turn handedness (unverified headless — one-line swap), TURN_SPEED_SCALE=2.0 compromise vs the snappy yaw ease, turn-on-stop feel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
204 lines
9.5 KiB
GDScript
204 lines
9.5 KiB
GDScript
class_name SandboxConstants
|
|
extends RefCounted
|
|
## T-1088 locomotion sandbox — every feel/tuning knob in one file.
|
|
## § references point at the T-1088 final design (see ticket). Proto-production:
|
|
## values migrate to Phase-5 homes at the T-962 gate.
|
|
##
|
|
## Stance move intervals are deliberately NOT duplicated here — read
|
|
## InputMapper.MOVE_INTERVAL_MS at runtime (single source, no drift; design §13.8).
|
|
|
|
# -- Coordinate & scale (design §2) -------------------------------------------
|
|
|
|
## D-066/D-222 — THE convention: one sim TilePosition step = one 0.5 m subtile.
|
|
## All sandbox positions and speeds are metres. See SandboxSpace for conversions.
|
|
const SUBTILE_M := 0.5
|
|
|
|
# -- Greybox walls + cutaway (design §3, §8) -----------------------------------
|
|
|
|
## Wall height in metres — walls shorter than the ~1.7 m character read wrong.
|
|
const WALL_H := 2.5
|
|
## Cutaway stub height (m) camera-side walls drop to.
|
|
const CUT_HEIGHT := 0.75
|
|
## Cutaway sightline-corridor half-width (m) — character is 1 subtile wide;
|
|
## this covers them plus margin. Corridor length is pitch-derived per frame.
|
|
const CUT_CORRIDOR_HALF_W := 1.4
|
|
## Cutaway smoothstep falloff band (m) at the corridor edges.
|
|
const CUT_BAND := 0.75
|
|
## Cutaway mode default: 0 = sightline corridor, 1 = bottom walls low, 2 = both.
|
|
## User verdict (live session 2026-07-06): "both" — known interiors open at a
|
|
## glance AND the corridor punches through unexplored exterior walls. C cycles.
|
|
const CUTAWAY_MODE := 2
|
|
|
|
# -- Locomotion interpolation (design §4) --------------------------------------
|
|
|
|
# Stance intervals: NOT duplicated — read InputMapper.MOVE_INTERVAL_MS at runtime.
|
|
|
|
## Leg-speed clamp for multi-tile latest-wins deltas: close within ~one interval.
|
|
const CATCHUP_MAX_FACTOR := 3.0
|
|
## 5 subtiles — matches the 2D TELEPORT_DISTANCE_THRESHOLD (main.gd:3). Beyond this,
|
|
## snap all channels (position, yaw, camera, 0.0-blend anim reset).
|
|
const SNAP_DIST_M := 2.5
|
|
## Walk↔idle flicker hysteresis (§4.2): is_moving stays true until at-target this long.
|
|
const IDLE_ENTER_DELAY_S := 0.18
|
|
|
|
# -- Facing / turn smoothing (design §5, D-151) ---------------------------------
|
|
|
|
## Yaw ease rate (1/s): yaw = lerp_angle(yaw, target, 1 - exp(-TURN_SHARPNESS * delta)).
|
|
const TURN_SHARPNESS := 14.0
|
|
## Per-stance yaw budget clamp, degrees/second.
|
|
const TURN_BUDGET_DEG := {
|
|
"Sprint": 1080.0,
|
|
"Walk": 720.0,
|
|
"Careful": 540.0,
|
|
"Crouch": 420.0,
|
|
"Idle": 600.0,
|
|
}
|
|
|
|
# -- Animation (design §6) -------------------------------------------------------
|
|
|
|
## Gait table (§6.1) — the ONLY place gait clip strings live. Explicit "lib/Clip"
|
|
## addressing (D-248 / T-1088): every locomotion loop lives in the UAL1 tier. Names
|
|
## are the verified imported names (dumped 2026-07-06 from ual1.glb): the glTF importer
|
|
## strips the "_Loop" suffix and sets loop mode, so "Walk_Loop" -> "Walk" (loop=1).
|
|
## Careful = Walk_Formal (D-053 stance readability at ortho distance; fallback if it
|
|
## reads "parade march": Walk at 0.6x — one table cell). Jog_Fwd (ual1) is the reserve
|
|
## if Sprint reads too aggressive at 2.5 m/s.
|
|
const GAIT_CLIP := {
|
|
"Sprint": {"idle": &"ual1/Idle", "moving": &"ual1/Sprint"},
|
|
"Walk": {"idle": &"ual1/Idle", "moving": &"ual1/Walk"},
|
|
"Careful": {"idle": &"ual1/Idle", "moving": &"ual1/Walk_Formal"},
|
|
"Crouch": {"idle": &"ual1/Crouch_Idle", "moving": &"ual1/Crouch_Fwd"},
|
|
}
|
|
|
|
## Native clip ground speed (m/s) for cadence sync (§6.2):
|
|
## speed_scale = clamp(rig_speed / NATIVE_MPS[clip], SPEED_SCALE_CLAMP.x, .y).
|
|
## Keyed by clip BASENAME (the part after "lib/") so it is addressing-agnostic.
|
|
## Initial guesses — tuned live against the DebugHud readout.
|
|
const NATIVE_MPS := {"Walk": 1.4, "Walk_Formal": 1.2, "Sprint": 3.2, "Crouch_Fwd": 0.9}
|
|
const SPEED_SCALE_CLAMP := Vector2(0.6, 1.8)
|
|
|
|
## Cross-fade seconds by transition kind (§6.3); teleport is a hard cut — a
|
|
## cross-map jump must not smear.
|
|
const BLEND := {
|
|
"idle_to_gait": 0.12,
|
|
"gait_to_idle": 0.20,
|
|
"gait_to_gait": 0.15,
|
|
"crouch": 0.25,
|
|
"teleport": 0.0,
|
|
}
|
|
|
|
# -- Stance-transition one-shots (T-1088; UAL1 Enter/Exit clips) ----------------------
|
|
#
|
|
# On a stance change, if the destination stance has an Enter clip OR the source stance
|
|
# has an Exit clip, play that one-shot (loop=0) and blend into the target loop when it
|
|
# elapses. The rig position channel is independent, so the character keeps gliding while
|
|
# the transition plays (movement never stalls). Only Sprint and Crouch have Enter/Exit
|
|
# clips in UAL1 (verified 2026-07-06); Walk/Careful transition with a plain gait blend.
|
|
# Priority when both apply (only Crouch<->Sprint): destination Enter wins over source
|
|
# Exit — "entering the new stance" is the salient action. Live-tune the edge if it reads
|
|
# wrong. Names are verified imported names (all loop=0 one-shots).
|
|
const STANCE_ENTER_CLIP := {
|
|
"Sprint": &"ual1/Sprint_Enter",
|
|
"Crouch": &"ual1/Crouch_Enter",
|
|
}
|
|
const STANCE_EXIT_CLIP := {
|
|
"Sprint": &"ual1/Sprint_Exit",
|
|
"Crouch": &"ual1/Crouch_Exit",
|
|
}
|
|
## Cross-fade (s) INTO a stance-transition one-shot. The blend back OUT to the target
|
|
## loop reuses the BLEND table (crouch clips take BLEND.crouch).
|
|
const STANCE_TRANSITION_BLEND := 0.12
|
|
|
|
# -- Turn-in-place one-shots (T-1088; UAL1 Turn90, UAL2 Turn180) ----------------------
|
|
#
|
|
# When IDLE and the yaw TARGET jumps (mouse-driven octant snap) by >= TURN_TRIGGER_DEG,
|
|
# play a turn one-shot synced to the rig's yaw ease, then fall back to the stance idle
|
|
# clip. L/R is chosen from the sign of the shortest-arc delta; >= TURN_180_DEG uses the
|
|
# 180 clip (UAL2). Retrigger is guarded — a fresh turn cannot start until the current
|
|
# one elapses. Octants snap at 45 deg, so effective triggers are 90 deg -> Turn90 and
|
|
# 135/180 deg -> Turn180. Names are verified imported names (all loop=0 one-shots).
|
|
## Minimum yaw-target jump (deg) that triggers a turn-in-place. Below this, the rig's
|
|
## normal yaw ease handles the reface with no clip.
|
|
const TURN_TRIGGER_DEG := 60.0
|
|
## At/above this jump (deg), use the Turn180 clip instead of Turn90.
|
|
const TURN_180_DEG := 135.0
|
|
## Turn one-shot clips keyed by "<arc>_<hand>". Turn90 is UAL1, Turn180 is UAL2.
|
|
## Hand: positive shortest-arc yaw delta -> "_L", negative -> "_R" (VERIFY handedness
|
|
## live — a swap is a one-line fix if the model turns the wrong way).
|
|
const TURN_CLIP := {
|
|
"90_L": &"ual1/Turn90_L",
|
|
"90_R": &"ual1/Turn90_R",
|
|
"180_L": &"ual2/Turn180_L",
|
|
"180_R": &"ual2/Turn180_R",
|
|
}
|
|
## Cross-fade (s) into and out of a turn one-shot.
|
|
const TURN_BLEND := 0.15
|
|
## Playback rate for turn one-shots. The UAL turn clips are deliberate (~1.7-2.0 s); the
|
|
## rig's idle yaw ease is snappy (~0.3 s for 180 deg), so at 1.0x the turn shuffle drags
|
|
## long past the settled body. 2.0x is an initial compromise — S9 live-tunes it against
|
|
## the on-screen yaw-ease duration (raise to shorten the turn, lower for weightier turns).
|
|
const TURN_SPEED_SCALE := 2.0
|
|
|
|
# -- Camera (design §7; D-148, D-015, D-158) -------------------------------------
|
|
|
|
## Pitch presets in degrees from horizontal — D-148 preset list is authoritative;
|
|
## the spike's -45 iso preset is struck. T-key cycles (sanctioned dev affordance).
|
|
const CAM_PITCH_PRESETS := [-30.0, -5.0, -80.0]
|
|
const CAM_DIST := 30.0
|
|
const CAM_ORTHO_SIZE := 9.0
|
|
## Exponential pivot-follow rate (1/s) — deliberately the ONLY soft layer (§0).
|
|
const CAM_FOLLOW_RATE := 6.0
|
|
## Preset tilt lerp rate (1/s).
|
|
const CAM_TILT_RATE := 3.0
|
|
## Follow lookahead knob for the tuning pass (seconds of velocity lead).
|
|
const LOOKAHEAD_S := 0.0
|
|
|
|
# -- Scene (design §1.2) -----------------------------------------------------------
|
|
|
|
## WorldRoot yaw — the single static D-148 map-rotation Transform3D. Set once in
|
|
## locomotion_sandbox.tscn; sim coords stay axis-aligned inside WorldRoot and the
|
|
## camera never rotates.
|
|
const MAP_ROTATION_DEG := 45.0
|
|
|
|
# -- Greybox tints (design §3.2) -----------------------------------------------------
|
|
|
|
## Four-state visibility tint: Color values are used as instance color directly;
|
|
## float values multiply albedo value (85% / 70%).
|
|
const TINTS := {
|
|
"forward": Color.WHITE,
|
|
"peripheral": 0.85,
|
|
"boundary": 0.70,
|
|
"remembered": Color(0.42, 0.45, 0.52),
|
|
}
|
|
|
|
# -- Input (design §9) ---------------------------------------------------------------
|
|
|
|
## Ground-plane deadzone (m) for the mouse facing provider — mirrors the 2D jitter guard.
|
|
const MOUSE_AIM_DEADZONE_M := 0.1
|
|
|
|
# -- Head/torso look-at during path-follow (T-1088 follow-facing) ---------------------
|
|
|
|
## Head bone yaw limit (deg) for the mouse look-at while the body walks a path.
|
|
const HEAD_LOOK_HEAD_LIMIT_DEG := 70.0
|
|
## Torso (spine_02) contribution limit (deg) — engages when looking far lateral/behind.
|
|
const HEAD_LOOK_TORSO_LIMIT_DEG := 30.0
|
|
## Look-at influence fade in/out (s) on follow start/end.
|
|
const HEAD_LOOK_FADE_S := 0.25
|
|
## Aim point height above the mouse ground hit (m) — roughly eye level.
|
|
const HEAD_LOOK_EYE_HEIGHT_M := 1.55
|
|
|
|
# -- Path preview / click-to-move (T-1088 live-session item 2) ------------------------
|
|
|
|
## Move-here marker fill on the hovered destination subtile — distinct accent,
|
|
## semi-opaque so the floor tint reads through.
|
|
const PATH_MARKER_COLOR := Color(0.30, 0.90, 1.0, 0.55)
|
|
## Optimal-path line colour (tile-center strip from the character to the destination).
|
|
const PATH_LINE_COLOR := Color(0.30, 0.90, 1.0, 0.90)
|
|
## Marker sits this far above the floor plane (m) — clears grid-line z-fighting.
|
|
const PATH_MARKER_Y := 0.02
|
|
## Path line sits slightly higher than the marker so it reads on top of it.
|
|
const PATH_LINE_Y := 0.06
|
|
## RMB hold (ms) past which a right-click is a LONG-PRESS = go-there-then-crouch
|
|
## (committed on release), below which it is the click/double-click gesture.
|
|
const RMB_LONG_PRESS_MS := 400
|