局域网怎么建设网站,海澜之家的网站建设目标,网站和网络建设调研情况,seo优化是怎么优化的在正常游戏里#xff0c;有些伤害技能会携带一些负面效果#xff0c;比如火焰伤害的技能会携带燃烧效果#xff0c;敌人在受到伤害后#xff0c;会接受一个燃烧的效果#xff0c;燃烧效果会在敌人身上持续一段时间#xff0c;并且持续受到火焰灼烧。 我们将在这一篇文章里…在正常游戏里有些伤害技能会携带一些负面效果比如火焰伤害的技能会携带燃烧效果敌人在受到伤害后会接受一个燃烧的效果燃烧效果会在敌人身上持续一段时间并且持续受到火焰灼烧。 我们将在这一篇文章里实现伤害技能附带负面效果并可以设置负面效果的参数来实现对敌人添加负面buff
添加负面效果标签
首先我们添加对应的伤害类型的负面标签 FGameplayTag DeBuff_Burn; //火属性负面效果 燃烧FGameplayTag DeBuff_Stun; //雷属性负面效果 眩晕FGameplayTag DeBuff_Arcane; //魔法伤害负面效果FGameplayTag DeBuff_Physical; //物理伤害负面效果 流血并且添加一个Map用于负面效果标签和属性抵抗表情对应抵抗可以用于降低负面效用的成功率
TMapFGameplayTag, FGameplayTag DeBuffsToResistance; //属性伤害标签对应负面标签将标签注册的标签管理器 /** 负面标签注册*/GameplayTags.DeBuff_Burn UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Burn),FString(火属性燃烧负面标签));GameplayTags.DeBuff_Stun UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Stun),FString(雷属性眩晕负面标签));GameplayTags.DeBuff_Arcane UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Arcane),FString(魔法属性负面标签));GameplayTags.DeBuff_Physical UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Physical),FString(物理属性流血负面标签));我们在应用负面效果时目标角色可以通过自身的对应类型的抵抗来降低负面效果应用的成功率所以我们需要一个对应的Map /** 负面标签和属性抵抗标签对于对应*/GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Burn, GameplayTags.Attributes_Resistance_Fire);GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Stun, GameplayTags.Attributes_Resistance_Lightning);GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Arcane, GameplayTags.Attributes_Resistance_Arcane);GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Physical, GameplayTags.Attributes_Resistance_Physical);添加负面效果配置项
如果我们需要添加一些负面效果相关的配置项然后使用Set By Caller 的方式去设置我们先在RPGDamageGameplayAbility.h里添加对负面效果的配置项 UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryDamage)FGameplayTag DeBuffDamageType FGameplayTag(); //负面效果伤害类型UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryDamage)float DeBuffChance 20.f; //触发负面的机率UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryDamage)float DeBuffDamage 5.f; //负面伤害UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryDamage)float DeBuffFrequency 1.f; //负面伤害触发间隔时间UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryDamage)float DeBuffDuration 5.f; //负面效果持续时间我们要通过Set ByCaller设置负面效果GE的属性那么我们选择使用标签这样不会出错 接着我们创建四个对应的标签 FGameplayTag DeBuff_Chance; //负面效果触发几率标签FGameplayTag DeBuff_Damage; //负面效果伤害标签FGameplayTag DeBuff_Duration; //负面效果持续时间标签FGameplayTag DeBuff_Frequency; //负面效果触发间隔标签然后注册 /** 负面效果配置标签*/GameplayTags.DeBuff_Chance UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Chance),FString(负面效果 触发几率));GameplayTags.DeBuff_Damage UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Damage),FString(负面效果 伤害));GameplayTags.DeBuff_Duration UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Duration),FString(负面效果 持续时间));GameplayTags.DeBuff_Frequency UGameplayTagsManager::Get().AddNativeGameplayTag(FName(DeBuff.Frequency),FString(负面效果 触发间隔));创建负面效果使用的结构体
接下来我们创建一个结构体用于在给目标应用负面效果时使用由于这个结构体的数据需要序列化以后传输到服务器端进行处理所以我们将其设置到RPGAbilityTypes.h文件内之前我们创建它是为了在代码内创建GE句柄时能够使用我们自定义的结构体增加了暴击和格挡的数据。 我们在里面创建一个结构体用来配置应用一个负面效果时所需要的所有数据
USTRUCT(BlueprintType)
struct FDamageEffectParams
{GENERATED_BODY()FDamageEffectParams(){}UPROPERTY()TObjectPtrUObject WorldContextObject nullptr; //当前场景上下文对象UPROPERTY()TSubclassOfUGameplayEffect DamageGameplayEffectClass nullptr; //需要应用的GE的类UPROPERTY()TObjectPtrUAbilitySystemComponent SourceAbilitySystemComponent; //源ASCUPROPERTY()TObjectPtrUAbilitySystemComponent TargetAbilitySystemComponent; //目标ASCUPROPERTY()TMapFGameplayTag, float DamageTypes; //技能造成的多种伤害和伤害类型UPROPERTY()float AbilityLevel 1.f; //技能等级UPROPERTY()FGameplayTag DeBuffDamageType FGameplayTag(); //负面效果伤害类型UPROPERTY()float DeBuffChance 0.f; //触发负面效果概率UPROPERTY()float DeBuffDamage 0.f; //负面效果伤害UPROPERTY()float DeBuffDuration 0.f; //负面效果持续时间UPROPERTY()float DeBuffFrequency 0.f; //负面效果触发频率
};在伤害技能基础类增加一个创建配置项的函数
有了配置项的结构体我们需要实现一个函数在伤害技能的基类上通过伤害技能的配置生成结构体 //创建技能负面效果使用的结构体FDamageEffectParams MakeDamageEffectParamsFromClassDefaults(AActor* TargetActor nullptr);然后实现通过技能上的配置生成配置项
FDamageEffectParams URPGDamageGameplayAbility::MakeDamageEffectParamsFromClassDefaults(AActor* TargetActor)
{FDamageEffectParams Params;Params.WorldContextObject GetAvatarActorFromActorInfo();Params.DamageGameplayEffectClass DamageEffectClass;Params.SourceAbilitySystemComponent GetAbilitySystemComponentFromActorInfo();Params.TargetAbilitySystemComponent UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);for(auto Pair : DamageTypes){const float ScaledDamage Pair.Value.GetValueAtLevel(GetAbilityLevel()); //根据等级获取技能伤害Params.DamageTypes.Add(Pair.Key, ScaledDamage);}Params.AbilityLevel GetAbilityLevel();Params.DeBuffDamageType DeBuffDamageType;Params.DeBuffChance DeBuffChance;Params.DeBuffDamage DeBuffDamage;Params.DeBuffDuration DeBuffDuration;Params.DeBuffFrequency DeBuffFrequency;return Params;
}在函数库添加一个通过配置项实现GE的应用
我们有了配置项可以将应用设置为通用的函数所以我们在函数库里增加一个静态函数只要结构体配置完全我们可以直接调用此函数完成内部逻辑 我们添加一个函数传入参数就是配置项 //通过技能生成的负面配置项应用技能负面效果UFUNCTION(BlueprintCallable, CategoryRPGAbilitySystemLibrary|GameplayEffects)static FGameplayEffectContextHandle ApplyDamageEffect(const FDamageEffectParams DamageEffectParams);然后实现此函数在函数内存创建GE的上下文和实例并通过标签的SetByCaller设置GE所使用的值最后应用到目标ASC上并返回GE的上下文句柄。 注意这里我们设置伤害的标签是属性伤害类型标签在应用时我们就可以负面效果类型获取当前GE是否设置了对应的负面效果
FGameplayEffectContextHandle URPGAbilitySystemBlueprintLibrary::ApplyDamageEffect(const FDamageEffectParams DamageEffectParams)
{const FRPGGameplayTags GameplayTags FRPGGameplayTags::Get();const AActor* SourceAvatarActor DamageEffectParams.SourceAbilitySystemComponent-GetAvatarActor();//创建GE的上下文句柄FGameplayEffectContextHandle EffectContextHandle DamageEffectParams.SourceAbilitySystemComponent-MakeEffectContext();EffectContextHandle.AddSourceObject(SourceAvatarActor);//根据句柄和类创建GE实例const FGameplayEffectSpecHandle SpecHandle DamageEffectParams.SourceAbilitySystemComponent-MakeOutgoingSpec(DamageEffectParams.DamageGameplayEffectClass, DamageEffectParams.AbilityLevel, EffectContextHandle);//通过标签设置GE使用的配置for(auto Pair : DamageEffectParams.DamageTypes){UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, Pair.Key, Pair.Value);}UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Chance, DamageEffectParams.DeBuffChance);UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, DamageEffectParams.DeBuffDamageType, DamageEffectParams.DeBuffDamage);UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Duration, DamageEffectParams.DeBuffDuration);UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Frequency, DamageEffectParams.DeBuffFrequency);//将GE应用给目标ASCDamageEffectParams.TargetAbilitySystemComponent-ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());return EffectContextHandle;
}修改生成发射物的函数
我们在ProjectileSpell.cpp文件里由于修改了增加的负面效果增加的部分所以我们需要对发射物生成进行修改不再创建发射物时创建GE而是修改为生成一个配置结构体供后续使用。
void UProjectileSpell::SpawnProjectile(const FVector ProjectileTargetLocation, const FGameplayTag SocketTag, const FName SocketName, const bool bOverridePitch, const float PitchOverride)
{const bool bIsServer GetAvatarActorFromActorInfo()-HasAuthority(); //判断此函数是否在服务器运行if (!bIsServer) return;if (GetAvatarActorFromActorInfo()-ImplementsUCombatInterface()){const FVector SocketLocation ICombatInterface::Execute_GetCombatSocketLocationByTag(GetAvatarActorFromActorInfo(), SocketTag, SocketName);FRotator Rotation (ProjectileTargetLocation - SocketLocation).Rotation(); //将方向转为旋转if(bOverridePitch){Rotation.Pitch PitchOverride; //覆写发射角度}FTransform SpawnTransform;SpawnTransform.SetLocation(SocketLocation);SpawnTransform.SetRotation(Rotation.Quaternion());//SpawnActorDeferred将异步创建实例在实例创建完成时相应的数据已经应用到了实例身上AProjectile* Projectile GetWorld()-SpawnActorDeferredAProjectile(ProjectileClass,SpawnTransform,GetOwningActorFromActorInfo(),CastAPawn(GetAvatarActorFromActorInfo()),ESpawnActorCollisionHandlingMethod::AlwaysSpawn);Projectile-DamageEffectParams MakeDamageEffectParamsFromClassDefaults();//确保变换设置被正确应用Projectile-FinishSpawning(SpawnTransform);}
}在AbilityTypes.h里增加参数
在之前的文章里 51. UE5 RPG 自定义FGameplayEffectContext我们实现自定义GE上下文的属性增加了格挡和暴击两个属性是否触发并实现了对其的序列化可以在客户端将内容复制到服务器端因为属性的处理都是在服务器端进行的这里有个交互的过程需要序列化。 我们给GE上下文增加多个属性这样生成的实例里可以设置这些属性 注意这里的标签没有设置反射需要我们自己实现类型在序列化时也有所体现。
protected:UPROPERTY()bool bIsBlockedHit false; //格挡UPROPERTY()bool bIsCriticalHit false; //暴击UPROPERTY()bool bIsSuccessfulDeBuff false; //成功应用负面效果UPROPERTY()float DeBuffDamage 0.f; //负面效果每次造成的伤害UPROPERTY()float DeBuffDuration 0.f; //负面效果持续时间UPROPERTY()float DeBuffFrequency 0.f; //负面效果触发频率间隔TSharedPtrFGameplayTag DamageType; //负面效果的伤害类型并增加其对应的设置方法
public:bool IsBlockedHit() const { return bIsBlockedHit; } //获取 格挡bool IsCriticalHit() const { return bIsCriticalHit; } //获取 暴击bool IsSuccessfulDeBuff() const { return bIsSuccessfulDeBuff; } //获取 应用负面效果float GetDeBuffDamage() const { return DeBuffDamage; } //获取 负面效果伤害float GetDeBuffDuration() const { return DeBuffDuration; } //获取 负面效果持续时间float GetDeBuffFrequency() const { return DeBuffFrequency; } //获取 负面效果伤害触发间隔TSharedPtrFGameplayTag GetDeBuffDamageType() const { return DamageType; } //获取 负面效果伤害类型void SetIsBlockedHit(const bool bInIsBlockedHit) { bIsBlockedHit bInIsBlockedHit; } // 设置 格挡void SetIsCriticalHit(const bool bInIsCriticalHit) { bIsCriticalHit bInIsCriticalHit; } // 设置 暴击void SetIsSuccessfulDeBuff(const bool bInIsSuccessfulDeBuff) { bIsSuccessfulDeBuff bInIsSuccessfulDeBuff; } //设置 应用负面效果void SetDeBuffDamage(const float InDamage) { DeBuffDamage InDamage; } //设置 负面效果伤害void SetDeBuffDuration(const float InDuration) { DeBuffDuration InDuration; } //设置 负面效果伤害void SetDeBuffFrequency(const float InFrequency) { DeBuffFrequency InFrequency; } //设置 负面效果伤害void SetDeBuffDamageType(const TSharedPtrFGameplayTag InDamageType) { DamageType InDamageType; } //设置 负面效果伤害类型我们还要修改它的序列化的方法让其能够复制到服务器端去处理。 首先是增加序列化内容 //自定义内容增加暴击和格挡触发存储if(bIsBlockedHit){RepBits | 1 7;}if(bIsCriticalHit){RepBits | 1 8;}if(bIsSuccessfulDeBuff){RepBits | 1 9;}if(DeBuffDamage 0.f){RepBits | 1 10;}if(DeBuffDuration 0.f){RepBits | 1 11;}if(DeBuffFrequency 0.f){RepBits | 1 12;}if(DamageType.IsValid()){RepBits | 1 13;}接着就是设置序列内容的长度 //使用了多少长度就将长度设置为多少Ar.SerializeBits(RepBits, 14);接着就是在服务器端的反序列化 //新增对暴击格挡的序列化或反序列化处理if (RepBits (1 7)){Ar bIsBlockedHit;}if (RepBits (1 8)){Ar bIsCriticalHit;}if (RepBits (1 9)){Ar bIsSuccessfulDeBuff;}if (RepBits (1 10)){Ar DeBuffDamage;}if (RepBits (1 11)){Ar DeBuffDuration;}if (RepBits (1 12)){Ar DeBuffFrequency;}if (RepBits (1 13)){if (Ar.IsLoading()) //判断是否在加载资源{if (!DamageType.IsValid()){DamageType TSharedPtrFGameplayTag(new FGameplayTag());}}DamageType-NetSerialize(Ar, Map, bOutSuccess);}这就完成了对自定义属性的添加并且能够实现服务器获取在AttributeSet里对属性进行处理。
接着我们在函数库里增加一些函数可以直接调用函数库内的函数实现对属性设置和获取 //获取当前GE是否成功应用负面效果UFUNCTION(BlueprintPure, CategoryRPGAbilitySystemLibrary|GameplayEffects)static bool IsSuccessfulDeBuff(const FGameplayEffectContextHandle EffectContextHandle);//获取当前GE负面效果伤害UFUNCTION(BlueprintPure, CategoryRPGAbilitySystemLibrary|GameplayEffects)static float GetDeBuffDamage(const FGameplayEffectContextHandle EffectContextHandle);//获取当前GE负面效果持续时间UFUNCTION(BlueprintPure, CategoryRPGAbilitySystemLibrary|GameplayEffects)static float GetDeBuffDuration(const FGameplayEffectContextHandle EffectContextHandle);//获取当前GE负面效果触发间隔UFUNCTION(BlueprintPure, CategoryRPGAbilitySystemLibrary|GameplayEffects)static float GetDeBuffFrequency(const FGameplayEffectContextHandle EffectContextHandle);//获取当前GE负面效果伤害类型UFUNCTION(BlueprintPure, CategoryRPGAbilitySystemLibrary|GameplayEffects)static FGameplayTag GetDeBuffDamageType(const FGameplayEffectContextHandle EffectContextHandle);bool URPGAbilitySystemBlueprintLibrary::IsSuccessfulDeBuff(const FGameplayEffectContextHandle EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext static_castconst FRPGGameplayEffectContext*(EffectContextHandle.Get())){return RPGEffectContext-IsSuccessfulDeBuff();}return false;
}float URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(const FGameplayEffectContextHandle EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext static_castconst FRPGGameplayEffectContext*(EffectContextHandle.Get())){return RPGEffectContext-GetDeBuffDamage();}return 0.f;
}float URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(const FGameplayEffectContextHandle EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext static_castconst FRPGGameplayEffectContext*(EffectContextHandle.Get())){return RPGEffectContext-GetDeBuffDuration();}return 0.f;
}float URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(const FGameplayEffectContextHandle EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext static_castconst FRPGGameplayEffectContext*(EffectContextHandle.Get())){return RPGEffectContext-GetDeBuffFrequency();}return 0.f;
}FGameplayTag URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(const FGameplayEffectContextHandle EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext static_castconst FRPGGameplayEffectContext*(EffectContextHandle.Get())){//如果当前存在设置了伤害类型if(RPGEffectContext-GetDeBuffDamageType().IsValid()){//取消指针return *RPGEffectContext-GetDeBuffDamageType();}}return FGameplayTag();
}在设置这里因为是需要在同一地方使用所以我们直接将其合并为了一个函数设置 //设置GE是否应用负面效果UFUNCTION(BlueprintCallable, CategoryRPGAbilitySystemLibrary|GameplayEffects)static void SetIsSuccessfulDeBuff(UPARAM(ref) FGameplayEffectContextHandle EffectContextHandle, bool bInIsSuccessfulDeBuff);//设置GE负面效果相关数值 负面效果伤害类型 负面效果伤害 负面效果持续时间 负面效果触发间隔时间UFUNCTION(BlueprintCallable, CategoryRPGAbilitySystemLibrary|GameplayEffects)static void SetDeBuff(UPARAM(ref) FGameplayEffectContextHandle EffectContextHandle, FGameplayTag InDamageType, float InDamage, float InDuration, float InFrequency);void URPGAbilitySystemBlueprintLibrary::SetIsSuccessfulDeBuff(FGameplayEffectContextHandle EffectContextHandle, const bool bInIsSuccessfulDeBuff)
{FRPGGameplayEffectContext* RPGEffectContext static_castFRPGGameplayEffectContext*(EffectContextHandle.Get());RPGEffectContext-SetIsSuccessfulDeBuff(bInIsSuccessfulDeBuff);
}void URPGAbilitySystemBlueprintLibrary::SetDeBuff(FGameplayEffectContextHandle EffectContextHandle, FGameplayTag InDamageType, const float InDamage, const float InDuration, const float InFrequency)
{FRPGGameplayEffectContext* RPGEffectContext static_castFRPGGameplayEffectContext*(EffectContextHandle.Get());//通过标签创建一个共享指针const TSharedPtrFGameplayTag DamageType MakeSharedFGameplayTag(InDamageType);RPGEffectContext-SetDeBuffDamageType(DamageType);RPGEffectContext-SetDeBuffDamage(InDamage);RPGEffectContext-SetDeBuffDuration(InDuration);RPGEffectContext-SetDeBuffFrequency(InFrequency);
}实现负面效果应用
实现了相应的函数后我们需要接着实现负面效果的应用我们在ExecCalc_Damage.cpp里专门实现了对目标进行造成的最终伤害的计算我们在内部实现负面效果的是否应用成功的计算首先获取到负面效果命中率然后通过目标抵抗降低命中率接着通过随机数判断当前是否需要应用负面效果。
void UExecCalc_Damage::DetermineDeBuff(const FGameplayEffectCustomExecutionParameters ExecutionParams, const FGameplayEffectSpec Spec, const FAggregatorEvaluateParameters EvaluationParameters, TMapFGameplayTag, FGameplayEffectAttributeCaptureDefinition TagsToCaptureDefs)
{const FRPGGameplayTags GameplayTags FRPGGameplayTags::Get();//遍历所有的负面效果伤害类型根据伤害类型是否赋值来判断是否需要应用负面效果for(const TTupleFGameplayTag, FGameplayTag Pair : GameplayTags.DeBuffsToResistance){FGameplayTag DeBuffDamageType Pair.Key; //获取到负面效果伤害类型const FGameplayTag ResistanceType Pair.Value; //获取到负面效果抵抗类型const float TypeDamage Spec.GetSetByCallerMagnitude(DeBuffDamageType, false, -1.f);//如果负面效果设置了伤害即使为0也需要应用负面效果if(TypeDamage -.5f){//获取负面效果命中率const float SourceDeBuffChance Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Chance, false, -1.f);//----------------获取负面效果抵抗------------float TargetDeBuffResistance 0.f; //计算目标对收到的负面效果类型的抵抗//检查对应的属性快照是否设置防止报错checkf(TagsToCaptureDefs.Contains(ResistanceType), TEXT(在ExecCalc_Damage中无法获取到Tag[%s]对应的属性快照), *ResistanceType.ToString());//通过抗性标签获取到属性快照的值const FGameplayEffectAttributeCaptureDefinition CaptureDef TagsToCaptureDefs[ResistanceType];ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(CaptureDef, EvaluationParameters, TargetDeBuffResistance);TargetDeBuffResistance FMath::Clamp(TargetDeBuffResistance, 0.f, 100.f); //将抗住限制在0到100//----------------计算负面效果是否应用------------const float EffectiveDeBuffChance SourceDeBuffChance * (100 - TargetDeBuffResistance) / 100.f; //计算出负面效果的实际命中率const bool bDeBuff FMath::RandRange(1, 100) EffectiveDeBuffChance; //判断此次效果是否实现命中if(bDeBuff){//获取GE上下文设置负面效果相关配置FGameplayEffectContextHandle ContextHandle Spec.GetContext();//设置当前应用负面效果成功URPGAbilitySystemBlueprintLibrary::SetIsSuccessfulDeBuff(ContextHandle, true);const float SourceDeBuffDuration Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Duration, false, 0.f);const float SourceDeBuffFrequency Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Frequency, false, 0.f);//设置负面效果 伤害类型 伤害 持续时间 触发频率URPGAbilitySystemBlueprintLibrary::SetDeBuff(ContextHandle, DeBuffDamageType, TypeDamage, SourceDeBuffDuration, SourceDeBuffFrequency);}}}
}将一部分代码转换为函数
有个小技巧我们还可以将其内容提取为单独函数 设置命名和参数 可以去除不需要的参数。
整理AttributeSet内的代码
代码写的越来越多会变成一坨所以我们需要将代码分离成函数这样方便后期的维护。 我们在自定义的AttributeSet里增加三个函数分别用于处理接收实际受到的伤害属性获取到的经验值以及需要处理应用负面效果的函数。 //处理传入的参数为伤害属性时处理的逻辑void HandleIncomingDamage(const FEffectProperties Props);//处理传入的参数为经验属性时处理的逻辑void HandleIncomingXP(const FEffectProperties Props);//如果当前伤害触发了负面效果处理的逻辑void HandleDeBuff(const FEffectProperties Props);有了这几个函数我们的代码就清晰了很多比如接收到属性的函数代码这里防止敌人在死亡后还会受到伤害我们在进行属性处理前判断角色是否处于死亡状态。
void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData Data)
{Super::PostGameplayEffectExecute(Data);FEffectProperties Props;SetEffectProperties(Data, Props);//判断当前目标是否已经死亡如果死亡将不再进行处理if(Props.TargetCharacter-ImplementsUCombatInterface() ICombatInterface::Execute_IsDead(Props.TargetCharacter)) return;if(Data.EvaluatedData.Attribute GetHealthAttribute()){SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));// UE_LOG(LogTemp, Warning, TEXT(%s 的生命值发生了修改当前生命值%f), *Props.TargetAvatarActor-GetName(), GetHealth());}if(Data.EvaluatedData.Attribute GetManaAttribute()){SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana()));}if(Data.EvaluatedData.Attribute GetIncomingDamageAttribute()){HandleIncomingDamage(Props);}if(Data.EvaluatedData.Attribute GetIncomingXPAttribute()){HandleIncomingXP(Props);}}由于它们都只需要在Props里拿值进行运算我们只需要一个Props属性即可。
void URPGAttributeSet::HandleIncomingXP(const FEffectProperties Props)
{const float LocalIncomingXP GetIncomingXP();SetIncomingXP(0);// UE_LOG(LogRPG, Log, TEXT(获取传入经验值%f), LocalIncomingXP);if(Props.SourceCharacter-ImplementsUPlayerInterface() Props.SourceCharacter-ImplementsUCombatInterface()){//获取角色当前等级和经验const int32 CurrentLevel ICombatInterface::Execute_GetPlayerLevel(Props.SourceCharacter);const int32 CurrentXP IPlayerInterface::Execute_GetXP(Props.SourceCharacter);//获取获得经验后的新等级const int32 NewLevel IPlayerInterface::Execute_FindLevelForXP(Props.SourceCharacter, CurrentXP LocalIncomingXP);const int32 NumLevelUps NewLevel - CurrentLevel; //查看等级是否有变化if(NumLevelUps 0){//如果连升多级我们通过for循环获取每个等级的奖励for(int32 i CurrentLevel; i NewLevel; i){//获取升级提供的技能点和属性点const int32 AttributePointsReward IPlayerInterface::Execute_GetAttributePointsReward(Props.SourceCharacter, i);const int32 SpellPointsReward IPlayerInterface::Execute_GetSpellPointsReward(Props.SourceCharacter, i);//增加角色技能点和属性点IPlayerInterface::Execute_AddToAttributePoints(Props.SourceCharacter, AttributePointsReward);IPlayerInterface::Execute_AddToSpellPoints(Props.SourceCharacter, SpellPointsReward);}//提升等级IPlayerInterface::Execute_AddToPlayerLevel(Props.SourceCharacter, NumLevelUps);//播放升级效果IPlayerInterface::Execute_LevelUp(Props.SourceCharacter);//将血量和蓝量填充满, 我们将设置变量SetHealth(GetMaxHealth());SetMana(GetMaxMana());}//将经验应用给自身通过事件传递在玩家角色被动技能GA_ListenForEvents里接收IPlayerInterface::Execute_AddToXP(Props.SourceCharacter, LocalIncomingXP);}
}在接收伤害的函数里我们增加对负面效果应用的判断如果当前应用的负面效果我们将调用负面效果函数处理逻辑。
void URPGAttributeSet::HandleIncomingDamage(const FEffectProperties Props)
{const float LocalIncomingDamage GetIncomingDamage();SetIncomingDamage(0.f);if(LocalIncomingDamage 0.f){const float NewHealth GetHealth() - LocalIncomingDamage;SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth()));const bool bFatal NewHealth 0.f; //血量小于等于0时角色将会死亡if(bFatal){//调用死亡函数ICombatInterface* CombatInterface CastICombatInterface(Props.TargetAvatarActor);if(CombatInterface){CombatInterface-Die();}//死亡时发送经验事件SendXPEvent(Props);}else{//激活受击技能FGameplayTagContainer TagContainer;TagContainer.AddTag(FRPGGameplayTags::Get().Effects_HitReact);// Props.TargetASC-CancelAbilities(TagContainer); //先取消之前的受击Props.TargetASC-TryActivateAbilitiesByTag(TagContainer); //根据tag标签激活技能}//获取格挡和暴击const bool IsBlockedHit URPGAbilitySystemBlueprintLibrary::IsBlockedHit(Props.EffectContextHandle);const bool IsCriticalHit URPGAbilitySystemBlueprintLibrary::IsCriticalHit(Props.EffectContextHandle);//显示伤害数字ShowFloatingText(Props, LocalIncomingDamage, IsBlockedHit, IsCriticalHit);//判断当前是否应用负面效果if(URPGAbilitySystemBlueprintLibrary::IsSuccessfulDeBuff(Props.EffectContextHandle)){HandleDeBuff(Props);}}
}通过代码创建GameplayEffect类
接下来我们要实现设置GE并将其应用到角色身上这里采用c编写的方式实现我会将代码和在UE里编辑的配置进行比对方便大家更清晰的熟悉每个配置项。 我们将在之前添加的HandleDeBuff函数中增加处理这个函数将在可以对目标应用负面效果时调用。 我们首先通过库函数获取到创建GE类的相关参数 //获取负面效果相关参数const FGameplayTag DeBuffType URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(Props.EffectContextHandle);const float DeBuffDamage URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(Props.EffectContextHandle);const float DeBuffDuration URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(Props.EffectContextHandle);const float DeBuffFrequency URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(Props.EffectContextHandle);然后我们设置一个名称并创建一个GE类 //创建GE所使用的名称并创建一个可实例化的GEFString DeBuffName FString::Printf(TEXT(DynamicDeBuff_%s), *DeBuffType.ToString());UGameplayEffect* Effect NewObjectUGameplayEffect(GetTransientPackage(), FName(DeBuffName));接下来就是修改GE类的配置项它们将在我们应用GE实例时产生效果。 首先我们设置GE为有时间限制的并设置对应时间 //设置动态创建GE的属性Effect-DurationPolicy EGameplayEffectDurationType::HasDuration; //设置GE为有时间限制的效果Effect-DurationMagnitude FScalableFloat(DeBuffDuration); //设置GE的持续时间然后就是设置Period Effect-Period FScalableFloat(DeBuffFrequency); //设置GE的触发策略间隔时间Effect-bExecutePeriodicEffectOnApplication false; //在应用后不会立即触发而是在经过了Period后才会触发Effect-PeriodicInhibitionPolicy EGameplayEffectPeriodInhibitionRemovedPolicy::NeverReset; //设置每次应用后不会重置触发时间接着就是叠加层数相关设置这里和我们所需的不同但是为了显示所有设置我全添加了 //设置可叠加层数Effect-StackingType EGameplayEffectStackingType::AggregateBySource; //设置GE应用基于释放者查看Effect-StackLimitCount 1; //设置叠加层数Effect-StackDurationRefreshPolicy EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; //在应用后重置时重置持续时间Effect-StackPeriodResetPolicy EGameplayEffectStackingPeriodPolicy::ResetOnSuccessfulApplication; //在应用时触发并重置Period时间Effect-StackExpirationPolicy EGameplayEffectStackingExpirationPolicy::ClearEntireStack; //GE时间到了默认清除所有层数还有可以清除单层的设置//Effect-OverflowEffects.Add() //在叠加层数超出时将触发此数组内的GE应用到角色Effect-bDenyOverflowApplication true; //设置为true时叠加层数超出时将不会刷新GE实例Effect-bClearStackOnOverflow true; //设置为true时叠加层数超出时将清除GE在5.3版本修改为了通过GEComponent来设置Actor身上的标签在老版是可以直接通过InheritableOwnedTagsContainer获取容器去修改 //在5.3版本修改为通过GEComponent来设置GE应用的标签向目标Actor增加对应的标签UTargetTagsGameplayEffectComponent TargetTagsGameplayEffectComponent Effect-AddComponentUTargetTagsGameplayEffectComponent();FInheritedTagContainer InheritableOwnedTagsContainer TargetTagsGameplayEffectComponent.GetConfiguredTargetTagChanges(); //获取到标签容器InheritableOwnedTagsContainer.AddTag(DeBuffType); //添加标签TargetTagsGameplayEffectComponent.SetAndApplyTargetTagChanges(InheritableOwnedTagsContainer); //应用并更新接着就是设置属性的修改 //设置属性修改const int32 Index Effect-Modifiers.Num(); //获取当前修改属性的Modifiers的长度也就是下一个添加的modify的下标索引Effect-Modifiers.Add(FGameplayModifierInfo()); //添加一个新的ModifyFGameplayModifierInfo ModifierInfo Effect-Modifiers[Index]; //通过下标索引获取ModifyModifierInfo.ModifierMagnitude FScalableFloat(DeBuffDamage); //设置应用的属性值ModifierInfo.ModifierOp EGameplayModOp::Additive; //设置属性运算符号ModifierInfo.Attribute URPGAttributeSet::GetIncomingDamageAttribute(); //设置修改的属性然后就通过GE创建GE实例并应用到目标身上 //创建GE实例并添加伤害类型标签应用GEFGameplayEffectContextHandle EffectContextHandle Props.SourceASC-MakeEffectContext();EffectContextHandle.AddSourceObject(Props.SourceCharacter);if(const FGameplayEffectSpec* MutableSpec new FGameplayEffectSpec(Effect, EffectContextHandle, 1.f)){FRPGGameplayEffectContext* RPGContext static_castFRPGGameplayEffectContext*(MutableSpec-GetContext().Get());const TSharedPtrFGameplayTag DeBuffDamageType MakeShareable(new FGameplayTag(DeBuffType));RPGContext-SetDeBuffDamageType(DeBuffDamageType);Props.TargetASC-ApplyGameplayEffectSpecToSelf(*MutableSpec);}最后粘贴一下完整代码
void URPGAttributeSet::HandleDeBuff(const FEffectProperties Props)
{//获取负面效果相关参数const FGameplayTag DeBuffType URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(Props.EffectContextHandle);const float DeBuffDamage URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(Props.EffectContextHandle);const float DeBuffDuration URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(Props.EffectContextHandle);const float DeBuffFrequency URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(Props.EffectContextHandle);//创建GE所使用的名称并创建一个可实例化的GEFString DeBuffName FString::Printf(TEXT(DynamicDeBuff_%s), *DeBuffType.ToString());UGameplayEffect* Effect NewObjectUGameplayEffect(GetTransientPackage(), FName(DeBuffName));//设置动态创建GE的属性Effect-DurationPolicy EGameplayEffectDurationType::HasDuration; //设置GE为有时间限制的效果Effect-DurationMagnitude FScalableFloat(DeBuffDuration); //设置GE的持续时间Effect-Period FScalableFloat(DeBuffFrequency); //设置GE的触发策略间隔时间Effect-bExecutePeriodicEffectOnApplication false; //在应用后不会立即触发而是在经过了Period后才会触发Effect-PeriodicInhibitionPolicy EGameplayEffectPeriodInhibitionRemovedPolicy::NeverReset; //设置每次应用后不会重置触发时间//设置可叠加层数Effect-StackingType EGameplayEffectStackingType::AggregateBySource; //设置GE应用基于释放者查看Effect-StackLimitCount 1; //设置叠加层数Effect-StackDurationRefreshPolicy EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; //在应用后重置时重置持续时间Effect-StackPeriodResetPolicy EGameplayEffectStackingPeriodPolicy::ResetOnSuccessfulApplication; //在应用时触发并重置Period时间Effect-StackExpirationPolicy EGameplayEffectStackingExpirationPolicy::ClearEntireStack; //GE时间到了默认清除所有层数还有可以清除单层的设置//Effect-OverflowEffects.Add() //在叠加层数超出时将触发此数组内的GE应用到角色// Effect-bDenyOverflowApplication true; //设置为true时叠加层数超出时将不会刷新GE实例// Effect-bClearStackOnOverflow true; //设置为true时叠加层数超出时将清除GE//在5.3版本修改为通过GEComponent来设置GE应用的标签向目标Actor增加对应的标签UTargetTagsGameplayEffectComponent TargetTagsGameplayEffectComponent Effect-AddComponentUTargetTagsGameplayEffectComponent();FInheritedTagContainer InheritableOwnedTagsContainer TargetTagsGameplayEffectComponent.GetConfiguredTargetTagChanges(); //获取到标签容器InheritableOwnedTagsContainer.AddTag(DeBuffType); //添加标签TargetTagsGameplayEffectComponent.SetAndApplyTargetTagChanges(InheritableOwnedTagsContainer); //应用并更新//设置属性修改const int32 Index Effect-Modifiers.Num(); //获取当前修改属性的Modifiers的长度也就是下一个添加的modify的下标索引Effect-Modifiers.Add(FGameplayModifierInfo()); //添加一个新的ModifyFGameplayModifierInfo ModifierInfo Effect-Modifiers[Index]; //通过下标索引获取ModifyModifierInfo.ModifierMagnitude FScalableFloat(DeBuffDamage); //设置应用的属性值ModifierInfo.ModifierOp EGameplayModOp::Additive; //设置属性运算符号ModifierInfo.Attribute URPGAttributeSet::GetIncomingDamageAttribute(); //设置修改的属性//创建GE实例并添加伤害类型标签应用GEFGameplayEffectContextHandle EffectContextHandle Props.SourceASC-MakeEffectContext();EffectContextHandle.AddSourceObject(Props.SourceCharacter);if(const FGameplayEffectSpec* MutableSpec new FGameplayEffectSpec(Effect, EffectContextHandle, 1.f)){FRPGGameplayEffectContext* RPGContext static_castFRPGGameplayEffectContext*(MutableSpec-GetContext().Get());const TSharedPtrFGameplayTag DeBuffDamageType MakeShareable(new FGameplayTag(DeBuffType));RPGContext-SetDeBuffDamageType(DeBuffDamageType);Props.TargetASC-ApplyGameplayEffectSpecToSelf(*MutableSpec);}}我们就可以去项目中测试是否有bug。