UE5学习笔记22-武器瞄准和武器自动开火

0、一些疑问的记录

1.UUserWidget类和AHUD类的区别。两者都是关于界面显示的类。

实践:

想让界面和用户有交互使用UUserWidget,如果不要交互只是显示使用AHUD类,例如使用UUserWidget类制作开始界面,游戏开始,游戏设置,自作人员等,使用AHUD类显示准心,角色血量,武器图标等。

理论:

  • 绘制方式

    • AHUD:通过Canvas直接在屏幕上绘制2D元素(代码驱动)。
    • UUserWidget:通过UMG系统创建基于Widget的UI,支持拖拽式设计、复杂的布局和交互。
  • 适用场景

    • AHUD:适合需要低级别控制的简单HUD,如准星、生命值、计时器等。
    • UUserWidget:适合复杂的、交互式的UI,如菜单、装备栏、对话框、设置界面等。
  • 复杂度

    • AHUD:简单的绘制系统,但对复杂UI需求不太友好。
    • UUserWidget:高度可定制和交互式,适合更复杂和丰富的用户体验。

2.UTexture和UTexture2D和UTexture3D的区别和何时使用

实践:

  1. UTexture2D 使用场景

    • 材质贴图:如常规的模型纹理(Albedo、Normal、Metallic等)。
    • UI图像:如按钮、背景、图标等二维界面元素。
    • 常规图像数据:如2D背景图片、纹理集等。
  2. UTexture3D 使用场景

    • 体积效果:如烟雾、云朵、火焰等,需要体积数据的效果。
    • 体积存储:如3D噪声、光照体积等。
    • 特殊材质:一些高级渲染技术和效果(如3D映射、Voxel-based渲染)。
  3. UTexture

    • 通用纹理基类:通常不会直接使用,而是作为基类为具体的纹理类型提供基础功能。

理论:

UTexture是纹理的基类不能直接使用

UTexture2D表示一个二维纹理(图像)用于给模型、材质和界面元素等应用图像数据,支持多级纹理细化(Mipmaps)、压缩格式(如DXT、BC5等),可用于渲染优化

UTexture3D表示三维纹理(体积纹理),用于存储和处理三维的纹理数据,每个数据点(Texel)不仅有二维的xy,还包括z方向的深度值

3.FTransform:

包含了物体的位置旋转和缩放

cpp 复制代码
FTransform MyTransform;
// 设置位置 MyTransform.SetTranslation(FVector(100.0f, 200.0f, 300.0f));
// 设置旋转 MyTransform.SetRotation(FQuat(FRotator(45.0f, 0.0f, 0.0f)));
// 设置缩放 MyTransform.SetScale3D(FVector(2.0f, 2.0f, 2.0f));
// 获取位置 FVector Position = MyTransform.GetTranslation();
// 获取旋转 FRotator Rotation = MyTransform.GetRotation().Rotator();
// 获取缩放 FVector Scale = MyTransform.GetScale3D();

4. 具有调试作用的函数

4.1DrawDebugLine画一条线

cpp 复制代码
void UKismetSystemLibrary::DrawDebugLine(
    const UObject* WorldContextObject,    //当前世界
    FVector LineStart,                    //其实位置
    FVector LineEnd,                      //终点位置
    FLinearColor LineColor = FLinearColor(1.f, 0.f, 0.f, 1.f), //颜色
    float Duration = 0.f,                 //持续时间
    float Thickness = 0.f                 //粗细
);

5.FindLookAtRotation函数返回的旋转默认返回X轴方向的旋转

一、完成了哪些

完成武器自动开火,武器瞄准是在界面会有准心显示,跳跃射击等动作时准心改变大小,给角色添加被击中时的动画,将角色网格体胶囊被击改成角

色模型被击,

二、准心

1.需要通过获得当前角色控制器(PlayerController)通过类APlayController::GetHUD()函数获得当前客户端的界面,函数返回一个类AHUD类的指针,定义一个类继承自AHUD类将返回的指针转到子类类型,通过DrwaHUD()函数和DrawTexture()函数绘制准心

