在前面的内容里已经创建好了多人游戏中瞄准的动画,但是在测试中会发现一个问题,角色本地持枪后上下瞄准动画是正常的,但是在其他客户端上会产生明显的异常效果,如下:

当玩家持枪低于水平0度的时候,自己看动画是正常的,但是在别人看来缺突然把武器举上天了。我们在动画类中的AimOffset函数加上如下测试代码:
1 | void ABlasterCharacter::AimOffset(float DeltaTime) |
这段代码可以看到在其他客户端上你所控制的对应的角色的Pitch值,当我将武器不断向下瞄准直至低于水平面时,可以看到角度从1直接到了359而不是负数,又因为角度变成了359,所以在其他客户端看来我所控制的角色才突然朝天看。而问题出现在虚幻网络传输过程中。
在虚幻的网络传输过程中,因为在联机游戏中往往有大量的信息需要传送,为了避免信息的拥堵,在网络传输过程中往往要对信息进行压缩,我们的Pitch数据正是在这一过程中因为数据压缩而发生了变化。从之前的博客我们可以知道,玩家的AO_Pitch值是从GetBaseAimRotation().Pitch拿到的,这里的Pitch值是写在CharacterMovementComponent组件里的,而我们知道,CharacterMovementComponent组件已经帮我们处理好了网络同步的问题,我们不需要手动同步关于角色移动组件的属性。不过这里问题就出在它帮我们处理的过程中。在虚幻5.2版本中,CharacterMovementComponent.h有一个叫PackYawAndPitchTo32的函数,在2766行。
1 | FORCEINLINE uint32 UCharacterMovementComponent::PackYawAndPitchTo32(const float Yaw, const float Pitch) |
这个函数就是压缩角色Pitch和Yaw在网络中传输的数据。我们点进CompressAxisToShort函数,这个函数名字很直观,压缩成更短的。
1 | FORCEINLINE uint16 TRotator<T>::CompressAxisToShort( T Angle ) |
可以看到这个函数可以将你传入的float类型的Pitch乘以65536再/360后与0xFFFF这个16个1的十六进制数进行相与,从而得到一个uint16类型,从而压缩了数据长度。更方便的在网络中进行传输,但这样做得到的结果则是一个没有符号的值,于是别的客户端得到的你的角色就是一个永远不会朝地上看,你一朝地上看角色就会看天的情况。
解决的办法是在AO_Pitch拿到值后进行一次检测,如果发现角度比90度大,那这个数据就是经过转换的,因为我们上下看,以水平为轴,角度只会从-90到90,而不是0到360。我们进行如下转换:
1 | void ABlasterCharacter::AimOffset(float DeltaTime) |
从而得到正确的值

现在我们来看一下log和结果

现在log中Pitch值正常从正变负,从其他客户端看动画也正常了。