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
+32 -33
View File
@@ -6,9 +6,22 @@ signal chain_updated(count: int, bonus: float)
@export var params: MovementParams = preload("res://movement/movement_params.gd").new()
var _machine: MovementStateMachine = null
var head_pivot: Node3D = null # FPSCameraRig node
var camera: Camera3D = null
func _ready() -> void:
var sm := _ensure_machine()
if sm:
sm.player = self
sm.params = params
sm.movement_event.connect(_on_movement_event)
# Find camera rig
head_pivot = get_node_or_null("HeadPivot")
if head_pivot:
camera = head_pivot.get_node_or_null("Camera3D")
# Explicitly ensure this node ticks after being attached by set_script().
func _ensure_process() -> void:
set_process(true)
set_physics_process(true)
@@ -25,49 +38,35 @@ func _ensure_machine() -> MovementStateMachine:
return null
func _unhandled_input(event: InputEvent) -> void:
func _physics_process(_delta: float) -> void:
var sm := _ensure_machine()
if not sm:
return
# Poll input in _physics_process so it's synchronized with the state machine tick
sm.input_jump_just_pressed = Input.is_action_just_pressed("jump")
sm.input_jump_pressed = Input.is_action_pressed("jump")
sm.input_sprint = Input.is_action_pressed("sprint")
sm.input_crouch = Input.is_action_pressed("crouch")
sm.input_dash = Input.is_action_just_pressed("dash")
sm.input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
# Raw 2D input
var raw_input := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
func _ready() -> void:
var sm := _ensure_machine()
if sm:
sm.player = self
sm.params = params
sm.movement_event.connect(_on_movement_event)
_ensure_process()
# Transform input direction by player yaw so movement is camera-relative
var forward := -global_transform.basis.z
forward.y = 0.0
forward = forward.normalized()
var right := global_transform.basis.x
right.y = 0.0
right = right.normalized()
var world_dir := (forward * (-raw_input.y) + right * raw_input.x)
if world_dir.length_squared() > 1.0:
world_dir = world_dir.normalized()
func _process(_delta: float) -> void:
var sm := _ensure_machine()
if not sm:
return
sm.input_jump_just_pressed = Input.is_action_just_pressed("jump")
sm.input_jump_pressed = Input.is_action_pressed("jump")
sm.input_sprint = Input.is_action_pressed("sprint")
sm.input_crouch = Input.is_action_pressed("crouch")
sm.input_dash = Input.is_action_just_pressed("dash")
sm.input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
# DEBUG: emit every frame so we can see whether inputs reach the controller.
var p = sm.player
print("MOVEMENT_DEBUG state=%s vel=%s input_dir=%s jump=%s sprint=%s crouch=%s dash=%s" % [
sm.current_state,
p.velocity if p else "NO_PLAYER",
sm.input_dir,
sm.input_jump_pressed,
sm.input_sprint,
sm.input_crouch,
sm.input_dash,
])
sm.input_dir = raw_input
sm.wish_dir_world = world_dir
func _on_movement_event(ev: String, data: Dictionary) -> void: