Loading

UE4-初学UMG(二)

在屏幕上显示控件

控件已经准备就绪,但我们还需要想办法让玩家看到它。本模块将介绍控件的各种显示方法、显示控件最合适的位置以及对应的考量和原因。

现在,我们的玩家平显UMG控件已经创建完毕,这是画面部分

image-20210615164921810

这是代码部分

image-20210615164940613

我们还需要在屏幕上显示这个界面,但在此之前,我们应该弄清楚它应该放在哪里,最合适的位置在哪里?其实很简单,这是玩家的HUD,应该和玩家关联,为什么?因为它反馈的是玩家的信息,例如,如果玩家被消灭了,我们可能就不需要再显示这些信息,如果玩家已被彻底消灭,并且即将重生,那么这是继续显示玩家生命值就会很奇怪,当然,你也可能希望它一直显示,但你可以把它放在其他位置,比如放在HUD类,即便我们没有用到它,也可以将它用于引用的创建和保存,或者放在GameState,在游戏内部控制它,或者放在玩家控制器中,也许是玩家控制器特有的内容,因此会一直在游戏中显示,比如菜单。

这个例子中,我们把它放在角色中,也就是我们的玩家,我们打开Character -> Blueprints -> Character_FirstHourUMG

image-20210617005126746

把它放在这里的某个地方,当我们创建玩家时,我们希望一同创建用户界面,操作非常简单

image-20210617005511354

依次创建 BeginPlay -> create widgets,第二个节点会询问我们要创建哪个控件,为哪个玩家所有,然后返回控件的引用,由于当前是单人游戏,所以不必关心为谁所有,只需关心要显示什么内容,所以选择我们的UMG_PlayerHUD

image-20210617005938032

编译并保存,点击播放,我们发现什么都没发生,因为仅仅创建控件,并不意味这个控件就会在屏幕上显示,创建完控件后,我们要获得控件的引用,然后再在屏幕上显示它,如果拖动返回值,也就是我们创建的控件,然后输入“Add",在User Interface下会出现两个选项

image-20210617010419974

90%的情况下,你应该使用”Add to Viewport“,”Add to Player Screen“主要用于分屏显示,如果你的游戏和分屏显示或同屏多人显示无关,你就应该选择"Add to Viewport",选择Add to Viewport,点击Play,于是页面就出现了。

image-20210617010721315

但是我们的生命值和弹药数都是假的,因为我们还没有设置任何逻辑,我们需要绑定我们的默认数值,并且使用之前在玩家HUD或UMG控件中创建的事件,我们来具体看一下,首先我们要做的是,创建一种方法,以便稍后和这个控件通信,我们可以为这个控件创建一个引用,来实现与它通信的功能。我们将控件的返回值断开,把它提升为变量(Promote to variable),把它命名为 PlayerUIRef,也就是玩家UI引用,之后如果要和控件通信,我们就可以访问我们的玩家UI引用

image-20210617011418759

image-20210617011655921

再把它们重新连起来,点击Play

image-20210617011734222

一切正常,除了多了一个玩家UI的引用,很好。接下来,我们需要让玩家UI更新生命值和弹药数量

在Character Stats下我们可以看到,默认生命值100,默认弹药数量20

image-20210617013123313

image-20210617013132036

image-20210617013138408

但我们的UI对此一无所知,每次游戏开始时,或玩家死后重生时,我们都要初始化数值,我们可以创建一个自定义事件,以便根据需要调用事件,我们可以把它封装成一个函数,使之更简洁,找到Functions,点击添加,命名为UpdateDefaultUI

image-20210617123822320

执行这个函数时,我们将与玩家UI通信。所以将PlayerUIRef拖进来,用它来更新生命值和弹药量

image-20210617123633319

我们只需传入数值给New Value,就能自动更新界面,十分简单,将刚才的两个变量Health和AmmoCount依次拖进来

image-20210617124854178

在拖入AmmoCount的时候,我们发现它的类型(Integer)与New Value(float)是不兼容的,简单的思考过后,我们想到弹药数量是不能是浮点数的,它只可能是整数,所以让我们回到HUD中

image-20210617125252092

将这个数值类型改为Integer,同时要注意先断开与右边节点的连接,再重新连接,否则编译会出错。

image-20210617125424377

好的,现在生命值和弹药已经可以自动更新了,我们点击Play

image-20210617125713699

发现它们并没有更新,这是为什么呢?是因为我们只创建了函数,却没有调用它,好的,接下来我们去调用它

image-20210617125910197

再次点击play

image-20210617125931456

成功了!

现在,我们已经关联了界面,屏幕上也能显示界面了,我们可以在这里看到

image-20210617130757824

