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

《UE5_C++多人TPS完整教程》学习笔记54 ——《P55 旋转根骨骼(Rotate Root Bone)》


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


文章目录

  • P55 旋转根骨骼(Rotate Root Bone)
  • 55.1 C++ 实现转身插值
  • 55.2 在蓝图中旋转根骨骼
  • 55.3 Summary


P55 旋转根骨骼(Rotate Root Bone)

本节课我们将在人物角色动画蓝图中加入蓝图节点 “旋转根骨骼”(Rotate Root Bone),使得我们的人物角色在进行转身时能旋转到位。
在这里插入图片描述


55.1 C++ 实现转身插值

  1. 我们想要人物角色在转身时,其根骨骼在转到 90° 之前保持静止,到达 90 ° 之后再将根骨骼向左或向右旋转。
  2. 在 “BlasterCharacter.h” 中声明可复制变量 “InterpAO_Yaw”,它将作为对 “AO_Yaw” 进行插值的中间变量。
    /*** BlasterCharacter.h ***/...UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()...private:...UPROPERTY(Replicated)									// 可复制变量float AO_Yaw;											// 人物角色瞄准偏移偏航角/*P55 旋转根骨骼(Rotate Root Bone)*/UPROPERTY(Replicated)									// 可复制变量float InterpAO_Yaw;										// 用于对 AO_Yaw 插值/*P55 旋转根骨骼(Rotate Root Bone)*/float AO_Pitch;											// 人物角色瞄准偏移俯仰角UPROPERTY(Replicated)									// 可复制变量FRotator StartingAimRotation;							// 人物角色的起始瞄准旋转UPROPERTY(Replicated)									// 可复制变量ETurningInPlace TurningInPlace;							// 人物角色的原地转身状态						void TurningInPlaceFunc(float DeltaTime);				// 实现人物角色原地转身...}
    
  3. 在 “BlasterCharacter.cpp” 的函数 “GetLifetimeReplicatedProps()” 中为 “InterpAO_Yaw” 注册生命周期,指明其复制条件为只在模拟端复制。接着,在瞄准偏移函数 “AimOffset()” 中修改代码,当人物角色站立时,启用控制器旋转控制偏航,人物角色正前方方向将随着摄像机转动改变;若未进行转身则令 “InterpAO_Yaw” 始终和 “AO_Yaw” 保持一致。随后,在转身函数 “TurningInPlaceFunc()” 中添加代码,如果人物角色要进行转身,则使用函数 “FInterpTo()” 对 “AO_Yaw” 进行插值并进行更新,当 “AO_Yaw” 小于一定值时,停止转身,重置人物角色起始瞄准旋转 “StartingAimRotaiton”。
    /*** BlasterCharacter.cpp ***/...// 重写复制属性函数
    void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
    {// 调用SuperSuper::GetLifetimeReplicatedProps(OutLifetimeProps);...DOREPLIFETIME_CONDITION(ABlasterCharacter, AO_Yaw, COND_SimulatedOnly);					// AO_Yaw 的计算只在权威端或本地控制端进行,复制只在模拟端进行/*P55 旋转根骨骼(Rotate Root Bone)*/DOREPLIFETIME_CONDITION(ABlasterCharacter, InterpAO_Yaw, COND_SimulatedOnly);			// InterpAO_Yaw 的计算只在权威端或本地控制端进行,复制只在模拟端进行/*P55 旋转根骨骼(Rotate Root Bone)*/DOREPLIFETIME_CONDITION(ABlasterCharacter, StartingAimRotation, COND_SimulatedOnly);	// StartingAimRotation 的计算只在权威端或本地控制端进行,复制只在模拟端进行DOREPLIFETIME_CONDITION(ABlasterCharacter, TurningInPlace, COND_SimulatedOnly);			// TurningInPlace 的复制只在模拟端进行
    }...// 瞄准偏移
    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 (HasAuthority() || IsLocallyControlled())										// 只在权威端或本地控制端计算 AO_Yaw{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;												// 获取人物角色瞄准偏航角/*P55 旋转根骨骼(Rotate Root Bone)*/// bUseControllerRotationYaw = false;										// 禁用控制器旋转偏航if (TurningInPlace == ETurningInPlace::ETIP_NotTurning) {					// 如果不转身InterpAO_Yaw = AO_Yaw;													// InterpAO_Yaw 始终和 AO_Yaw 保持一致}bUseControllerRotationYaw = true;											// 启用用控制器旋转偏航,人物角色正前方方向将随着摄像机转动改变/*P55 旋转根骨骼(Rotate Root Bone)*/TurningInPlaceFunc(DeltaTime);}}...}...// 实现人物角色原地转身
    void ABlasterCharacter::TurningInPlaceFunc(float DeltaTime)
    {// 输出左右转动摄像机时 AO_Yaw 的值,可以观察到持续向右转动时 AO_Yaw 为更大的正值,持续向左转动时 AO_Yaw 为更小的负值// UE_LOG(LogTemp, Warning, TEXT("AO_Yaw: %f"), AO_Yaw);if (AO_Yaw > 90.f) {TurningInPlace = ETurningInPlace::ETIP_Right;							// 向右转身} else if (AO_Yaw < -90.f) {TurningInPlace = ETurningInPlace::ETIP_Left;							// 向左转身}/*P55 旋转根骨骼(Rotate Root Bone)*/if (TurningInPlace != ETurningInPlace::ETIP_NotTurning) {					// 如果开始向左或向右转身InterpAO_Yaw = FMath::FInterpTo(InterpAO_Yaw, 0.f, DeltaTime, 4.f);		// 由于未开始转身前 InterpAO_Yaw 与 AO_Yaw 相等,因此开始转身时这里相对于从 0.0 到 AO_Yaw 插值AO_Yaw = InterpAO_Yaw;													// 将每一帧的插值赋给 AO_Yaw,以便在蓝图中获取 AO_Yaw,它的相反数将作为旋转根骨骼节点的入参if (FMath::Abs(AO_Yaw) < 15.f) {										// 当 AO_Yaw 小于一定值,这里设为 15.0TurningInPlace = ETurningInPlace::ETIP_NotTurning;					// 停止转身StartingAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); // 重置起始瞄准旋转}}/*P55 旋转根骨骼(Rotate Root Bone)*/
    }
    

