Files
agame/character/player/player_motor.gd
T
2026-10-09 15:51:24 +08:00

101 lines
4.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class_name PlayerMotor
extends Node
## 移动组件:重力、跳跃缓冲、水平加减速。
##
## 只算速度写回 body.velocity,**不调 move_and_slide()** —— 推进由 Player 统一做。
## 这样「算速度 → 推进 → 播动画」的顺序永远固定,不受节点树顺序影响。
## 跳跃输入缓冲(秒):按下跳跃后这段时间内若落地,自动补上一次起跳
const JUMP_BUFFER_TIME: float = 0.1
@export_group("移动")
## 【设计基准】以人类为参照做等比缩放:人 1.75 m,游戏惯例 walk 5.0 / sprint 8.5 m/s。
## 本角色身高 0.15 m,系数 k = 0.15 / 1.75 ≈ 0.0857,下面所有线性量都是原值 × k。
## 这样「每秒走过几个自身体长」和人类一致,动作比例看起来才对。
##
## 换尺寸时的公式:线性量(速度/加速度)× k',跳跃初速度 × √k'(因为跳跃高度 = v²/2g)。
##
## 常规移动速度(米/秒)—— 约 2.9 身长/秒,和人类走路同一比例
@export var walk_speed: float = 0.4286
## 疾跑速度(米/秒)—— 约 4.9 身长/秒
@export var sprint_speed: float = 0.7286
## 蹲行速度(米/秒)—— 默认是走速的一半。蹲下时按住 Shift 也不会疾跑。
## 注意:改这个值要同步改 player_animator.gd 的 sneak_ref_speed,否则蹲走时脚步会打滑。
@export var crouch_speed: float = 0.2143
## 地面加速度(米/秒²)—— 约 0.11 秒加到全速,起步干脆
@export var acceleration: float = 3.8571
## 空中加速度(米/秒²)
@export var air_acceleration: float = 1.0286
## 起跳初速度(米/秒)—— 跳跃高度 ≈ 0.109 m,约 0.73 身高
## 注意是 ×√k 而不是 ×k:跳跃高度 = v² / 2g,想让高度也等比缩,速度就得开方。
@export var jump_velocity: float = 1.4639
## 本帧是否在疾跑 / 是否按住蹲键。
## Player 读这两个值转交给 PlayerAnimator 切片段,crouching 还用来压低摄像机。
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()
# 只有真正按了方向键才允许疾跑(避免原地"疾跑"触发跑动动画)
var wants_sprint := Input.is_action_pressed("sprint") and input_dir.length_squared() > 0.000001
crouching = Input.is_action_pressed("crouch")
# 蹲伏优先于疾跑:蹲着时按住 Shift 不算疾跑,速度取蹲行速度。
sprinting = wants_sprint and not crouching
var target_speed := walk_speed
if crouching:
target_speed = crouch_speed
elif sprinting:
target_speed = sprint_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