本文为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://www.pswp.cn/pingmian/96061.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96061.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/96061.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树莓派 Ubuntu 24.04 开机换源总结

1. 图形界面 (桌面版) 如果你刷的是 Ubuntu Desktop 24.04&#xff1a;打开 Software & Updates&#xff08;软件和更新&#xff09;。在 Ubuntu Software 标签里找到 Download from 下拉菜单。默认只有 Main server 和 Server for China&#xff0c;如果想要更多选择&…

工业显示器在地铁电力监控与运维中的应用

在地铁电力监控与运维中&#xff0c;工业显示器凭借其高可靠性、环境适应性和强大的功能集成&#xff0c;成为保障地铁供电系统安全稳定运行的核心设备。以下从关键应用场景、技术优势及实际案例三个维度展开分析&#xff1a;一、核心应用场景变配电室与环控电控室监控 工业显示…

Docker 快速部署单节点 NiFi 1.27

Docker 快速部署单节点 NiFi 1.27 前言 Apache NiFi 是一款强大的数据集成工具&#xff0c;专注于数据的采集、处理和分发&#xff0c;具有可视化流程设计、强大的容错能力等特点。通过 Docker 部署可以快速搭建环境&#xff0c;省去复杂的配置步骤。本文介绍如何使用官方镜像…

php redis 中文API文档手册

php redis 中文API文档手册 Redis::__construct构造函数 $redis new Redis();connect, open 链接redis服务 参数 host: string&#xff0c;服务地址 port: int,端口号 timeout: float,链接时长 (可选, 默认为 0 &#xff0c;不限链接时间) 注: 在redis.conf中也有时间&#xf…

Windows环境下实现GitLab与Gitee仓库代码提交隔离

1. 背景 在开发工作中&#xff0c;我需要同时使用2个代码托管平台&#xff1a;公司统一使用的GitLab和个人学习用的 Gitee。我希望能够在同一台电脑上方便地管理和提交两个平台的代码&#xff0c;实现账号和提交内容的有效隔离。 前提条件&#xff1a; 已安装Git Bash、Tort…

深度解析:抗辐射电源芯片 ASP4644S2B 在空间环境中的单粒子效应表现

摘要&#xff1a;随着航天技术的飞速发展&#xff0c;空间电子设备面临着日益复杂和严苛的辐射环境挑战。单粒子效应&#xff08;SEE&#xff09;作为辐射环境对半导体器件影响的主要形式之一&#xff0c;极大地影响着航天电子系统的可靠性和稳定性。本文通过系统梳理国科安芯推…

【RabbitMQ】如何在 Ubuntu 安装 RabbitMQ

1. 安装部署 Erlang 环境 RabbitMQ 是一套开源的消息队列服务软件&#xff0c;基于 Erlang 语言编写的&#xff0c;因此&#xff0c;在安装 RabbitMQ 之前&#xff0c;我们需要先部署 Erlang 环境&#xff0c;再安装 RabbitMQ 环境&#xff08;就像运行 Java 程序&#xff0c;…

vue集成高德地图API工具类封装

import axios, { AxiosInstance, AxiosResponse } from axios;// 高德地图 API 响应基础结构 interface AMapResponse {status: string;info: string;infocode: string; }// 逆地理编码响应结构 interface RegeoResponse extends AMapResponse {regeocode: {formatted_address:…

手写 Tomcat

文章目录02 初出茅庐:构造一个极简的 HttpServerRequestResponseHttpServer03 动态 Response : 按照规范构造返回流04 各司其职的 Server : 拆分响应模块与处理模块HttpConnectorHttpProcessor05 Server 性能提升: 设计多个 ProcessorHttpConnectorHttpProcessor06 规范化: 引入…

嵌入式ARM架构学习3——启动代码

