当前位置: 首页 > news >正文

《UE5_C++多人TPS完整教程》学习笔记50 ——《P51 多人游戏中的俯仰角(Pitch in Multiplayer)》


本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P51 多人游戏中的俯仰角(Pitch in Multiplayer)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephen Ulibarri 发布在 Udemy 上的课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
在这里插入图片描述


文章目录

  • P51 多人游戏中的俯仰角(Pitch in Multiplayer)
  • 51.1 旋转体的同步机制
  • 51.2 映射 AO_Pitch 区间
  • 51.3 Summary


P51 多人游戏中的俯仰角(Pitch in Multiplayer)

本节课我们将对瞄准偏移俯仰角变量 “AO_Pitch” 进行网络同步,以解决上节课中服务器和客户端人物角色瞄准偏移动画不同步的问题。
在这里插入图片描述


51.1 旋转体的同步机制

  1. 打开 Visual Studio,在 “BlasterCharacter.cpp” 中的 “AimOffset()” 函数中添加调试代码,将客户端上服务器所控制的人物角色的瞄准偏移俯仰角 “Pitch” 打印到消息日志中。

    /*** BlasterCharacter.cpp ***/...// 瞄准偏移
    void ABlasterCharacter::AimOffset(float DeltaTime)
    {if (Combat && Combat->EquippedWeapon == nullptr) return;FVector Velocity = GetVelocity();												// 获取人物角色速度向量Velocity.Z = 0.f;																// 不关心 Z 轴速度,设置为 0float Speed = Velocity.Size();													// 获取人物角色速度向量的模(大小)bool bIsInAir = GetCharacterMovement()->IsFalling();							// 判断人物角色是否掉落从而判断人物角色是否在空中if (Speed == 0.f && !bIsInAir) {												// 当人物角色静止站立且不跳跃时FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f);	// 获取人物角色当前瞄准旋转FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation);	// 标准化获取 CurrentAimRotation 和 StartingAimRotation 的差量AO_Yaw = DeltaAimRotation.Yaw;												// 获取人物角色瞄准偏航角bUseControllerRotationYaw = false;											// 禁用控制器旋转偏航}if (Speed > 0.f || bIsInAir) {													// 当奔跑或跳跃时StartingAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f);			// 改变奔跑或跳跃状态转换为静止站立状态时的起始瞄准旋转AO_Yaw = 0.f;																// 由于启用了控制器旋转偏航,人物角色朝向始终面向控制器当前朝向,因此设置 AO_Yaw 为 0 bUseControllerRotationYaw = true;											// 启用控制器旋转偏航}AO_Pitch = this->GetBaseAimRotation().Pitch;									// 获取人物角色瞄准俯仰角/* P51 多人游戏中的俯仰角*/if (!HasAuthority() && !IsLocallyControlled()) {UE_LOG(LogTemp, Warning, TEXT("AO_Pitch: %f"), AO_Pitch);}/* P51 多人游戏中的俯仰角*/
    }...
    
  2. 编译后进行测试。当我们控制服务器上的人物角色持续向下进行瞄准时,可以看到客户端上打印出的 “AO_Pitch” 的值从大于 0 减小到 0,然后再跳跃到 360.0360.0 开始减小,所以客户端上服务器控制的人物角色会向上进行瞄准。这便是问题所在,因为我们本来期望的是 “AO_Pitch” 的值在减小到 0 之后在负数的区间 [-90,0) 里面连续变化,这样 “AO_Pitch” 的值就可以和我们瞄准偏移 “HipAimOffset” 和 “AimAimOffset”中的垂直轴 [-90,0) 区间对应上。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 出现这样的测试结果与虚幻引擎对旋转体 Yaw、Pitch 和 Roll 三个方向上的分量值进行网络同步的机制有关。为了减小网络同步的压力,通常在发送方会将它们进行压缩传输,在此过程中首先会将角度限定在 [0->360) 的浮点数区间,任何角度的负值都会变成正值,以便在压缩时映射到 [0->65535) 的字节区间。而在接收方,解压缩过程则相反,将角度从字节值重新映射到 [0->360) 的浮点值。

    PlayerInfo 高频同步解决方案
    需求目的
    分析一个常见的需求: “在 1P 客户端显示 3P 的 Transform ”。
    显然,在客户端存在 3P 的 Pawn 时,可以直接取 PawnTransform;但出于性能考虑,会进行各种 AOI 机制,在较远距离时客户端会将 3P 的 Pawn 裁剪掉,只留下 PlayerState(或者某个不被剪裁的数据 Channel) 用于同步。
    一个直观的想法是将 Transform 直接通过对应的 PlayerState 属性同步给所有客户端;但出于性能考虑,对于同步一般会开启 PushModel;这种高频字段会频繁将 PlayerState 对应 ActorChannelMarkDirty,导致 PushModel 功能基本失效,频繁进行同步的 Diff 等大开销的操作;所以需要一个机制对这种情况进行优化。
    核心思路
    对于 DS,创建一个 Channel 专门用于同步 Player 的高频变化信息,如 LocationRotation 等;
    对于同步的信息,进行适当的同步降频(不需要每帧同步)、字节压缩(舍弃部分精度,精确到 float 没有意义);
    同时为了保证 Client 的信息相对正确(同步降频会导致 Location 不连续),在 1P Client 进行信息的预测插值;


    —— 《[UE] PlayerInfo高频同步解决方案》

  4. 具体可以参阅虚幻引擎的源码,值得注意的是函数 “CompressAxisToShort()” 中在将角度从浮点数量化为字节值、四舍五入后使用了位运算 “& 0xFFFF,其作用主要在于只保留最后 16位,剔除 16 位前所有的二进制值,这样返回值的类型就是 “uint16” 。

    /*** Rotator.h ***/...template<typename T>
    FORCEINLINE uint16 TRotator<T>::CompressAxisToShort( T Angle )
    {// map [0->360) to [0->65536) and mask off any windingreturn FMath::RoundToInt(Angle * (T)65536.f / (T)360.f) & 0xFFFF;
    }template<typename T>
    FORCEINLINE T TRotator<T>::DecompressAxisFromShort( uint16 Angle )
    {// map [0->65536) to [0->360)return (Angle * (T)360.f / (T)65536.f);
    }...
    

