文章目录
前言
该版本是基于一版本(版本一没有删掉,因为也是我辛苦的结果有感情,在此之前还有个版本就给删了,最开始的ECC并不太理想),再次结合aura的实践方式来进行修改,但是我个人感觉不是很理想,来点路过的大佬教教
添加更多的Tag
添加如此之多的Tag主要是用来传递需要增加的额外攻击
cpp
// 伤害类型
// 物理伤害
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)
cpp
// 伤害类型
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
cpp
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;
};
cpp
FDamageDefinition::FDamageDefinition()
: BaseDamage{0.f}
{
}
FGenericDamageEffectDef::FGenericDamageEffectDef()
:DamageEffect{nullptr},
PushVelocity{0.f}
{
}
应用伤害处也需要进行调整
因为多种伤害的缘故,每个的值又是独立的,再不拆解ECC的情况下,进行多次应用
cpp
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下,将获取的值创建了两个结构体,一个是受击方,一个是攻击方,属性的加成可能会有自己的物抗和魔抗来增加伤害,因此创建了两个结构体来操作
cpp
// 幻雨喜欢小猫咪
#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
{
//存储标签和属性快照对应的Map
TMap<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 //伤害
));
}
}
伤害的获取方面依然是在属性中获取
cpp
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
cpp
#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一下嘻嘻嘻