角色动画

This commit is contained in:
2026-10-02 09:32:11 +08:00
parent 5173b45066
commit d367c8370b
10 changed files with 240 additions and 68 deletions
+17 -14
View File
@@ -1,15 +1,13 @@
extends CharacterBody3D
## 第三人称控制器:只管「怎么动」—— 输入、移动物理、相机、血条、联机分叉。
## 表现层(动画)在 Body/PlayerAnimator 里,本文件不认识任何片段名。
## 表现层(动画)在 Body/AnimationPlayer 上(挂 player_animator.gd),本文件不认识任何片段名。
##
## 场景结构:
## Player (CharacterBody3D) ← 只负责移动与碰撞,自身不旋转
## ├─ Collision (CollisionShape3D, CapsuleShape3D)
## ├─ Body (Node3D) ← 视觉层,朝移动方向转身;换角色模型时挂在它下面
## │ ├─ PlayerAnimator (PlayerAnimator.gd) ← 动作状态机:只读运动、只写动画
## │ └─ character-male-e2 (Node3D)
## │ ├─ character-male-e/Skeleton3D ← 骨骼 + 蒙皮网格(character-male-e.glb)
## │ └─ AnimationPlayer ← 动作库,由 PlayerAnimator 驱动
## │ ├─ mouse (Node3D) ← 模型(mouse.glb):MouseRig/Skeleton3D + 各部位 MeshInstance3D
## │ └─ AnimationPlayer ← 动作库 + 表现层(挂 player_animator.gd):只读运动、只写动画
## ├─ CameraRig (Node3D) ← 承担偏航 yaw,独立于本体旋转
## │ └─ SpringArm (SpringArm3D) ← 承担俯仰 pitch,自动避障
## │ └─ Camera (Camera3D)
@@ -20,7 +18,7 @@ extends CharacterBody3D
## 滑索:走到缆下按 E 挂上,沿缆滑向低端,缆上按 S 刹车、空格跳离。
## 手感参数(速度/刹车/吸附半径)全在 Zipline 组件上,本文件只读不定义 —— 见文件末尾。
##
## 操作:WASD/方向键移动,鼠标转视角,Shift 疾跑,空格跳跃,E 滑索,Esc 释放/锁定鼠标
## 操作:WASD/方向键移动,鼠标转视角,Shift 疾跑,Ctrl 蹲下(蹲着走即潜行),空格跳跃,E 滑索,Esc 释放/锁定鼠标
## 跳跃输入缓冲帧数(输入事件在渲染帧到达,物理帧读取,避免漏跳)
const JUMP_BUFFER_FRAMES: int = 6
@@ -62,8 +60,8 @@ const JUMP_BUFFER_FRAMES: int = 6
## 滑索操作提示,同样在 player.tscn 里配好,默认隐藏
@onready var _hint: Label3D = $InteractHint
## 动画状态机(挂在 Body 下)。只把「运动结果」交给它,也只知道它会自己播动画。
@onready var animator: PlayerAnimator = $Body/PlayerAnimator
## 动画状态机(挂在 Body/AnimationPlayer 上)。只把「运动结果」交给它,也只知道它会自己播动画。
@onready var animator: PlayerAnimator = $Body/AnimationPlayer
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity", 9.8))
var _yaw: float = 0.0
@@ -197,7 +195,12 @@ func _physics_process(delta: float) -> void:
move_and_slide()
# 动画只读运动结果。控制器不认识任何片段名,动画器也不认识输入。
animator.set_motion(velocity, is_on_floor(), Input.is_action_pressed("sprint"))
animator.set_motion(
velocity,
is_on_floor(),
Input.is_action_pressed("sprint"),
Input.is_action_pressed("crouch"),
)
# ---------------- 滑索 ----------------
@@ -256,10 +259,10 @@ func _detach_zipline(hop: bool = false) -> void:
## 让物理引擎去推反而会和「贴着曲线走」这个硬约束打架。
## velocity 仍然维护 —— 一是给动画器读,二是脱手后能带着惯性飞出去。
func _ride_zipline(delta: float) -> void:
var tan := _zipline.tangent(_zip_t) * float(_zip_dir)
var tan_dir := _zipline.tangent(_zip_t) * float(_zip_dir)
# 重力沿缆方向的分量:缆越陡加速越猛,平缆上几乎不加速。
# 下坡时 tan.y 是负的,所以前面要加负号。
_zip_speed += -_gravity * _zipline.gravity_scale * tan.y * delta
# 下坡时 tan_dir.y 是负的,所以前面要加负号。
_zip_speed += -_gravity * _zipline.gravity_scale * tan_dir.y * delta
if Input.is_action_pressed("move_back"):
_zip_speed -= _zipline.brake_accel * delta
_zip_speed = clampf(_zip_speed, 0.0, _zipline.max_speed)
@@ -276,8 +279,8 @@ func _ride_zipline(delta: float) -> void:
_zip_t = next_t
_zip_grab_offset = _zip_grab_offset.lerp(Vector3.ZERO, clampf(delta / 0.12, 0.0, 1.0))
global_position = _zipline.ride_point(_zip_t) + _zip_grab_offset
velocity = tan * _zip_speed
var travel_yaw := atan2(-tan.x, -tan.z)
velocity = tan_dir * _zip_speed
var travel_yaw := atan2(-tan_dir.x, -tan_dir.z)
body.rotation.y = lerp_angle(body.rotation.y, travel_yaw, clampf(turn_speed * delta, 0.0, 1.0))
# 相机也跟着摆到行进方向背后 —— 否则人是横着飞的,看不见前面要去哪。
# 速率刻意比转身慢:玩家用鼠标看的视角不会被立刻拽回去。