文章目錄
- 添加技能需要的東西
- 添加本地播放GC
- 添加沖刺tag
- 添加一個新的TA用于檢測敵方單位
- 添加沖刺GA
- 到角色中監聽加速移動速度的回調
- 創建蒙太奇
- 添加GE
- 添加到數據表中
- 添加到角色中
- 糾錯
添加技能需要的東西
添加本地播放GC
在UCAbilitySystemStatics
中添加
/*** 在本地觸發指定的游戲提示效果(如技能特效、攻擊反饋等)* * @param CueTargetActor 觸發游戲提示的目標Actor(如角色、技能釋放者)* @param HitResult 包含命中位置、法線等碰撞信息的結果對象* @param GameplayCueTag 標識游戲提示類型的GameplayTag(如"Ability.Attack.Basic")*/static void SendLocalGameplayCue(AActor* CueTargetActor, const FHitResult& HitResult, const FGameplayTag& GameplayCueTag);
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
// 沖刺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)
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
#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);
};
#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); // 隊伍IDDOREPLIFETIME(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)
{// 忽略無效Actorif (!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
#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);
};
#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);}
}
到角色中監聽加速移動速度的回調
// 加速移動速度改變回調void MoveSpeedAccelerationUpdated(const FOnAttributeChangeData& Data);
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吧
隨后放進英雄資產里等他自己初始化吧
然后就可以開沖了