feat: implement player movement controller and state machine with dash, slide, and wall-run mechanics

This commit is contained in:
DottsGit
2026-06-03 21:55:10 -04:00
parent 5726aa1043
commit da277e9d35
14 changed files with 1092 additions and 329 deletions
+85 -35
View File
@@ -6,55 +6,105 @@ var params: MovementParams:
get: return machine.params
var elapsed: float = 0.0
var _saved_capsule_height: float = 0.0
var _slide_direction: Vector3 = Vector3.ZERO
func enter(_data: Dictionary = {}) -> void:
elapsed = 0.0
machine.register_chain_mechanic("slide")
# Lower player capsule
var player_node = machine.player
var shape: CapsuleShape3D = machine.player.shape_owner_get_shape(0, 0)
# Save original capsule height and halve it
var shape: CapsuleShape3D = _get_capsule()
if shape:
shape.height = shape.height * 0.5
_saved_capsule_height = shape.height
shape.height = _saved_capsule_height * 0.5
# Slide in the direction of current velocity (momentum-based)
var hvel := Vector3(machine.player.velocity.x, 0.0, machine.player.velocity.z)
if hvel.length_squared() > 0.01:
_slide_direction = hvel.normalized()
else:
# Fallback: player forward
_slide_direction = -machine.player.global_transform.basis.z
_slide_direction.y = 0.0
_slide_direction = _slide_direction.normalized()
# Set initial slide velocity
var slide_speed := maxf(hvel.length(), params.slide_speed)
machine.player.velocity.x = _slide_direction.x * machine.get_effective_speed(slide_speed)
machine.player.velocity.z = _slide_direction.z * machine.get_effective_speed(slide_speed)
func exit() -> void:
pass
machine.slide_cooldown_timer = params.slide_cooldown
# Restore original capsule height
var shape: CapsuleShape3D = _get_capsule()
if shape and _saved_capsule_height > 0.0:
shape.height = _saved_capsule_height
func update(delta: float) -> void:
elapsed += delta
var vel: Vector3 = machine.player.velocity
var forward: Vector3 = -machine.player.global_transform.basis.z
if forward.length_squared() > 1.0:
forward = forward.normalized()
vel.x = forward.x * params.slide_speed * machine.get_effective_speed(1.0)
vel.z = forward.z * params.slide_speed * machine.get_effective_speed(1.0)
vel.x *= pow(params.slide_friction, delta)
vel.z *= pow(params.slide_friction, delta)
machine.player.velocity = vel
machine.player.move_and_slide()
var player := machine.player
var vel: Vector3 = player.velocity
if machine.player.is_on_floor():
machine.on_ground = true
var speed := Vector3(vel.x, 0.0, vel.z).length()
if speed < params.slide_min_speed or elapsed > params.slide_duration:
var end_shape: CapsuleShape3D = machine.player.shape_owner_get_shape(0, 0)
if end_shape:
end_shape.height = end_shape.height * 2.0
machine.register_chain_mechanic("slide_end")
machine.switch_to("ground")
return
# ── Apply friction to horizontal velocity ─────────────────────────────
vel.x *= pow(params.slide_friction, delta * 10.0)
vel.z *= pow(params.slide_friction, delta * 10.0)
# ── Gravity (for slopes) ──────────────────────────────────────────────
if not player.is_on_floor():
vel.y -= params.gravity * delta
else:
var fall_shape: CapsuleShape3D = machine.player.shape_owner_get_shape(0, 0)
if fall_shape:
fall_shape.height = fall_shape.height * 2.0
machine.on_ground = false
machine.switch_to("air")
vel.y = -2.0 # Strong floor snap
if not machine.input_crouch or not machine.input_sprint:
var cancel_shape: CapsuleShape3D = machine.player.shape_owner_get_shape(0, 0)
if cancel_shape:
cancel_shape.height = cancel_shape.height * 2.0
machine.register_chain_mechanic("slide_cancel")
player.velocity = vel
player.move_and_slide()
# ── Update ground status ──────────────────────────────────────────────
if player.is_on_floor():
machine.on_ground = true
machine.current_jump_count = 0
else:
machine.on_ground = false
# ── End conditions ────────────────────────────────────────────────────
var hspeed := Vector3(player.velocity.x, 0.0, player.velocity.z).length()
# Slide ended: too slow
if hspeed < params.slide_min_speed:
machine.switch_to("ground")
return
# Player released crouch
if not machine.input_crouch:
machine.switch_to("ground")
return
# Fell off edge
if not machine.on_ground and machine.coyote_timer <= 0.0:
machine.switch_to("air")
return
# Jump out of slide
if machine.input_jump_just_pressed:
player.velocity.y = params.jump_velocity
var hvel := Vector3(player.velocity.x, 0.0, player.velocity.z)
if hvel.length_squared() > 0.01:
var dir := hvel.normalized()
var jump_speed := minf(hvel.length() + params.slide_jump_speed_boost, params.bunny_hop_speed_cap)
player.velocity.x = dir.x * jump_speed
player.velocity.z = dir.z * jump_speed
machine.current_jump_count = 1
machine.on_ground = false
machine.register_chain_mechanic("slide_jump")
machine.switch_to("air")
return
func _get_capsule() -> CapsuleShape3D:
for child in machine.player.get_children():
if child is CollisionShape3D and child.shape is CapsuleShape3D:
return child.shape as CapsuleShape3D
return null