Event Tick

一 核心逻辑拆解(从左到右)
整个流程由一个红色节点触发,顺着白色的执行线(Execution Flow)向右依次执行:
1. 触发源:每帧更新
-
节点:
Event Tick -
功能: 这是逻辑的入口。引擎每帧都会调用它,输出的
Delta Seconds是两帧之间的时间间隔。
2. 第一步:同步机械臂(相机)旋转
-
数据获取:
Get Control Rotation节点获取当前控制器(通常是玩家鼠标或手柄视角)的旋转量(Rotator),并被展开成了Roll(翻滚角)、Pitch(俯仰角)、Yaw(偏航角)。 -
目标对象:
Arm(通常是控制相机距离和旋转的SpringArmComponent弹簧臂)。 -
操作:
Set World Rotation将控制器的 Pitch 和 Yaw 赋值给机械臂,而 Roll 保持为 0.0。 -
效果: 实现玩家动鼠标时,游戏内的相机视角(通过机械臂)跟着上下左右旋转,且视角不会发生左右倾斜。
那么问题来了:为什么传入 y和z?
这个问题直击虚幻引擎中空间坐标系(3D Coordinate System)和欧拉角(Rotator)的核心设计。
简单直接的答案是:在虚幻引擎中,Y 轴(Pitch)控制上下抬头,Z 轴(Yaw)控制左右转头。传入这两个轴,相机才能跟随玩家鼠标的上下左右移动来环顾四周。
为了让你彻底明白,我们需要拆解一下 UE 的旋转模型以及为什么唯独要漏掉 X 轴。
1. 虚幻引擎的欧拉角(FRotator)定义
在三维空间中,UE 采用的是 左手坐标系(X向前的航空坐标系)。对于旋转(Rotator),它的三个轴定义如下:
| 轴向 | 欧拉角名称 | 控制的动作 | 鼠标的对应映射 |
|---|---|---|---|
| X 轴 | Roll(翻滚角) | 围绕前进轴旋转(就像飞机左右侧翻、歪脖子) | 通常不映射给鼠标 |
| Y 轴 | Pitch(俯仰角) | 围绕左右轴旋转(就像上下抬头、低头) | 对应鼠标上下移动(Y轴) |
| Z 轴 | Yaw(偏航角) | 围绕上下轴旋转(就像左右转头、转身) | 对应鼠标左右移动(X轴) |
2. 为什么要传入 Y (Pitch) 和 Z (Yaw) ?
在这段蓝图中,Get Control Rotation 获取的是玩家控制器的旋转状态(也就是玩家鼠标搓出来的视角方向)。
-
传入 Z (Yaw): 当你把鼠标在桌面上左右 晃动时,控制器的 Z 值(Yaw)在发生改变。把它传给
Arm(机械臂),游戏里的相机就会绕着角色左右转圈,让你看到角色的左边和右边。 -
传入 Y (Pitch): 当你把鼠标在桌面上上下 推拉时,控制器的 Y 值(Pitch)在发生改变。把它传给
Arm,相机会绕着角色上下抬高或降低,让你看到头顶的天空或脚下的地板。
结论: 只有同时把 Y 和 Z 传进去,玩家的视角才能实现完整的"上下仰望 + 左右环视"。
3. 为什么偏偏把 X (Roll) 设为 0.0?
你可以试想一下,如果把 Get Control Rotation 的 Return Value X (Roll) 也连进 Set World Rotation 会发生什么?
-
视觉灾难: 游戏世界里的地平线会开始左右倾斜。当你斜着甩鼠标时,相机会像喝醉了一样产生侧翻,屏幕直接歪掉。
-
常规设计: 在绝大多数第一人称(FPS)或第三人称(TPS)游戏中,除非角色处于"受击震动"、"飞船驾驶"或"卧倒"状态,否则相机的物理垂直面必须永远与地面垂直。也就是说,Roll 必须死死地固定为 0。
3. 第二步:状态更新(自定义函数/事件)
-
节点:
Update Player State和Update Move Speed -
功能: 这两个是自定义的函数或事件。它们顺着每帧的执行流被调用,用于实时更新玩家的内部状态机和移动速度数据。因为 Target 是
self,说明这些逻辑就写在当前蓝图类内部。
4. 第三步:Debug 调试打印
-
数据获取: 从角色的
Character Movement(移动组件)中拉出一条线,获取它的Max Walk Speed(当前最大步行速度,Float 类型)。 -
类型转换: 绿色的浮点数(Float)通过一个转换节点(那个小小的绿色变粉色的节点),隐式转换成了粉色的字符串(String)。
-
操作:
Print String节点将这个速度值实时打印在游戏屏幕的左上角(Print to Screen已勾选),并且会向日志输出。
update函数:
1.update player state

从软件工程的角度来看,这是一个非常典型的状态机(State Machine)的分支判定逻辑 。它通过嵌套的 if-else(在蓝图中体现为 Branch 节点)和多输入条件,来实时决定并更新玩家当前的枚举状态 E_PlayerState。
核心判定逻辑拆解(决策树分析)
我们可以顺着白色的执行线和红色的布尔条件线,把这个复杂的连线图拆解成一棵清晰的决策树:
阶梯 1:优先判断是否在"瞄准"
-
输入条件: 获取布尔变量
Want to Aim(玩家是否按下瞄准键)。 -
逻辑分支:
-
True: 直接进入最上方的SET节点,将状态切换为Aim(瞄准) 。逻辑在此终结,不再关心后面的移动和速度。 -
False: 进入下一个分支(阶梯 2)。
-
阶梯 2:判断是否有"前后移动输入"
-
输入条件: 获取浮点数变量
Move X(通常代表玩家按 W/S 键的前后输入轴数值)。通过一个>节点,判断Move X > 0.0。 -
逻辑分支:
-
True(有向前移动的意图): 走向中路的Branch。此时进一步判断玩家是否按下了跑动键Want to Run:-
True: 状态设置为Run(奔跑)。 -
False: 状态设置为Walk(步行)。
-
-
False(没有向前移动,可能停下或在后退): 走向下路的Branch(阶梯 3)。
-
阶梯 3:通过物理速度兜底(防滑步/检测静止)
-
数据获取: 从
Character Movement组件中获取三维速度向量Velocity,通过Vector Length计算出当前实际的物理速率(Speed)。 -
输入条件: 判断物理速率是否
> 0.0(即角色在游戏世界里是否真的在动)。 -
逻辑分支:
-
True(虽然没有向前输入,但由于惯性或后退,角色还在移动): 状态设置为Walk(步行)。 -
False(速度彻底为 0,完全静止): 状态设置为Idle(待机/空闲)。
-
2.update player move speed

这个函数的逻辑非常纯粹:根据当前的角色状态,动态调整组件的最高移动速度。
-
输入源: * 获取左侧的枚举变量
E Player State(也就是上一步通过各种Branch判定出来的结果,包含 Idle, Run, Walk, Aim 等)。 -
核心枢纽(
Select节点):-
这个节点充当了
switch-case语句或查找表(Lookup Table) 的角色。 -
它将
E Player State映射为具体的浮点数值(Float):-
当状态为
Idle时 输出400.0 -
当状态为
Run时 输出600.0 -
当状态为
Walk时 输出400.0 -
当状态为
Aim时 输出265.0(瞄准时移动变慢,经典的射击游戏设定)
-
-
-
目标写入:
-
从右下角的
Character Movement(移动组件)中拉出引线。 -
调用
SET 最大行走速度(即 C++ 中的MaxWalkSpeed),将Select节点过滤出来的数值写进去。
-