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

《UE5_C++多人TPS完整教程》学习笔记49 ——《P50 应用瞄准偏移(Applying Aim Offset)》


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


文章目录

  • P50 应用瞄准偏移(Aim Offset)
  • 50.1 在蓝图中应用瞄准偏移
  • 50.2 使用 C++ 代码驱动瞄准偏移
  • 50.3 Summary


P50 应用瞄准偏移(Aim Offset)

本节课我们将对创建好的瞄准偏移进行应用,这包括在动画蓝图中使用相关变量去驱动瞄准偏移。
在这里插入图片描述


50.1 在蓝图中应用瞄准偏移

  1. 在 “BlasterAnimBP” 的 “AnimGraph” 蓝图面板,添加蓝图节点 “新保存的缓存姿势”(New Save cached pose),将其重命名为 “Equipped”,并与原先的 “Equipped” 状态机节点相连,这样我们就可以在蓝图的多个位置通过添加蓝图界定啊 “使用缓存姿势“Equipped”(Use cached pose ‘Equipped’)” 节点来使用 “Equipped” 状态机节点。

  2. 每个骨骼的分层混合(Layered blend per bone)
    请添加图片描述

  3. 在骨骼 “SK_EpicCharacter_Skeleton” 中,“spine_01” 是分开上下半身的骨骼节点(spine_01 is what separates the lower half from the upper half of the body),因此我们选择这个骨骼节点在 “每个骨骼的分层混合” 蓝图节点中进行混合。
    在这里插入图片描述

  4. 选择 “每个骨骼的分层混合” 蓝图节点,在右侧细节面板中展开 “层设置”(Layer Setup),接着展开 “索引 [0]”(Index [0]),然后再展开 “分支过滤器”(Branch Filters)、“索引 [0]”(Index [0]),设置 “骨骼名称”(Bone Name)为 “spine_01”。
    在这里插入图片描述

  5. 在右侧资产浏览器中将 “HipAimOffset” 移动至面板中生成蓝图节点,并绘制下图中的蓝图。
    在这里插入图片描述


