斯坦福UE4C++课程P34-P38音频动画UI
首先是作业2的实现。
我们创建一个魔法球基类,供角色发射魔法球、黑洞、后面的AI发射魔法球继承。
tip:包含BlueprintNativeEvent的UFUNCTION在cpp文件实现时要加_Implementation后缀。
改变较多,建议看作者github源码,我在这里把之前做的作业2换成了作者的实现,便于后边的工作。

接下来我们创建声音提示
新建一个Sound Cue文件MagicProjectile_Loop,其编辑界面类似于蓝图。
拖进来wave player节点,选中一个音频文件和looping表示循环播放。

我们发射魔法球后,如果距离离我们远了,该声音应该减小,可以直接在Sound Cue文件左侧选择Attenuation(衰减)栏的override来配置。
也可以单独新建一个衰减Attenuation文件作为前者的配置。

我们开启Enable Volume Attenuation(音量衰减)和Enable spatialization(空间化)
然后把在Sound Cue文件选中使用该衰减文件即可。
接下来新建一个Sound Cue文件,是魔法球撞击音效,同样使用前面的衰减文件。

我们还可以添加各种各样的混合声音节点,比如Doppler多普勒效应,靠近声音源时,声波频率增大,音高变高,反之变低。

接下来进入动画蓝图
这里推荐看

tip:ctrl+P可以快速搜索资产
我们找到角色动画蓝图Gideon_AnimBlueprint
动画蓝图核心概念有图表Graphs和动画图表Animation Graphs。图表和蓝图类似,节点每帧调用,包含运行时角色运动的各种信息;动画图表决定了资产的姿势。
在动画蓝图中添加Blend Poses by bool:

接下来我们得看角色是否真的活着,我们不在动画蓝图判断,而是在别的地方直接赋值给IsAlive。
我们进入角色AttributeComponent,新建
我们希望外部能够调用该函数,且在动画蓝图中可以调用,所以在public下,设置为蓝图可调用。
函数体只需
因为该函数只是检查Health,为了Health在函数体不被修改,我们设置该函数为const函数,这意味着我们只能通过该函数读取数据('Read-Only' Function)。
编译后,回到动画蓝图。
进入事件图表,从Try Get Pawn Owner拖出从类获取组件节点,获取到SAttributeComponet,调用IsAlive函数,返回值赋值给动画蓝图里的IsAlive变量。
现在的效果:

我们发现,角色播放死亡动画后,仍可以正常移动,因为我们没有设置停止移动的机制。
在角色C++类添加函数
我们可以在构造函数里调用该函数,但使用PostInitializeComponents()是更好的选择。
.h文件添加
.cpp文件添加
保证当属性组件的OnHealthChanged事件发生时都会调用角色类刚写的OnHealthChanged函数。
现在角色死亡后,我们不能够移动角色了。
后续可以做更多工作,比如死亡UI,调整摄像机等;可以在gamemode中对角色的死亡作出反应。

接下来我们添加三维UI(伤害数字),当我们击中之前创建的不会动的敌人(Dummy)时会显示伤害数字。
创建伤害数字UI:
project world to screen是把三维坐标投影到二维屏幕坐标,除以视口比例是为了适配不同大小的UI(比如伤害数字UI的DPI是720P),使得UI显示的位置统一。set render translation是把伤害数字平移到目标位置渲染出来。构造事件意思是伤害数字UI显示2秒后删除数字。

在Dummy敌人类蓝图中:
把self赋值给AttachTo,让伤害数字显示在敌人的位置。


最后是作业3
首先是第一部分

首先我们给魔法球添加飞行音效和击中音效。
重写父类的OnActorHit函数,添加撞击粒子系统和撞击音效。
接下来把角色全身材质替换为M_CharacterSimple,用之前做的材质函数为该材质添加闪光效果。
我们发现M_CharacterSimple有作者为我们留下的两个节点,在材质函数中添加好输入节点后(作业要求Color和Speed能够被我们修改),把该两节点连接到自发光颜色Emissive Color。

在角色类中为TimeToHit分配值(和前面的Dummy敌人同理):
效果如下:

接下来我们在属性组件AttributeComponent添加HealthMax:
在构造函数中为其赋值为100。
替换生命值控件蓝图中的Health为HealthMax,并限制Health在0~HealthMax之间。

在属性组件ApplyHealthChange函数下,Health+=Delta后添加:
实现限制。
现在角色死亡后血量不会继续降低:

接下来我们添加伤害数字,使用Expose on Spawn来为数字赋值(之前我们做的Damage控件蓝图中的AttachTo变量勾选了Expose on Spawn,使得create widget节点显示了AttachTo)。
我们在角色属性类中添加变量DamageAmount(设置为public,便于在魔法球类中访问该变量的值,来给角色添加伤害(之前我们直接用的-20.0f的伤害)),构造函数中设置值为20。
魔法球类中添加伤害的语句变为:
回到damage控件蓝图,为damage文本绑定函数:

我们在角色类中单击属性组件,添加OnHealthChanged事件:

运行游戏,魔法球打中角色时的效果:

然后我们给伤害数字添加动画,做得带感一点。
damage控件蓝图左下角新建动画,做好后在构造事件后play animation。

现在的效果:

接下来我们做作业3的第二部分:Health Potion血瓶和发射魔法球前的粒子特效、相机震动

由于要求用Interact,所以我们模仿之前宝箱的做法。
HealthPotion.h
HealthPotion.cpp
效果如下:

接下来我们添加魔法球发出前的粒子系统和魔法球撞击时的摄像机晃动。
在角色类的primaryAttack中,发出魔法球的前0.2秒,我们添加:
效果:

最后是添加魔法球撞击相机震动
在魔法球类.h文件添加
cpp文件在OnActorHit函数添加相机震动
编译后在魔法球蓝图选择一个shake类(ExampleContent/CameraShake中有5个)
效果:
