UE5多人MOBA+GAS 番外篇:同时造成多种类型伤害,以各种属性值的百分比来应用伤害(版本二)
文章目录
- 前言
- 添加更多的Tag
- 进行全面采用`GameplayTag`
- 应用伤害处也需要进行调整
- 最后ECC的修改
前言
该版本是基于一版本(版本一没有删掉,因为也是我辛苦的结果有感情,在此之前还有个版本就给删了,最开始的ECC并不太理想),再次结合aura的实践方式来进行修改,但是我个人感觉不是很理想,来点路过的大佬教教
添加更多的Tag
添加如此之多的Tag主要是用来传递需要增加的额外攻击
// 伤害类型// 物理伤害CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(DamageType_AttackDamage)// 魔法伤害CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(DamageType_MagicDamage)// 真实伤害CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(DamageType_TrueDamage)// 属性基础// 最大生命值CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_MaxHealth)// 当前生命值CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_Health)// 最大魔法值CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_MaxMana)// 当前魔法值CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_Mana)// 攻击力CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_AttackPower)// 魔法强度CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_MagicPower)// 物抗CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_Armor)// 魔抗CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_MagicResistance)// 移动速度CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Attribute_MoveSpeed)
// 伤害类型UE_DEFINE_GAMEPLAY_TAG_COMMENT(DamageType_AttackDamage, "DamageType.AttackDamage", "物理伤害")UE_DEFINE_GAMEPLAY_TAG_COMMENT(DamageType_MagicDamage, "DamageType.MagicDamage", "魔法伤害")UE_DEFINE_GAMEPLAY_TAG_COMMENT(DamageType_TrueDamage, "DamageType.TrueDamage", "真实伤害")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_MaxHealth, "Attribute.MaxHealth", "最大生命值")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_Health, "Attribute.Health", "生命值")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_MaxMana, "Attribute.MaxMana", "最大法术值")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_Mana, "Attribute.Mana", "法术值")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_AttackPower, "Attribute.AttackPower", "攻击力")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_MagicPower, "Attribute.MagicPower", "魔法强度")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_Armor, "Attribute.Armor", "护甲")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_MagicResistance, "Attribute.MagicResistance", "魔法抗性")UE_DEFINE_GAMEPLAY_TAG_COMMENT(Attribute_MoveSpeed, "Attribute.MoveSpeed", "移动速度")
进行全面采用GameplayTag
USTRUCT(BlueprintType)
struct FDamageDefinition
{GENERATED_BODY()
public:FDamageDefinition();// 基础伤害UPROPERTY(EditAnywhere)FScalableFloat BaseDamage;// 属性的百分比伤害加成UPROPERTY(EditAnywhere, meta = (Categories = "Attribute"))TMap<FGameplayTag, float> AttributeDamageModifiers;
};
// 伤害效果定义
USTRUCT(BlueprintType)
struct FGenericDamageEffectDef
{GENERATED_BODY()
public:FGenericDamageEffectDef();// 伤害类型UPROPERTY(EditAnywhere)TSubclassOf<UGameplayEffect> DamageEffect;// 伤害类型UPROPERTY(EditAnywhere, meta = (Categories = "DamageType"))TMap<FGameplayTag,FDamageDefinition> DamageTypeDefinitions;// 力的大小UPROPERTY(EditAnywhere)FVector PushVelocity;
};
FDamageDefinition::FDamageDefinition(): BaseDamage{0.f}
{
}FGenericDamageEffectDef::FGenericDamageEffectDef():DamageEffect{nullptr},PushVelocity{0.f}
{
}
应用伤害处也需要进行调整
因为多种伤害的缘故,每个的值又是独立的,再不拆解ECC的情况下,进行多次应用
void UCGameplayAbility::ApplyDamage(AActor* TargetActor,const FGenericDamageEffectDef& Damage, int Level)
{const UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();AActor* AvatarActor = GetAvatarActorFromActorInfo();// 创建效果上下文, 设置能力 、源对象 和 施加者FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext();ContextHandle.SetAbility(this);ContextHandle.AddSourceObject(AvatarActor);ContextHandle.AddInstigator(AvatarActor, AvatarActor);// 传属性,应用伤害for (const auto& TypePair : Damage.DamageTypeDefinitions){// 创建效果Spec句柄,指定效果类、能力等级和上下文FGameplayEffectSpecHandle EffectSpecHandle = ASC->MakeOutgoingSpec(Damage.DamageEffect, Level, ContextHandle);float TotalModifier = TypePair.Value.BaseDamage.GetValueAtLevel(Level);for (const auto& Modifier : TypePair.Value.AttributeDamageModifiers){UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(EffectSpecHandle, Modifier.Key, Modifier.Value);}UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(EffectSpecHandle, TypePair.Key, TotalModifier);// 在目标上应用游戏效果规范ApplyGameplayEffectSpecToTarget(GetCurrentAbilitySpecHandle(),GetCurrentActorInfo(),GetCurrentActivationInfo(),EffectSpecHandle,UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(TargetActor));}
}
最后ECC的修改
在ECC下,将获取的值创建了两个结构体,一个是受击方,一个是攻击方,属性的加成可能会有自己的物抗和魔抗来增加伤害,因此创建了两个结构体来操作
// 幻雨喜欢小猫咪#include "GAS/Executions/ECC_AttackDamage.h"#include "GAS/Core/CAttributeSet.h"
#include "GAS/Core/CHeroAttributeSet.h"
struct FSourceDamageStatics
{// 最大生命值DECLARE_ATTRIBUTE_CAPTUREDEF(MaxHealth);// 当前生命值DECLARE_ATTRIBUTE_CAPTUREDEF(Health);// 最大魔法值DECLARE_ATTRIBUTE_CAPTUREDEF(MaxMana);// 当前魔法值DECLARE_ATTRIBUTE_CAPTUREDEF(Mana);// 攻击力DECLARE_ATTRIBUTE_CAPTUREDEF(AttackPower);// 魔法强度DECLARE_ATTRIBUTE_CAPTUREDEF(MagicPower);// 自己护甲DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);// 自己魔抗DECLARE_ATTRIBUTE_CAPTUREDEF(MagicResistance);// 移动速度DECLARE_ATTRIBUTE_CAPTUREDEF(MoveSpeed);// 护甲穿透DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetration);// 护甲穿透百分比DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetrationPercent);// 法术穿透DECLARE_ATTRIBUTE_CAPTUREDEF(MagicPenetration);// 法术穿透百分比DECLARE_ATTRIBUTE_CAPTUREDEF(MagicPenetrationPercent);// 伤害加深DECLARE_ATTRIBUTE_CAPTUREDEF(DamageAmplification);FSourceDamageStatics(){// 参数:1.属性集 2.属性名 3.目标还是自身 4.是否设置快照(true为创建时获取,false为应用时获取)DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MaxHealth, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, Health, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MaxMana, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, Mana, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, AttackPower, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MagicPower, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, Armor, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MagicResistance, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MoveSpeed, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, ArmorPenetration, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, ArmorPenetrationPercent, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, MagicPenetration, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, MagicPenetrationPercent, Source, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, DamageAmplification, Source, false);}
};// 静态数据访问函数(单例模式)
static FSourceDamageStatics& SourceDamageStatics()
{static FSourceDamageStatics Statics;return Statics;
}
struct FTargetDamageStatics
{// 敌方的物理防御DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);// 敌方的法术抗性DECLARE_ATTRIBUTE_CAPTUREDEF(MagicResistance);// 伤害减免DECLARE_ATTRIBUTE_CAPTUREDEF(DamageReduction);FTargetDamageStatics(){// 参数:1.属性集 2.属性名 3.目标还是自身 4.是否设置快照(true为创建时获取,false为应用时获取)DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, Armor, Target, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MagicResistance, Target, false);DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, DamageReduction, Target, false);}
};// 静态数据访问函数(单例模式)
static FTargetDamageStatics& TargetDamageStatics()
{static FTargetDamageStatics Statics;return Statics;
}UECC_AttackDamage::UECC_AttackDamage()
{// 将属性添加到捕获列表中// 添加源RelevantAttributesToCapture.Add(SourceDamageStatics().MaxHealthDef);RelevantAttributesToCapture.Add(SourceDamageStatics().HealthDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MaxManaDef);RelevantAttributesToCapture.Add(SourceDamageStatics().ManaDef);RelevantAttributesToCapture.Add(SourceDamageStatics().AttackPowerDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MagicPowerDef);RelevantAttributesToCapture.Add(SourceDamageStatics().ArmorDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MagicResistanceDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MoveSpeedDef);RelevantAttributesToCapture.Add(SourceDamageStatics().ArmorPenetrationDef);RelevantAttributesToCapture.Add(SourceDamageStatics().ArmorPenetrationPercentDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MagicPenetrationDef);RelevantAttributesToCapture.Add(SourceDamageStatics().MagicPenetrationPercentDef);RelevantAttributesToCapture.Add(SourceDamageStatics().DamageAmplificationDef);// 添加目标RelevantAttributesToCapture.Add(TargetDamageStatics().ArmorDef);RelevantAttributesToCapture.Add(TargetDamageStatics().MagicResistanceDef);RelevantAttributesToCapture.Add(TargetDamageStatics().DamageReductionDef);
}void UECC_AttackDamage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams,FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{//存储标签和属性快照对应的MapTMap<FGameplayTag, FGameplayEffectAttributeCaptureDefinition> TagsToCaptureDefs;// TODO:添加新的需要修改的属性值,添加新的标签和值//添加标签和属性快照对应的数据TagsToCaptureDefs.Add(TGameplayTags::Attribute_MaxHealth, SourceDamageStatics().MaxHealthDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_Health, SourceDamageStatics().HealthDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_MaxMana, SourceDamageStatics().MaxManaDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_Mana, SourceDamageStatics().ManaDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_AttackPower, SourceDamageStatics().AttackPowerDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_MagicPower, SourceDamageStatics().MagicPowerDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_Armor, SourceDamageStatics().ArmorDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_MagicResistance, SourceDamageStatics().MagicResistanceDef);TagsToCaptureDefs.Add(TGameplayTags::Attribute_MoveSpeed, SourceDamageStatics().MoveSpeedDef);// 获取游戏效果规范和上下文const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();FGameplayEffectContextHandle EffectContextHandle = Spec.GetContext();// 获取来源和目标标签const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();// 初始化评估参数FAggregatorEvaluateParameters EvaluateParameters;EvaluateParameters.SourceTags = SourceTags;EvaluateParameters.TargetTags = TargetTags;float DamageAdd = 0.0f;for (auto& TagToCaptureDef : TagsToCaptureDefs){const float Coefficient = Spec.GetSetByCallerMagnitude(TagToCaptureDef.Key, false, -1);if (Coefficient <= 0.0f) continue;float AttributeValue = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(TagToCaptureDef.Value, EvaluateParameters, AttributeValue);DamageAdd += AttributeValue * Coefficient / 100.0f;}// 获取伤害加深float DamageAmp = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(SourceDamageStatics().DamageAmplificationDef, EvaluateParameters, DamageAmp);// 获取敌方的伤害减免float DamageReduction = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(TargetDamageStatics().DamageReductionDef, EvaluateParameters, DamageReduction);// 获取基础攻击伤害float BaseAttackDamage = Spec.GetSetByCallerMagnitude(TGameplayTags::DamageType_AttackDamage, false, -1);// 物理伤害的处理if (BaseAttackDamage > 0.0f){BaseAttackDamage += DamageAdd;// 获取护甲穿透百分比float ArmorPenetrationPercent = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(SourceDamageStatics().ArmorPenetrationPercentDef, EvaluateParameters, ArmorPenetrationPercent);// 获取护甲穿透float ArmorPenetration = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(SourceDamageStatics().ArmorPenetrationDef, EvaluateParameters, ArmorPenetration);// 获取目标护甲float TargetArmor = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(TargetDamageStatics().ArmorDef, EvaluateParameters, TargetArmor);// 1. 处理固定护甲穿透TargetArmor = FMath::Max(0.0f, TargetArmor - ArmorPenetration);// 2. 处理百分比护甲穿透TargetArmor = FMath::Max(0.0f, TargetArmor * (1.0f - FMath::Min(ArmorPenetrationPercent, 100.0f) / 100.0f));// 3. 计算护甲减免(计算出来的是免伤率)float ArmorReduction = TargetArmor / (TargetArmor + 100.0f);BaseAttackDamage *= (1.0f - FMath::Min(ArmorReduction / 100.0f + DamageReduction/100.0f, 1.0f));// 4. 应用伤害加深(百分比提升)BaseAttackDamage *= (1.0f + DamageAmp / 100.0f);// 5. 输出到AttackDamage属性if (BaseAttackDamage > 0.0f){// 添加输出修饰符OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UCAttributeSet::GetAttackDamageAttribute(), //获取到伤害属性EGameplayModOp::Override, BaseAttackDamage //伤害));}}// 计算基础法术伤害值float BaseMagicDamage = Spec.GetSetByCallerMagnitude(TGameplayTags::DamageType_MagicDamage, false, -1.f);if (BaseMagicDamage > 0){BaseMagicDamage += DamageAdd;// 获取法术穿透百分比float MagicPenetrationPercent = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(SourceDamageStatics().MagicPenetrationPercentDef,EvaluateParameters, MagicPenetrationPercent);// 获取法术穿透float MagicPenetration = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(SourceDamageStatics().MagicPenetrationDef,EvaluateParameters, MagicPenetration);// 获取目标法抗float TargetMagicResistance = 0.0f;ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(TargetDamageStatics().MagicResistanceDef, EvaluateParameters, TargetMagicResistance);// 1. 处理固定法术穿透TargetMagicResistance = FMath::Max(0.0f, TargetMagicResistance - MagicPenetration);// 2. 处理百分比法术穿透TargetMagicResistance = FMath::Max(0.0f, TargetMagicResistance * (1.0f - FMath::Min(MagicPenetrationPercent, 100.0f) / 100.0f));// 3. 计算法抗减免(计算出来的是免伤率)float MagicResistanceReduction = TargetMagicResistance / (TargetMagicResistance + 100.0f);BaseMagicDamage *= (1.0f - FMath::Min(MagicResistanceReduction / 100.0f + DamageReduction/100.0f, 1.0f));// 4. 应用伤害加深(百分比提升)BaseMagicDamage *= (1.0f + DamageAmp / 100.0f);OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UCAttributeSet::GetMagicDamageAttribute(), //获取到伤害属性EGameplayModOp::Override, BaseMagicDamage //伤害));}// 计算基础真实伤害值float BaseTrueDamage = Spec.GetSetByCallerMagnitude(TGameplayTags::DamageType_TrueDamage, false, -1.f);if (BaseTrueDamage > 0.0f){BaseTrueDamage += DamageAdd;// 计算伤害减免BaseTrueDamage *= (1.0f - FMath::Min(DamageReduction/100.0f, 1.0f));// 应用伤害加深(百分比提升)BaseTrueDamage *= (1.0f + DamageAmp / 100.0f);OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UCAttributeSet::GetTrueDamageAttribute(), //获取到伤害属性EGameplayModOp::Override, BaseTrueDamage //伤害));}
}
伤害的获取方面依然是在属性中获取
void UCAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{FEffectProperties Props;SetEffectProperties(Data, Props);if (Data.EvaluatedData.Attribute == GetHealthAttribute()){SetHealth(FMath::Clamp(GetHealth(), 0, GetMaxHealth()));SetCachedHealthPercent(GetHealth()/GetMaxHealth());}if (Data.EvaluatedData.Attribute == GetManaAttribute()){SetMana(FMath::Clamp(GetMana(), 0, GetMaxMana()));SetCachedManaPercent(GetMana()/GetMaxMana());}// 物理伤害if (Data.EvaluatedData.Attribute == GetAttackDamageAttribute()){float NewDamage = GetAttackDamage();SetAttackDamage(0.f);if (NewDamage > 0.f){UE_LOG(LogTemp, Warning, TEXT("物理: %f"), NewDamage)Damage(Props, TGameplayTags::DamageType_AttackDamage, NewDamage);}}// 魔法伤害if (Data.EvaluatedData.Attribute == GetMagicDamageAttribute()){float NewDamage = GetMagicDamage();SetMagicDamage(0.f);if (NewDamage > 0.f){UE_LOG(LogTemp, Warning, TEXT("魔法伤害: %f"), NewDamage)Damage(Props,TGameplayTags::DamageType_MagicDamage, NewDamage);}}// 真实伤害if (Data.EvaluatedData.Attribute == GetTrueDamageAttribute()){float NewDamage = GetTrueDamage();SetTrueDamage(0.f);if (NewDamage > 0.f){UE_LOG(LogTemp, Warning, TEXT("真实伤害: %f"), NewDamage)Damage(Props,TGameplayTags::DamageType_TrueDamage, NewDamage);}}
}
创建一个GE放入ECC
回顾一下这个是ECC,伤害的计算都在这里Execute_Implementation
#pragma once#include "CoreMinimal.h"
#include "GameplayEffectExecutionCalculation.h"
#include "ECC_AttackDamage.generated.h"/*** */
UCLASS()
class UECC_AttackDamage : public UGameplayEffectExecutionCalculation
{GENERATED_BODY()
public:UECC_AttackDamage();virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;
};
如果有更好的方案,请允许让我copy一下嘻嘻嘻