用Bolt实现角色运动控制(2):RPG式第三人称角色控制


RPG式第三人称角色控制

视频教学地址:B站视频,YouTube在线观看
工程文件下载地址:

PS:本教学使用的是Bolt1.4版,并未包含在工程文件包中,请大家自行购买安装

基本需求:

  • 俯45°视角固定跟随摄影机;
  • 按住右键绕角色旋转摄影机视角;
  • 键盘ASWD控制Player在摄影机视图中前后左右运动。

实现原理

  • 使用Transform: Rotate Around单元来旋转摄影机
  • 使用Rigidbody来运动角色
    - 通过设置Rigidbody.velocity属性来控制角色移动速度
    - 通过Transform: Transform Direction单元来将世界坐标系的速度矢量转换为相机坐标系的速度矢量
    - 通过Transform: LookAt单元来旋转角色使其始终面朝运动方向
  • 通过Animator Controller来控制角色动画

实现步骤:

首先设置场景:

新建一个4×1×4的Plane当做地面,重置position,添加一个带地面贴图纹理的材质,避免地面太白看不清楚。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第1张图片

从Asset Store下载免费的牛仔低模作为Player角色(Low Poly Cowboy),并导入自带资源中的idle、run、walk三段动画数据。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第2张图片
用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第3张图片
用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第4张图片

将Cowboy模型的prefab拖入场景,新建一个Animator Controller素材(取名Cowboy_AC),并指定给Cowboy的Animator组件上的Controller。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第5张图片

这种下载的Asset资源大多都给大家做好了prefab,直接使用即可。但有些不太规范的Asset仅提供FBX甚至OBJ模型文件,就需要手动去设置三维模型的导入配置了。

双击Cowboy_AC打开Animator窗口。我们可以添加一个Blend Tree来作为初始默认State,然后在Blend Tree里用3个Motion(idle、walk、run)来做动作混合。在Parameter一栏中添加速度控制float类型变量MoveSpeed

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第6张图片
用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第7张图片

这样设置的结果是:当MoveSpeed = 0时,角色播放idle动画,增加MoveSpeed值使得idle动画与walk动画融合,直到MoveSpeed = 0.5时完全播放walk动画,继续增加MoveSpeed值,walk动画与run动画开始融合,直到MoveSpeed = 1,角色完全播放run动画。

通常我们不会直接在美术素材上添加交互,所以我还是在场景中专门添加了一个Player空物体,将Cowboy作为Player的子物体,然后在Player上做交互。

  • 确保Cowboy上没有诸如Collider或者Rigidbody这样的交互相关组件,只是一个纯粹的美术素材
  • 在Player上添加Capsule Collider,设置成合适的大小
  • 在Player上添加Rigidbody,设置相关参数
  • 在Player上添加Flow Machine,改成Embed模式
用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第8张图片

然后制作角色的运动

点击“Edit Graph”开始制作交互逻辑。

基本的控制方法如下图所示,通过Input: Get Axis节点获取键盘的输入,转换为速度矢量后输出给Rigidbody: Set Velocity节点。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第9张图片

但这样的速度是相对于世界坐标系的,而不是相对于摄影机坐标系。

为了让整个交互的结构显得清晰一些,我选择将计算Velocity的流程与设置Player运动的流程区分开来。

新建一个Graph变量Velocity (类型为Vector3),在一个Graph中用变量Velocity输出给Set Velocity节点,在另一个Graph中计算这个Velocity究竟应该怎么取值。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第10张图片

首先在Create Vector 3节点后面添加一个Transform Direction节点,并添加一个Camera: Get Main节点来获得当前主摄影机以链接给Transform Direction

添加一个Debug: Draw Ray节点来描绘出变量Velocity的方向以供我们判断。

这时我们可以看到,原本世界坐标的“上下左右”被转换成了摄影机视图的“上下左右”。但这里出现了一个新问题,那就是摄影机视图的“上下左右”其实也并不是我们所想要的正确方向,我们其实希望Player的速度矢量依然是在世界坐标系的XZ平面上。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第11张图片

为了解决这个问题,我将Transform Direction输出的Vector3重新拆开,获得其x和z轴上的分量,然后再用Create Vector 3重新组合成一个新的Vector 3。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第12张图片

这样依然有问题,虽然Velocity的方向匹配上了,但其Forward方向上的数值却被缩小了,而且摄影机越是“俯视”,最终的Velocity.z就越小。

因此,我需要添加一个Normalize节点,将Create Vector 3所输出的Velocity矢量进行“标准化”。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第13张图片

Normalize可以将一个输入的矢量转换为一个方向不变,矢量长度恒定为1的新矢量。

如此一来,我们搞定了Velocity的方向问题,但也因为Normalize的关系,失去了按下按键之后输入数值“渐变”的效果,因为不论输入的控制数值多小,都会被Normalize为长度为1的Velocity。

如果想要找回这种“渐变”效果,可以用一个Vector3: Magnitude节点计算出原始输入的矢量长度,然后与Normalize节点的输出结果相乘。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第14张图片

仔细观察一下,还可以发现一个之前忽视了的问题,那就是当键盘控制角色“斜”走的时候,实际得到的速度值其实是大于键盘控制角色“直”走的。这是因为斜走需要x和z轴都输入,而其合并后的Magnitude其实长度不是1,而是1.414。

想要解决这个问题,需要分别对输入的x和z分量进行一定的缩放,然后再用其缩放后的结果去计算新的Magnitude。

我这里提供一种解决方案来进行这一缩放操作,即:对原始输入的Vector3进行Normalize,然后分别用Normalize之后的x和z分量去乘原始输入的Horizontal和Vertical数值。大家也可以思考一下还有什么别的办法可以解决这个问题。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第15张图片