50.2 使用 C++ 代码驱动瞄准偏移

  1. 蓝图节点 “HipAimOffset” 需要与 “Yaw” 和 “Pitch” 相关的变量进行驱动。打开 Visual Studio,在 “BlasterAinmInstance.h” 中声明变量 “AO_Yaw”(记录人物角色瞄准偏移偏航角)和 “AO_Pitch”(记录瞄准偏移俯仰角);在 “BlasterCharacter.h” 中同样声明 “AO_Yaw” 和 “AO_Pitch”,并声明人物角色瞄准起始旋转 “人物角色的起始瞄准旋转” 以及瞄准偏移函数 “AimOffset()”。

    /*** BlasterAinmInstance.h ***/...UCLASS()
    class BLASTER_API UBlasterAnimInstance : public UAnimInstance
    {GENERATED_BODY()...private:...UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;bool bAiming;								// 是否在瞄准UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;float YawOffset;							// 人物角色水平方向偏航角UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;float Leaning;								// 人物角色身体倾斜度FRotator CharacterRotationLastFrame;		// 人物角色上一帧的旋转FRotator CharacterRotation;					// 人物角色当前的旋转FRotator DeltaRotation;						// 旋转体插值的当前值/* P50 应用瞄准偏移(Applying Aim Offset)*/UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;float AO_Yaw;								// 瞄准偏移偏航角UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;float AO_Pitch;								// 瞄准偏移俯仰角/* P50 应用瞄准偏移(Applying Aim Offset)*/
    };
    
    /*** BlasterCharacter.h ***/...UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()...protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;// 与轴映射相对应的回调函数void MoveForward(float Value);	// 人物角色前进或后退void MoveRight(float Value);	// 人物角色左移或右移void Turn(float Value);			// 人物角色视角左转或右转void LookUp(float Value);		// 人物角色俯视或仰视// 与动作映射相对应的回调函数void EquipButtonPressed();		// 人物角色装备武器void CrouchButtonPressed();		// 人物角色蹲伏void AimButtonPressed();		// 人物角色开始瞄准void AimButtonReleased();		// 人物角色停止瞄准/* P50 应用瞄准偏移(Applying Aim Offset)*/void AimOffset(float DeltaTime);/* P50 应用瞄准偏移(Applying Aim Offset)*/...private:...UFUNCTION()void OnRep_OverlappingWeapon(AWeapon* LastWeapon);		// OverlappingWeapon 的 Repnotify 函数UPROPERTY(VisibleAnyWhere)class UCombatComponent* Combat;							// 添加枪战功能组件类UFUNCTION(Server, Reliable)void ServerEquipButtonPressed();						// 人物角色装备武器的 RPC 函数/* P50 应用瞄准偏移(Applying Aim Offset)*/    float AO_Yaw;                                           // 人物角色瞄准偏移偏航角float AO_Pitch;                                         // 人物角色瞄准偏移俯仰角FRotator StartingAimRotation;							// 人物角色的起始瞄准旋转/* P50 应用瞄准偏移(Applying Aim Offset)*/...
    };
    
  2. 在 “BlasterCharacter.cpp” 中完成 “AimOffset()” 的定义,获取人物角色瞄准偏航角 “AO_Yaw” 及俯仰角 “AO_Pitch”,并且使得人物角色在静止站立且不跳跃时,人物角色的正前方朝向不跟随跟随我们的控制器(鼠标)改变;而在奔跑和跳跃时,人物角色的正前方朝向跟随我们的控制器进行改变,始终为控制器的当前朝向。接下来,不要忘记在 “Tick()” 函数中调用 “AimOffset()”。

    /*** BlasterCharacter.cpp ***/.../* P50 应用瞄准偏移(Applying Aim Offset)*/
    #include "Kismet/KismetMathLibrary.h"
    /* P50 应用瞄准偏移(Applying Aim Offset)*/.../* P50 应用瞄准偏移(Applying Aim Offset)*/
    // 瞄准偏移
    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;											// 启用控制器旋转偏航}	
    }
    /* P50 应用瞄准偏移(Applying Aim Offset)*/...// Called every frame
    void ABlasterCharacter::Tick(float DeltaTime)
    {Super::Tick(DeltaTime);// // 如果有与人物角色重叠的武器,则在每个 tick 都显示拾取组件// if (OverlappingWeapon) OverlappingWeapon->ShowPickupWidget(true);/* P50 应用瞄准偏移(Applying Aim Offset)*/AimOffset(DeltaTime);/* P50 应用瞄准偏移(Applying Aim Offset)*/
    }...
    
  3. 在 “BlasterCharacter.h” 定义内联函数 “GetAO_Yaw()” 和 “GetAO_Pitch()”,以便在 “BlasterAinmInstance.cpp” 的 函数 “NativeUpdateAnimation()” 中进行调用,获取 “AO_Yaw” 和 “AO_Pitch” 的值。

    /*** BlasterCharacter.h ***/...UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()...public:...bool IsWeaponEquipped();	// 判断是否装备了武器bool IsAiming();			// 判断是否在瞄准/* P50 应用瞄准偏移(Applying Aim Offset)*/FORCEINLINE float GetAO_Yaw() const { return AO_Yaw; }FORCEINLINE float GetAO_Pitch() const { return AO_Pitch; }/* P50 应用瞄准偏移(Applying Aim Offset)*/
    };
    
    /*** BlasterAinmInstance.cpp ***/...// 原生(Native)类更新函数 NativeUpdateAnimation() 覆写,用于在每一帧调用以更新动画
    void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime)		
    {...// 获取人物角色的倾斜度CharacterRotationLastFrame = CharacterRotation;							// 保存人物角色上一帧的旋转CharacterRotation = BlasterCharacter->GetActorRotation();				// 获取当前人物角色的旋转const FRotator Delta = UKismetMathLibrary::NormalizedDeltaRotator(CharacterRotation, CharacterRotationLastFrame);	// 标准化获取人物角色当前旋转与上一帧旋转的差量const float Target = Delta.Yaw / DeltaTime;								// Delta 的 Yaw 值可能会很小,需要除以 DeltaTime(每一帧的时间)进行缩放,获取目标 Yaw 值const float Interp = FMath::FInterpTo(Leaning, Target, DeltaTime, 6.f);	// FMath::FInterpTo() 实现 Lean 到 Target 插值(Interporlation),保证平滑过渡,这里最后一个参数 6.f 是插值速度Leaning = FMath::Clamp(Interp, -90.f, 90.f);							// FMath::Clamp() 限制数值范围,如果 Interp 的值超过设定的最小值 -90.0 或最大值 90.0,就将 Lean 赋值为设定的最小值或最大值;否则直接将 Interp 赋值给 Lean。/* P50 应用瞄准偏移(Applying Aim Offset)*/AO_Yaw = BlasterCharacter->GetAO_Yaw();									// 从 BlasterCharacter 获取 AO_YawAO_Pitch = BlasterCharacter->GetAO_Pitch();								// 从 BlasterCharacter 获取 AO_Pitch								/* P50 应用瞄准偏移(Applying Aim Offset)*/
    }
    
  4. 编译后,回到虚幻引擎中 “BlasterAnimBP” 的 “AnimGraph” 蓝图面板,添加 “获取 AO Yaw”(Get AO Yaw)和 “获取 AO Yaw”(Get AO Yaw)节点,并分别与 “HipAimOffset” 节点的 “Yaw” 和 “Pitch” 引脚进行连接。
    在这里插入图片描述

  5. 编译、保存后进行测试,可以观察到人物角色在静止站立且不跳跃时,人物角色的前方朝向不跟随跟随我们的控制器,其向左、向右的瞄准动画可以正确播放。在奔跑和跳跃时,人物角色的正前方朝向跟随我们的控制器进行改变,始终为控制器的当前朝向。
    在这里插入图片描述
    在这里插入图片描述

  6. 在人物角色蓝图类 “BP_BlasterCharacter” 中将人物角色网格体旋转 90°,再次进行测试,可以观察向上、向下瞄准动画能正确播放。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  7. 回到虚幻引擎中 “BlasterAnimBP” 的 “AnimGraph” 蓝图面板,在右侧资产浏览器中将 “AimAimOffset” 移动至面板中生成蓝图节点,绘制下图所示的蓝图。
    在这里插入图片描述

  8. 编译、保存后进行测试,可以观察到人物角色在静止站立且不跳跃进行瞄准时,人物角色的前方朝向不跟随跟随我们的控制器,其向左、向右、向上、向下的瞄准动画可以正确播放。
    在这里插入图片描述
    在这里插入图片描述

  9. 当我们控制服务器上的人物角色进行向下瞄准时,会在客户端上发现服务器的人物角色会向上瞄准,即出现服务器和客户端动画不同步的问题,我们将在下节课中探讨并解决这个问题
    在这里插入图片描述