角色控制器可以通过角色类指针调用Controller获得,获得的是一个AController类,定义类继承APlayerController,类APlayerController继承自AController。类APlayController和AHUD是UE5中已经存在的类。

2.创建新的C++类分别继承自APlayController和AHUD。将玩家控制器类命名为BlasterPlayerController界面类命名为ABasterHUD(并定义对应蓝图类)

3.类AABasterHUD代码

中间有个点

头文件

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "ABasterHUD.generated.h"

//如上图一共五个资产文件 所以结构体中有五个UTexture2D指针

USTRUCT(BlueprintType)
struct FHUDPackage
{
	GENERATED_BODY()
public:
	class UTexture2D* CrosshairsCenter;
	UTexture2D* CrosshairsLeft;
	UTexture2D* CrosshairsRight;
	UTexture2D* CrosshairsTop;
	UTexture2D* CrosshairsBottom;
	float CrosshairSpreed;//当人物奔跑跳跃时,准心大小改变
	FLinearColor CrosshairsColor;
};

/**
 * 
 */
UCLASS()
class BLASTER_API AABasterHUD : public AHUD
{
	GENERATED_BODY()
	
public:
	/* 该函数会在每一帧被调用 */
	virtual void DrawHUD() override;

private:
	FHUDPackage HUDPackage;

	//绘画准心
	void DrawCrosshair(UTexture2D* Texture , FVector2D ViewPointCenter , FVector2D Spreed , FLinearColor CrosshairsColor);

	UPROPERTY(EditAnywhere)
	float CrosshairSpreadMax = 16.f; //准心扩展或缩小的速度
public:
	FORCEINLINE void SetHUDPackage(const FHUDPackage& Package) { HUDPackage = Package; }
};

源文件

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "ABasterHUD.h"

void AABasterHUD::DrawHUD()
{
	Super::DrawHUD();

	FVector2D ViewportSize;
	if (GEngine)
	{
		/* 获得视口大小 */
		GEngine->GameViewport->GetViewportSize(ViewportSize);
		const FVector2D ViewportCenter(ViewportSize.X / 2.0, ViewportSize.Y / 2.0);

		float SpreadScaled =  CrosshairSpreadMax* HUDPackage.CrosshairSpreed;

		if (HUDPackage.CrosshairsCenter)
		{
			FVector2D Spreed(0.f, 0.f);
			DrawCrosshair(HUDPackage.CrosshairsCenter, ViewportCenter, Spreed, HUDPackage.CrosshairsColor);
		}
		if (HUDPackage.CrosshairsLeft)
		{
			FVector2D Spreed(-SpreadScaled, 0.f);
			DrawCrosshair(HUDPackage.CrosshairsLeft, ViewportCenter , Spreed, HUDPackage.CrosshairsColor);
		}
		if (HUDPackage.CrosshairsRight)
		{
			FVector2D Spreed(SpreadScaled, 0.f);
			DrawCrosshair(HUDPackage.CrosshairsRight, ViewportCenter , Spreed, HUDPackage.CrosshairsColor);
		}
		if (HUDPackage.CrosshairsTop)
		{
			FVector2D Spreed(0.f , -SpreadScaled);
			DrawCrosshair(HUDPackage.CrosshairsTop, ViewportCenter , Spreed, HUDPackage.CrosshairsColor);
		}
		if (HUDPackage.CrosshairsBottom)
		{
			FVector2D Spreed(0.f, SpreadScaled);
			DrawCrosshair(HUDPackage.CrosshairsBottom, ViewportCenter , Spreed, HUDPackage.CrosshairsColor);
		}
	}
}

