这次我们不会讲Player Weapons Manager脚本,虽然同上一期分析的脚本属于一个Manager文件夹,但是其实没有必要连在一起,因为我写这个系列也主要是为了那些像我一样的新手甚至刚入门的各位的,这个时候就要考虑脚本的实际作用的联系而不是层次上的联系了,个人认为武器属于FPS的核心但是也是同时次要的,在开发中我们设计的时候必须逐步完善一个个问题,所以说了这么多废话,当下的意思就是把整个角色本体控制讲完之后,敌人讲完之后我们在来了解武器交互。
回归正文,这次我们要说的是这个脚本Player Character Controller
通过观察这张图大家也可以大致了解这个脚本的功能,角色控制的具体实现就在这里面了。
同样的,先看函数集合
除了三个处理流程的官方函数以外,还剩下9个函数,我们先讲解这9个函数之后再返回来解析处理流程。
1.OnDie() //角色死亡预处理
这里可以看出是角色死亡的时候首先将一个死亡状态布尔值给赋值为true,下面一句其实英文注释已经说明了,就是禁止死亡后角色还能切换武器(通过将武器列表置为空),然后广播一个事件,PlayerDeathEvent,后面会有函数专门接收这个事件并处理(广播的话,类似于你站在广场上叫爸爸,你没有说明谁是你爸爸,但是如果有人回头答应的就是接收信息,你和他打起来就是处理信息),所以这个函数主要就是在角色死的时候做一个目击者,然后大喊撒日朗,等管理者处理剩下的事。
2.GroundCheck() //地面检测
这个函数名字大家应该不陌生,刚开始接触unity时中文教程里面有过相应的实现教导,地面检测的话,是一个重要的条件,此处先是创建一个临时浮点变量choseGroundCheckDistance取一个二元运算值,通过判断当前是否在地面上来取对应的距离值(地面上取前一个值,否则取后面一个值)。将bool值IsGrounded置为false,以此重新判断当前是否在地面,m_GroundNormal是一个法线向量(垂直与地面的方向)。
3.HandleCharacterMovement() //角色移动处理
这个函数体可以说是整个脚本的重心,100多行结合了角色移动与视角,音效的调用,由于其中已经分类功能,所以我们分开细说
3.1 Part1 - 角色的上下左右旋转
第一部分,加括号这里个人认为是为了在视觉上分离各个功能区块,实际执行不影响顺序,transform.Rotate()函数,旋转控制,此处为创建一个三维向量作为角色的新的视角角度(vector3),此处就调用到了我们上一期讲到的角色输入的预处理脚本,m_InputHandler是其一个实例对象,我们转到这个函数的实现,我们就可以看到,的确是其功能调用,具体功能可以看上一期的解析,如图。
因为此处只处理水平轴旋转,所以新的Vector3其实只有一个获取角色输入的水平轴的值在乘以定义的一个旋转速度变量以及一个旋转系数,其结果值作为角色水平轴变化时的变化量,Space.self 是指定角色的旋转一定是围绕当前角色的局部坐标的水平轴来变换的。
第二部分, 垂直轴的变换控制,这里改摄像机的旋转角度是因为这个摄像机是绑定在角色头上的,水平旋转的时候,只需角色旋转就可以让摄像机跟着转,当时垂直旋转的时候角色只有头会转,甚至如果为第一人称摄像机就是头,这里也一样,摄像机就是头,第一行功能与水平轴一致,只不过赋值对象不同罢了。第二行就是垂直旋转的角度限制,因为水平旋转是可以一周的,但是头显然做不到垂直旋转一周,最多旋转到看到自己的脚就可以了,除非角色是生化危机里面的开花头丧尸(它似乎也不行),第三句就是将加以限制之后的旋转值赋值给角色第一人称相机。其实就这么点功能,我这儿有点废话了。
3.2 Part2 -
此处主要就是处理角色的移动与碰撞问题,如果要实现一个比较合理的移动的话,光是调用几个函数是不够的,应该考虑到各种限制与模拟问题。此处的脚本实现很细致,我们在自己理解的时候最好也想一下自己以后做的时候调优的时候用得到吗,当然现在处于摸索初期,大可不必学到就用。第一句,bool值 isSprinting 先判断当前角色是否在冲刺状态,返回的状态值存储起来为后面速度调整备用。第二句,判断当前如果角色处于冲刺状态,则设置角色下蹲后状态自动切换返回(实践出来的现象,不一定准确),函数实现如图
其中两个形参作用为 1.crouched 角色是否蹲下了。2.ignoreObstructions 是否忽略障碍物
第一句,如果角色下蹲了,就调整角色高度(此处调整的是角色胶囊体的高)
第二句,在不忽略障碍物的条件下进行碰撞体检测,foreach循环判断是否存在
函数及形参:Collider[] OverlapCapsule(Vector3 point0(胶囊体底部中心点), Vector3 point1(胶囊体顶部中心点), float radius(胶囊体半径), int layerMask(属于哪个层));
foreach循环检查与角色胶囊体碰撞的所有collider,有本身之外的碰撞体时,状态不可改变,类似于下图这种情况(当然也有可能本人理解错误)
否则的话就调整下蹲或者站立高度
所以这个函数的状态分为:
1.bool crouched 为 true, bool ignoreObstructions 为 true ------>蹲下站立自由切换
2.bool crouched 为 true, bool ignoreObstructions 为 false ------->可以蹲下,但是无法站立
3.bool crouched 为 false, bool ignoreObstructions 为 true ------->可以恢复站立
4.bool crouched 为 false, bool ignoreObstructions 为 false ------->切换状态后自动切换回当前状态
所以经过我们分析之后发现SetCrouchingState函数的作用就是根据角色站立或蹲下不同状态做相应操作限制,并且调整不同状态的高度显示。
由于这里间隔上一个语句较远了所以我们重新贴一下图片:
float speedModifier 判断当前是否为冲刺状态然后根据状态赋予不同速度值
Vector3 worldspaceMoveInput 是根据角色输入的移动以及方向,将其变量转换为世界坐标下的向量
第一部分,处理角色在地面上的移动,所以整个代码块必须在IsGrounded成立的基础上才会执行。
①通过上面的转换后的世界向量以及声明的速度相关变量计算一个预期角色速度
②将角色速率做一个线性插值,将状态转换时的速度切换平滑处理
第二部分(④),处理角色跳跃,条件限制为当前角色在地面上并且按下了跳跃键
4.1当角色状态无变化并且无外部障碍物限制时,首先将水平速度提出单独计算赋值。
然后向角色上方施加一个力作为角色跳跃的速率。
播放跳跃的音效。
记录上次跳跃时间,防止持续高频响应跳跃。
将地面判断置为false
第三部分(⑤),处理角色移动时的脚步音效
float chosenFootstepSfxFrequency 取值为根据角色是否为冲刺状态选择对应的音效播放频率
当步数频率过高时播放对应的冲刺音效,将步频记录清零直到下一次状态切换。
当步数频率正常时,持续记录步频数
第四部分(⑧),实现角色在空中的加速度变化以及速度限制,实现重力牵引
没什么难点,而且大家看英文注释就明白了,以上的几个部分就是角色在不同状态的数值设置,每一个部分都有英文注释,我这里写的很乱属于是神志不清了,接下来的三步就是将最终计算结果赋值到角色。
最后一个部分是根据胶囊体指定范围探测障碍物并且据此调整速度
其胶囊体探测函数参数如图:
此处仅贴一张其中函数的调用:
4.IsNormalUnderSlopeLimit //判断当前坡度,角色能否直接移动
返回一个bool比较值,比较的是当前角色的局部垂直轴和接触平面的垂直轴的角度是否大于对角色上坡角度的最大限制角,即前方接触平面是可以走的坡还是高地。
5.GetCapsuleBottomHemisphere //直译获取当前角色胶囊下半圆弧的中心点
当前胶囊体世界坐标即为胶囊体的中心坐标沿着垂直方向乘以一个半径距离即为胶囊体中心点
6. GetCapsuleTopHemisphere //获取上半圆弧的中心点
需要注意此处不同于上一部分,传入了一个高度值,只是因为胶囊体的上半圆的中心点是会有变化的,这是基于将一个胶囊体看作一个角色,下半圆是下体,上半圆是上身包括头部,所以可以看作脖子长度以及头部扭动等导致角色的上半顶高度降低升高,所以需要传入当前角色的高度信息再获取中心点。
7.UpdateCharacterHeight //直译就是实时获取当前角色的高度
根据实际情况决定是否更新角色高度
即时更新高度:跳过平滑更新,直接将高度修改到目标值,同时修改瞄准点于摄像机等关联物体高度,例如游戏初始化加载时
平滑更新高度:更多的是模拟角色控制时的高度变化于关联物体的协调流畅,这将运用于整局游戏中。
总结以下:这篇文章其实是去年11月上半旬就已经完成了,但是写完以后完了发送,今天看到了草稿里面有两个这个系列的文章,才发现的,所以我随便改了一点,对于这种文章个人认为已经没有写下去的必要了,毕竟个人理解浅薄难免带歪读者,现在的chatgpt以及相关镜像等等大家都可以搜索使用,放一段代码上去解释很全面,相信用过的都知道,所以这个水文系列就此打住了。