50.3 Summary

本节课我们成功实现了瞄准偏移(Aim Offset)系统在角色动画中的应用。首先,我们在动画蓝图中创建了缓存姿势节点,将装备武器的状态机输出保存为 “Equipped” 缓存姿势,以便在多个位置复用。接着,使用 “每个骨骼的分层混合”(Layered blend per bone)节点,基于 “spine_01” 骨骼将上半身瞄准动画与下半身移动动画进行混合,确保瞄准动作只影响上半身。
在 C++ 代码中,我们在 “BlasterCharacter” 类中添加了 “AO_Yaw” 和 “AO_Pitch” 变量来控制瞄准偏移的角度,完成了 “AimOffset()” 函数的定义以实现当人物角色静止站立时禁用控制器偏航旋转,基于起始旋转计算偏航差量;当奔跑或跳跃时,启用控制器偏航旋转,重置偏航值为 0。同时,我们还通过定义内联函数 “GetAO_Yaw()” 和 “GetAO_Pitch()” 向动画实例 “BlasterAnimInstance” 传递 “AO_Yaw” 和 “AO_Pitch” 的值。
在动画蓝图中,我们将获取到的 “AO_Yaw” 和 “AO_Pitch” 值分别连接到 “HipAimOffset” 或 “AimAimOffset” 的对应输入引脚,驱动人物角色能够在静止时身体保持面向原始方向,仅武器随鼠标移动瞄准,并正确播放各个方向(左、右、上、下)的瞄准动画。
当我们控制服务器上的人物角色进行向下瞄准时,会在客户端上发现服务器的人物角色会向上瞄准,即出现服务器和客户端动画不同步的问题,这将在后续课程中解决。
在这里插入图片描述