51.2 映射 AO_Pitch 区间

  1. 了解了虚幻引擎对旋转体分量值进行网络同步的机制,接下来我们只需将 “AO_Pitch” 的变化区间 [270, 360) 映射到 [-90, 0) 即可。在 “BlasterCharacter.cpp” 中的 “AimOffset()” 函数中定义两个区间,然后调用 “FMath::GetMappedRangeValueClamped()” 进行区间映射即可。
    /*** BlasterCharacter.cpp ***/...// 瞄准偏移
    void ABlasterCharacter::AimOffset(float DeltaTime)
    {if (Combat && Combat->EquippedWeapon == nullptr) return;FVector Velocity = GetVelocity();												// 获取人物角色速度向量Velocity.Z = 0.f;																// 不关心 Z 轴速度,设置为 0float Speed = Velocity.Size();													// 获取人物角色速度向量的模(大小)bool bIsInAir = GetCharacterMovement()->IsFalling();							// 判断人物角色是否掉落从而判断人物角色是否在空中if (Speed == 0.f && !bIsInAir) {												// 当人物角色静止站立且不跳跃时FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f);	// 获取人物角色当前瞄准旋转FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation);	// 标准化获取 CurrentAimRotation 和 StartingAimRotation 的差量AO_Yaw = DeltaAimRotation.Yaw;												// 获取人物角色瞄准偏航角bUseControllerRotationYaw = false;											// 禁用控制器旋转偏航}if (Speed > 0.f || bIsInAir) {													// 当奔跑或跳跃时StartingAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f);			// 改变奔跑或跳跃状态转换为静止站立状态时的起始瞄准旋转AO_Yaw = 0.f;																// 由于启用了控制器旋转偏航,人物角色朝向始终面向控制器当前朝向,因此设置 AO_Yaw 为 0 bUseControllerRotationYaw = true;											// 启用控制器旋转偏航}AO_Pitch = this->GetBaseAimRotation().Pitch;									// 获取人物角色瞄准俯仰角/* P51 多人游戏中的俯仰角*/// if (!HasAuthority() && !IsLocallyControlled()) {//	 UE_LOG(LogTemp, Warning, TEXT("AO_Pitch: %f"), AO_Pitch);// }if (AO_Pitch > 90.f && !IsLocallyControlled()) {								// 对于其他机器上非本地控制的人物角色FVector2D InRange(270.f, 360.f);FVector2D OutRange(-90.f, 0.f);AO_Pitch = FMath::GetMappedRangeValueClamped(InRange, OutRange, AO_Pitch);	// 将区间 [270,360) 映射到 [-90,0)}/* P51 多人游戏中的俯仰角*/
    }...
    
  2. 编译后进行测试,可以发现无论是 “HipAimOffset” 还是 “AimAimOffset” 的动画都能在所有机器上正确同步。在这里插入图片描述在这里插入图片描述

