UE5多人MOBA+GAS 45、制作冲刺技能

文章目录


添加技能需要的东西

添加本地播放GC

UCAbilitySystemStatics中添加

cpp 复制代码
	/**
	 * 在本地触发指定的游戏提示效果(如技能特效、攻击反馈等)
	 * 
	 * @param CueTargetActor 触发游戏提示的目标Actor(如角色、技能释放者)
	 * @param HitResult 包含命中位置、法线等碰撞信息的结果对象
	 * @param GameplayCueTag 标识游戏提示类型的GameplayTag(如"Ability.Attack.Basic")
	 */
	static void SendLocalGameplayCue(AActor* CueTargetActor, const FHitResult& HitResult, const FGameplayTag& GameplayCueTag);
cpp 复制代码
void UCAbilitySystemStatics::SendLocalGameplayCue(AActor* CueTargetActor, const FHitResult& HitResult,
	const FGameplayTag& GameplayCueTag)
{
	FGameplayCueParameters CueParams;
	CueParams.Location = HitResult.ImpactPoint;
	CueParams.Normal = HitResult.ImpactNormal;

	UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(CueTargetActor, GameplayCueTag, EGameplayCueEvent::Executed, CueParams);
}

添加冲刺tag

cpp 复制代码
	// 冲刺
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Dash)
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Dash_Start)
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Dash_Cooldown)
cpp 复制代码
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Dash, "Ability.Dash", "冲刺技能")
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Dash_Start, "Ability.Dash.Start", "冲刺技能开始")
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Dash_Cooldown, "Ability.Dash.Cooldown", "冲刺技能冷却")

添加一个新的TA用于检测敌方单位

TargetActor_Around

cpp 复制代码
#pragma once

#include "CoreMinimal.h"
#include "GenericTeamAgentInterface.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "TargetActor_Around.generated.h"

class USphereComponent;
/**
 * 圆形范围目标检测器,用于检测角色周围的敌对目标
 * 实现队伍关系接口(IGenericTeamAgentInterface)用于敌我识别
 */
UCLASS()
class ATargetActor_Around : public AGameplayAbilityTargetActor, public IGenericTeamAgentInterface
{
	GENERATED_BODY()
public:
	ATargetActor_Around();
	
	// 配置检测参数:检测半径、队伍ID和本地视觉提示标签
	void ConfigureDetection(float DetectionRadius, const FGenericTeamId& InTeamId, const FGameplayTag& InLocalGameplayCueTag);

	/** 实现IGenericTeamAgentInterface接口 - 设置队伍ID */
	virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
	
	/** 实现IGenericTeamAgentInterface接口 - 获取队伍ID */
	FORCEINLINE virtual FGenericTeamId GetGenericTeamId() const override { return TeamId; }
	
	/** 网络属性复制 */
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
	
private:
	UPROPERTY(Replicated)
	FGenericTeamId TeamId; // 当前检测器的队伍ID(用于敌我识别)

	// 根组件(用于位置定位)
	UPROPERTY(VisibleDefaultsOnly, Category = "Comp")
	TObjectPtr<USceneComponent> RootComp;

	// 球形碰撞体(用于范围检测)
	UPROPERTY(VisibleDefaultsOnly, Category = "Targeting")
	TObjectPtr<USphereComponent> DetectionSphere;

	// 检测半径(网络同步)
	UPROPERTY(ReplicatedUsing = OnRep_TargetDetectionRadiusReplicated)
	float TargetDetectionRadius;

	// 检测半径复制回调(客户端同步时更新碰撞体大小)
	UFUNCTION()
	void OnRep_TargetDetectionRadiusReplicated();

	// 本地视觉提示标签(命中目标时播放的视觉效果)
	UPROPERTY(Replicated)
	FGameplayTag LocalGameplayCueTag;

