feat(movement): add movement state machine with air/ground/wallrun/slide/dash/ground states
- MovementStateMachine: core state machine base - MovementParams: export-rich tunable resource - StateGround/StateAir: grounded & aeriel locomotion with coyote, buffered jump, bunny hop - StateWallRun / StateWallCling: wall-based traversal with wall-jump off - StateSlide: sprint-parallel slide with friction fallout - StateDash: burst dash with momentum brake - PlayerMovementController: entry point wiring to CharacterBody3D - input_map.cfg: full action map reference file - characters/player/player_base.tscn: placeholder scene - movement/tests/test_fsm_runner.gd: 9-state unit test runner Closes #1
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
extends Node
|
||||
class_name StateAir
|
||||
|
||||
var machine: MovementStateMachine
|
||||
var params: MovementParams:
|
||||
get: return machine.params
|
||||
|
||||
|
||||
func enter(_data: Dictionary = {}) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func exit() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func update(delta: float) -> void:
|
||||
var vel: Vector3 = machine.player.velocity
|
||||
|
||||
# Apply standard gravity
|
||||
vel.y -= params.gravity * delta
|
||||
|
||||
# Input direction
|
||||
var input_v := machine.input_dir
|
||||
var wish_dir := Vector3(input_v.x, 0.0, -input_v.y)
|
||||
if wish_dir.length_squared() > 1.0:
|
||||
wish_dir = wish_dir.normalized()
|
||||
|
||||
var effective_speed := machine.get_effective_speed(params.max_air_speed)
|
||||
|
||||
var hvel := Vector3(vel.x, 0.0, vel.z)
|
||||
var target_vel := wish_dir * effective_speed * params.air_control
|
||||
|
||||
hvel = hvel.lerp(Vector3(hvel.x + target_vel.x, 0.0, hvel.z + target_vel.z), 1.0 - exp(-params.air_acceleration * delta))
|
||||
|
||||
vel.x = hvel.x
|
||||
vel.z = hvel.z
|
||||
|
||||
# Bunny hop: if jump pressed and on ground near-peak of arc → extra boost
|
||||
if machine.input_jump_just_pressed and machine.on_ground:
|
||||
vel.y = params.jump_velocity * params.bunny_hop_impulse
|
||||
machine.player.velocity = vel
|
||||
machine.register_chain_mechanic("bunny_hop")
|
||||
machine.current_jump_count += 1
|
||||
machine.on_ground = false
|
||||
machine.switch_to("air")
|
||||
return
|
||||
|
||||
# Double Jump
|
||||
if machine.input_jump_just_pressed and not machine.on_ground and machine.current_jump_count < params.double_jump_max_count:
|
||||
vel.y = params.double_jump_velocity
|
||||
machine.player.velocity = vel
|
||||
machine.current_jump_count += 1
|
||||
machine.register_chain_mechanic("double_jump")
|
||||
machine.switch_to("air")
|
||||
return
|
||||
|
||||
machine.player.velocity = vel
|
||||
machine.player.move_and_slide()
|
||||
|
||||
# Landing
|
||||
if machine.player.is_on_floor():
|
||||
machine.on_ground = true
|
||||
machine.current_jump_count = 0
|
||||
machine.switch_to("ground")
|
||||
return
|
||||
|
||||
# Wall run transition
|
||||
if not machine.wall_normal.is_zero_approx():
|
||||
machine.switch_to("wall_run")
|
||||
Reference in New Issue
Block a user