void AABasterHUD::DrawCrosshair(UTexture2D* Texture, FVector2D ViewPointCenter , FVector2D Spreed , FLinearColor CrosshairsColor)
{
	const float TextureWidth = Texture->GetSizeX();
	const float TextureHigh = Texture->GetSizeY();
	const FVector2D TexturePoint(
		ViewPointCenter.X - (TextureWidth / 2.0) + Spreed.X,
		ViewPointCenter.Y - (TextureHigh / 2.0) + Spreed.Y
	);
	/**
	void DrawTexture(
    UTexture* Texture,              // 1. 纹理对象
    float ScreenX,                  // 2. 屏幕上绘制位置的 X 坐标
    float ScreenY,                  // 3. 屏幕上绘制位置的 Y 坐标
    float ScreenW,                  // 4. 纹理的宽度(缩放后的)
    float ScreenH,                  // 5. 纹理的高度(缩放后的)
    float CoordinateX = 0.f,        // 6. 纹理UV坐标的X起始点
    float CoordinateY = 0.f,        // 7. 纹理UV坐标的Y起始点
    float CoordinateW = 1.f,        // 8. 纹理UV的宽度
    float CoordinateH = 1.f,        // 9. 纹理UV的高度
    FLinearColor TintColor = FLinearColor::White,  // 10. 颜色渲染/染色 (可以用来控制透明度)
    EBlendMode BlendMode = SE_BLEND_Translucent,   // 11. 混合模式 (用于控制如何混合纹理与背景)
    float Scale = 1.f,              // 12. 缩放比例
    bool bScalePosition = false,    // 13. 是否同时缩放位置
    float Rotation = 0.f,           // 14. 纹理旋转角度
    FVector2D RotPivot = FVector2D(0.5f, 0.5f)     // 15. 旋转中心点 (以纹理为中心默认旋转)
	);
	*/
	DrawTexture(
		Texture,
		TexturePoint.X,
		TexturePoint.Y,
		TextureWidth,
		TextureHigh,
		0.f,
		0.f,
		1.f,
		1.f,
		CrosshairsColor
	);
}

4.不同的武器可能会有不同的准心,所以在武器类中定义(Weapon.h)

cpp 复制代码
	UPROPERTY(EditAnywhere, Category = Crosshairs)
	class UTexture2D* CrosshairsCenter; //静态纹理类

	UPROPERTY(EditAnywhere, Category = Crosshairs)
	class UTexture2D* CrosshairsLeft; //静态纹理类

	UPROPERTY(EditAnywhere, Category = Crosshairs)
	class UTexture2D* CrosshairsRight; //静态纹理类

	UPROPERTY(EditAnywhere, Category = Crosshairs)
	class UTexture2D* CrosshairsTop; //静态纹理类

	UPROPERTY(EditAnywhere, Category = Crosshairs)
	class UTexture2D* CrosshairsBottom; //静态纹理类

5.编译后,打开对应的武器的蓝图,设置如下

6.纹理资产设置,想让纹理显示在界面上,所以是一个2D类型的纹理需要确保纹理是2D类型

7.在游戏模式蓝图中设置对应属性(BP_xxx都是自己定义的蓝图类)

  1. 设置准心纹理

头文件

在战斗组件类中定义(准心会放大缩小,所以需要使用插值,函数会有时间参数(DeltaTime))

cpp 复制代码
/* 准心 */
void SetHUDCrosshairs(float DeltaTime); //每帧调用
/* 准心 */

/**
* HUD and Crosshairs
*/

float CrosshairVelocityFactor; //行走速度
float CrosshairInAirFactor;    //是否在空中,跳跃
float CrosshairAimFactor;      //瞄准
float CrosshairShootingFactor; //蹲下
FHUDPackage HUDPackage;

class ABlasterPlayerController* Controller; //可以通过这个类去获得HUD
class AABasterHUD* HUD;

源文件