	// 碰撞体进入检测范围时的回调
	UFUNCTION()
	void ActorInDetectionRange(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
};
cpp 复制代码
#include "TargetActor_Around.h"

#include "Abilities/GameplayAbility.h"
#include "Components/SphereComponent.h"
#include "GAS/Core/CAbilitySystemStatics.h"
#include "Net/UnrealNetwork.h"


// Sets default values
ATargetActor_Around::ATargetActor_Around()
{
	PrimaryActorTick.bCanEverTick = true;
	// 网络设置:在服务器和客户端同步
	bReplicates = true;
	// 重要:确保服务器能产生目标数据
	ShouldProduceTargetDataOnServer = true;
	// 创建根组件
	RootComp = CreateDefaultSubobject<USceneComponent>("Root Comp");
	SetRootComponent(RootComp);

	// 创建球形碰撞体用于范围检测
	DetectionSphere = CreateDefaultSubobject<USphereComponent>("Detection Sphere");
	DetectionSphere->SetupAttachment(GetRootComponent());

	// 配置碰撞设置:只检测Pawn类型的重叠
	DetectionSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
	DetectionSphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
	// 绑定重叠开始事件回调
	DetectionSphere->OnComponentBeginOverlap.AddDynamic(this, &ATargetActor_Around::ActorInDetectionRange);

}

void ATargetActor_Around::ConfigureDetection(float DetectionRadius, const FGenericTeamId& InTeamId,
	const FGameplayTag& InLocalGameplayCueTag)
{
	// 设置队伍关系
	SetGenericTeamId(InTeamId);
	// 更新碰撞体大小
	DetectionSphere->SetSphereRadius(DetectionRadius);
	// 同步到网络变量
	TargetDetectionRadius = DetectionRadius;
	// 设置视觉提示标签
	LocalGameplayCueTag = InLocalGameplayCueTag;
}

void ATargetActor_Around::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
	TeamId = NewTeamID;
}

void ATargetActor_Around::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	// 注册需要网络同步的属性
	DOREPLIFETIME(ATargetActor_Around, TeamId);					// 队伍ID
	DOREPLIFETIME(ATargetActor_Around, LocalGameplayCueTag);	// 视觉标签
	DOREPLIFETIME(ATargetActor_Around, TargetDetectionRadius);	// 检测半径
}

void ATargetActor_Around::OnRep_TargetDetectionRadiusReplicated()
{
	// 客户端收到半径更新时,同步更新碰撞体大小
	DetectionSphere->SetSphereRadius(TargetDetectionRadius);
}

void ATargetActor_Around::ActorInDetectionRange(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	// 忽略无效Actor
	if (!OtherActor) return;

	// 获取能力拥有者(避免检测到自己)
	AActor* AvatarActor = nullptr;
	if (OwningAbility)
	{
		AvatarActor = OwningAbility->GetAvatarActorFromActorInfo();
	}

	// 忽略自身和拥有者
	if (OtherActor == AvatarActor) return;
	if (OtherActor == this) return;

	// 队伍关系检查:只处理敌对目标
	if (GetTeamAttitudeTowards(*OtherActor) != ETeamAttitude::Hostile) return;

	// 服务器处理目标数据
	if (HasAuthority())
	{
		// 构建目标数据
		FGameplayAbilityTargetDataHandle TargetDataHandle;
		FGameplayAbilityTargetData_ActorArray* ActorArray = new FGameplayAbilityTargetData_ActorArray;
		ActorArray->SetActors(TArray<TWeakObjectPtr<AActor>>{OtherActor});
		TargetDataHandle.Add(ActorArray);
		
		// 通知能力系统目标已就绪
		TargetDataReadyDelegate.Broadcast(TargetDataHandle);
	}
	
	// 播放GC特效
	FHitResult HitResult;
	HitResult.ImpactPoint = OtherActor->GetActorLocation();  // 命中点为目标位置
	HitResult.ImpactNormal = (OtherActor->GetActorLocation() - GetActorLocation()).GetSafeNormal(); // 命中方向
	UCAbilitySystemStatics::SendLocalGameplayCue(OtherActor, HitResult, LocalGameplayCueTag);
}

添加冲刺GA

GA_Dash

cpp 复制代码
#pragma once

#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GA_Dash.generated.h"

class UCharacterMovementComponent;
/**
 * 冲刺能力类,使角色能够向目标方向冲刺并对路径上的敌人造成伤害
 */
UCLASS()
class CRUNCH_API UGA_Dash : public UCGameplayAbility
{
	GENERATED_BODY()
public:
	// 激活能力时调用
	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
	
	// 结束能力时调用
	virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;

private:
	// 冲刺动作的动画蒙太奇
	UPROPERTY(EditDefaultsOnly, Category = "Anim")
	TObjectPtr<UAnimMontage> DashMontage;

	// 目标检测半径(单位:厘米)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	float TargetDetectionRadius = 300.f;

	// 本地游戏提示标签(用于视觉效果)
	UPROPERTY(EditDefaultsOnly, Category = "GameplayCue")
	FGameplayTag LocalGameplayCueTag;

	// 目标检测器附加的骨骼名称
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	FName TargetActorAttachSocketName = "TargetDashCenter";

	// 目标检测器类(圆形范围检测)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	TSubclassOf<class ATargetActor_Around> TargetActorClass;

	// 命中目标后的击退速度
	UPROPERTY(EditDefaultsOnly, Category = "Effects")
	float TargetHitPushSpeed = 3000.f;

	// 命中目标时应用的伤害效果
	UPROPERTY(EditDefaultsOnly, Category = "Effects")
	FGenericDamageEffectDef DamageEffect;

