目录
7.10.2 Create Dynamic Material Instance
7.14 案例十四 —— 背包元素拖拽到快捷栏 / 快捷栏元素互相拖拽
7.15 案例十五 —— 背包元素拖拽到鼠标指定位置(世界场景中)
一、什么是 UI 系统
UI 全称 User Interface (用户界面)。开始菜单界面、设置界面、背包界面等等都属于 UI 界面。
在 UE3 / UE4 时期,HUD(Head-Up Display)就是主要的 UI 系统,但使用起来比较麻烦,需要自己编写绘制逻辑。
在 UE4 之后,Epic 推出了 UMG(Unreal Motion Graphics UI Designer),更直观强大。可以拖拽按钮、文本、图片,像在 Photoshop 或 Unity 的 UI 系统里那样设计界面。
但是 HUD 仍然存在,因为它属于底层的 GameMode 系统,常常用来管理 UMG Widget 的创建与显示。可以这么理解:
- HUD = 显示层入口
- UMG = 具体的界面内容
二、创建控件蓝图
控件蓝图 规范命名为 WBP_。


我们尽量把变量存到 BP_HUD 或者 WBP_MainWidget 中,因为它们两个是一直存在的。如果你存到某个子控件蓝图(比如某个按钮)中,容易造成内存泄漏问题。
三、添加到视口

上述方法一般写在 BP_HUD 中(之后我们就可以只对 WBP_MainWidget 操作就可以了)。
举个例子,在 LOL 中玩家需要在界面看到 血条、经验条、小地图等。这些 UI 在 HUD 初始化时创建并添加到屏幕,不会依赖某一个关卡。
什么时候在 关卡蓝图 中写呢?
关卡蓝图只控制该关卡独有的逻辑,比如你做了一个特定关卡,里面的 UI 只在特定关卡中出现,在其他关卡中不需要,那么你可以写在关卡蓝图中。
四、设计器
4.1 插槽(Canvas Panel Slot)
4.1.1 锚点
锚点是 控件相对于父容器(Canvas Panel)的定位参考点,定义了控件如何随着屏幕尺寸变化而调整自身位置和大小。
举个例子,我想在游戏界面的右下方设置小地图,首先我需要将锚点移到右下角。注意观察控件初始位置。

现在我有两种方法,第一种 调整位置 X = Y = -500。

第二种 调整 Alignment X = Y = 1。

4.1.2 位置与 Alignment
见上方案例。
4.1.3 尺寸
略
4.1.4 ZOrder
如下所示,当三个控件 ZOrder = 0 时,左侧箭头 越往下优先级越高。

如果我们将某个控件 ZOrder 设置的非常大,那么它也会优先显示。(首先看这个)

【扩展】比如我们在 Overlay 下面有 Image 和 Button,如果我们想让按钮点击生效,可以采取以下几种方式:
- 将 Button 位置调整到 Image 下面。
- 如果 Button 位置在 Image 上面,调整两者 ZOrder 也可。
- 如果 Button 位置在 Image 上面,调整 Image 的 Visibility 也可。

【扩展 + +】上述 Visibility 属性还有很多使用场景。现在设想一个场景,普通用户面对一个可交互物体会出现 2 个按钮,但 VIP 用户面对一个可交互物体会出现 4 个按钮。我们的做法是:一开始生成 4 个按钮,如果遇到普通用户,就将后两个按钮 的 Visibility 属性进行设置即可。

4.2 插槽(Other Panel Slot)
面板控件(除 Canvas Panel )下的子控件是没有上述属性的,只能通过 填充和对齐 来调整位置。
4.3 控件介绍
五、事件图表

六、资产设置
在 UI 设计过程中,我们会用到很多纹理贴图、材质、Sprite 等等。需要对其进行简单设置。
纹理设置如下:

如果有很多纹理,怎么一口气进行设置呢?如下操作:

材质设置如下:

七、交互通信案例
7.1 案例一
我想让角色走到 可交互物体前 时,屏幕下方提示 按 F 进行交互。
首先我们在设计器中进行设计,然后将 该控件 设置为变量。

在事件图表中编写逻辑。


在 BP_Parent 中编写逻辑调用 WBP_MainWidget 的函数。

7.2 案例二 —— 播放动画 / 鼠标显示
我想让游戏左下角有一个选择按钮,并可以播放动画。