cpp 复制代码
void UCombatComponent::SetHUDCrosshairs(float DeltaTime)
{
	if (Character == nullptr || Character->Controller == nullptr) return;

	Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;

	if (Controller)
	{
		HUD = HUD == nullptr ? Cast<AABasterHUD>(Controller->GetHUD()) : HUD;
		if (HUD)
		{

			if (EquippedWeapon)
			{
				HUDPackage.CrosshairsCenter = EquippedWeapon->CrosshairsCenter;
				HUDPackage.CrosshairsLeft = EquippedWeapon->CrosshairsLeft;
				HUDPackage.CrosshairsRight = EquippedWeapon->CrosshairsRight;
				HUDPackage.CrosshairsBottom = EquippedWeapon->CrosshairsBottom;
				HUDPackage.CrosshairsTop = EquippedWeapon->CrosshairsTop;

			}
			else
			{
				HUDPackage.CrosshairsCenter = nullptr;
				HUDPackage.CrosshairsLeft = nullptr;
				HUDPackage.CrosshairsRight = nullptr;
				HUDPackage.CrosshairsBottom = nullptr;
				HUDPackage.CrosshairsTop = nullptr;

			}
			// 计算准心扩展/缩小

			// [0 , 600] -> [0 , 1]
			FVector2D WalkSpeedRange(0.f, Character->GetCharacterMovement()->MaxWalkSpeed);
			FVector2D VeclocityMultiplierRange(0.f, 1.f);
			FVector Velocity = Character->GetVelocity();
			Velocity.Z = 0.f;
			/*
			float GetMappedRangeValueClamped(
				const TInterval<float>&InputRange,  // 输入范围
				const TInterval<float>&OutputRange, // 输出范围
				float Value                          // 输入的数值
			);*/
			/* 函数返回的值是Velocity.Size()在映射中的值 */
			CrosshairVelocityFactor = FMath::GetMappedRangeValueClamped(WalkSpeedRange, VeclocityMultiplierRange, Velocity.Size());

			if (Character->GetCharacterMovement()->IsFalling())
			{
				CrosshairInAirFactor = FMath::FInterpTo(CrosshairInAirFactor, 2.25f, DeltaTime, 2.25f);
			}
			else
			{
				CrosshairInAirFactor = FMath::FInterpTo(CrosshairInAirFactor, 0.f, DeltaTime, 30.f);
			}
			if (bAiming)
			{
				CrosshairAimFactor = FMath::FInterpTo(CrosshairAimFactor, 0.58f, DeltaTime, 30.f);
			}
			else
			{
				CrosshairAimFactor = FMath::FInterpTo(CrosshairAimFactor, 0.f, DeltaTime, 30.f);
			}

			CrosshairShootingFactor = FMath::FInterpTo(CrosshairShootingFactor, 0.f, DeltaTime, 40.f);

			HUDPackage.CrosshairSpreed =
				0.5f +
				CrosshairVelocityFactor +
				CrosshairInAirFactor -
				CrosshairAimFactor +
				CrosshairShootingFactor;

			HUD->SetHUDPackage(HUDPackage);
		}
	}
}

void UCombatComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	SetHUDCrosshairs(DeltaTime);
}

9.将武器发射子弹的方向和瞄准方向保证统一,并且只想在本地客户端上让右手骨骼和目标在同一个方向。

9.1在角色动画类中定义右手的旋转,让右手旋转到瞄准的目标的方向,代码如下

头文件

cpp 复制代码
UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))/** 蓝图只读 类型是Character 允许私有访问 */
FRotator RightHandRotation;

UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))/** 蓝图只读 类型是Character 允许私有访问 */
bool bLocallyControlled;

源文件 , 注释是测试代码,第一个if判断当前是否有武器,武器指针存在,武器网格体存在,角色网格体存在,第二个if判断当前角色是否是本地控制(是否是当前客户端),不想在代理商显示

cpp 复制代码
void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
	Super::NativeUpdateAnimation(DeltaTime);

	if (bWeaponEquipped && EquippedWeapon && EquippedWeapon->GetWeaponMesh() && BlasterCharacter->GetMesh())
	{

		if (BlasterCharacter->IsLocallyControlled())
		{
			bLocallyControlled = true;
			FTransform RightHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("Hand_R"), ERelativeTransformSpace::RTS_World);
			//让当前对象从当前位置朝向目标位置所需的旋转
            //FindLookAtRotation函数返回的旋转默认返回X轴方向的旋转
			FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(RightHandTransform.GetLocation(), RightHandTransform.GetLocation() + (RightHandTransform.GetLocation() - BlasterCharacter->GetHitTarget()));
			RightHandRotation = FMath::RInterpTo(RightHandRotation, LookAtRotation, DeltaTime, 30.f);
			//RightHandRotation = UKismetMathLibrary::FindLookAtRotation(RightHandTransform.GetLocation(), RightHandTransform.GetLocation() + (RightHandTransform.GetLocation() - BlasterCharacter->GetHitTarget()));
		}
		//FTransform MuzzleTipTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("MuzzleFlash"), ERelativeTransformSpace::RTS_World);
		//FVector MuzzleX(FRotationMatrix(MuzzleTipTransform.GetRotation().Rotator()).GetUnitAxis(EAxis::X));
		//DrawDebugLine(GetWorld(), MuzzleTipTransform.GetLocation(), MuzzleTipTransform.GetLocation() + MuzzleX * 1000.f, FColor::Red);
		//
		//DrawDebugLine(GetWorld(), MuzzleTipTransform.GetLocation(), BlasterCharacter->GetHitTarget(), FColor::Black);
	}
}