这样我们就把所需要的Velocity给计算出来了,最后还可以再乘上一个自定义的MaxSpeed (Float)变量,来整体放大角色的运动速度。这一阶段完成的完整Graph如下:

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第16张图片

接下来制作角色的旋转

当角色的运动方向发生变化时,其自身也会发生旋转来保持始终正面(forward)朝向运动的方向。这一需求通常都可以通过Transform: LookAt节点来实现。最基本的Graph是这样的:

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第17张图片

LookAt本质上是让一个物体的正面指向空间中的一个点或另一个物体(的中心点)。它有很多“分身”,我们这里选择的是带有World PositionWorld Up参数的那个分身。

想要LookAt一个具体的方向而不是具体的点,可以用这个方向和物体本身的位置(position)做相加(Add)计算,这样就得到了一个合适的点位置。

这样做出来的效果是一个“突变”效果,就是角色的旋转是突然变化的,而不是我们所希望的“转向”目标方向。想要得到“逐渐转向”的效果,需要用到Lerp相关的计算。

Lerp是很常见的一种数学计算函数,通常用来将一个值“逐渐变化成”另一个值。它的本质是根据一个T值(代表变化的程度)来取得值A向值B做“缓入缓出”变化的过程中某一个特定中间点的值C,但我们可以通过每帧计算值C向值B进行Lerp而得到新的值C的方式,来呈现值B逐渐变化为值C的过程。

这一数学原理应用到我们的Graph中就是这样的:

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第18张图片

我用Transform: Get Forward获得Player当前的forward方向,然后将其用Vector3: Lerp向目标方向(也就是Velocity的)做Lerp计算。由于Lerp的结果将会通过LookAt旋转角色进而改变Player自身的forward方向,每帧Player的Forward都会逐渐靠近Velocity方向,这样就得到了一个逐渐变化的过程。

这里,T值决定了变化的快慢。T = 0 时不会变化,T = 1 时变化瞬间完成。我们可以这样理解,T = 0.1 代表这个变化会在10帧内完成。如果不喜欢用帧数来决定这个过程,可以添加一个Per Second节点来控制这个T值。Per Second可以将“每帧”值转换成“每秒”值,Per Second = 1 代表这个变化会在1秒内完成,Per Second = 10 代表这个变化会在0.1秒内完成。

控制角色动画

Player这个角色的动画其实我们已经在Animator中设置好了,通过MoveSpeed变量控制Blend Tree上三个动画片段之间的混合。但这个Animator组件并不在Player上,而在其子物体Cowboy上。我们只能通过变量的方式来调用并设置不属于自身的组件的变量。

新建一个Update Event,连接一个Animator: Set Float节点。新建一个变量Animator(Animator类型),手动指定其取值为Cowboy上的Animator组件。将这个变量连给Graph中的Set Float节点。

修改Set Float节点的“Name”输入为“MoveSpeed”,将变量Velocity计算出其Magnitude值(也就是当前速度快慢数值),连接给Set Float节点的“Value”。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第19张图片

这样做其实是在用Player实时的运动速度快慢来驱动Animator Controller中的Blend Tree中三个动画片段的混合,以达到一种“自动化”的程度。也就是说,我无需手动设置什么时候该“走”什么时候该“跑”,只要角色运动速度达到一定的程度,角色就会自动选择是站立还是走还是跑。

目前我设置的MaxSpeed是2,也就是说角色的最大速度不可能超过2,实测发现这样的运动速度是和动画不匹配的,太慢了,脚步会“滑”,很不真实。

通过测试,可以找出最适合这个Cowboy模型的walk动画的速度值是1.6,最适合run动画的速度值是4.8。

MaxSpeed变量设置为5,修改Blend Tree中三段动画的混合阈值如下图:

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第20张图片

我们就可以得到一个比较匹配我们模型的“站走跑”动画混合了。

相机旋转和跟随

在上一讲,相机旋转是通过直接旋转来实现的,这一讲中是第三人称视角,需要相机绕着Player旋转(始终看向Player),所以选择使用Transform: Rotate Around,但基本思路是一样的。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第21张图片
  • Transform: Rotate Around需要一个旋转中心点,这里使用Player的轴心点上方1.2单位高度的位置(大概是角色胸口的位置)来作为这个中心点;
  • Transform: Rotate Around还需要一个旋转速度,这里使用鼠标X轴位移来计算得到这个值。

在这个例子中,相机不仅需要能够旋转,还需要能够跟随Player一起运动。最简单的办法是实时获取Player的position,添加一个固定Offset变量,再用其结果设置相机本身的位置(position)。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第22张图片

这两个功能单独用起来都没有问题,但如果一起使用(运动过程中旋转摄影机),整个效果就不对了。这是因为:1. 相机旋转实际上改变了相机与Player的相对位移(也就是Offset变量所应该体现的内容);2. 相机需要先跟随Player移动位置,再绕Player旋转,最后更新Offset值以供下一帧使用,所以这3个操作需要做成一个Graph以保证其运行顺序。

因此,最终的相机交互逻辑修改如下:

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第23张图片

在这个Graph中,每帧首先根据Offset值设置好相机位置,然后判断是否需要旋转相机,如果需要,旋转后更新Offset值。另外,我还做了一个Start Event上的交互逻辑让游戏运行时能够自动计算出初始的Offset值来。

用Bolt实现角色运动控制(2):RPG式第三人称角色控制_第24张图片

至此,我们就完成了这个简单的RPG式第三人称角色控制的交互逻辑。

你可能感兴趣的:(用Bolt实现角色运动控制(2):RPG式第三人称角色控制)