feat: overhaul movement for flow — momentum-preserving states, smooth crouch, landing feel
- Quake-style air-strafe accelerate (real speed gain from strafing; external speed from dash/rockets never clamped) - Wall run: keeps entry momentum (incl. upward carry), accelerates along the wall instead of hard-setting velocity, gravity fades in over the run - Slide: slope physics (gravity projected along floor accelerates downhill), flat-ground decel instead of multiplicative friction, entry boost, direct slide->wallrun and slide->dash transitions - Dash: cooldown 10s -> 2s, per-player (was a static shared across instances), momentum fully kept on exit, FOV punch event - Grapple: taut pendulum with active rope control (W reels in, S pays out), release keeps swing energy - Wall climb: carries upward momentum in, jump-off kick, shared vault helper - Crouch capsule: single owner in the machine, smoothly lerped, ceiling check; camera eye height follows via crouch factor (no more head snapping) - Landing: machine emits 'land' events scaled by impact; camera dip + pitched down thud; footsteps get pitch variation - Camera rig: event-driven (land dip, dash FOV kick, vault pitch impulse), slide roll tilt - Model: Jump vs Fall split by vertical velocity, Land one-shot on heavy landings, wall-run body lean (synced to remotes via synced_wall_side) Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
c4a538a469
commit
0ba14a9469
@@ -28,27 +28,23 @@ func update(delta: float) -> void:
|
||||
grav *= params.low_jump_multiplier
|
||||
vel.y -= grav * delta
|
||||
|
||||
# ── Air control ───────────────────────────────────────────────────────
|
||||
# ── Air control: Quake-style accelerate ───────────────────────────────
|
||||
# Only the velocity component ALONG wish_dir is capped, so turning while
|
||||
# strafing genuinely gains speed. Total input-driven speed is bounded by
|
||||
# max_air_speed; externally-gained speed (dash, rockets) is never clamped.
|
||||
var wish_dir: Vector3 = machine.wish_dir_world
|
||||
var hvel := Vector3(vel.x, 0.0, vel.z)
|
||||
|
||||
if wish_dir.length_squared() > 0.01:
|
||||
wish_dir = wish_dir.normalized()
|
||||
var current_speed = hvel.length()
|
||||
|
||||
# Add a strong force in the wish direction
|
||||
var air_accel = params.air_strafe_accel * delta
|
||||
var new_hvel = hvel + wish_dir * air_accel
|
||||
|
||||
# Limit the speed so we don't gain speed purely from air control.
|
||||
# We only allow air control to change our direction (strafing)
|
||||
# or to accelerate us up to our walk speed if we started slow.
|
||||
var max_allowed_speed = maxf(current_speed, params.walk_speed)
|
||||
|
||||
if new_hvel.length() > max_allowed_speed:
|
||||
new_hvel = new_hvel.normalized() * max_allowed_speed
|
||||
|
||||
hvel = new_hvel
|
||||
var wish_speed: float = minf(machine.get_effective_speed(params.walk_speed), params.max_air_speed)
|
||||
var current_along := hvel.dot(wish_dir)
|
||||
var add_speed := wish_speed - current_along
|
||||
if add_speed > 0.0:
|
||||
var accel_speed: float = minf(params.air_strafe_accel * delta, add_speed)
|
||||
# Quake's trick: cap per-tick projection so sharp turns give the gain
|
||||
accel_speed = minf(accel_speed, params.air_wish_speed_cap)
|
||||
hvel += wish_dir * accel_speed
|
||||
else:
|
||||
# No input: slight air drag (very subtle)
|
||||
hvel *= (1.0 - 0.5 * delta)
|
||||
@@ -61,8 +57,8 @@ func update(delta: float) -> void:
|
||||
|
||||
# ── Landing ───────────────────────────────────────────────────────────
|
||||
if player.is_on_floor():
|
||||
machine.on_ground = true
|
||||
machine.current_jump_count = 0
|
||||
var fall_speed: float = maxf(-vel.y, 0.0)
|
||||
machine.notify_landed(fall_speed)
|
||||
|
||||
# Bunny hop: if jump was buffered or pressed on landing frame
|
||||
if (machine.input_jump_pressed or machine.jump_buffer_time > 0.0) and machine.jump_cooldown_timer <= 0.0:
|
||||
@@ -105,44 +101,25 @@ func update(delta: float) -> void:
|
||||
var fwd_wall = machine.detect_wall_forward()
|
||||
if fwd_wall.hit:
|
||||
if fwd_wall.is_short:
|
||||
# Instant Vault
|
||||
var look_dir := -player.global_transform.basis.z
|
||||
var h_look := Vector3(look_dir.x, 0.0, look_dir.z)
|
||||
if h_look.length_squared() > 0.01:
|
||||
h_look = h_look.normalized()
|
||||
player.velocity = h_look * params.wall_climb_vault_forward
|
||||
player.velocity.y = params.wall_climb_vault_up
|
||||
if player.vault_player:
|
||||
player.vault_player.play()
|
||||
|
||||
# Quick camera animation (tilt up and forward bob)
|
||||
var camera = player.camera
|
||||
if camera:
|
||||
var tween = player.create_tween()
|
||||
tween.tween_property(camera, "rotation_degrees:x", camera.rotation_degrees.x + 10.0, 0.15).set_trans(Tween.TRANS_SINE)
|
||||
tween.parallel().tween_property(camera, "v_offset", -0.2, 0.15).set_trans(Tween.TRANS_SINE)
|
||||
tween.chain().tween_property(camera, "rotation_degrees:x", camera.rotation_degrees.x, 0.2).set_trans(Tween.TRANS_SINE)
|
||||
tween.parallel().tween_property(camera, "v_offset", 0.0, 0.2).set_trans(Tween.TRANS_SINE)
|
||||
|
||||
machine.wall_cooldown_timer = 0.3
|
||||
machine.do_vault()
|
||||
return
|
||||
else:
|
||||
# Check if moving directly into wall and looking generally towards it
|
||||
var look_dir := -player.global_transform.basis.z
|
||||
var looking_towards := look_dir.dot(-fwd_wall.normal) > 0.0
|
||||
var moving_towards := wish_dir.dot(-fwd_wall.normal) > cos(deg_to_rad(params.wall_climb_max_angle))
|
||||
|
||||
|
||||
if looking_towards and moving_towards and machine.can_wall_climb:
|
||||
machine.wall_normal = fwd_wall.normal
|
||||
machine.switch_to("wall_climb")
|
||||
return
|
||||
|
||||
|
||||
# Fallback to wall run
|
||||
var wall_n := machine.detect_wall_horizontal()
|
||||
if wall_n != Vector3.ZERO:
|
||||
var w_look_dir := -player.global_transform.basis.z
|
||||
var h_look := Vector3(w_look_dir.x, 0.0, w_look_dir.z).normalized()
|
||||
|
||||
|
||||
# Require looking mostly along the wall (angle > 41 degrees from normal)
|
||||
# and inputting mostly along the wall (angle > 31 degrees from normal, allows W+A/D diagonally into wall)
|
||||
if abs(h_look.dot(wall_n)) < 0.75 and abs(wish_dir.dot(wall_n)) < 0.85:
|
||||
@@ -150,6 +127,6 @@ func update(delta: float) -> void:
|
||||
return
|
||||
|
||||
# ── Dash ──────────────────────────────────────────────────────────────
|
||||
if machine.input_dash:
|
||||
if machine.input_dash and machine.dash_cooldown_timer <= 0.0:
|
||||
machine.switch_to("dash")
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user