动画蓝图

将平移(Transform),缩放(Scale) ,透明度(Alpha)去掉不显示(可以根据自己需要)

将动画/UseCachePoses节点直接连接到该节点上会有如图的效果,中间会自动生成一个节点,另一边的连线连接到正确的位置同样也有一个节点

最后整体流程

  1. 装备武器时的视场角的缩放

角色类偷头文件定义 (获得相机组件类指针的函数)

cpp 复制代码
FORCEINLINE UCameraComponent* GetFollowCamera() const { return FollowCamera; };

武器类头文件定义

cpp 复制代码
	/**
	*	瞄准时武器需要放大多少倍
	*/
	UPROPERTY(EditAnywhere)
	float ZoomedFOV = 30.f; //视场角的缩放

	UPROPERTY(EditAnywhere)
	float ZoomInterpSpeed = 20.f; // 缩放的速度

战斗组件类中头文件

cpp 复制代码
	/**
	*	瞄准的视场角
	*/
	
	/* 默认的视场角 没有瞄准 */
	float DefaultFOV;

	UPROPERTY(EditAnywhere, Category = Combat)
 	float ZommedFOV = 30.f;

	float CurrentFOV;

	UPROPERTY(EditAnywhere, Category = Combat)
	float ZoomInterpSpeed = 20.f; // 缩放的速度

	void InterpFOV(float DeltaTime);

FORCEINLINE float GetZoomedFOV() const { return ZoomedFOV; };
FORCEINLINE float GetZoomInterpSpeed() const { return ZoomInterpSpeed; };

战斗组件类中源文件

cpp 复制代码
void UCombatComponent::InterpFOV(float DeltaTime)
{
	if (EquippedWeapon == nullptr) return;

	if (bAiming)
	{
		CurrentFOV = FMath::FInterpTo(CurrentFOV, EquippedWeapon->GetZoomedFOV(), DeltaTime, EquippedWeapon->GetZoomInterpSpeed());
	}
	else
	{
		CurrentFOV = FMath::FInterpTo(CurrentFOV, DefaultFOV, DeltaTime, ZoomInterpSpeed);
	}
	if (Character && Character->GetFollowCamera())
	{
		Character->GetFollowCamera()->SetFieldOfView(CurrentFOV);
	}
}

void UCombatComponent::BeginPlay()
{
	Super::BeginPlay();

	if (Character)
	{
		Character->GetCharacterMovement()->MaxWalkSpeed = BaseWalkSpeed;

		if (Character->GetFollowCamera())
		{
			DefaultFOV = Character->GetFollowCamera()->FieldOfView; //获得最开始的视场角
			CurrentFOV = DefaultFOV;//当前视角初始化
		}
	}
}

void UCombatComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);


	if (Character && Character->IsLocallyControlled())
	{
		InterpFOV(DeltaTime);
	}
}

三、更改准心颜色,在瞄准人物时将准星变成红色

0.没有更改准心颜色的功能将上面的代码删掉Color相关即可

1.创建UE接口类,角色类继承该接口类

  1. 接口类使用

2.1具体使用查看UE5官方文档,英文版使用浏览器的自动翻译Unreal Engine 中的接口 |Unreal Engine 5.4 文档 |Epic 开发者社区 (epicgames.com)https://dev.epicgames.com/documentation/en-us/unreal-engine/interfaces-in-unreal-engine 2.2当前使用说明

角色类继承了public IInteractWithCrosshairsInterface,I开头的接口类

2.3战斗成分类中在Tick函数中 TraceHitResult时检测到的目标的结果类型时FHitResult