	// 冲刺过程中应用的持续效果
	UPROPERTY(EditDefaultsOnly, Category = "Effects")
	TSubclassOf<UGameplayEffect> DashEffect;
	
	// 当前激活的冲刺效果句柄
	FActiveGameplayEffectHandle DashEffectHandle;

	// 推动角色前进的定时器句柄
	FTimerHandle PushForwardInputTimerHandle;

	// 推动角色沿当前方向前进
	void PushForward();
	
	// 缓存角色移动组件
	UPROPERTY()
	TObjectPtr<UCharacterMovementComponent> OwnerCharacterMovementComponent;

	// 动画事件触发时开始冲刺逻辑
	UFUNCTION()
	void StartDash(FGameplayEventData Payload);

	// 目标检测完成回调
	UFUNCTION()
	void TargetReceived(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
};
cpp 复制代码
#include "GA_Dash.h"

#include "AbilitySystemComponent.h"
#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"
#include "Abilities/Tasks/AbilityTask_WaitGameplayEvent.h"
#include "Abilities/Tasks/AbilityTask_WaitTargetData.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GAS/TA/TargetActor_Around.h"

void UGA_Dash::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
                               const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	// 检查能力是否可提交(资源消耗等)和动画是否有效
	if (!K2_CommitAbility() || !DashMontage)
	{
		// 条件不满足则立即结束能力
		K2_EndAbility();
		return;
	}

	// 确保在服务端或预测有效时执行
	if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo))
	{
		// 创建并播放冲刺动画蒙太奇
		UAbilityTask_PlayMontageAndWait* PlayDashMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, DashMontage);
		// 绑定动画结束/中断事件到能力结束
		PlayDashMontage->OnBlendOut.AddDynamic(this, &UGA_Dash::K2_EndAbility);
		PlayDashMontage->OnCancelled.AddDynamic(this, &UGA_Dash::K2_EndAbility);
		PlayDashMontage->OnInterrupted.AddDynamic(this, &UGA_Dash::K2_EndAbility);
		PlayDashMontage->OnCompleted.AddDynamic(this, &UGA_Dash::K2_EndAbility);
		PlayDashMontage->ReadyForActivation();

		// 等待动画中的冲刺开始事件
		UAbilityTask_WaitGameplayEvent* WaitDashStartEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Dash_Start);
		WaitDashStartEvent->EventReceived.AddDynamic(this, &UGA_Dash::StartDash);
		WaitDashStartEvent->ReadyForActivation();
	}
}

void UGA_Dash::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
	const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
	// 获取能力系统组件
	UAbilitySystemComponent* OwnerAbilitySystemComponent = GetAbilitySystemComponentFromActorInfo();
	// 移除冲刺效果
	if (OwnerAbilitySystemComponent && DashEffectHandle.IsValid())
	{
		OwnerAbilitySystemComponent->RemoveActiveGameplayEffect(DashEffectHandle);
	}

	// 清除推进定时器
	if (PushForwardInputTimerHandle.IsValid())
	{
		GetWorld()->GetTimerManager().ClearTimer(PushForwardInputTimerHandle);
	}
	
	Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}

void UGA_Dash::PushForward()
{
	// 如果存在移动组件,则沿角色前方持续推动
	if (OwnerCharacterMovementComponent)
	{
		// 获取角色前方向量
		FVector ForwardActor = GetAvatarActorFromActorInfo()->GetActorForwardVector();
		// 添加移动输入
		OwnerCharacterMovementComponent->AddInputVector(ForwardActor);
		// 设置下一帧继续推动(循环递归调用)
		PushForwardInputTimerHandle = GetWorld()->GetTimerManager().SetTimerForNextTick(this, &UGA_Dash::PushForward);
	}
}