我们还可以添加声音。其中 Sound Cue 是 声音蓝图,在里面可以把不同的声音节点组合起来,做出更丰富的效果,当然我们也可以直接把 wav 格式的文件进行添加也可。

然后我们创建动画,动画第 0 秒缩放 X = Y = 0,动画第 1 秒缩放 X = Y = 1。并且我们设置 Overlay_Select 初始缩放为 0。

然后在事件图表中设置播放。

最后在 WBP_MainWidget 中导入 MBP_SelectButton 并调整位置。

【补充】鼠标显示

【个人理解】Set Input Mode Game And UI —— 可以接受 Game 和 UMG 两种状态的输入。为什么此时鼠标移动,画面不会移动呢?因为这里面涉及到优先级的问题。鼠标的输入事件(点击、移动)优先交给 UMG 处理,所以此时鼠标移动不会导致画面移动。
- Set Input Mode Game And UI —— 可以接受 Game 和 UMG 两种状态的输入
- Set Input Mode UI Only —— 只接受 UMG 的输入
- Set Input Mode Game Only —— 只接受 Game 的输入
7.3 案例三 —— 传送
在案例二的基础上,我想按下不同的按钮并传送到相应的位置。
控制角色移动 —— 【Get Player Character】-【Set Actor Location】。
控制角色旋转 —— 【Get Player Controller】-【Set Control Rotation】。
因为有多个传送点,如果我们用 Get All Actors Of Class 节点,确实可行。但是通过 Get All Actors Of Class 节点得到的数组我们是不知道顺序的,那么怎么办呢?
首先在 BP_TPPoints 中设置 Name 变量,并勾选 可编辑实例。

然后将场景的各个传送点的 Name 做好设置。并进行如下设置。


但是使用 Get All Actors Of Class 节点的开销是很大的。我们有什么办法优化呢?参考 UE5基础应用 —— 04 - 蓝图简单使用_虚幻蓝图-CSDN博客 中的案例。

我们直接在 BP_TPPoint 中对 WBP_SelectButton 中的变量进行赋值。这里为什么要在开始前使用 Delay 节点呢?因为我们的 BP_HUD 中的 Create Widget 节点需要时间,如果不 Delay 的话,我们的 BP_MainWidget 还没有创建出来呢。

7.4 案例四 —— 过场动画
在案例三的基础上实现过场动画效果。


【细节】我们在 WBP_A 播放 WBP_B 的动画时,如果要调用 Play Animation 节点时,记得把 Target 给连上。
7.5 案例五 —— 小地图
在界面右上角实现小地图效果。
创建 角色 Point。


创建 WBP_Map。

创建生成 角色 Point 函数。

创建移除 角色 Point 函数。(在角色死亡时调用即可)

每一帧更新 角色 Point 位置。
【补充】这里小地图大小为 250 * 250。世界场景地图大小为 8000 * 8000,世界场景地图左上角为 (-4000,-4000)。这个信息怎么得到呢?我们可以在地图左上角放置一个 Actor,然后观察其位置即可。如果找不到左上角,先打开顶视图,找到位置再放置。

在 BP_Player 中调用生成 角色 Point 函数。

在 BP_AIBase 中调用生成 角色 Point 函数。
【补充】因为 AI 死亡后会进行刷新,这样又会生成一个 BP_AIBase,所以会重新生成一个红点。

7.6 案例六 —— 显示按钮
案例一改进版本,我想让角色走到 可交互物体前 时,屏幕下方出现不同样式的按钮。
首先在 WBP_Button 中设计按钮样式。

在 WBP_Button 的事件图表中将想要改变的属性 创建变量并设置为 可编辑实例、生成时公开。这样做的目的是将来在使用 Creat Widget 节点创建该按钮时会出现这个属性。


在 WBP_MainWidget 的水平框中添加按钮。

WBP_MainWidget 中添加 / 移除按钮函数。


在 BP_Parent 中进行调用。(记得把添加按钮函数中的 Last Index 和 Colors 参数设置为可编辑实例,然后在子类中修改不同的值即可实现效果)

