写在前面
这里主要是介绍一下Character的实现,顺带也介绍一下UE4资源的获取;
一、UE4资源获取
1. 地图的获取
1.1 下载资源
- 在Epic Games Launcher的虚幻商城中搜索内容,带有环境标签的就主要是地图资源;
- 有一些是免费的资源和适用于初学者的资源,而且每个月还会有本月免费资源,所以还是很不错的o( ̄▽ ̄)ブ;
- 选择资源的时候注意资源的支持的引擎版本要和使用的引擎版本匹配;
- 这里以太阳神庙项目作为例子(因为它是免费而且适用于UE4.24);
- 购买项目之后,切换界面到库->保管库,刚刚购买的项目就在放在这里;
- 选择创建项目,就会自动将项目下载到指定的本地位置;
- 创建完成后,在我的工程中双击项目打开即可;
1.2 加载到自己的项目
- 然后是将太阳神庙项目中需要的地图迁移到自己的项目中;
- 对内容浏览器Maps文件夹右键->迁移,迁移的路径为自己项目下的Content文件夹;
- 地图资源通常是放在Content文件夹中的Maps文件夹里面的,所以记得在命名的时候符合规范(参考之前的Unreal Engine05:UE4基本概念一文),这样在迁移之后就不会出现资源的放置混乱;
- 如果原来有Maps文件夹,就会和之前的地图资源合并放到同一个文件夹中,如果没有则正好可以获得一个Maps文件夹;
1.3 修改默认地图
- 在Editor主面板中的设置->地图和模式,修改编辑器开始地图和游戏默认地图;
2. 角色的获取
1. 下载资源
- 在Epic Games Launcher的虚幻商城中搜索内容,带有角色标签的就主要是角色资源;
- 人物建模越精细,则角色资源就越大;
- 这里用的是动画初学者内容包,是免费的,而且很小;
2. 加载到自己的项目
- 由于角色资源不是一个完整的项目,所以是添加到工程而不是创建工程;
- 选择自己的项目添加即可,添加后项目的Content文件夹中会增加一个AnimStarterPack文件夹,里面就是刚刚添加的资源;
- 里面包含了角色的动画、骨架、材质和纹理等资源;
3. 绑定到Character上
- 用Character的C++类创建一个蓝图类;
- 点击
Mesh
组件(这个组件是继承自ACharacter
的,普通的Pawn并不会默认带有),然后在Mesh->骨架网格体中选择一个角色的骨架即可;
- 然后还需要做一些调整:
- 将骨架移动到胶囊体的中心;
- 调整胶囊体
CapsuleComponent
的大小以覆盖骨架;
- 调整后如下:
二、创建一个Character的C++类
- 创建的C++类也是放在Source文件夹中的Public和Private文件夹中;
- 选择Character作为继承的父类;
- 仅从头文件来看,Character类和Pawn类的头文件是一样的,并无区别;
- 但从继承的
ACharacter
类来看,可以看到比APawn
类多了三个组件,如下:
1. 增加摄像机和弹簧臂
- 在头文件中加入摄像机和弹簧臂组件;
-
在构造函数中初始化弹簧臂和摄像机;
-
一些注意的点如下:
- 将组件的
bUsePawnControlRotation
设置为true
,则该组件将跟随Pawn的控制器进行旋转,也就是将旋转的控制加到Pawn的控制器上即可,无需单独设置弹簧臂的旋转; - 弹簧臂需要跟随旋转,摄像机由于是挂载在弹簧臂的插槽上的,所以会自动跟着弹簧臂旋转,无需单独设置;
- 将组件的
-
在生成蓝图类之后一定要在细节面板中的Camera Settings->使用Pawn控制旋转确认弹簧臂和摄像机的使用控制器设置是否和代码的中的一致(因为我发现即使是在C++中写了勾选,但在蓝图类中还是不一致的);
2. 增加输入控制映射
- 在项目设置->输入中增加Jump、Look_Up和Turn_Right,如下:
- Look_Up实际上就是视角上下移动,但这里不是直接控制摄像机或者弹簧臂了,而是传到Controller;
- Turn_Right实际上就是视角左右移动,但这里也不是直接控制摄像机或者弹簧臂了,而是传到Controller;
3. 用Controller实现输入控制
- 由于Pawn可以由Controller控制其运动,因此它运动的控制函数实现最好是都交给Controller,而不是像之前那样直接用Actor的状态实现运动;
- 在头文件定义响应事件函数,如下:
- 实现
MoveForward()
和MoveRight()
函数,如下:
-
一些注意的点如下:
- 这里是使用了控制器的旋转角度分量作为Pawn的前进方向的,而不是像之前那样直接用
GetActorRightVector()
函数,也就是把运动的控制统一放到了Controller上,而不是直接在Actor上进行; - 这样的话,因为控制器的旋转角度就是弹簧臂的旋转角度,也是摄像机的旋转角度,因此实际上就是以摄像机当前的局部坐标系作为Pawn移动的相对方向;
- 当然,这种写法只能在Pawn及其派生类上用,因为Pawn才能拥有Controller;
- 推荐是这种写法,它和自定义运动组件(参看:Unreal Engine09:自定义Pawn运动组件一文)并不冲突,因为运动组件的作用是在
AddMovementInput()
函数中生效的,也就是说只要使用了AddMovementInput()
函数,自定义运动组件就能生效;
- 这里是使用了控制器的旋转角度分量作为Pawn的前进方向的,而不是像之前那样直接用
-
实现
TurnAtRight()
和TurnLookup()
函数,如下:
- 一些注意的点如下:
- 这里是直接修改了Controller的旋转值,而不是像之前那样,分别用
SetActorRotation()
修改Actor的旋转值和调用弹簧臂组件的函数修改弹簧臂的旋转值; - 也就是把旋转的控制也加到的Controller上,然后弹簧臂跟随Controller旋转;
- 也推荐是这种写法,相当于是将控制全部交给Controller;
- 特别注意:
- 这里用了速率来控制旋转的快慢,也就是
AddControllerPitchInput()
中增加的旋转角度等于旋转速率乘以两帧之间的时间,相当于是之前在Tick()
中实现的逻辑,只不过这里没有了DeltaTime
作为传入的参数; TurnLookup()
函数中,传入AddControllerPitchInput()
的参数是-Value
,这是因为鼠标上推是Y值为正,但实际上控制器(也就是弹簧臂,因为弹簧臂旋转的角度和控制器相同)应该向下旋转视角才能向上看,所以刚好是和传入值是相反的;
- 这里用了速率来控制旋转的快慢,也就是
- 这里是直接修改了Controller的旋转值,而不是像之前那样,分别用
4. 绑定输入到响应事件函数
- 在
SetupPlayerInputComponent
中绑定映射,如下:
- 一些注意的点如下:
- 这里第一次实现了操作映射,需要绑定两个响应事件函数,一个是按下按钮时执行,一个是抬起按钮时执行;
- 跳起动作直接是调用了
ACharacter
的自带函数,因为这个动作输入既不会导致视角的旋转,也不会导致Character位置的移动,所以可能实现上没有那么多的花里胡哨;
5. 其他的一些动作控制设置
- 在构造函数里面还需要再设置一些变量,如下:
三、动画蓝图
- 动画蓝图用于让Character在运动过程中使用动画;
1. 新建一个动画实例C++类
- 创建的C++类也是放在Source文件夹中的Public和Private文件夹中;
- 选择AnimInstance作为继承的父类;
- 此时的头文件和
.cpp
是空的,没有其他的内容,如下:
- 这个动画实例类主要是用来获取Pawn的一些当前状态,这样在后续的动画控制中才能够根据Pawn的状态执行不同的动画;
1. 头文件
- 头文件的实现如下:
- 在头文件中增加两个记录Pawn状态的向量,一个是Pawn当前的速度,一个是Pawn是否跳起腾空;
Pawn
指针指向当前动画作用的Pawn类对象,也就是说动画不能脱离角色来进行实现(这在逻辑上很合理);NativeInitializeAnimation()
是动画的初始化函数,相当于是构造函数的功能;UpdateAnimationProperties()
是用于获取Pawn的运动状态并绑定到当前类属性中;
2. cpp实现
- 实现初始化函数如下:
- 实现的
UpdateAnimationProperties()
函数如下:
2. 新建一个动画蓝图类
- 这里并不是直接用C++类派生一个蓝图类的,而是需要先创建更高级一点的蓝图类;
- 新建一个
Animations
文件夹; - 在内容浏览器中右键动画->动画蓝图,创建动画蓝图:
- 选择
AnimInstance
作为父类,目标骨架则选择Character对应的骨架资源;
- 这样该动画就能够和骨骼资源绑定在一起了;
- 打开该动画蓝图,选择类设置->类选项->父类,设置为刚刚创建的动画实例类
UManAnimInstance
;
2.1 新建一维混合空间动画
-
在内容浏览器中右键动画->混合空间1D新建一个一维混合空间动画;
-
在动画界面编辑一维混合空间动画的结果如下:
- 一些注意的点如下:
- 界面右下方是动画资源浏览器,和当前骨骼有关的动画都已经在这里面了;
- 绿色图标的代表动画序列,可以看做是最基本的动画资源;
- 橙色图标的代表混合空间,可以看做是多个动画序列的叠加和组合而成的动画资源;
- 双击动画资源可以在界面中预览和修改对应的动画;
- 在界面下方是一维动画轴(这也是一维混合空间的命名由来),可以在轴上的不同位置放置已有的动画作为关键帧,从而生成组合动画;
- 绿色菱形点是当前预览窗口显示的动画效果;
- 白色菱形点是动画关键帧,通常起码一头一尾是要有一个关键帧的;
- 界面左方的资源详情中可以修改一维动画轴的一些设置;
- 修改名称可以为一维动画轴命名;
- 修改最小轴值和最大轴值可以修改轴的定义域;
- 增加网格分区数量可以增加轴上可以插入关键帧的位置;
- 修改内插类型可以调整关键帧之间的过渡动画生成效果;
- 这里在
0.0
处使用Idle_Rifle_Hit动画资源表示站立,在20.0
处使用Walk_Fwd_Rifle_Ironsights动画资源表示行走,在100.0
处使用Jog_Fwd_Rifle表示慢跑; - 这样就完成了一维混合空间动画的制作;
- 界面右下方是动画资源浏览器,和当前骨骼有关的动画都已经在这里面了;
2.2 新建二维混合空间动画
- 在内容浏览器中右键动画->混合空间新建一个一维混合空间动画;
- 在动画界面编辑二维混合空间动画的结果如下:
- 一些注意的点如下:
- 在界面下方是二维动画轴(这也是二维混合空间的命名由来),可以在轴上的不同位置放置已有的动画作为关键帧,从而生成组合动画;
- 绿色菱形点是当前预览窗口显示的动画效果;
- 白色菱形点是动画关键帧,通常起码每条最外面的边上是要有一个关键帧的;
- 界面左方的资源详情中可以修改一维动画轴的一些设置;
- 修改名称可以为一维动画轴命名;
- 修改最小轴值和最大轴值可以修改轴的定义域;
- 增加网格分区数量可以增加轴上可以插入关键帧的位置;
- 修改内插类型可以调整关键帧之间的过渡动画生成效果;
- 这里将水平坐标命名为Direction,范围是[-180, 180],垂直坐标命名为Speed,范围是[0, 100];
- 关键帧还是用之前一维动画轴用过的站、走、跑三种动画,但走和跑用到了默认实现的向前、向左、向右和向后四种动画,
-90
用的是向左,90
用的是向右,-180
和180
用的都是向后,0
用的是向前,具体的动画序列如下:
- 在界面下方是二维动画轴(这也是二维混合空间的命名由来),可以在轴上的不同位置放置已有的动画作为关键帧,从而生成组合动画;
- 这样就完成了二维混合空间动画的制作;
2.3 编写蓝图
- 在动画蓝图中的事件图表窗口中编写蓝图逻辑;
- 这里只需要每帧调用C++中实现的
UpdateAnimationProperties()
函数获取Pawn的状态即可;
2.4 编写动画图表
- 添加一个状态机,命名为“Man State Machine”,如下:
- 编写状态机,每个状态可以视为一个动画,不同状态有对应的跳转逻辑,如下:
- 下面介绍每个状态和跳转逻辑的蓝图实现;
- (1) Stand_Walk_Run状态:
- 通过C++定义的
MovementSpeed
来控制实现站->走->跑三种动画,用的是刚刚创建的一维混合空间动画; - 这里的一维混合空间动画的
Speed
输入就是刚刚在动画里面的一维动画轴的横坐标;
- 通过C++定义的
- (2) 转到Jump的逻辑:
- 用C++定义的
IsJumping
来控制跳转,这里最终输出的是一个布尔结果;
- (3) Jump的状态:
- 直接用已有的动画序列即可;
- (4) 跳转到In_Air的逻辑:
- 这里用到了一个 剩余时间(比率) 的函数,也就是前一个状态的动画播放到20%的时候就转到下一个状态;
- 但其实腾空的状态完全可以不要,这里只是为了演示复杂状态机对动画的控制而已;
- (5) In_Air的状态:
- 这里用了正向行走作为腾空动画,因为动画资源中并没有腾空的动画;
- (6) 转到On_Floor的逻辑:
IsJumping
为假即落地;
- (7) On_Floor的状态:
- 这里仅用Jump_From_Stand动画序列的0.676s后的动画,可以在细节面板中的Settings->初始位置中设置,而且要取消循环动画的勾选;
- (8) 转到Stand_Walk_Run的逻辑:
- 前一个状态的动画放完了就可以跳转到初始状态;
- 另外,可以将Character的运动动画和状态机对应来调试,在当前面板点击播放,然后按照下面选择Character的对应对象即可;
3. 在Character中使用该动画蓝图
- 在Character蓝图类中选中Mesh组件,在细节面板的Animation->动画模式中选择使用动画蓝图,在动画类中选择刚刚创建的动画蓝图类;