51.3 Summary

本节课我们成功解决了多人游戏中瞄准偏移俯仰角(Pitch)的网络同步问题。
在测试中我们发现,在客户端上服务器所控制的人物角色持续向下瞄准时,“AO_Pitch” 的值会从 360 开始递减而不是保持在 [-90,0) 的区间,导致瞄准方向相反。通过深入分析虚幻引擎的旋转体同步机制,我们了解到虚幻引擎将 [0->360) 角度值映射到 [0->65536) 再进行压缩传输进行网络传输,这导致了负角度值被转换为正角度值,从而造成客户端与服务器上的瞄准动画显示不一致。
因此,我们在 “BlasterCharacter.cpp” 中的 “AimOffset()” 函数中通过调用 “FMath::GetMappedRangeValueClamped()” 函数,将 “AO_Pitch” 的 [270,360) 区间映射到 [-90,0) 的区间,确保所有机器上的人物角色瞄准偏移的动画都能够正确同步。
在这里插入图片描述



文章转载自:

http://q0L5UJFJ.fpryg.cn
http://feibkzGt.fpryg.cn
http://ns6ngWmo.fpryg.cn
http://MnPrq1Y0.fpryg.cn
http://zIDHin8I.fpryg.cn
http://mXfsdMww.fpryg.cn
http://LlRnMiwi.fpryg.cn
http://fYYC2FVn.fpryg.cn
http://oUYxrT5E.fpryg.cn
http://oHjaMM9G.fpryg.cn
http://xjHUZnEL.fpryg.cn
http://JRxmU1Ec.fpryg.cn
http://ISix2wGB.fpryg.cn
http://X6vcgLa5.fpryg.cn
http://9LxuaddX.fpryg.cn
http://OTpFN1u7.fpryg.cn
http://Nc2jPTz4.fpryg.cn
http://6r2vWEQI.fpryg.cn
http://0itcUJC5.fpryg.cn
http://lARFHKpJ.fpryg.cn
http://xG3Dti8x.fpryg.cn
http://efLJ8eEp.fpryg.cn
http://AYBdHSoC.fpryg.cn
http://Kt44zlPT.fpryg.cn
http://aykCZGRM.fpryg.cn
http://3faRtlnZ.fpryg.cn
http://rauEdoUE.fpryg.cn
http://aVPx7fRz.fpryg.cn
http://ZbwnJsUO.fpryg.cn
http://cTdwP6rM.fpryg.cn
http://www.dtcms.com/a/375659.html

相关文章:

  • RL【5】:Monte Carlo Learning
  • 深度解析HTTPS:从加密原理到SSL/TLS的演进之路
  • minio 文件批量下载
  • 【算法专题训练】19、哈希表
  • AJAX入门-URL、参数查询、案例查询
  • 安装ultralytics
  • Eino ChatModel 组件指南摘要
  • 腾讯codebuddy-cli重磅上线-国内首家支持全形态AI编程工具!
  • 基于PCL(Point Cloud Library)的点云高效处理方法
  • UVa1302/LA2417 Gnome Tetravex
  • STC Link1D电脑端口无法识别之升级固件
  • 【C++】LLVM-mingw + VSCode:Windows 开发攻略
  • SRM系统有哪些核心功能?企业该如何科学选型?
  • LINUX99 centos8:网络 yum配置;shell:while [ $i -ne 5 ];do let i++ done
  • 【陇剑杯2025】密码复现(部分)
  • 漫谈《数字图像处理》之图像自适应阈值处理
  • Melon: 基于marker基因的三代宏基因组分类和定量软件
  • 水题记录1.7
  • JVM 执行引擎详解!
  • lua中 string.match返回值
  • 2025-安装集成环境XAMPP
  • 整体设计 之 绪 思维导图引擎 :思维价值链分层评估的 思维引导和提示词导航 之 引 认知系统 之6之 序 认知元架构 之1(豆包助手 之3)
  • 【教学类-07-10】20250909中3班破译电话号码(手写数字版、撕贴版、头像剪贴底纹版、抄写填空版)
  • 【初阶数据结构】算法复杂度
  • PowerBI 的双隐藏,我在QuickBI 里也找到了
  • AI赋能训诂学:解码古籍智能新纪元
  • 微服务雪崩问题与系统性防御方案
  • css3之grid布局
  • git config --global user.name指令报错时的解决方案
  • 三维仿真软件中渲染层面的孔洞优化方法调研