UE5 蓝图 FPS 01 Event Tick

Event Tick

一 核心逻辑拆解(从左到右)

整个流程由一个红色节点触发,顺着白色的执行线(Execution Flow)向右依次执行:

1. 触发源:每帧更新

  • 节点: Event Tick

  • 功能: 这是逻辑的入口。引擎每帧都会调用它,输出的 Delta Seconds 是两帧之间的时间间隔。

2. 第一步:同步机械臂(相机)旋转

  • 数据获取: Get Control Rotation 节点获取当前控制器(通常是玩家鼠标或手柄视角)的旋转量(Rotator),并被展开成了 Roll (翻滚角)、Pitch (俯仰角)、Yaw (偏航角)。

  • 目标对象: Arm(通常是控制相机距离和旋转的 SpringArmComponent 弹簧臂)。

  • 操作: Set World Rotation 将控制器的 PitchYaw 赋值给机械臂,而 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 RotationReturn Value X (Roll) 也连进 Set World Rotation 会发生什么?

  • 视觉灾难: 游戏世界里的地平线会开始左右倾斜。当你斜着甩鼠标时,相机会像喝醉了一样产生侧翻,屏幕直接歪掉。

  • 常规设计: 在绝大多数第一人称(FPS)或第三人称(TPS)游戏中,除非角色处于"受击震动"、"飞船驾驶"或"卧倒"状态,否则相机的物理垂直面必须永远与地面垂直。也就是说,Roll 必须死死地固定为 0

3. 第二步:状态更新(自定义函数/事件)

  • 节点: Update Player StateUpdate 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

这个函数的逻辑非常纯粹:根据当前的角色状态,动态调整组件的最高移动速度。

  1. 输入源: * 获取左侧的枚举变量 E Player State(也就是上一步通过各种 Branch 判定出来的结果,包含 Idle, Run, Walk, Aim 等)。

  2. 核心枢纽(Select 节点):

    • 这个节点充当了 switch-case 语句或查找表(Lookup Table) 的角色。

    • 它将 E Player State 映射为具体的浮点数值(Float):

      • 当状态为 Idle 时 输出 400.0

      • 当状态为 Run 时 输出 600.0

      • 当状态为 Walk 时 输出 400.0

      • 当状态为 Aim 时 输出 265.0(瞄准时移动变慢,经典的射击游戏设定)

  3. 目标写入:

    • 从右下角的 Character Movement(移动组件)中拉出引线。

    • 调用 SET 最大行走速度(即 C++ 中的 MaxWalkSpeed),将 Select 节点过滤出来的数值写进去。

相关推荐
A charmer11 小时前
零基础学OC:变量与基本数据类型(C++开发者速通版)[特殊字符]
开发语言·c++·objective-c
楼田莉子12 小时前
C++20现代特性:概念与约束
开发语言·c++·后端·学习·c++20
aluluka12 小时前
C++ 20 协程的探索
c++·c++20
重生之小比特12 小时前
【初阶C++】入门基础
开发语言·c++
程序leo源12 小时前
Qt界面优化详解
linux·c语言·开发语言·c++·qt·c#
一只QAQ12 小时前
c++小巧思
c++·笔记·学习
郝学胜-神的一滴12 小时前
Qt 高级开发 017:中文乱码
开发语言·c++·qt·程序人生·用户界面
晚风予卿云月12 小时前
【模拟】多项式输出 & 蛇形方阵 & 字符串展开
c++·算法·模拟算法·随笔·竞赛练习
智者知已应修善业13 小时前
【51单片机按键加减1若不释放自动加减】2023-11-24
c++·经验分享·笔记·算法·51单片机