一 汇编补充&#xff1a;area reset, code, readonlycode32entry;mov r0, #4 ; r0 4;mov r1, r0 ; r1 r0;mov r2, r1, lsl #1 ;r2 r1 << 1 乘2;mov r3, r1, lsr #1 ;r3 r1 >> 1 除2;mov r4, r1, ror #2;mov r0, #100 ;100是十进制 转为16进制赋值给十进制;mov …

PNPM库离线安装方案

以下是几种可行的方案&#xff0c;推荐优先使用方案一。 方案一&#xff1a;使用离线镜像&#xff08;Offline Mirror&#xff09; - 最优雅、最PNPM的方式 这是 PNPM 官方推荐的处理离线环境的方式。它会在内网电脑上创建一个所有依赖包的压缩文件&#xff08;tarball&#x…

[Wit]CnOCR模型训练全流程简化记录(包括排除BUG)

stepfile:step 00 创建数据集 目录结构 yourproject -data --myset ---images #存放训练图片 ---dev.tsv #测试标签 tsv格式 图片文件名\t内容 ---train.tsv #训练标签 tsv格式 图片文件名\t内容 -train_config.json -train_config_gpu.json -fix_cnocr_encoding.py step 01 创…

Sklearn(机器学习)实战:鸢尾花数据集处理技巧

1.数据集的使用&#xff1a;先使用load导入鸢尾花数据&#xff1a;from sklearn.datasets import load_iris然后定义一个函数来查看鸢尾花数据集&#xff1a;数据集的获取&#xff1a;iris load_iris()print(鸢尾花的数据集&#xff1a;\n,iris)使用iris[DESCR]来查看数据及里…

【企业微信】接口报错:javax.net.ssl.SSLHandshakeException

详细报错信息 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target原因 关于qyapi…

光子芯片驱动的胰腺癌早期检测:基于光学子空间神经网络的高效分割方法

光子芯片驱动的胰腺癌早期检测:基于光学子空间神经网络的高效分割方法 1 论文核心概念 本文提出了一种基于集成光子芯片的光学子空间神经网络(Optical Subspace Neural Network, OSNN),用于胰腺癌的早期检测与图像分割。其核心思想是利用光子芯片的高并行性、低延迟和低能…

Scikit-learn Python机器学习 - 特征降维 压缩数据 - 特征提取 - 主成分分析 (PCA)

锋哥原创的Scikit-learn Python机器学习视频教程&#xff1a; 2026版 Scikit-learn Python机器学习 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程主要讲解基于Scikit-learn的Python机器学习知识&#xff0c;包括机器学习概述&#xff0c;特征工程(数据…

【Python】pytorch安装(使用conda)

# 创建 PyTorch 虚拟环境 conda create -n pytorch_env python3.10# 激活环境 conda activate pytorch_env# 安装 PyTorch&#xff08;CPU版本&#xff09; conda install pytorch torchvision torchaudio cpuonly -c pytorch# 或者安装 GPU 版本&#xff08;如果有NVIDIA显卡&…

ThreeJS骨骼示例

<html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>骨骼动画混合演示</title><style>body {margin: 0;padding: …

python + Flask模块学习 1 基础用法

目录 Flask 的主要作用 常用扩展 Flask 基本用法 1. 安装 Flask&#xff08;再安装个postman用来调试测试API哈 2. 最小化应用示例 3. 运行应用 Flask 是一个轻量级的 Python Web 框架&#xff0c;它简洁灵活&#xff0c;适合快速开发 Web 应用和 API。它被称为 "微…

python数据可视化之Matplotlib(8)-Matplotlib样式系统深度解析:从入门到企业级应用

作者&#xff1a;浪浪山齐天大圣 描述&#xff1a;深入探索Matplotlib样式系统的核心机制&#xff0c;掌握从基础样式到企业级样式管理的完整解决方案引言 在数据可视化的世界里&#xff0c;一个优秀的图表不仅要准确传达数据信息&#xff0c;更要具备专业的视觉效果。Matplotl…