文章转载自:

http://WvMsAx1b.kyjyt.cn
http://f83JXiNX.kyjyt.cn
http://PzRVdi6T.kyjyt.cn
http://AQpftGcr.kyjyt.cn
http://emkT7UN6.kyjyt.cn
http://fgdg3m7m.kyjyt.cn
http://KBrj7BA6.kyjyt.cn
http://glXGOPM1.kyjyt.cn
http://KbkIG3te.kyjyt.cn
http://1EWNlmq4.kyjyt.cn
http://eAKmRYVn.kyjyt.cn
http://YAqt74ph.kyjyt.cn
http://YhvUc6bd.kyjyt.cn
http://U3lQvZ2j.kyjyt.cn
http://phSVKLjb.kyjyt.cn
http://na5FI6R9.kyjyt.cn
http://ENCerqsI.kyjyt.cn
http://FH8oKj9t.kyjyt.cn
http://zUIW4CC6.kyjyt.cn
http://Lmu71tef.kyjyt.cn
http://x7tFgncD.kyjyt.cn
http://ssxk8Z2G.kyjyt.cn
http://zJJR9eci.kyjyt.cn
http://zrqR5WmY.kyjyt.cn
http://F6ip1MhO.kyjyt.cn
http://76vG6pEd.kyjyt.cn
http://bat0iAUm.kyjyt.cn
http://Ug5iGIeZ.kyjyt.cn
http://MJ0GTbK6.kyjyt.cn
http://3JBsneLS.kyjyt.cn
http://www.dtcms.com/a/375164.html

相关文章:

  • 微信小程序罗盘功能开发指南
  • 【C++进阶】---- map和set的使用
  • [差分数组]2327. 知道秘密的人数
  • 微调Qwen2.5模型的完整指南
  • SpringBoot之日志处理(logback和AOP记录操作日志)
  • DTO简单讲解
  • ERP和MES、WMS、CRM,到底怎么配合?
  • 【实战中提升自己】总部项目实施全方面总结
  • MS9127S USB投屏控制芯片(LVDS输出)
  • rook-ceph的osd块存储权重数据迁移脚本
  • 少儿编程C++快速教程之——3. 数组和矩阵操作
  • 【Kubernetes知识点】CRD客户资源定义及Gateway
  • 移动开发 - Uni-app跨端跨平台开发 - 学习/实践
  • 物联网时序数据管理的利器:为何IoTDB备受青睐?
  • 意识智能体:大模型的下一个进化方向?
  • 探索大模型的前沿:从GPT到LLAMA,看看AI如何改变世界
  • 西门子 S7-200 SMART 核心指令(布尔指令、传送指令、数学运算指令和比较指令)详解
  • llama-stack实战:Python构建Llama应用的可组合开发框架(8k星)
  • App Store 软件上架全流程详解,iOS 应用发布步骤、uni-app 打包上传与审核要点完整指南
  • Linux系统命令行git的使用
  • Ubuntu服务器安装fish-speech本地语音部署教程
  • Tomcat的基础使用
  • 无人机电压模块技术剖析
  • 无人机图传通信模组——16公里抗干扰稳定传输的高性能解决方案
  • 高效 P2P 文件传输工具:FileSync 利用 WebRTC 技术实现极速安全传输
  • UOS20系统安装与 SSH/XRDP 远程访问功能配置指南
  • Spark学习(python版本)
  • 【XR行业应用】XR+工业,从远程协助到数字孪生,如何颠覆制造业?
  • 将Varjo XR技术融入飞行模拟器,有效降低成本提升训练效果
  • 深入了解linux系统—— POSIX信号量