## 前言 之前使用GameplayTasks的事件处理会出现延迟问题。具体体现在如果使用机制来添加BlockAbilityTag,在狂按技能的情况下会出现Tag不能正常生成导致可以不停ActivateAbility的问题。 ## 问题成因 1. 问题的原因就是使用GameplayTasks不能即时添加GE,会有一定延迟。 2. 清除GameplayEffect方式有问题,应该使用只清除该技能所附加的GE。 ## 解决思路 在解决清除GE问题的情况下: 1. GASDocument中的解决方法:使用AbilityTag作为BlockAbilityTag,之后通过AnimNotify发送EndAbility事件Tag,提前结束Ability。(不会结束掉Montage播放) 2. 在AnimNotifyBegin函数中直接添加GE,根据TotalDuration设置持续时间。(但如果Montage突然被中断,就很难操作了,而且这样会与Ability耦合) 3. 增加公共CD(测试过效果不佳) ## PlayMontage修改 1. 传递事件使用专门的Event.Montage.xxxx作为事件标签 2. 重写AnimNotify的GetNotifyName函数,使用标签名作为AnimNotify的显示名称。 ``` void UGDGA_FireGun::EventReceived(FGameplayTag EventTag, FGameplayEventData EventData) { // Montage told us to end the ability before the montage finished playing. // Montage was set to continue playing animation even after ability ends so this is okay. if (EventTag == FGameplayTag::RequestGameplayTag(FName("Event.Montage.EndAbility"))) { EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false); return; } // Only spawn projectiles on the Server. // Predicting projectiles is an advanced topic not covered in this example. if (GetOwningActorFromActorInfo()->GetLocalRole() == ROLE_Authority && EventTag == FGameplayTag::RequestGameplayTag(FName("Event.Montage.SpawnProjectile"))) { AGDHeroCharacter* Hero = Cast(GetAvatarActorFromActorInfo()); if (!Hero) { EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); } FVector Start = Hero->GetGunComponent()->GetSocketLocation(FName("Muzzle")); FVector End = Hero->GetCameraBoom()->GetComponentLocation() + Hero->GetFollowCamera()->GetForwardVector() * Range; FRotator Rotation = UKismetMathLibrary::FindLookAtRotation(Start, End); FGameplayEffectSpecHandle DamageEffectSpecHandle = MakeOutgoingGameplayEffectSpec(DamageGameplayEffect, GetAbilityLevel()); // Pass the damage to the Damage Execution Calculation through a SetByCaller value on the GameplayEffectSpec DamageEffectSpecHandle.Data.Get()->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Damage")), Damage); FTransform MuzzleTransform = Hero->GetGunComponent()->GetSocketTransform(FName("Muzzle")); MuzzleTransform.SetRotation(Rotation.Quaternion()); MuzzleTransform.SetScale3D(FVector(1.0f)); FActorSpawnParameters SpawnParameters; SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; AGDProjectile* Projectile = GetWorld()->SpawnActorDeferred(ProjectileClass, MuzzleTransform, GetOwningActorFromActorInfo(), Hero, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); Projectile->DamageEffectSpecHandle = DamageEffectSpecHandle; Projectile->Range = Range; Projectile->FinishSpawning(MuzzleTransform); } } ``` ``` /** Apply a gameplay effect to the owner of this ability */ UFUNCTION(BlueprintCallable, Category = Ability, DisplayName="ApplyGameplayEffectToOwner", meta=(ScriptName="ApplyGameplayEffectToOwner")) FActiveGameplayEffectHandle BP_ApplyGameplayEffectToOwner(TSubclassOf GameplayEffectClass, int32 GameplayEffectLevel = 1, int32 Stacks = 1); /** Non blueprintcallable, safe to call on CDO/NonInstance abilities */ FActiveGameplayEffectHandle ApplyGameplayEffectToOwner(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const UGameplayEffect* GameplayEffect, float GameplayEffectLevel, int32 Stacks = 1) const; /** Apply a previously created gameplay effect spec to the owner of this ability */ UFUNCTION(BlueprintCallable, Category = Ability, DisplayName = "ApplyGameplayEffectSpecToOwner", meta=(ScriptName = "ApplyGameplayEffectSpecToOwner")) FActiveGameplayEffectHandle K2_ApplyGameplayEffectSpecToOwner(const FGameplayEffectSpecHandle EffectSpecHandle); FActiveGameplayEffectHandle ApplyGameplayEffectSpecToOwner(const FGameplayAbilitySpecHandle AbilityHandle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEffectSpecHandle SpecHandle) const; // ------------------------------------- // Apply Gameplay effects to Target // ------------------------------------- /** Apply a gameplay effect to a Target */ UFUNCTION(BlueprintCallable, Category = Ability, DisplayName = "ApplyGameplayEffectToTarget", meta=(ScriptName = "ApplyGameplayEffectToTarget")) TArray BP_ApplyGameplayEffectToTarget(FGameplayAbilityTargetDataHandle TargetData, TSubclassOf GameplayEffectClass, int32 GameplayEffectLevel = 1, int32 Stacks = 1); /** Non blueprintcallable, safe to call on CDO/NonInstance abilities */ TArray ApplyGameplayEffectToTarget(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayAbilityTargetDataHandle& Target, TSubclassOf GameplayEffectClass, float GameplayEffectLevel, int32 Stacks = 1) const; /** Apply a previously created gameplay effect spec to a target */ UFUNCTION(BlueprintCallable, Category = Ability, DisplayName = "ApplyGameplayEffectSpecToTarget", meta=(ScriptName = "ApplyGameplayEffectSpecToTarget")) TArray K2_ApplyGameplayEffectSpecToTarget(const FGameplayEffectSpecHandle EffectSpecHandle, FGameplayAbilityTargetDataHandle TargetData); TArray ApplyGameplayEffectSpecToTarget(const FGameplayAbilitySpecHandle AbilityHandle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEffectSpecHandle SpecHandle, const FGameplayAbilityTargetDataHandle& TargetData) const; ```