我们关联了事件,可以调用它们来更新界面

image-20210617130829216

然后,在玩家出生时,会创建界面,并使用真实的数值,动态更新界面,而非写死的数值,我们实际上拥有实时动态信息,但我们还不能实时更新UI,当我们收集医疗包时,它们并不会调用更新函数,不过我们已经完成了基本设置

image-20210617130915728

更新用户界面

静态UI的用途很广泛,但是一旦涉及动态事件,我们就需要在界面上实时更新数据。这节课我们将学习如何在数据变动时实时更新数据。

现在,每当我们开始游戏,用户界面就能自动更新默认数据,问题是,每当数据改变时,比如弹药与生命值发生改变时,用户界面不会更新,接下来我们将学习一些有关的更新方法。

image-20210617185618049

这里,我们已经创建了两个事件,每当我们将新数值传入它们时,它们就会调用UI逻辑,触发这些事件,并更新文本内容

但是还有另一种方法,就是属性绑定(property binding),属性绑定本质上是一种改变绑定属性的方法

image-20210617185909980

例如,我们的文本可以更改,颜色和不透明度也可以更改,这些都是绑定属性,而且它们会自动为我们更新

好的,演示一下这个功能的用法,点击右侧的bind,下拉出现create binding

image-20210617190046242

出现如下界面,在return value输入文本内容,编译保存

image-20210617190143683

回到人物蓝图,把update dafault UI的线断掉

image-20210617190347437

点击play

image-20210617190420357

但它有一个问题,绑定函数基本上每帧都会运行,我们可以通过添加一个打印字符串的节点来证明这一点

image-20210617224224106

每次调用函数,都会打印一个字符串Hello

image-20210617224353173

点击play,我们发现屏幕上会显示大量Hello

绑定的缺点就是,如果某些UI无需经常变动,例如玩家的生命值或弹药数量,它们就没有必要每帧都去更新,这个函数每帧都会更新,所以会产生不必要的开销,所以像这样绑定属性并没有多大用处,这么做很简单,但会消耗大量性能,我们最好不要这样做,我来演示一下为什么不要这样做:

获取人物,转换成第一人称角色,点击右键,选择纯类型转换

image-20210618000606628

然后get AmmoCount,这实际上是获得了当前人物的弹药数量,然后返回这个数值

image-20210618000827794

点击Play

image-20210618000856663

当你开枪时,注意右下角,弹药数量发生变化了,如果我们拾取弹药,数量也会自动更新image-20210618001109479

但缺点是,如果我们打印字符串,让它打印我们的弹药数量,再点击play

image-20210618001149036

你会发现游戏会不断显示弹药数量,如果我们开枪,它会继续显示更新后的数量,每一帧都在更新,我们其实没有执行什么逻辑,但它仍然在更新,所以开销会很大,除非你确定每一帧都要更新内容,否则就不要这么做,只有计时器、倒计时、时钟,这类需要每帧精准显示的功能,才需要使用绑定功能,我们删除这个函数,回到之前的状态。

我们将改为主动推送的方法,我们其实已经使用了这种方法,每当更新默认UI时,即便只更新一次,我们也将数据推送给了UI,让我们在这里使用这种方法。

image-20210618101603014

角色蓝图中的打印字符串节点通常表示发生了某些事件,这里,当我们调整弹药时,我们会设置新的数值,然后打印新的数值,生命值也是一样

image-20210618101809577

删除这些信息,把玩家的UI引用拖进来,更新弹药,这样连接:

image-20210618102100415

这样,当弹药数量发生变动时,在调用这个事件后,弹药数量就会更新到用户界面上,生命值也一样

image-20210618102349018

这里还有两个地方要更新,来看这里

image-20210618102545522

每当我们开枪,弹药数量会更新,然后打印字符串,让我们重复刚才的操作,复制粘贴

image-20210618103015532

image-20210618103032644

现在,原来的打印逻辑会改为更新我们的UI,点击Play测试一下

image-20210618104158217

image-20210618104134138

image-20210618104116913

拾取伤害包,生命值就会下降,拾取医疗包,生命值就会上升,如果开枪射击,弹药量就会减少,拾取子弹,弹药量会增加,左上角依然会显示一些内容,提示我们拾取了某个对象。

另一个问题是我们的UI无法正确识别屏幕长宽比

image-20210618104453297

image-20210618104630534

image-20210618104507447

屏幕中央的准星,和左下角右下角的文本框,都会因为屏幕的缩放而产生变化,这是因为我们的用户界面并没有使用正确的布局。

调整用户界面

创建完基础界面后,我们发现界面中有一些瑕疵。在本模块中,我们将探究出现瑕疵的原因,并探讨如何为玩家改善界面。