55.2 在蓝图中旋转根骨骼

  1. 编译后,打开虚幻引擎,在 “BlasterAnimBP” 的 “AnimGraph” 事件面板中添加蓝图节点 “旋转根骨骼”(Rotate Root Bone),绘制下图所示的蓝图。
    在这里插入图片描述

  2. 人物角色进行转身之后还无法回到待机状态,因此创建 “TurnLeft到Idle” 和 “TurnRight到Idle” 的状态规则转换,并为其绘制蓝图,当人物角色 移动速度大于 0Speed > 0)或转身动画 “剩余时间(比率)”(Time Remaining (ratio) < 0.1小于 0.1 到或在空中(bIsInAir == true)或未进行转身时,由转身状态过渡到待机状态。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 为了让转身更自然一些,可以设置 “TurnLeft” 和 “TurnRight” 动画播放速率为 1.5
    在这里插入图片描述在这里插入图片描述

  4. 仿照上述步骤,构建蹲伏转身到蹲伏待机的过度规则蓝图。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

  5. 编译、保存后进行多人游戏测试,先操控服务器上的人物角色进行转身和蹲伏转身,再操控其中客户端,可以发现所有机器上转身和蹲伏转身的动画都能非常自然流畅,且都能正确地进行网络同步。

    注意
    如果没有对变量 “InterpAO_Yaw” 进行网络同步,在多人游戏测试时人物角色会出现身体抖动的情况。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述


55.3 Summary

本节课我们成功实现了角色转身时的根骨骼旋转功能,使转身动画更加自然流畅。首先在 C++ 层面,我们添加了 “InterpAO_Yaw” 变量用于对 “AO_Yaw” 进行平滑插值处理,并对 “InterpAO_Yaw” 进行网络同步,保证后续的转身动画不会出现抖动。接着,在“TurningInPlaceFunc()” 中使用 “FInterpTo()” 函数实现 “AO_Yaw” 插值,保证转身角度可以平滑地过渡。在动画蓝图方面,添加了 “旋转根骨骼” 节点,使用 “InterpAO_Yaw” 值控制根骨骼旋转,基于人物角色移动速度、转身动画剩余时间和空中状态的进行判断,构建转身(蹲伏转身)到待机(蹲伏待机)的状态转换规则。随后,调整转身动画的播放速率使转身更加自然。测试结果表明,人物角色转身时身体能够平滑转向,且在多人游戏环境下服务器和所有客户端上都能正确同步显示转身动画。
在这里插入图片描述


http://www.dtcms.com/a/390652.html

相关文章:

  • go资深之路笔记(五)用系统信号实现优雅关机
  • C++实战㉔】解锁C++ STL魔法:list与deque实战秘籍
  • Linux 系统指令——助力信奥初赛
  • LVS详解:构建高性能Linux负载均衡集群
  • 【Linux网络编程】网络层协议-----IP协议
  • 电池AH的定义与WH关系
  • 谙流 ASK 技术解析(四):负载均衡引擎
  • 乾元通渠道商中标国家华中区域应急救援中心应急救援装备采购项目
  • 网络原理补充——NAT/NAPT、代理服务、内网穿透、交换机
  • 深入 HTTP 协议:剖析 1688 商品详情 API 的请求构造与签名机制
  • 共用体union和大小端模式
  • 2022年下半年 系统架构设计师 案例分析
  • LeetCode 面试经典 150_哈希表_有效的字母异位词(42_242_C++_简单)
  • go webrtc - 3 工程演示
  • JVM(五)-- 执行引擎
  • 微算法科技(NASDAQ:MLGO)量子架构搜索技术:突破变分量子算法性能瓶颈,实现量子计算的鲁棒优化
  • 海亮科技亮相第十一届亚教展,“教育 + AI”赋能县域教育振兴
  • JMeter的配置元件
  • Charles与Postman、JMeter结合使用教程:高效接口调试与性能测试方案
  • 【Haddop】Hive的离线分析与Sqoop的数据集成
  • 嵌入式 Linux 基础入门笔记(1)
  • Starlink 2.0与3GPP NTN技术对比分析:颠覆性优势与产业格局重构
  • 鸿蒙Next用户文件管理全解析:安全、高效、跨设备的未来体验
  • 简形电力JX2202 智能测试系统:重构新能源电力检测效率标准
  • AI识别视频中动物与人物的技术深度解析
  • iOS 上架完整流程指南 苹果应用发布步骤、App Store 上架流程
  • MySQL-CRUD 操作及常用查询语法详解
  • 玳瑁的嵌入式日记---0919(ARM)
  • Objective-C —— APIs declaration 自定义
  • 【XTDrone】笔记5:control文件详解