cpp 复制代码
if (TraceHitResult.GetActor() && TraceHitResult.GetActor()->Implements<UInteractWithCrosshairsInterface>())
{
	HUDPackage.CrosshairsColor = FColor::Red;
}
else
{
	HUDPackage.CrosshairsColor = FColor::White;
}

2.4若在测试时碰到角色在移动时摄像机贴在了当前角色身上时,可能是因为摄像机碰到了其他角色,在角色C++类的构造中添加以下代码,取消角色和摄像机的碰撞,若还会碰撞,请查看角色类蓝图中摄像机的碰撞在哪里打了√

cpp 复制代码
GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);

四、当人物靠在墙边时,摄像头会紧贴在角色上,看不见前方的东西

1.判断角色和摄像机的距离是否小于特定的值,如果小于将角色网格体隐藏,不小于显示

角色类头文件

cpp 复制代码
	void HideCameraIfCharactorClose();

	UPROPERTY(EditAnywhere)
	float CameraThreadhold = 200.f;

角色类源文件

cpp 复制代码
void ABlasterCharacter::HideCameraIfCharactorClose()
{
	if (!IsLocallyControlled()) return;

	if ((FollowCamera->GetComponentLocation() - GetActorLocation()).Size() < CameraThreadhold)
	{
		GetMesh()->SetVisibility(false);
		if (Combat && Combat->EquippedWeapon && Combat->EquippedWeapon->GetWeaponMesh())
		{
			Combat->EquippedWeapon->GetWeaponMesh()->bOwnerNoSee = true;
		}
	}
	else
	{
		GetMesh()->SetVisibility(true);
		if (Combat && Combat->EquippedWeapon && Combat->EquippedWeapon->GetWeaponMesh())
		{
			Combat->EquippedWeapon->GetWeaponMesh()->bOwnerNoSee = false;
		}
	}
}

五、自定义碰撞频道

0.我将角色骨骼设置成新的碰撞,在子弹类中定义了该类型的碰撞,这样在子弹命中人物时会判定在骨骼体上而不是网格体。之后会判断命中的身体部位是哪

1.项目设置中设置如下没在引擎-碰撞中新建Object通道如下图

2.在项目名.h的文件中定义,例如我的项目是Blaster

Blaster.h

cpp 复制代码
#define ECC_SkeletalMesh ECollisionChannel::ECC_GameTraceChannel1

3.在会发生碰撞的物体中将设置碰撞的代码如下

cpp 复制代码
CollisionBox->SetCollisionResponseToChannel(ECC_SkeletalMesh, ECollisionResponse::ECR_Block);

4.设置碰撞类型,在对应蓝图中将怕碰撞预设的对象类型设置成1中的新建的对应的名字

六、 复制运动的通知函数

1.问题描述

当控制角色旋转是在其他客户端上会有轻微抖动问题

2.原因

角色蓝图中调用了RotateRootBone节点旋转骨头,在自己的客户端和服务器上,该旋转骨头会每帧调用,在其他客户端上,我们的角色在其他客户端上是代理角色,并不会每帧调用旋转骨骼的节点,所以会有抖动产生

3.解决

重写 virtual void OnRep_ReplicatedMovement() override;函数这是一个AActor类中关于运动的回调函数,在运动时会执行该函数

角色类头文件

cpp 复制代码
/* 解决当前旋转骨骼不是每帧调用时会在其他客户端存在抖动问题 */
void SimProxiesTurn();
/* 解决当前旋转骨骼不是每帧调用时会在其他客户端存在抖动问题 */

//在AActor.h中定义,当角色移动是会调用当前回调
virtual void OnRep_ReplicatedMovement() override;

蓝图中进行判断用
FORCEINLINE bool ShouldRotateRootBone() const { return bRotateRootBone; };

bool bRotateRootBone;//是否进行了骨骼旋转

float TimeSinceLastMovementReplication;//多长时间没有进行旋转

角色类源文件

cpp 复制代码
void ABlasterCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

		TimeSinceLastMovementReplication += DeltaTime;
		//当一段时间没有移动时调用回调函数
		if (TimeSinceLastMovementReplication > 0.25f)
		{
			OnRep_ReplicatedMovement();
		}
		CalculateA0_Pitch();
}