void UGA_Dash::StartDash(FGameplayEventData Payload)
{
	// 在服务端应用冲刺效果
	if (K2_HasAuthority())
	{
		if (DashEffect)
		{
			DashEffectHandle = BP_ApplyGameplayEffectToOwner(DashEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));
		}
	}

	// 本地控制角色:启动连续推进
	if (IsLocallyControlled())
	{
		// 缓存移动组件
		OwnerCharacterMovementComponent = GetAvatarActorFromActorInfo()->GetComponentByClass<UCharacterMovementComponent>();
		// 启动推进循环
		PushForwardInputTimerHandle = GetWorld()->GetTimerManager().SetTimerForNextTick(this, &UGA_Dash::PushForward);
	}

	// 创建目标检测任务
	UAbilityTask_WaitTargetData* WaitTargetData = UAbilityTask_WaitTargetData::WaitTargetData(
		this, 
		NAME_None, 
		EGameplayTargetingConfirmation::CustomMulti,  // 自定义确认方式
		TargetActorClass
	);
	
	// 绑定目标检测完成回调
	WaitTargetData->ValidData.AddDynamic(this, &UGA_Dash::TargetReceived);
	WaitTargetData->ReadyForActivation();

	// 生成目标检测器
	AGameplayAbilityTargetActor* TargetActor;
	WaitTargetData->BeginSpawningActor(this, TargetActorClass, TargetActor);

	// 配置目标检测器
	ATargetActor_Around* TargetActorAround = Cast<ATargetActor_Around>(TargetActor);
	if (TargetActorAround)
	{
		// 设置检测半径、队伍过滤和视觉提示
		TargetActorAround->ConfigureDetection(TargetDetectionRadius, GetOwnerTeamId(), LocalGameplayCueTag);
	}

	// 完成生成
	WaitTargetData->FinishSpawningActor(this, TargetActor);

	// 将检测器附加到角色骨骼
	if (TargetActorAround)
	{
		TargetActorAround->AttachToComponent(
			GetOwningComponentFromActorInfo(), 
			FAttachmentTransformRules::SnapToTargetNotIncludingScale, 
			TargetActorAttachSocketName
		);
	}
}

void UGA_Dash::TargetReceived(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{
	// 服务端处理:对目标应用效果
	if (K2_HasAuthority())
	{
		// 应用伤害效果
		ApplyDamageToTargetDataHandle(TargetDataHandle, DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));
		// 击退目标
		PushTargetsFromOwnerLocation(TargetDataHandle, TargetHitPushSpeed);
	}
}

到角色中监听加速移动速度的回调

cpp 复制代码
	// 加速移动速度改变回调
	void MoveSpeedAccelerationUpdated(const FOnAttributeChangeData& Data);
cpp 复制代码
void ACCharacter::BindGASChangeDelegates()
{
	if (CAbilitySystemComponent)
	{
		CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Dead).AddUObject(this, &ACCharacter::DeathTagUpdated);
		CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Stun).AddUObject(this, &ACCharacter::StunTagUpdated);
		CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Aim).AddUObject(this, &ACCharacter::AimTagUpdated);
		CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Focus).AddUObject(this, &ACCharacter::FocusTagUpdated);
		
		CAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CAttributeSet->GetMoveSpeedAttribute()).AddUObject(this, &ACCharacter::MoveSpeedUpdated);
		CAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetMaxHealthAttribute()).AddUObject(this, &ACCharacter::MaxHealthUpdated);
		CAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetMaxManaAttribute()).AddUObject(this, &ACCharacter::MaxManaUpdated);
		CAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetMoveAccelerationAttribute()).AddUObject(this, &ACCharacter::MoveSpeedAccelerationUpdated);
	}
}

void ACCharacter::MoveSpeedAccelerationUpdated(const FOnAttributeChangeData& Data)
{
	GetCharacterMovement()->MaxAcceleration = Data.NewValue;
}

创建蒙太奇

为其添加一个插槽,用来塞一个TA进去

添加GE

冲刺的GE

冷却GE

伤害GE

成本GE

再创建一个TA塞进去

添加到数据表中

添加到角色中

纠错

这个时候就会又bro发出疑问了,为什么放了冲刺技能后我不能动了呢

因为我们一开始的时候那个加速度没有给他初始化导致的

那只能创建一个GE来应用一个初始的Value,免得这个无限效果消失的时候变成了默认的0蛋

角色中的加速度是2048,那我直接应用一个2048吧

随后放进英雄资产里等他自己初始化吧

然后就可以开冲了

相关推荐
android_xc20 分钟前
Android Studio适配butterknife遇到的坑
android·ide·android studio·butterknife
2501_9159184134 分钟前
uni-app 项目 iOS 上架效率优化 从工具选择到流程改进的实战经验
android·ios·小程序·uni-app·cocoa·iphone·webview
00后程序员张1 小时前
如何在不同 iOS 设备上测试和上架 uni-app 应用 实战全流程解析
android·ios·小程序·https·uni-app·iphone·webview
米豆同学3 小时前
SufraceFlinger图像合成原理(3)-SurfaceFlinger中Layer的创建和销毁
android
米豆同学3 小时前
SufraceFlinger图像合成原理(2)-SurfaceFlinger与应用进程间的通信
android
用户2018792831673 小时前
uses-library:系统应用报NoClassDefFoundError问题
android
叽哥3 小时前
Kotlin学习第 4 课:Kotlin 函数:从基础定义到高阶应用
android·java·kotlin
mg6683 小时前
安卓玩机工具----安卓“搞机工具箱”最新版 控制手机的玩机工具
android·智能手机
诺诺Okami3 小时前
Android Framework- Activity启动2
android
米豆同学3 小时前
SystemUI plugin 开发
android