文章目錄
- 給升龍制作可連段
- 緩存下一連段
- 用普攻鍵來觸發升龍后續的連段
- 在角色中發送按下普攻標簽事件
- 在升龍中接收按下事件,觸發連段以及傷害和力量的傳遞
- 最后在藍圖中設置一下
- 升龍技能的完整代碼
給升龍制作可連段
給升龍技能添加一些連段
緩存下一連段
緩存下一連段的名稱
// 處理連招階段切換事件UFUNCTION()void HandleComboChangeEvent(FGameplayEventData EventData);// 下一個連招階段的名稱FName NextComboName;
UUpperCut::UUpperCut()
{// 阻止帶有Ability_BasicAttack標簽的技能激活BlockAbilitiesWithTag.AddTag(TGameplayTags::Ability_BasicAttack);
}void UUpperCut::StartLaunching(FGameplayEventData EventData)
{if (K2_HasAuthority()){// 推動自己向上PushTarget(GetAvatarActorFromActorInfo(), FVector::UpVector * UpperCutLaunchSpeed);// 獲取命中目標的數量int HitResultCount = UAbilitySystemBlueprintLibrary::GetDataCountFromTargetData(EventData.TargetData);// 對所有命中的目標執行擊飛和傷害for (int i = 0; i < HitResultCount; i++){FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(EventData.TargetData, i);PushTarget(HitResult.GetActor(), FVector::UpVector * UpperCutLaunchSpeed);ApplyGameplayEffectToHitResultActor(HitResult, LaunchDamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}}// 監聽連招切換、提交、傷害等事件UAbilityTask_WaitGameplayEvent* WaitComboChangeEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Combo_Change, nullptr, false, false);WaitComboChangeEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboChangeEvent);WaitComboChangeEvent->ReadyForActivation();
}
void UUpperCut::HandleComboChangeEvent(FGameplayEventData EventData)
{// 獲取事件標簽FGameplayTag EventTag = EventData.EventTag;if (EventTag == TGameplayTags::Ability_Combo_Change_End){// 下一個連招名稱置空NextComboName = NAME_None;UE_LOG(LogTemp, Warning, TEXT("清除連招"))return;}// 獲取下一個連段的名稱TArray<FName> TagNames;UGameplayTagsManager::Get().SplitGameplayTagFName(EventTag, TagNames);// Tag最后一段的名稱比如Combo02,03,04等NextComboName = TagNames.Last();UE_LOG(LogTemp, Warning, TEXT("下一個Combo:%s"), *NextComboName.ToString())
}
用普攻鍵來觸發升龍后續的連段
主要就是升龍期間,阻止普通技能的觸發,在升龍添加阻擋標簽的目的就是這個,然后需要在角色中發送按鍵按下事件,讓升龍GA接收,然后觸發切換到下一個蒙太奇。
在角色中發送按下普攻標簽事件
添加兩個Tag,用來將普攻按下和抬起的信息發送出去
// 按下普通攻擊CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_BasicAttack_Pressed)// 抬起CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_BasicAttack_Released)
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_BasicAttack_Pressed, "Ability.BasicAttack.Pressed", "按下普通攻擊")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_BasicAttack_Released, "Ability.BasicAttack.Released", "釋放普通攻擊鍵")
因為角色的輸入只是發生在客戶端,服務器并不知道角色的輸入,因此在CCharacter
中創建服務器發送事件
#pragma region GAS組件相關
public:virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;// 在服務器端向自身發送游戲事件UFUNCTION(Server, Reliable, WithValidation)void Server_SendGameplayEventToSelf(const FGameplayTag& EventTag, const FGameplayEventData& EventData);
void ACCharacter::Server_SendGameplayEventToSelf_Implementation(const FGameplayTag& EventTag,const FGameplayEventData& EventData)
{UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(this, EventTag, EventData);
}/*** @brief 驗證在服務器端向自身發送游戲事件的操作是否有效*/
bool ACCharacter::Server_SendGameplayEventToSelf_Validate(const FGameplayTag& EventTag,const FGameplayEventData& EventData)
{// 返回 true,表示操作有效return true;
}
在玩家角色CPlayerCharacter
輸入中檢測按鍵的按下,發送游戲事件
void ACPlayerCharacter::HandleAbilityInput(const FInputActionValue& InputActionValue, ECAbilityInputID InputID)
{bool bPressed = InputActionValue.Get<bool>();// 按下if (bPressed){GetAbilitySystemComponent()->AbilityLocalInputPressed(static_cast<int32>(InputID));}else{GetAbilitySystemComponent()->AbilityLocalInputReleased(static_cast<int32>(InputID));}// 按下的是普攻鍵if (InputID == ECAbilityInputID::BasicAttack){FGameplayTag BasicAttackTag = bPressed ? TGameplayTags::Ability_BasicAttack_Pressed : TGameplayTags::Ability_BasicAttack_Released;// 1. 本地直接廣播(觸發客戶端即時反饋)// 2. 服務器RPC廣播(確保權威狀態同步)UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(this, BasicAttackTag, FGameplayEventData());Server_SendGameplayEventToSelf(BasicAttackTag, FGameplayEventData());}
}
在升龍中接收按下事件,觸發連段以及傷害和力量的傳遞
在CGameplayAbilityTypes
創建一個傷害效果的結構體,用來存儲GE以及觸發的力度大小
// 傷害效果定義
USTRUCT(BlueprintType)
struct FGenericDamageEffectDef
{GENERATED_BODY()public:FGenericDamageEffectDef();UPROPERTY(EditAnywhere)TSubclassOf<UGameplayEffect> DamageEffect;UPROPERTY(EditAnywhere)FVector PushVelocity;
};
FGenericDamageEffectDef::FGenericDamageEffectDef():DamageEffect{nullptr},PushVelocity{0.f}
{
}
回到升龍函數中補全代碼,像之前做Combo一樣,設置下一個蒙太奇片段,獲取當前片段的傷害,在應用傷害的時候,保持一下自己也在空中,對方也在空中,所有創建了上面的這個結構體。
private:// 連招階段對應的傷害效果定義表UPROPERTY(EditDefaultsOnly, Category = "Combo")TMap<FName, FGenericDamageEffectDef> ComboDamageMap;// 空中連招的力UPROPERTY(EditDefaultsOnly, Category = "Launch")float UpperComboHoldSpeed = 100.f;// 獲取當前連招階段的傷害效果定義const FGenericDamageEffectDef* GetDamageEffectDefForCurrentCombo() const;// 處理連招提交事件UFUNCTION()void HandleComboCommitEvent(FGameplayEventData EventData);// 處理連招傷害事件UFUNCTION()void HandleComboDamageEvent(FGameplayEventData EventData);
const FGenericDamageEffectDef* UUpperCut::GetDamageEffectDefForCurrentCombo() const
{UAnimInstance* OwnerAnimInstance = GetOwnerAnimInstance();if (OwnerAnimInstance){// 獲取當前片段名稱FName CurrentComboName = OwnerAnimInstance->Montage_GetCurrentSection(UpperCutMontage);// 獲取當前片段對應的傷害效果const FGenericDamageEffectDef* EffectDef = ComboDamageMap.Find(CurrentComboName);if (EffectDef){return EffectDef;}}// 沒找到返回一個空結構return nullptr;
}void UUpperCut::StartLaunching(FGameplayEventData EventData)
{if (K2_HasAuthority()){// 推動自己向上PushTarget(GetAvatarActorFromActorInfo(), FVector::UpVector * UpperCutLaunchSpeed);// 獲取命中目標的數量int HitResultCount = UAbilitySystemBlueprintLibrary::GetDataCountFromTargetData(EventData.TargetData);// 對所有命中的目標執行擊飛和傷害for (int i = 0; i < HitResultCount; i++){FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(EventData.TargetData, i);PushTarget(HitResult.GetActor(), FVector::UpVector * UpperCutLaunchSpeed);ApplyGameplayEffectToHitResultActor(HitResult, LaunchDamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}}// 監聽連招切換、提交、傷害等事件UAbilityTask_WaitGameplayEvent* WaitComboChangeEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Combo_Change, nullptr, false, false);WaitComboChangeEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboChangeEvent);WaitComboChangeEvent->ReadyForActivation();UAbilityTask_WaitGameplayEvent* WaitComboCommitEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_BasicAttack_Pressed);WaitComboCommitEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboCommitEvent);WaitComboCommitEvent->ReadyForActivation();UAbilityTask_WaitGameplayEvent* WaitComboDamageEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Combo_Damage);WaitComboDamageEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboDamageEvent);WaitComboDamageEvent->ReadyForActivation();
}void UUpperCut::HandleComboCommitEvent(FGameplayEventData EventData)
{// 按晚了if (NextComboName == NAME_None) return;UAnimInstance* OwnerAnimInstance = GetOwnerAnimInstance();if (!OwnerAnimInstance) return;OwnerAnimInstance->Montage_SetNextSection(OwnerAnimInstance->Montage_GetCurrentSection(UpperCutMontage), NextComboName, UpperCutMontage);
}void UUpperCut::HandleComboDamageEvent(FGameplayEventData EventData)
{if (K2_HasAuthority()){// 擊飛一下自己,免得掉下去了PushTarget(GetAvatarActorFromActorInfo(), FVector::UpVector * UpperComboHoldSpeed);// 獲取當前片段對應的傷害效果const FGenericDamageEffectDef* EffectDef = GetDamageEffectDefForCurrentCombo();if (!EffectDef){return;}int HitResultCount = UAbilitySystemBlueprintLibrary::GetDataCountFromTargetData(EventData.TargetData);for (int32 i = 0; i < HitResultCount; i++){FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(EventData.TargetData, i);// 計算推力方向(根據自身朝向變換推力向量)FVector PushVel = GetAvatarActorFromActorInfo()->GetActorTransform().TransformVector(EffectDef->PushVelocity);// 推動目標PushTarget(HitResult.GetActor(), PushVel);// 對目標應用傷害效果ApplyGameplayEffectToHitResultActor(HitResult, EffectDef->DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}}
}
最后在藍圖中設置一下
修正一下
添加一些Combo以及End,還有傷害的傳遞
創建Combo的GE,復制GE_UpperCut_Launch_Damage
改一下數字
以及最后擊的GE
最后放進GA的Map里
注釋掉這兩個,關閉運動似乎會讓尸體在空中滯留(不確定,反正這個也沒啥用了)
升龍技能的完整代碼
// 幻雨喜歡小貓咪#pragma once#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GAS/Core/CGameplayAbilityTypes.h"
#include "UpperCut.generated.h"/*** */
UCLASS()
class CRUNCH_API UUpperCut : public UCGameplayAbility
{GENERATED_BODY()
public: UUpperCut();// TODO: 可能在這里添加手動結束任務的邏輯// virtual void K2_EndAbility() override;// 激活技能時調用virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
private:// 連招階段對應的傷害效果定義表UPROPERTY(EditDefaultsOnly, Category = "Combo")TMap<FName, FGenericDamageEffectDef> ComboDamageMap;// 上勾拳擊飛階段的傷害效果UPROPERTY(EditDefaultsOnly, Category = "Launch")TSubclassOf<UGameplayEffect> LaunchDamageEffect;// 上勾拳擊飛速度UPROPERTY(EditDefaultsOnly, Category = "Launch", meta = (DisplayName = "擊飛力的大小"))float UpperCutLaunchSpeed = 1000.f;// 空中連招的力UPROPERTY(EditDefaultsOnly, Category = "Launch")float UpperComboHoldSpeed = 100.f;// 上勾拳動畫MontageUPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> UpperCutMontage;// 獲取當前連招階段的傷害效果定義const FGenericDamageEffectDef* GetDamageEffectDefForCurrentCombo() const;// 啟動擊飛效果UFUNCTION()void StartLaunching(FGameplayEventData EventData);// 處理連招階段切換事件UFUNCTION()void HandleComboChangeEvent(FGameplayEventData EventData);// 處理連招提交事件UFUNCTION()void HandleComboCommitEvent(FGameplayEventData EventData);// 處理連招傷害事件UFUNCTION()void HandleComboDamageEvent(FGameplayEventData EventData);// 下一個連招階段的名稱FName NextComboName;
};
// 幻雨喜歡小貓咪#include "UpperCut.h"#include "AbilitySystemBlueprintLibrary.h"
#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"
#include "Abilities/Tasks/AbilityTask_WaitGameplayEvent.h"
#include "GAS/Core/TGameplayTags.h"UUpperCut::UUpperCut()
{// 阻止帶有Ability_BasicAttack標簽的技能激活BlockAbilitiesWithTag.AddTag(TGameplayTags::Ability_BasicAttack);
}void UUpperCut::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{if (!K2_CommitAbility()){K2_EndAbility();return;}// 服務器執行if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)){UAbilityTask_PlayMontageAndWait* PlayUpperCutMontageTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, UpperCutMontage);PlayUpperCutMontageTask->OnBlendOut.AddDynamic(this, &UUpperCut::K2_EndAbility);PlayUpperCutMontageTask->OnCancelled.AddDynamic(this, &UUpperCut::K2_EndAbility);PlayUpperCutMontageTask->OnCompleted.AddDynamic(this, &UUpperCut::K2_EndAbility);PlayUpperCutMontageTask->OnInterrupted.AddDynamic(this, &UUpperCut::K2_EndAbility);PlayUpperCutMontageTask->ReadyForActivation();UAbilityTask_WaitGameplayEvent* WaitLaunchEventTask = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Uppercut_Launch);WaitLaunchEventTask->EventReceived.AddDynamic(this, &UUpperCut::StartLaunching);WaitLaunchEventTask->ReadyForActivation();}NextComboName = NAME_None;
}const FGenericDamageEffectDef* UUpperCut::GetDamageEffectDefForCurrentCombo() const
{UAnimInstance* OwnerAnimInstance = GetOwnerAnimInstance();if (OwnerAnimInstance){// 獲取當前片段名稱FName CurrentComboName = OwnerAnimInstance->Montage_GetCurrentSection(UpperCutMontage);// 獲取當前片段對應的傷害效果const FGenericDamageEffectDef* EffectDef = ComboDamageMap.Find(CurrentComboName);if (EffectDef){return EffectDef;}}// 沒找到返回一個空結構return nullptr;
}void UUpperCut::StartLaunching(FGameplayEventData EventData)
{if (K2_HasAuthority()){// 推動自己向上PushTarget(GetAvatarActorFromActorInfo(), FVector::UpVector * UpperCutLaunchSpeed);// 獲取命中目標的數量int HitResultCount = UAbilitySystemBlueprintLibrary::GetDataCountFromTargetData(EventData.TargetData);// 對所有命中的目標執行擊飛和傷害for (int i = 0; i < HitResultCount; i++){FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(EventData.TargetData, i);PushTarget(HitResult.GetActor(), FVector::UpVector * UpperCutLaunchSpeed);ApplyGameplayEffectToHitResultActor(HitResult, LaunchDamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}}// 監聽連招切換、提交、傷害等事件UAbilityTask_WaitGameplayEvent* WaitComboChangeEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Combo_Change, nullptr, false, false);WaitComboChangeEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboChangeEvent);WaitComboChangeEvent->ReadyForActivation();UAbilityTask_WaitGameplayEvent* WaitComboCommitEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_BasicAttack_Pressed);WaitComboCommitEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboCommitEvent);WaitComboCommitEvent->ReadyForActivation();UAbilityTask_WaitGameplayEvent* WaitComboDamageEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Combo_Damage);WaitComboDamageEvent->EventReceived.AddDynamic(this, &UUpperCut::HandleComboDamageEvent);WaitComboDamageEvent->ReadyForActivation();
}void UUpperCut::HandleComboChangeEvent(FGameplayEventData EventData)
{// 獲取事件標簽FGameplayTag EventTag = EventData.EventTag;if (EventTag == TGameplayTags::Ability_Combo_Change_End){// 下一個連招名稱置空NextComboName = NAME_None;UE_LOG(LogTemp, Warning, TEXT("清除連招"))return;}// 獲取下一個連段的名稱TArray<FName> TagNames;UGameplayTagsManager::Get().SplitGameplayTagFName(EventTag, TagNames);// Tag最后一段的名稱比如Combo02,03,04等NextComboName = TagNames.Last();UE_LOG(LogTemp, Warning, TEXT("下一個Combo:%s"), *NextComboName.ToString())
}void UUpperCut::HandleComboCommitEvent(FGameplayEventData EventData)
{// 按晚了if (NextComboName == NAME_None) return;UAnimInstance* OwnerAnimInstance = GetOwnerAnimInstance();if (!OwnerAnimInstance) return;OwnerAnimInstance->Montage_SetNextSection(OwnerAnimInstance->Montage_GetCurrentSection(UpperCutMontage), NextComboName, UpperCutMontage);UE_LOG(LogTemp, Warning, TEXT("觸發連招:%s"), *NextComboName.ToString())
}void UUpperCut::HandleComboDamageEvent(FGameplayEventData EventData)
{if (K2_HasAuthority()){// 擊飛一下自己,免得掉下去了PushTarget(GetAvatarActorFromActorInfo(), FVector::UpVector * UpperComboHoldSpeed);// 獲取當前片段對應的傷害效果const FGenericDamageEffectDef* EffectDef = GetDamageEffectDefForCurrentCombo();if (!EffectDef){return;}int HitResultCount = UAbilitySystemBlueprintLibrary::GetDataCountFromTargetData(EventData.TargetData);for (int32 i = 0; i < HitResultCount; i++){FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(EventData.TargetData, i);// 計算推力方向(根據自身朝向變換推力向量)FVector PushVel = GetAvatarActorFromActorInfo()->GetActorTransform().TransformVector(EffectDef->PushVelocity);// 推動目標PushTarget(HitResult.GetActor(), PushVel);// 對目標應用傷害效果ApplyGameplayEffectToHitResultActor(HitResult, EffectDef->DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}}
}