void ABlasterCharacter::OnRep_ReplicatedMovement()
{
	Super::OnRep_ReplicatedMovement();

	//if (GetLocalRole() == ENetRole::ROLE_SimulatedProxy)
	//{
	//	SimProxiesTurn();
	//}
	SimProxiesTurn();
	TimeSinceLastMovementReplication = 0.f;
}

void ABlasterCharacter::SimProxiesTurn()
{
	//处理模拟界面的转弯
	if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return;

	bRotateRootBone = false;
	float Speed = CalculateSpeed();
	if (Speed > 0.f)
	{
		TurningInPlace = ETurningInPlace::ETIP_NotTurning;
		return;
	}

	//CalculateA0_Pitch();
	ProxyRotationLastFrame = ProxyRotation;
	ProxyRotation = GetActorRotation();
	//计算两个角度的最小旋转角度变化在 [-180, 180] 范围内
	ProxyYaw = UKismetMathLibrary::NormalizedDeltaRotator(ProxyRotation, ProxyRotationLastFrame).Yaw;

	if (FMath::Abs(ProxyYaw) > TurnThreshold)
	{
		if (ProxyYaw > TurnThreshold)
		{
			TurningInPlace = ETurningInPlace::ETIP_Right;
		}
		else if (ProxyYaw < -TurnThreshold)
		{
			TurningInPlace = ETurningInPlace::ETIP_Left;
		}
		else
		{
			TurningInPlace = ETurningInPlace::ETIP_NotTurning;
		}
		return;
	}
	TurningInPlace = ETurningInPlace::ETIP_NotTurning;
}

float ABlasterCharacter::CalculateSpeed()
{
	/* 只想获得平面上xy的速度的向量,将z方向的速度置为0 */
	FVector Velocity = GetVelocity();
	Velocity.Z = 0.f;
	return Velocity.Size();
}

4.蓝图中获得是否旋转骨头的bool类型变量,在动画蓝图中判断如果旋转了骨头则直接旋转没有旋转骨头就直接角色旋转

在判断是否具有本地控制权限(FullBody就是上面的流程(使用了cache poses))

七、自动射击

1.使用了FTimerManager类让武器间隔一段时后再次发射子弹

战斗类头文件

cpp 复制代码
	/**
	*	自动开火
	*/

	FTimerHandle FireTimer; //定时器类
	bool bCanFire = true;

	void StartFireTimer();

	void FireTimerFinished();

战斗类源文件

cpp 复制代码
void UCombatComponent::StartFireTimer()
{
	if (EquippedWeapon == nullptr || Character == nullptr) return;
	Character->GetWorldTimerManager().SetTimer(
		FireTimer,
		this,
		&UCombatComponent::FireTimerFinished,
		EquippedWeapon->FireDelay
	);
}

void UCombatComponent::FireTimerFinished()
{
	if (EquippedWeapon == nullptr) return;
	bCanFire = true;
	if (bFireButtonPressed && EquippedWeapon->bAutomatic)
	{
		Fire();
	}
}

武器类头文件

cpp 复制代码
	/**
	*	自动开火
	*/

	UPROPERTY(EditAnywhere, Category = Combat)
	float FireDelay = 0.15f; //开火速率

	UPROPERTY(EditAnywhere, Category = Combat)
	bool bAutomatic = true; //武器是否自动
相关推荐
武子康7 分钟前
大数据-212 数据挖掘 机器学习理论 - 无监督学习算法 KMeans 基本原理 簇内误差平方和
大数据·人工智能·学习·算法·机器学习·数据挖掘
CXDNW15 分钟前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
使者大牙16 分钟前
【大语言模型学习笔记】第一篇:LLM大规模语言模型介绍
笔记·学习·语言模型
ssf-yasuo29 分钟前
SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记
论文阅读·笔记·prompt
As977_38 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
ajsbxi41 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
Rattenking42 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
TeYiToKu1 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws1 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
道法自然04021 小时前
Ethernet 系列(8)-- 基础学习::ARP
网络·学习·智能路由器