UE4多人FPS游戏持枪视角旋转优化

UE4多人FPS游戏持枪视角旋转优化

1 问题概述

在传统多人FPS游戏中,一般对于玩家操控的角色会有两个骨骼模型,分别是自己控制的第一人称以及其他玩家看到的第三人称模型,同时这两个模型也有其各自的动画。常见的第一人称通常只会保留手臂,其他玩家看到的第三人称模型的持枪瞄准方向会跟随第一人称的摄像机旋转进行相应的调整,而在第三人称持枪视角旋转时,会出现卡顿(不平滑)。以下以虚幻引擎官方的游戏示例项目Shoot Game为例,可以在虚幻引擎商店中免费获取完整项目。

2 原理分析

在ShootGame项目中,第三人称的持枪视角旋转是使用2D的混合空间(Blende Space)合成的,该资产位于Content/Characters/HeroTPP/HeroTPP_AimOffsets,其工作原理是预先制作9个不同视角的持枪动画,通过获取该角色第一视角下的摄像机旋转(Pitch、Yaw)控制其进行动画的混合。以下是动画资产截图:

![image-20211120184746147](https://gitee.com/tanfu2/picgo/raw/master/pic/2021-11-20 18-47-54_image-20211120184746147.png)

在角色的动画蓝图中,通过每帧获取摄像机的旋转对混合空间的Pitch和Yaw进行设置以控制持枪的动作的视角,以下是第三人称的AnimGraph的部分节点:

![image-20211120191750847](https://gitee.com/tanfu2/picgo/raw/master/pic/2021-11-20 19-18-25_image-20211120191750847.png)

以下是部分蓝图程序节点,这部分代码在Event Blueprint Update Animation中执行:

![image-20211120192129501](https://gitee.com/tanfu2/picgo/raw/master/pic/2021-11-20 19-21-33_image-20211120192129501.png)

GetAimOffsets是ShooterCharacter类的一个函数,用于获取第三人称对应第一人称的视角的数值,其代码如下:

FRotator AShooterCharacter::GetAimOffsets() const
{
	const FVector AimDirWS = GetBaseAimRotation().Vector();
	const FVector AimDirLS = ActorToWorld().InverseTransformVectorNoScale(AimDirWS);
	const FRotator AimRotLS = AimDirLS.Rotation();

	return AimRotLS;
}

上述解决方案理论上能表现地比较良好,但是由于这是多人游戏,还需要考虑到服务器的问题。当其他玩家的旋转视角操作上传到服务器时,服务器将各个玩家的数据同步到所有客户端,但是由于服务器性能有限,其更新频率远不如客户端,特别是视角旋转是一种高频且在短时间内数据波动较大的操作,其它玩家的摄像机旋转信息同步到当前客户端时就会出现数据的“卡顿”(举个例子,我在本地操作视角上下匀速抬头,假设Pitch是从-90渐变到90,由于本地帧数高,数据变化可以理解为-90、-87、84...87、90,这样自己看起来比较丝滑,但是服务器同步数据的频率有限,到其他客户端可能就变成-90、-60、-30...60、90),所以在其他玩家看来,原本我在本地丝滑的视角旋转,到其他客户端观察到的就是卡顿和闪烁。实际上,角色的移动如果不经过处理同样也会这样,但是虚幻引擎强大的GamePlay框架对于这种经典的FPS问题已经有了现成的解决方案,角色移动组件(CharacterMovement)就很好地解决了类似问题。当然上述的视角旋转观察不平滑的问题,也是我在研究官方的案例中发现的,我也算是半个强迫症,于是乎就想到去简单的解决这个问题。

3 解决方案

对于角色移动组件,我没有对类似问题的解决方法进行深入研究,但是我猜想它可能是对数据进行插值,让目标数据更加平滑,所以对于视角旋转的不平滑我设想的解决方法也是如此。其基本思路是让第三视角旋转延迟更新,在这部分延迟的时间内对当前旋转到目标旋转(也就是第一视角实际的旋转)进行插值,获取的值用于控制第三视角的旋转。以下是实际修改之后蓝图节点代码:

![image-20211120212433213](https://gitee.com/tanfu2/picgo/raw/master/pic/2021-11-20 21-24-37_image-20211120212433213.png)

图中SmoothTime用于保存延迟更新的时间,LastAimRotator用于保存上一帧第三视角实际的旋转值,左下方的SmoothTime上方的绿线连接的是

Event Blueprint Update Animation事件的Delta Time X,一般来说延迟时间的具体数值应该保持在一定合理的范围内,既不能过小(依旧卡顿)也不能过大(延迟太高,视觉误差过大),通常根据服务器的更新频率设定。当然上述解决方案存在优化的空间,并不是十分完美但胜在简单易懂,上述的插值实际上并不是线性的,由于Alpha数值基本固定,越接近实际旋转时数值更新地越慢,无限接近实际旋转的数值。最后实际的动画表现效果表现良好达到预期。

上一篇:Edge 开发工具(未完)


下一篇:【炼丹术】EfficientDet训练模型学习总结