文章目录
- PlayerStart 初始化
- 设置默认 Level
- BP_Character 初始化
- BP_Character 添加动画
- BP_Character 攻击
- BP_Enemy 初始化 以及 AI 运动
- Camera Collision 相机碰撞
- BP_Character 生命以及伤害
- Wave Spawner 波生成
- UI 初始化以及 Damage Screen
- 指定位置随机生成
- 添加声音
- 环境 Environment Level
- 替换 Character Asset
- 完结撒花
PlayerStart 初始化
PlayerStart 指的是角色出生点,也就是当前关卡中角色初始化时出现的位置
初始化角色由 Game Mode 指定,而关卡中的 Game Mode 由 World Setting 中的 Game Mode Override 指定
创建一个 Game Mode 的 Blueprint GM_singleplayer
,将导入到 World Setting 中后,我们需要在 Game Mode 中去定义 Chracter,同时创建一个 Chracter 的 Blueprint BP_player
,将其导入到 GM_singleplayer
之中
这样 PlayerStart 的角色初始化完毕了
设置默认 Level
这里设置默认 Level 可以防止重新打开的时候是黑屏即没有默认关卡
BP_Character 初始化
Mesh 指的是静态网格体,其基本操作设置如下所示,导入网格图,在 Transform 的 Mesh 中 location 的 z 设置为-89, rotation 的 z 设置为-90,用于调整网格体位置,使其与 capsule 对齐。
仅仅做这些还不够,这样并没有摄像头,我们并不能对视野进行移动,在游戏中我们可以使用 F8
跳出人物视口进行观察,与 ESC
不同的是,后者是直接退出游戏,前者是在人物视口和上帝视口进行切换。
为此我们需要一个摄像头从人物外面观察,这里添加一个 SpringArm ,并在其中添加一个 Camera ,注意在 SpringArm 中,我们需要启用 Use Pawn Control Rotation,这可以接受输入并移动弹簧臂。
现在可以在外面看到 Character ,目前存在一个问题,那就是无法控制 Character 的移动,在这里我们为 Character 添加移动功能。
首先在主目录创建一个 Input 文件夹,右键创建一个 Input Mapping Context,并命名为 IMC_PlayerInput
,这可以实现给输入做映射,相当于一个字典
那么有了字典,我们首先需要创建键,右键创建一个 Input Action,并命名为 IA_Move
在这里 IA_Move
主要实现移动功能,包括前后左右位移,因此最好给 Input Action 设置两个维度的输入进而区别前后左右
回到 IMC_PlayerInput
,将 IA_Move
导入进去,然后添加激活按键 WSAD
,这里需要意识到的是,每一次激活按键只会改变一个值,默认是第一个值,IA_Move
中设置了一个二维变量,假设是 [ x , y ] [x, y] [x,y] ,从俯视图来看,这里定义 x x x 为横向(水平)运动, y y y 为竖向(垂直)运动。
其中 WS
设置为垂直运动对应 y y y ,那么我们需要将 x x x 和 y y y 调换位置,同时 W
与 S
应该是相反数,这里通过在 modifiers运用 Swizzle Input Axis Values
去调换 x x x 和 y y y 的位置;这里运用 Negate
去取相反数,同理对 AD
采取一致的操作。
这样 IA_Move
设置在 IMC_PlayerInput
中设置完毕,接下来我们需要在 BP_Player
中使用 IA_Move
首先 Add Mapping Context
,把 IMC_PlayerInput
添加进去,这样我们就可以使用 IA_Move
通过 IA_Move
定义移动,这里 Return Value Y(Patch)
不连,采取默认为0的方式,因为前后左右相对于 Character
来说默认是在 Patch 为 0 的情况即俯视的情况,所以这里不去链接。
这里定义了 IA_Move
,还不能移动窗口方向,为此我们继续定义一个 IA_Look
,由于有 Yaw(左右)
和 Pitch(上下)
,因此同样定义为二维变量
AI_Look
在 IMC_PlayerInput
相对容易定义,Mouse XY 2D-Axis
可以直接生成二位变量
在 BP_Player
中,也比较好定义,利用两个 Add Input
可以实现 Add Controller Yaw Input
,Add Controller Pitch Input
。注意:如果不起作用,查看 SpringArm 中, Use Pawn Control Rotation 是否启用
目前还存在一个问题,那就是人物一直朝前看,我们只能看见人物的背影
为此我们需要在 BP_Player
中设置两个东西,第一个是 Use Controller Rotation Yaw 设置为 False,这样我们在旋转窗口的时候,人物的朝向不会发生变化;
第二个是 Orient Rotation to Movement 设置为 True,这样在上帝视角和Character视角不一致的情况下,Character视角会运动到上帝视角。
问题修复如下
BP_Character 添加动画
目前人物都是滑行,没有动画,这里给 character 的静态网格体导入走路的动作
首先在 Animation
文件夹中创建 Animation Blueprint 文件,命名为 ABP_Player
在右下侧 Asset Browser 窗口中将动画拖入到 AnimGraph
编译,就可以实现动画
接着我们在 BP_Player
中给 Mesh 添加 ABP_Player
这样就可以给 Character 添加动画。为了使 Character 在不同的情况下如移动有不同的表现,在这里使用 Blend Space 对 动画进行混合
创建 Blend Space 1D 并命名为 IdleJogBlendSpace1D
在 IdleJogBlendSpace1D
中拖入 Idle,Walk,Run这三个动画,并移入到时间轴中
同时,在这里我们需要定义一个变量去控制这三种状态转换,这里定义变量名为 Speed ,这里的最小值和最大值表示变量的区间,很好理解,Smoothing Time 表示动作之间的切换时间,一般设置为0.2足够。
这里 IdleJogBlendSpace1D
就设置完毕了,IdleJogBlendSpace1D
其本质上依然是一个动画,因此我们需要在动画蓝图中导入,打开 ABP_Player
,在 AnimGraph
中将 IdleJogBlendSpace1D
导入,并将 Speed
提升为变量
连接 Speed
,得到如下蓝图
然后在 Event Graph 中获取 Pawn 的速度 velocity,由于速度是三个维度向量,我们需要使用 Vector Length 将向量转化为 1D标量速度
ABP_Player
设置完毕,运行可以发现可以自由运动,并且动画很流畅
BP_Character 攻击
和 BP_Character 初始化一样,我们需要给攻击添加输入,在 Input 下创建 IA_Attack
在Attack中,我们只需要有简单的判别功能来判断是否按键,因此,IA_Attack
的 Value Type 只需要设置为 Digital(bool)
同时,将 IA_Attack
添加到 IMC_PlayerInput
中,并设置鼠标左键作为输入
现在输入搞定了,接下来便是动画部分,这里我们使用 Mixamo 中的动画,这里以踢腿的动画为例子
按下面要求下载,这里保留 Skin
方便后面操作
下载完毕后,在 Characters 目录下新创建文件夹,这里以该任务模型的名字命名 MMA,导入 FBX 文件的静态网格体 Mesh 和动画 Animation
这里要注意,记得导入动画,默认是不导入的 Import Animations
创建四个文件夹,将导入的文件分类整理好
打开 Animations
文件夹,可以发现动画 Animation Sequence 是绑定在 MMA 的骨骼上的
为了让 UE5 小白使用该动作,我们需要对该动画进行重定向 Retarget Animations
导出到UE5小白的动画文件夹,打开可以看到效果很好
该动画有缺点,即攻击动画速度要快和准,这样的动画前摇和后摇太长,因此我们需要对动画去帧处理,然后可以在 Rate Scale 中调整播放动画的速度
处理后得到符合要求的动画,在 BP_Player 中使用该动作之前,我们需要了解 Play Animation 和 Play Montage 的区别:简单来说,前者播放动作后不能复原到默认状态,而后者 Play Montage 可以,因此我们应该将动画 Animation Sequence 转化为动画蒙太奇 Animation Montage
Montage 类似于 Blend Space,可以拖入多个动画,Montage可以绑定多个事件进行操作
ok,输入和动画全部搞定,接下来在 BP_Player
中结合输入和动画
设置完毕至目前应该是无效果的,因为 Montage 需要在 AnimGraph 中有一个 slot 才可以使用,这里回到 ABP_Player
,在 AnimGraph 中设置 DefaultSlot
动画搞定,看效果,这里要提到的是,如果要实现 Play Montage 的同时移动,需要禁用 Animation Sequence 中的 EnableRootMotion,默认是禁用了的;如果要实现 Play Montage 时移动失效,并使用 Montage 的 Root 移动,需要启用Animation Sequence 中的 EnableRootMotion
由于是攻击,有碰撞判定,接下来设置判定的位置和时间,判定的位置我们可以在 BP_Player
中使用 Arrow 来调整
判定的位置设置如下
判定时间有两种方式,第一种是采用延迟的方式,即 Delay
;不过这种很难调整;第二种是使用 Montage notify 的方式,在 Montage 上设置 notify 如下;
设置完毕 notify 后,直接将 On Notify Begin 与 Multi Sphere Trace for Objects 连接
此时设置完毕了,效果如下
BP_Enemy 初始化 以及 AI 运动
BP_Enemy 类似于 BP_Character,不同的是,由于我们不需要从 enemy 本身进行观察,因此我们不需要设置 camera 以及 springarm
导入 Quinn 的 mesh, 并设置 mesh 的 location,同时设定 Animation ,这里设置和 BP_Player 一样的蓝图
现在 BP_Enemy 已经初始化,接下来开始为 BP_Enemy 添加 AI 运动功能,wonder 以及 chase
在执行AI运动功能之前,我们需要先导入一个寻路网格体,NavMeshBoundsVolume,并把大小调整至足够大
有了寻路网格体,这样 BP_Enemy 才能运动到指定位置,同时在面对转向动作时,我们依然要记得把 Orient Rotation to Movement 设置为 True,这样可以避免突然转向
Use Controller Rotation Yaw 由于不需要控制 BP_Enemy ,所以是可以关可以不关的状态,不过最好还是关掉
接下来实现 wonder 事件
效果
然后实现 chase 事件
效果
最后直接把事件绑定在 Event BeginPlay 上就行,当然最好把 max walk speed 调整为较小值
Camera Collision 相机碰撞
在两个 Character 碰撞时,可能会导致相机出现抖动,我们需要在 Character 的 Blueprint 中 设置两个 coliision,分别是 Mesh 的 collision 以及 CapsuleComponent 的 collision
在设置 camera 为 Ignore 后,碰撞问题就解决了
BP_Character 生命以及伤害
首先设置生命 Health,数据类型设置为 浮点类型 Float,然后设置一个默认值,这样生命就设置完毕了
伤害分为施加伤害以及收到伤害,首先是施加伤害,施加伤害的目标首先是需要检测的,在 BP_Character 攻击中我们使用了 Sphere Trace For Objects 来检测目标,然后判断是否命中 Apply Damage 即可,同时设置上海数值
收到伤害后,我们使用 Event AnyDamage 来接受伤害
然后将伤害系统移植到 BP_Player 和 BP_Enemy 上,在 Chase 中,要注意循环是添加在 Montage 播放完毕之后的
Wave Spawner 波生成
首先我们创建一个 BP_Actor 蓝图命名为 BP_WaveSpawner
来创建 Enemy 生成点
双击打开 BP_WaveSpawner
,为了让 BP_Actor 可视,添加一个 Sprite,Billboard,这是一个使图片能在任何角度正面朝向视角的 component,放置的时候要注意高度,不然生成的 Enemy 可能会卡在墙里
然后实现一个基本的 spawner 使其能生成 BP_Enemy,注意这里可能会生成多个,在 Collision Handling Override 设置中设置为 Try To Adjust Location, But Always Spawn
但是光这样生成的 Enemy是 AI运动 不了的,我们需要在 BP_Enemy 中的 Auto Possess AI 中选择 Placed in World or Spawned
这样使用 spawn 生成的模型也可以 AI运动
现在要实现植物大战僵尸一样的效果,击败每一波敌人后会出现下一波敌人直到通关
首先我们定义一个函数生成指定数量的敌人
然后生成一个数据变量设置每一波 Enemy 数量
创建当前波的生成事件,然后使用 Event BeginPlay 生成事件
在 BP_Enemy 的 Event AnyDamage 事件中判断敌人数量并生成当前波,为此延迟三秒 Destroy BP_Enemy 重新计算所有BP_Enemy 数量,当数量为 0 的时候生成当前波
得到效果如下
UI 初始化以及 Damage Screen
首先创建一个 Widget Blueprint,命名为 WB_HUD
,当然我们可以将其分开,例如我们可以创建小部件 WB_HealthBar
打开 WB_HealthBar
,首先添加一个 Canvas Panel,可以在上面添加东西
在 Canvas Panel 上添加一个进度条
接下来我们绑定这个 HealthBar,有两种方式,第一种是进入右上角蓝色框 Graph
中设置函数并设置返回值然后回到红框进行绑定,第二种是通过红色框点击 bind ,Create Binding 创建函数进行绑定。
函数中操作如下
Widget Blueprint 绑定完毕如下
接着我们可以创建一个收到伤害效果,这里继续创建 Widget Blueprint,命名为 WB_HUD
,在上面添加 Canvas Panel 加入 Image
然后在 Appearance 中设置颜色
设置完毕后,我们可以添加动画,点击最上方的 window 选择 animations,就会出现动画窗口
设置动画,并设置动画名称为 DamageScreen
动画设置完毕后,点击右上角的 Graph 回到 Graph 页面,创建一个自定义事件 Damage Screen
接着在这里实现文字功能来显示第几波,回到 Designer 页面,添加文本
在 Appearance 的 Justification 中居中
然后点击绑定 Text
实现函数如下:
然后将两个 Widget Blueprint 添加到 Viewport 中,这里我们可以将两个 Widget Blueprint 添加到 BP_Player 中
WB_Health
没有触发事件是最简单的,直接在 BP_Player 的 Event BeginPlay 中 Create Widget ,然后 Add to Viewport 就好
接下来是 WB_HUD
,由于里面包含一个动画触发,所以我们需要将 HUB 设置为一个变量,然后再添加到 Viewport
然后将 Damage Screen
添加到 Event AnyDamage 中
实现效果如下
指定位置随机生成
我们在 BP_WaveSpawner 中设置一个 Spawn Position
然后在关卡中设置其默认位置
搞定
添加声音
这里有五个声音,下载下来
Footstep: https://freesound.org/people/swuing/s…
Attack Grunt: https://freesound.org/people/MrFossy/…
Damage Grunt: https://freesound.org/people/whisperb…
Wave Completed: https://freesound.org/people/Kenneth_…
Forest Sounds Looping: https://freesound.org/people/rolandas…
在 Content 目录下创建 Audio文件夹,把声音文件放进去
重复性的音频会让人感到无趣,我们可以使用 MetaSound Sourse 使音频动态随机变化
用随机值 random float 去调整 Pitch Shift
现在的声音并没有距离范围,即目标在很远如果贴上了这个声音一样可以听见,我们可以给声音添加一个3D效果,创建一个 Sound Attenuation
在 Sound Attenuation 中设置 Inner Radius 和 Falloff Distance
然后将 Sound Attenuation 运用到 MetaSound Sourse,即 SA_Character
运用到 MS_Footstep
衰减 Attenuation 已经做了,接下来我们把声音加到动画上,我们回到 Animation Sequence 上,这里走路的 Animation 有 walk,run 我们在其下脚的时间帧上添加声音
将 Play Sound 的 detail 的 Sound 切换成 MS_Footstep
这样脚步声就添加完毕,现在添加攻击以及受伤的声音,由于攻击有动画,所以其设置和 Footstep 一样,将声音用MetaSound Sourse 包装后直接添加到动画中
受伤是没有动画的,在将受伤音效包装完毕后,我们是直接添加到 BP 之中,首先是 BP_Player
然后是 BP_Enemy
攻击和受伤的声音添加完毕了,然后我们来添加切换 Wave 的声音,这里的声音可以不发生变化,不需要使用 MetaSound Sourse 来包装,同时这里由于不涉及到距离的关系,可以直接使用 2D 来播放
这里受伤的声音太大了,我们可以在MS_Damage 中调节音量的大小,这里调节为原来的一半
环境 Environment Level
这里导入免费资产 Stylized Nature Pack
重新打开项目,可以看到
我们首先进入 Level 文件夹,直接复制 Testing 重命名为 WavesMap
然后删除 Floor,即地板,因为我们要重新创建一个 Landscape
接着我们点击 Selection Mode 切换至 Landscape Mode
这里 Enable Edit Layers,同时 Section Size 设置为 31 x 31 效果要好一些,然后直接创建就行
创建完毕后,可以看到 Landscape 光溜溜的什么也没有,是因为 Landscape 是图层的组合,这里我们需要应用图层,因此我们打开 Paint ,设置 Layers
返回 Selection Mode,得到 Landscape 效果如下
然后回到 Landscape Mode ,我们利用 Sculpt 来创造一些小山
创建完毕后,我们切换 Landscape 进入 Foliage Mode,在这里我们添加一些 Foliage
将 FoliageTypes 的植物分批次 Paint 在 Landscape 上
同时可以调整 Foliage 的大小和密度
创建完毕 Foliage 之后,我们开始添加草,草使用 Landscape 来创造更好,返回 Selection Mode,在 Stylized_PBR_Nature 文件目录下创建一个 Landscape Grass Type,命名为 LG_Grass
打开 LG_Grass
,设置 Grass Mesh,Grass Density,Cull Distance
然后,选择 Landscape 中的 Landscape Material
进入 Landscape Material,添加 Landscape Layer Sample,并连接 Landscape Grass,Landscape Grass 中设置为 LB_Grass
同时要保证 Landscape Layer Sample 要和 Landscape Layer Blend 中 Layer Grass_01 一致
返回 Level,可以得到效果如下
草地成功添加进去,同时可以看到 Foliage 可以随着风飘动,这里可以设置风的速度,在材质 Material Instance 里面
这里颜色需要调整,我们可以使用 PostProcess Volume 对颜色进行后期处理,首先添加 PostProcess Volume
启用 PostProcess Volume 中的 Infinite Extent,可以让范围不局限于小方块之中
然后在 Exposure 中 调整 Min Max EV100,分别位置为1
在 Global 中 设置饱和度和对比度 Saturation ,Contrast
然后调整直射光 DirectionalLight
颜色问题处理完毕,接下来是 Nanite,启用和禁用 Static Mesh 的 Nanite 功能如下
对比是否启用,可以观察到启用 Nanite 的效果要比不启用的效果更绿
最后还有树的碰撞部分,从严格意义来讲是 BlockAll 形式,直接设置就好
环境部分结束
替换 Character Asset
这里导入 Stylized Character Kit: Casual 01 上面的资产工程文件
由于这里的人物模型是根据UE4定做的,所以这里我们进行一次重定向 Retarget
然后使用新的 Mesh 和 新的动画蓝图 ABP_Player,这里由于 Attack 的动画不在 ABP_Player 上,我们要对其单独重定向
然后在 BP_Player 中设置,首先是 Mesh 和 Animation
然后是 Attack
敌人可以设置为随机的 Mesh,首先在 BP_Enemy 中创建 Skeletal Mesh Array,然后导入 Mesh 设置 Mesh
但是这里还会出现一个 BUG,那就是 Mesh 无法 simulated physics,因为这些 skeletal mesh 没有 physics assets,在create and assign physics assets之后,就可以正常 simulated physics 了
游戏效果如下
可以添加背景音乐,可以直接循环拖入游戏中,首先打开背景音乐 Forest_Sounds_Looping,启用 Loop
然后拖入游戏中
到此,游戏已经完成 !!
完结撒花
耗时两天半!!