此刻,我们的项目中,当我们调整屏幕尺寸时,UI无法按我们的预期保留在原处,准星这类UI会完全消失掉,我们来解决一下这个问题。

回到HUD界面,检查我们设置HUD的方式,在之前的学习中,讲过父控件负责控制子控件的布局方式,当时有看到Anchor这个功能,也就是左上角的星型或花型形状

image-20210618105459806

让我们重新检查各个元素,把它们放在合适的位置,首先是屏幕中央的准星,它的锚点位于左上角,这意味着,当我们缩放用户界面时,让我们控制右下角的缩放箭头进行测试

image-20210618105717466

在某些位置,UI会无法显示,让我们把锚点移到中间,Canvas Panel为我们提供了锚点系统,点击Anchors

image-20210618105939056

这里提供了各类设置锚点的方式,注意下方的提示,你可以按住shift键更新对齐,或者按住Ctrl更新位置,这些快捷键会更新下面这些参数

image-20210618111010793

我们选择

image-20210618111211836

发现锚点移到了中间

image-20210618111349937

这意味着当我们调整UI界面时,准星会保留在屏幕中央,点击play

image-20210618111419488

image-20210618111428223

进行缩放,我们发现准星会一直固定在中间,但放大后我们发现锚点的中心位置和准星图片的中心位置并没有对齐

image-20210618111540337

这是因为尺寸和对齐方式不对,对齐(Alignment)的范围在0到1之间,0表示没有,或0%,1表示全部,或100%,左上角是(0,0),右下角是(1,1),所以如果要对齐到中心位置,就要设置成(0.5,0.5),并且把准星的偏移距离改为0,即相对于屏幕中心没有任何偏移

image-20210618111919931

image-20210618112212395

现在锚点和准星都已经完美居中了,接下来让我们看看生命值UI,它位于左下角,所以锚点也应位于左下角,调整一下它的对齐方式和位置

image-20210618113436096

注意这个位置(position)是相对于锚点的位置,x=40表示位于锚点右侧40个像素,y=-40表示位于锚点上侧40个像素

image-20210618113727426

对于右侧的文本框做同样的操作,锚点位于右下角,对齐(1,1)

image-20210618114343114

点击play,观察效果

image-20210618114441974

image-20210618114450902

可以看到无论我们怎么进行缩放,UI都会尽可能地适应屏幕的大小,但你会发现一个问题,这实际上设计到了一种高级功能,主要是让界面更美观,我们已经设置了锚点,并且让UI能自适应屏幕,在右下角,当前弹药量为20,他当前的锚点位置是右下角,如果我们让弹药量降低到个位数,我们会发现整个UI都向右移动了

弹药数量UI布局改变

当我们拾取弹药后,整个UI又会向左移动,由于我们的疏忽,导致UI会自己变动,无法保持固定,首先,我们没有使用等宽字体,字体间距不是固定的,所以字体本身会发生变化,第二,这里的这个数值100,他也会左右变化,如果是100,就是3个字符,如果是99,就是2个字符,如果是9,就是1个字符,由于我们的锚点在右侧,所有会从左边向右缩进,我们可以在控件中叠加控件,然后让它们使用各自不同的对齐方式,这是其中一种方法。

我们要把它改为固定大小,我们希望整个UI的大小是固定的,我们可以在这里调整,不要勾选Size to content,把它改成固定大小,比如260*50

image-20210618121858640

现在UI会是固定的尺寸,当数量发生变化时,不会左右移动,我们点击Play

当弹药数降为个位数时,Ammo: 不会再发生移动,移动的只是显示弹药数量的字符串,这是因为文本框不会再调整大小,它的大小是固定的,并且与锚点固定,如果你不喜欢这种方式,你可以让UI相互嵌套,稍微演示一下:

我们用一个水平框体,它可以用来水平排列控件

image-20210618122536908

我们可以在左侧显示“弹药”,在右侧显示弹药数量,框体中的控件会拥有单独的设置,同样,父控件负责控制子控件的布局,控制子节点如何在框体中排列,这里两个文本框的填充方式都是Auto

image-20210618122943603

它被设置为尽可能填充,不过我们可以调整它们的布局

将弹药数量改为水平右对齐,垂直居中,然后选择Fill

image-20210618123207065

左边的改成水平左对齐,垂直居中,选择Fill

image-20210618123240809

image-20210618123402282

这样,Ammo: 就会永远左对齐,位置不会发生变动,而弹药数量会自动右对齐,以上就是如何通过嵌套控件来管理控件的显示布局

posted @ 2021-08-13 17:34  泠枫Jun  阅读(825)  评论(0编辑  收藏  举报