UE5多人MOBA+GAS 21、给升龙添加连段攻击,从角色的按下事件中传递事件给GA
文章目录
- 给升龙制作可连段
- 缓存下一连段
- 用普攻键来触发升龙后续的连段
- 在角色中发送按下普攻标签事件
- 在升龙中接收按下事件,触发连段以及伤害和力量的传递
- 最后在蓝图中设置一下
- 升龙技能的完整代码
给升龙制作可连段
给升龙技能添加一些连段
缓存下一连段
缓存下一连段的名称
// 处理连招阶段切换事件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));}}
}