【补充】现在我们只是调整了按钮的颜色,如果我们想要调整更多的内容,其实用结构体更为简单。
7.7 案例七 —— 显示按钮 +
我现在案例六的基础上实现实际效果。
前提是我必须在 BP_Button 中获得 BP_Parent 的对象引用,才能调用它的 Open 方法。


上述操作已经是基础操作了,不再赘述。然后我们在 BP_Button 中就可以调用 BP_Parent 中的 Open 方法了。
可交互物体可能有多个按钮,每个按钮负责不同的操作。但是蓝图怎么知道我们点击了哪个按钮呢?所以我们要在按钮创建时给它一个索引。
勾选可编辑实例和生成时公开,我们才能在按钮创建时赋予它索引。



可以看到根据索引不同,不同的按钮负责不同的操作。

7.8 案例八 —— 显示按钮 + +
我想在案例七的基础上实现新的效果,比如电视有三个按钮,点击一个按钮变亮并且其余按钮变灰。然后我再次靠近电视时,依旧保留原有状态。
在 BP_Parent 中声明变量记录索引。

当我点击电视不同按钮时,将索引记录下来。

下面是按钮变亮/变暗逻辑。

在按钮创建完时进行调用。

点击按钮时也进行调用。

案例七中在 WBP_MainWidget 中我声明了一个 BP_Parent 变量,然后在 BP_Parent 中进行赋值。其中我添加按钮操作写在赋值前面,这本来没有什么问题。
但是在案例八中,我由于在 WBP_MainWidget 中添加了 Button_Light 函数(这个函数在添加按钮操作结束后立即执行,此时 BP_Parent 变量并没有赋值),并且函数使用到了 WBP_MainWidget 中的 BP_Parent 变量。这就出现了严重 bug。
所以只需要将添加按钮操作写在赋值后面即可。
7.9 案例九 —— 点击 Actor 触发事件
上述案例我们都是点击 UI 触发事件,现在我们补充一个小知识点 —— 点击 Actor 触发事件。
首先在 BP_PlayerController 中勾选 Enable Click Events。

使用 ActorOnClicked 事件。

7.10 案例十 —— 更换模型和材质
7.10.1 Set Material
使用 Set Material 节点更换现有材质。此外,这里顺便讲一下更换模型。


或者使用下面方法。

7.10.2 Create Dynamic Material Instance
首先在材质编辑器里我们要把想要修改的属性暴露出来。
我们在蓝图中首先通过 Create Dynamic Material Instance 节点创建材质实例,然后使用 Set Material(3D Mesh) / Set Brush from Material(UI 控件) 节点将材质实例进行赋予,最后使用 Set Scalar Parameter Value、Set Vector Parameter Value、Set Texture Parameter Value 等节点,动态调整材质实例的参数。(建议先 Set Material / Set Brush from Material ,再修改参数,这样逻辑更清晰。)
这里截取老师之前的一个案例截图,供作参考。

【补充】如果我们使用 组件的 Create Dynamic Material Instance 节点,后续就不需要 Set Material / Set Brush from Material 啦。
7.10.3 使用材质参数集
首先创建合适的材质参数集,然后在材质编辑器里,我们可以通过 CollectionParameter 节点调用合适的材质参数集。
最后我们在蓝图中使用 Set Scalar / Vector Parameter Value 节点 —— 指定 Collection 资产、参数名和新值。调用此节点后,所有使用该集合该参数的材质会即时更新。
【总结表格】

7.11 案例十一 —— 3D UI

勾选 Receive Hardware Input,Widget 组件会像普通的 2D UI 一样,接收鼠标点击、移动、键盘输入等事件。

这里简单试验一下。

同样我发现 Widget 组件 本身也有点击事件,但我经过尝试发现无法触发,我总结了下原因:
- UMG 按钮点击事件 → 是 UI 内部逻辑,靠 Receive Hardware Input 就能触发。
- Widget 组件的点击事件 → 是组件级别的点击,但默认不会触发,因为没有碰撞支持。
7.12 案例十二 —— 切换关卡
创建两个关卡。
L_StartGame 为空白关卡,实现登录界面。(这是我们的游戏初始地图)
L_TestMap 为真实关卡。

在 L_StartGame 的关卡蓝图中创建逻辑。

创建登陆界面。

