(0)本文简述
本文主要对UMG模块中各控件的关键属性与设置进行了简要说明,并以某些具体示例来对某些UI效果的实现方法进行了图文讲解。
2023.11.2 - 备注:越上班越懒得写这些,一年多没更新了,现在更懒得更了,当时刚入职,爱记录这些比较初级的东西,后边有兴趣写点有意思的吧~233333
(1)UI的创建、显示、移除
Create Widget:首先要构建这个UI。
构建完成后,将该UI添加到视口中,这样玩家就能在屏幕中看到该UI。
Widget被附加到了Viewport(也可以是Player's sreen)上,Viewport为其Parent,关闭该UI即为从视口中移除。
(2)CanvasPanel主要属性备注(后续补充)
1)Visibility
- Visible:可见也可使用鼠标进行交互
- Collapsed:不可见,可理解为完全销毁
- Hidden:只是不可见,但仍占据布局空间。
- Not Hit-Testable(Self&All Children):可见,但是无法用鼠标进行交互,自身与其子类均不可。
- Not Hit-Testable(Self Only):可见,无法用鼠标进行交互,仅限自身。
2)Advanced->Tool Tip Widget
For example:对于一个背包系统,当鼠标悬停在某个物品上时,可以显示出该物品相应的详情。
3)Accessibility(可访问性)
该项一般不会进行修改处理。
4)Transform
询问了一下前辈,将其翻译成转换是不准确的,参考3D数学中的翻译,或对于一个刚体的变换,翻译成“位姿”或许更好?该选项下设计到位置、尺寸、旋转、shear(简单理解就是倾斜度)、Pivot(绕轴枢进行旋转等变换,比如0.5,0.5则表示枢点在控件中心,0,0则表示在左上角)。
(3)Text Block主要属性备注(后续补充)
1))Slot
- 画布的左上角的坐标为(0,0),向下/右方向进行为负方向,不同的锚点位置根据画布分辨率的不同坐标位置也发生变化。
- Position X/Y表示的是画布中的某个控件对锚点的相对位置。
- SizeX/Y表示空间框的大小,与字体大小无关。
- Anchors中的线一般用于拉伸。(对于进度条,一般将其Anchors设置为某种锚线,故Offset Left、Offset Top等分别表示该控件某条边线与左锚线、上锚线的距离)
- SizetoContent(自适应大小)
2)Appearance(决定呈现方式)
- Opacity与其用不透明度来进行理解,不如直接理解为遮罩,在Color Picker中的Alpha值即为遮罩值,该值为0表示完全透明,为1表示完全不透明。
(5)UI响应玩家输入及机制
1)在进入某个UI界面后接受按键输入
- UI->Details->Focus:设置焦点,有的UI需要焦点(菜单、背包等),有的UI不需要焦点(血条、状态条等)。
- 先设置UI是否可被聚焦,然后再设置焦点聚焦到UI上。
2)关于按键事件
FUNCTIONS->Override->OnKeyDown(在widget中无法直接调用按键事件节点)
Event Reply Structure:这个结构体表示你当前处理的事件能否跟着输入流一起继续向下流动。
- Capture Mouse 捕获鼠标,用于锁定某个Widget,调用后将只能和锁定的Widget进行响应,该Widget的父类与子类同样无法响应。
- Clear User Focus 清空用户焦点
- Handled 表示这个事件已经处理完成,如果输出是handled,那么执行完某段事件后输入就会从输入流中移除。比如,UI下一级还有一层子UI,它就接受不到这个按键事件,上图示例中,执行完print string的操作后,该输入就会被移除。
- Lock Mouse 锁定鼠标
- Release Mouse Capture 释放鼠标捕获
- Set Mouse Position 设置鼠标位置
- Set User Focus 设置用户焦点
- Unhandled 执行完某段事件后输入会从输入流中继续向下流动,如果是在3.中的例子,UI下一级的子UI依旧能接收到该输入。
- Unlock Mouse 解锁定鼠标
(6)UI响应玩家输入及机制
- SetInputModeGameAndUI:设置输入模式为游戏与UI中均可以接受输入。
- SetInputModeGameOnly:仅游戏中才能接受输入,UI不接受输入。
- SetInputModeUIOnly:仅UI能接受输入,游戏中不接受输入。
(7)ProgressBar&Image控件
1)ZOrder相关说明
- 如果不设置ZOrder的值,即均为默认的0,那么会根据CanvasPanel中的控件顺序来进行渲染。如下图所示。进度条控件由于后于Text控件渲染,故在Text控件上方。
2. ZOrder值越大,则该控件越晚渲染,故如果你将Text控件的ZOrder值设置为1,那么进度条控件就会优先渲染。
2)ProgressBar->Style相关
该部分用于设置ProgressBar的进度条和背景的填充图片、尺寸、绘制方式等。
Marquee常用于场景加载时,制作持续读条的效果。
Tips:一个进度条可以看做是两张Image的叠加,是图像的综合。
(8)尺寸、水平与垂直框
1)Size Box
- 通常用于强行控制子控件的大小。
- 如下图所示,将之前较大的ProgressBar控件拖入Size Box,其尺寸被强行压缩成了Size Box的形状。
2)关于Child Layout(子布局)
- 如下图所示,当勾选宽度重写后,再勾选上方的Size To Content(尺寸自适应),则可以观察到进度条的宽度被修改成了100。
2. 如果将子布局中的Min Desired Width等选项进行勾选,那么如果将一张图片添加进子布局中,则该图片的大小会被强行压缩在(100,1000)的范围内,如果这个图片小于长/宽小于100,则会被拉伸到100;图片的长/宽位于100到1000内,则不发生变化;图片长/宽大于1000,则会被压缩到1000。
3. 覆盖和限制同时勾选时,只执行覆盖!
4. 当你取消勾选Size To Content时,对SizeBox进行拉伸会导致其中填充的Image发生形变。采用Min/Max Aspect Ratio即可保证达到你想要的拉伸之后的效果,如果你将以上两个值均设置为1,则无论你怎么拉伸,填充的Image均不会发生形变。
3)Horizontal Box&Vertical Box
可以通过水平框和垂直框的嵌套来更自由灵活地实现你想要的显示效果。
(9)Spacer&Border&Overlay控件
1)Spacer(填充控件)
控件本身自带的Padding填充远不如Spacer灵活。如下图所示,在一个horizontal box中的两个Image中填充了宽为50的Spacer控件。
2)Border(背景控件)
类似于Spacer控件,可以在两个控件中间填充自定义背景。其余的调节选项和前面介绍的其它控件类似。
3)Overlay(叠加控件)
- overlay控件可以使新加入的控件自动层叠,越前面的控件会被压在最底下。如下图所示,叠加了8层图片,Image_213(红色图像)会被放置在最底层。
(10)Button控件(使用最广泛的组件)
1)button的四个基本状态:Normal、Hovered、Pressed、Disabled
与之前介绍的控件类似,可以为Button控件设置按钮图标,鼠标移动上去后的效果变化,点击后的图标变化。
鼠标悬停到button之上后的显示效果如下图所示。
2)Normal Padding/Pressed Padding
- 该选项仅对button控件的子控件生效。比如给button控件添加一个Image控件(背包系统中常用,道具栏通常如此实现),使用以上选项即可调整物品图标的大小。
- 比如将Left Padding值改为50,可以明显观察到Image发生了右移。
3)注意Pressed控件和Clicked控件的差异
- Clicked操作实际包含了Pressed的操作,通过测试可以观察到,当在屏幕点击按钮时,屏幕同时打印了Clicked和Pressed;
- 另外长按鼠标时屏幕会打印Pressed,如果此时在Button的范围内松开鼠标左键,则依旧会打印Clicked,如果在Button范围外松开,则仅打印Pressed。
3. 可设置点击方法,决定Down and Up或Mouse Down等哪种情况算一次点击事件。注意Precise Click(精确点击),点击还未松开的情况下移动鼠标指针,不会被判定为点击事件。
(11)UI动画制作相关
1)以只狼的钩锁提示点UI为例
- 如下图所示,一个钩锁提示点UI的实际构成与进度条类似,只是进度条的形式表现为“进度圆”。
2. 注意红框中的部分,点击+Animation可以创建一个动画,而动画必定存在一个时间轴(UE4中该部分确实是Timeline),点击+Track可以添加一个轨迹,如下图所示,可以为UI控件添加各种事件轨迹。
3. 回顾一下只狼的钩锁点UI是如何变化的?
当sekiro靠近钩锁点时,钩锁点中心的绿点会逐渐扩大,当绿色点完全覆盖外部白圈的时候,sekiro可以投出钩锁,同时内部的绿点会播放一个先微微增大然后淡化消失的动画表现,同时外部白圈也会消失,这就是我们需要实现的效果。
故我们首先需要设置外部白圈UI(BackGround)在Visable和Hidden之间变换,打好关键帧;然后我们需要设置绿点(Filling)的颜色以及不透明度(Color and Opacity)的变化,最后我们需要设置好Filling的大小的变化(Transform)。
4. 分别添加3.中分析过的Track,分为背景和填充两大部分,
5. 点击具体Track中的+,设置其具体的变化的关键帧,你可以直接修改属性的数值,但通常我们不这么做,因为表现不够直观。
6. 点击自动创建关键帧,这样就能根据你在视口中的修改操作来自动创建关键帧。自动创建关键帧的好处在于能比较直观地观察到UI效果的变化。
7. <- + ->分别表示移动到左边的上一个关键帧的位置、在当前时间轴位置添加关键帧、移动到右边的下一个关键帧的位置。
8. 测试刚刚创建的效果,创建一个点击事件(实际游戏中当然是自己设计一个自定义事件),使用PlayAnimation即可播放,可以看到之前创建的UI动画以变量的形式存在于Graphs中。
(12)UI显示如何与数据发生关联
1)前置工作
先做个血条咯,将这个widget命名为HUD,游戏一开始就要显示血条。制作方法参考前面的章节可轻松完成。
2)开始关联(绑定&事件分配器)
- 给Percent添加一个绑定,这里其实有两种方法,一种是与一个计算该百分比的函数进行绑定,也可以先在Hud中声明一个变量值,然后再与该变量进行绑定。(本示例中采用第一种)
2. 另一种方式是使用事件分配器,事件分配器就是 一种分配事件的逻辑。(我创建了一个名为ChangeHP的事件分配器)
将事件分配器拖入到视口中,可以看到如下选项。
- Call:调用这个事件分配器。
- Bind:绑定这个事件分配器,只能绑定函数。
- Unbind:解绑定这个事件分配器。
- Unbind all:解绑定所有事件分配器。
在Hud中使用Bind,将某一事件(ChangeHP2,暂未写具体逻辑)绑定到ChangeHP这一事件分配器上。
然后可以在另一蓝图(本示例中是写在Character蓝图中),使用call来调用ChangeHP这个事件分配器,这样也实现了属性和UI显示的绑定。
3. 为了方便测试与说明,在我的character蓝图添加如下逻辑,每隔一秒在0到100的范围内更改角色的当前生命值。)
4. 在Hud中获取character的引用,接着获取其当前血量和最大血量,计算其百分比。这样就实现了玩家血量与UI显示血量的关联与同步。
(本小节中所有写法均属于不规范的写法,仅供测试)
(13)UI渲染相关
1)角色头顶的血条
如下图所示(这里没有调整血条位置,使用的是和怪物相同的血条)。
- 给Character添加一个Widget组件。
2. Space这里可以选择World或Screen,World常用于实现3D血条(血条会跟着玩家旋转),Screen常用于实现2D血条(无论你的角色朝向何方,血条都是平铺显示);
- Widget Class选择自己编辑好的Widget,本案例设置为血条UI的Widget;
- DrawSize(绘制尺寸)&Draw at Desired Size(绘制成理想尺寸):Space选择Screen时,即使绘制尺寸为0,UI也能够正常显示,如果选择的是World,绘制尺寸为0时,不管你是否勾选绘制成理想尺寸,屏幕均不会显示出该UI,但当你设置任意大于0的值时,UI能正常显示。
- GeometryMode:Space选择World时候,可以选择Cylinder将血条变成这种弯曲的立体血条。
- 3D血条存在一个问题,当视角从人物背面看过去的时候是无法在屏幕中观察到的,所以要想实现立体血条能进行双面显示,就需勾选Rendering中的Is Two Sided选项。
(14)制作一个可随意拖拽的UI(常用于游戏背包中的道具拖拽)
该示例中的逻辑仅涉及UI的逻辑,后端的数据交换不在此进行叙述。
在完成鼠标按下函数的重写之后(本示例中采用鼠标右键按下进行物品拖拽),重写拖拽检测函数,这个函数执行如下逻辑:(该部分逻辑写在拖出道具的UI)
(无须关心我的示例UI的结构本身是如何设计)
1,创建一个Drag Drop的操作,使用上图的蓝图节点,class选择自己创建的DragDropItem类,这个DragDropItem用于存放你所拖拽的UI的相关信息。(当前UI的名字、数量、图标等)
2,创建一个拖拽图标,该UI仅需显示当前拖拽道具的icon即可。(你愿意多显示更多的物品信息,随你自定义)
3,因为我们拖出来道具UI后在要拖动到的位置放下鼠标(背包格子1拖动到背包格子2),故Drop逻辑写在拖拽结束位置的UI中。
该部分逻辑也很简单,就是将DragDropItem中存放的道具信息赋给当前物品栏UI的icon、num等即可。以上就实现了一个最简单的物品拖拽的逻辑。多个格子的背包的实现,请自行去了解ViewList控件。
实际上在实现一个背包系统的时候,应完全避免UI与UI之间的通信,所有数据通信也应该只能发生在前端与后端之间,部分不规范的数据获取方式要尽量避免。(去学习设计模式)你在这里拖拽的道具是道具的UI,并不是道具本身,所有的数据处理都在后端进行(C++ or 蓝图)。