feat: implement player movement controller and state machine with dash, slide, and wall-run mechanics
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user