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
@@ -15,10 +15,10 @@ func enter(_data: Dictionary = {}) -> void:
|
||||
if machine.player.wallrun_player and not machine.player.wallrun_player.playing:
|
||||
machine.player.wallrun_player.play()
|
||||
|
||||
# Give an initial upward boost
|
||||
# Carry existing upward momentum into the climb so jump→climb chains flow
|
||||
var player := machine.player
|
||||
player.velocity.y = params.wall_climb_speed
|
||||
|
||||
player.velocity.y = maxf(player.velocity.y, params.wall_climb_speed)
|
||||
|
||||
var hvel := Vector3(player.velocity.x, 0.0, player.velocity.z)
|
||||
player.velocity.x = hvel.x * 0.5
|
||||
player.velocity.z = hvel.z * 0.5
|
||||
@@ -30,7 +30,7 @@ func exit() -> void:
|
||||
func update(delta: float) -> void:
|
||||
elapsed += delta
|
||||
var player := machine.player
|
||||
|
||||
|
||||
if player.wallrun_player and not player.wallrun_player.playing:
|
||||
player.wallrun_player.play()
|
||||
|
||||
@@ -39,15 +39,15 @@ func update(delta: float) -> void:
|
||||
_detach()
|
||||
return
|
||||
|
||||
# ── Look/Move away to transition to wall run ──────────────────────────────
|
||||
# ── Look/Move away to transition to wall run ──────────────────────────
|
||||
var look_dir := -player.global_transform.basis.z
|
||||
var wish_dir := machine.wish_dir_world
|
||||
|
||||
|
||||
var looking_towards := look_dir.dot(-machine.wall_normal) > 0.0
|
||||
var moving_sideways := false
|
||||
if wish_dir.length_squared() > 0.01:
|
||||
moving_sideways = wish_dir.dot(-machine.wall_normal) <= cos(deg_to_rad(params.wall_climb_max_angle))
|
||||
|
||||
|
||||
if not looking_towards or moving_sideways:
|
||||
# We are looking away or moving sideways, transition to wall run
|
||||
# The wall run state will figure out tangent and side
|
||||
@@ -58,22 +58,36 @@ func update(delta: float) -> void:
|
||||
else:
|
||||
_detach()
|
||||
return
|
||||
|
||||
|
||||
# ── Jump off the wall (backward kick) ─────────────────────────────────
|
||||
if machine.input_jump_just_pressed and machine.jump_cooldown_timer <= 0.0:
|
||||
player.velocity = machine.wall_normal * params.wall_run_jump_off_normal
|
||||
player.velocity.y = params.jump_velocity
|
||||
machine.register_chain_mechanic("wall_jump")
|
||||
machine.jump_cooldown_timer = params.jump_cooldown
|
||||
machine.movement_event.emit("jump", {})
|
||||
if player.jump_player:
|
||||
player.jump_player.play()
|
||||
_detach()
|
||||
return
|
||||
|
||||
# ── Ledge Vault Check ─────────────────────────────────────────────────
|
||||
var fwd_wall = machine.detect_wall_forward()
|
||||
if not fwd_wall.hit:
|
||||
# The forward lower ray missed, we fell off the wall completely
|
||||
_detach()
|
||||
return
|
||||
|
||||
|
||||
if fwd_wall.is_short:
|
||||
# The upper ray missed, meaning we reached the top of the wall!
|
||||
_vault()
|
||||
machine.do_vault()
|
||||
machine.wall_normal = Vector3.ZERO
|
||||
machine.wall_side = 0.0
|
||||
machine.switch_to("air")
|
||||
return
|
||||
|
||||
# ── Movement ──────────────────────────────────────────────────────────
|
||||
# Slow down over time
|
||||
var speed_factor = maxf(0.0, 1.0 - (elapsed / params.wall_climb_duration))
|
||||
# ── Movement: ease out over the climb ─────────────────────────────────
|
||||
var speed_factor: float = pow(maxf(0.0, 1.0 - (elapsed / params.wall_climb_duration)), 0.7)
|
||||
player.velocity.y = params.wall_climb_speed * speed_factor
|
||||
|
||||
# Push slightly into the wall to maintain contact
|
||||
@@ -95,31 +109,3 @@ func _detach() -> void:
|
||||
machine.wall_cooldown_timer = 0.3
|
||||
machine.can_wall_climb = false
|
||||
machine.switch_to("air")
|
||||
|
||||
func _vault() -> void:
|
||||
var player := machine.player
|
||||
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()
|
||||
|
||||
# Impulse
|
||||
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_normal = Vector3.ZERO
|
||||
machine.wall_side = 0.0
|
||||
machine.wall_cooldown_timer = 0.3
|
||||
machine.switch_to("air")
|
||||
|
||||
Reference in New Issue
Block a user