class_name PlayerMotor extends Node ## 移动组件:重力、跳跃缓冲、水平加减速。 ## ## 只算速度写回 body.velocity,**不调 move_and_slide()** —— 推进由 Player 统一做。 ## 这样「算速度 → 推进 → 播动画」的顺序永远固定,不受节点树顺序影响。 ## 跳跃输入缓冲(秒):按下跳跃后这段时间内若落地,自动补上一次起跳 const JUMP_BUFFER_TIME: float = 0.1 @export_group("移动") ## 常规移动速度(米/秒) @export var walk_speed: float = 5.0 ## 疾跑速度(米/秒) @export var sprint_speed: float = 8.5 ## 地面加速度(米/秒²) @export var acceleration: float = 45.0 ## 空中加速度(米/秒²) @export var air_acceleration: float = 12.0 ## 起跳初速度(米/秒) @export var jump_velocity: float = 5.0 ## 本帧是否在疾跑 / 是否按住蹲键。Player 读这两个值转交给 PlayerAnimator。 var sprinting: bool = false var crouching: bool = false var _body: CharacterBody3D var _look_rig: Node3D var _gravity: float = 9.8 var _jump_buffer: float = 0.0 ## 由 Player 在 _ready() 注入依赖。look_rig 用来把输入转成「相对视线」的方向。 func setup(body: CharacterBody3D, look_rig: Node3D) -> void: _body = body _look_rig = look_rig _gravity = float(ProjectSettings.get_setting("physics/3d/default_gravity", 9.8)) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("jump"): _jump_buffer = JUMP_BUFFER_TIME ## 每物理帧由 Player 调用一次,必须排在 move_and_slide() 之前。 ## 只写 _body.velocity,不做位移推进。 func step(delta: float) -> void: if _body == null or _look_rig == null: return # --- 重力 --- if _body.is_on_floor(): if _body.velocity.y < 0.0: _body.velocity.y = 0.0 else: _body.velocity.y -= _gravity * delta # --- 跳跃(读取输入缓冲)--- if _jump_buffer > 0.0: _jump_buffer -= delta if _body.is_on_floor(): _body.velocity.y = jump_velocity _jump_buffer = 0.0 # --- 相对视线方向的水平输入 --- # 只用 rig 的 basis(只有 yaw),低头看地时前进方向不会跟着往下扎。 var input_dir := Input.get_vector("move_left", "move_right", "move_back", "move_forward") var look_basis := _look_rig.global_transform.basis var wish := look_basis.x * input_dir.x - look_basis.z * input_dir.y wish.y = 0.0 if wish.length_squared() > 0.000001: wish = wish.normalized() # 只有真正按了方向键才允许疾跑(避免原地"疾跑"触发跑动动画) sprinting = Input.is_action_pressed("sprint") and input_dir.length_squared() > 0.000001 crouching = Input.is_action_pressed("crouch") var target_speed := sprint_speed if sprinting else walk_speed var accel := acceleration if _body.is_on_floor() else air_acceleration var horizontal := Vector2(_body.velocity.x, _body.velocity.z) horizontal = horizontal.move_toward(Vector2(wish.x, wish.z) * target_speed, accel * delta) _body.velocity.x = horizontal.x _body.velocity.z = horizontal.y