通过点击按钮进入真实关卡。(下面操作偏演示)
1. Does Save Game Exist:检测磁盘上指定的存档槽(Slot)是否存在存档文件。
2. Load Game from Slot:从磁盘上读取存档槽中的存档文件,并将内容加载到内存中。返回一个 SaveGame 对象实例。
3. Create Save Game Object:在内存中新建一个 SaveGame 对象实例(我们需要事先创建自己的 SaveGame 蓝图类 并进行选择),但不涉及磁盘操作。
4. Save Game to Slot:把内存中的 SaveGame 对象写入磁盘文件,保存到指定的存档槽中。
5. Open Level:用于切换关卡。(虽然我们一般通过 GameInstance 存储跨关卡共享的数据,但 Open Level 节点的 Options 参数有点特殊,会跟随关卡切换传递给新关卡的 GameMode)。

我们创建的 BP_SaveGame 蓝图类如下。

7.13 案例十三 —— 背包
创建结构体 S_Backpack_Item 。

创建背包元素 WBP_Backpack_Item。

下面是老操作了,因为后面我们要动态添加。

创建背包 WBP_Backpack。

然后在背包中动态添加。(结构体信息放在了 BP_HUD 中)

在 WBP_MainWidget 中进行添加。

7.14 案例十四 —— 背包元素拖拽到快捷栏 / 快捷栏元素互相拖拽
功能:我们想把背包中的 Widget 拖拽到快捷栏中 / 或者快捷栏互相拖拽。
重载 On Mouse Button Down 函数。


当用户点击该 Widget 时,会触发 On Mouse Button Down 函数。其中,Mouse Event 参数包含一些重要的信息(如鼠标哪个键被按下、按下时的鼠标位置等)。
Detect Drag if Pressed 节点接收 Mouse Event,它会检查按下的按键是否与 Drag Key 相同,以及用户是否在按下鼠标按钮后进行了拖拽操作。
重载 On Drag Detected 函数。


在 1 的基础上,当用户拖动鼠标时,会触发 On Drag Detected 函数。
我们通过使用 Create Drag Drop Operation 节点创建拖拽操作对象。其中,Payload 参数表示拖拽时需要传递的数据(自然是 Self 啦)。Default Drag Visual 参数表示拖拽过程中显示的控件副本( 此控件不需要保留上述两个函数,因为它只是一个副本)。Pivot 参数表示拖拽时鼠标在控件副本的位置(个人感觉 Center Center 即可)。
重载 On Drop 函数。

在 2 的基础上,当用户拖拽释放在控件上(重载 On Drop 函数的控件)时,会触发 On Drop 函数。
我们创建 WBP_Backpack_Item_DropDown(快捷栏),注意我们需要在快捷栏中重载 On Drop 函数。其中,Operation 参数就是 1 的拖拽操作对象。
7.15 案例十五 —— 背包元素拖拽到鼠标指定位置(世界场景中)
在 7.14 案例 2 的基础上,重载 On Drag Cancelled 事件。
Get Mouse Position on Viewport:返回整个屏幕的鼠标坐标,单位为屏幕像素 / DPI Scale。
Deproject Screen to World:用于将 2D 屏幕坐标转换为 3D 世界坐标与方向。(个人猜测期望输入的是屏幕像素)

小总结:

7.16 案例十六 —— 显示背包元素信息
创建 WBP_ItemInfo。

在 WBP_MainWidget 中进行添加,并初始设置隐藏。


在 WBP_Backpack_Item 中创建鼠标进入/移动/离开逻辑。

Get Mouse Position 需要先除以 DPI Scale,以适应屏幕缩放。
然后需要加 2,防止鼠标同时移动到 背包元素 或者 背包元素信息 两者之上,出现背包元素信息闪烁的情况。

7.17 案例十七 —— 轮盘效果
创建 WBP_TurnTable。
Overlay_Background 为灰色底图。
Overlay_Light 为蓝色高亮,但初始设置透明度为 0。
Overlay_Image 为图标(此处图标并无实际用处,只是为了确认位置)。

控制 Pointer 随鼠标转动、控制小格变亮、动态赋予图标逻辑如下:

控制小格变亮具体逻辑如下:
我们还额外实现了 Main_Widget 中小图标的更换。

通过按键 生成 / 消除 轮盘。


实现小图标更换是一个新添的小功能,我尝试了一下。

6010

被折叠的 条评论
为什么被折叠?



