Files
settled-reach/client/scripts/sandbox/sandbox_constants.gd
T
jpmschweitzerandClaude Fable 5 a7801a942a feat(client): 3D locomotion sandbox — character walks the live Gauntlet (T-1088)
New SR_LIVE sandbox scene: CharacterVisual composited in a 3D greybox world
derived from server snapshots. Per-leg constant-velocity interpolation keyed
to the stance throttle, 'server feet / client eyes' facing (wire octant while
moving, client aim octant idle), cadence-synced gait state machine on
AnimationPlayer custom blends, D-148 orthographic follow camera (-30deg
default, T-cycle presets), sim-space grid shader, camera-side wall cutaway,
accumulating never-evict tile store with four-state visibility tint.

Additive seams only: InputMapper.facing_angle_provider (2D path unchanged),
CharacterVisual.play_animation blend_time param + get_animation_player().
Visual harness gains per-scenario scene field + SR_AUTOPILOT input scripting.
210 new gdUnit assertions across five suites; verified live (230/230 total,
clean smoke, screenshot at .cache/screenshots/locomotion_idle_live.png).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:23:16 +02:00

118 lines
4.8 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 half-disc radius (m) around the character.
const CUT_RADIUS := 5.0
## Cutaway smoothstep falloff band (m) at the disc edge.
const CUT_BAND := 1.5
# -- 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 animation clip strings live.
## Clip names are the imported bare names: library "", "_Loop" stripped by the glTF
## importer, case-sensitive (design-input §2.2). 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 is the reserve if Sprint reads too aggressive at 2.5 m/s.
const GAIT_CLIP := {
"Sprint": {"idle": &"Idle", "moving": &"Sprint"},
"Walk": {"idle": &"Idle", "moving": &"Walk"},
"Careful": {"idle": &"Idle", "moving": &"Walk_Formal"},
"Crouch": {"idle": &"Crouch_Idle", "moving": &"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).
## 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,
}
# -- 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