重构伤害类型
CGameplayAbilityTypes
中重构伤害类型
UENUM(BlueprintType)
enum class EDamageType : uint8
{
PhysicalDamage UMETA(DisplayName="物理伤害"), // 物理伤害
MagicDamage UMETA(DisplayName="魔法伤害"), // 魔法伤害
TrueDamage UMETA(DisplayName="真实伤害"), // 真实伤害
};
//
USTRUCT(BlueprintType)
struct FDamageDefinition
{
GENERATED_BODY()
public:
FDamageDefinition();
// 基础伤害
UPROPERTY(EditAnywhere)
FScalableFloat BaseDamage;
// 属性的百分比伤害加成
UPROPERTY(EditAnywhere)
TMap<FGameplayAttribute, float> AttributeDamageModifiers;
};
// 伤害效果定义
USTRUCT(BlueprintType)
struct FGenericDamageEffectDef
{
GENERATED_BODY()
public:
FGenericDamageEffectDef();
// 伤害类型
UPROPERTY(EditAnywhere)
TSubclassOf<UGameplayEffect> DamageEffect;
// 伤害类型
UPROPERTY(EditAnywhere)
TMap<EDamageType,FDamageDefinition> DamageTypeDefinitions;
// 力的大小
UPROPERTY(EditAnywhere)
FVector PushVelocity;
};
FDamageDefinition::FDamageDefinition()
: BaseDamage{0.f}
{
}
FGenericDamageEffectDef::FGenericDamageEffectDef()
:DamageEffect{nullptr},
PushVelocity{0.f}
{
}
重构伤害应用
修改一下CGameplayAbility
中的伤害配置
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);
// 配置伤害,这个也可以拆开来
MakeDamage(Damage, Level);
// 创建效果Spec句柄,指定效果类、能力等级和上下文
FGameplayEffectSpecHandle EffectSpecHandle = ASC->MakeOutgoingSpec(Damage.DamageEffect, Level, ContextHandle);
// 在目标上应用游戏效果规范
ApplyGameplayEffectSpecToTarget(GetCurrentAbilitySpecHandle(),
GetCurrentActorInfo(),
GetCurrentActivationInfo(),
EffectSpecHandle,
UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(TargetActor));
}
void UCGameplayAbility::MakeDamage(const FGenericDamageEffectDef& Damage, int Level)
{
// 通通置为0
float BaseAttackDamage = 0.f;
float BaseMagicDamage = 0.f;
float BaseTrueDamage = 0.f;
for (const auto& TypePair : Damage.DamageTypeDefinitions)
{
float TotalModifier = TypePair.Value.BaseDamage.GetValueAtLevel(Level);
for (const auto& Modifier : TypePair.Value.AttributeDamageModifiers)
{
bool bFound ;
float AttributeValue = GetAbilitySystemComponentFromActorInfo()->GetGameplayAttributeValue(Modifier.Key, bFound);
if (bFound)
{
TotalModifier += AttributeValue * Modifier.Value / 100.0f;
}
}
switch (TypePair.Key)
{
case EDamageType::PhysicalDamage :
BaseAttackDamage = TotalModifier;
break;
case EDamageType::MagicDamage :
BaseMagicDamage = TotalModifier;
break;
case EDamageType::TrueDamage :
BaseTrueDamage = TotalModifier;
break;
default:
break;
}
}
GetAbilitySystemComponentFromActorInfo()->ApplyModToAttribute(UCAttributeSet::GetBaseAttackDamageAttribute(), EGameplayModOp::Override, BaseAttackDamage);
GetAbilitySystemComponentFromActorInfo()->ApplyModToAttribute(UCAttributeSet::GetBaseMagicDamageAttribute(), EGameplayModOp::Override, BaseMagicDamage);
GetAbilitySystemComponentFromActorInfo()->ApplyModToAttribute(UCAttributeSet::GetBaseTrueDamageAttribute(), EGameplayModOp::Override, BaseTrueDamage);
}
添加新的属性以及接收属性的修改
修改一下属性CAttributeSet
,为其添加一些新的东西
USTRUCT()
struct FEffectProperties
{
GENERATED_BODY()
FEffectProperties(){}
UPROPERTY()
FGameplayEffectContextHandle EffectContextHandle;
UPROPERTY()
UAbilitySystemComponent* SourceASC = nullptr;
UPROPERTY()
AActor* SourceAvatarActor = nullptr;
UPROPERTY()
AController* SourceController = nullptr;
UPROPERTY()
ACharacter* SourceCharacter = nullptr;
UPROPERTY()
UAbilitySystemComponent* TargetASC = nullptr;
UPROPERTY()
AActor* TargetAvatarActor = nullptr;
UPROPERTY()
AController* TargetController = nullptr;
UPROPERTY()
ACharacter* TargetCharacter = nullptr;
};
// 传值物理的基础伤害
UPROPERTY(ReplicatedUsing = OnRep_BaseAttackDamage)
FGameplayAttributeData BaseAttackDamage;
ATTRIBUTE_ACCESSORS(UCAttributeSet, BaseAttackDamage)
// 魔法的基础伤害
UPROPERTY(ReplicatedUsing = OnRep_BaseMagicDamage)
FGameplayAttributeData BaseMagicDamage;
ATTRIBUTE_ACCESSORS(UCAttributeSet, BaseMagicDamage)
// 真伤的基础伤害
UPROPERTY(ReplicatedUsing = OnRep_BaseTrueDamage)
FGameplayAttributeData BaseTrueDamage;
ATTRIBUTE_ACCESSORS(UCAttributeSet, BaseTrueDamage)
UFUNCTION()
void OnRep_BaseAttackDamage(const FGameplayAttributeData& OldBaseAttackDamage);
UFUNCTION()
void OnRep_BaseMagicDamage(const FGameplayAttributeData& OldBaseMagicDamage);
UFUNCTION()
void OnRep_BaseTrueDamage(const FGameplayAttributeData& OldBaseTrueDamage);
// 设置效果属性
void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const;
// 伤害处理函数
void Damage(const FEffectProperties& Props, EDamageType Type, const float Damage);
网络复制中添加一下新的变量
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)
{
Damage(Props, EDamageType::PhysicalDamage, 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,EDamageType::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,EDamageType::TrueDamage, NewDamage);
}
}
}
void UCAttributeSet::OnRep_BaseAttackDamage(const FGameplayAttributeData& OldBaseAttackDamage)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, BaseAttackDamage, OldBaseAttackDamage);
}
void UCAttributeSet::OnRep_BaseMagicDamage(const FGameplayAttributeData& OldBaseMagicDamage)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, BaseMagicDamage, OldBaseMagicDamage);
}
void UCAttributeSet::OnRep_BaseTrueDamage(const FGameplayAttributeData& OldBaseTrueDamage)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, BaseTrueDamage, OldBaseTrueDamage);
}
void UCAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const
{
//Source 效果的所有者 Target 效果应用的目标
Props.EffectContextHandle = Data.EffectSpec.GetContext();
Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();
//获取效果所有者的相关对象
if (IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
{
Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get();
Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get();
if (Props.SourceAvatarActor != nullptr && Props.SourceController == nullptr)
{
if (const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
{
Props.SourceController = Pawn->Controller;
}
}
if (Props.SourceController)
{
Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
}
}
if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
{
Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
}
}
void UCAttributeSet::Damage(const FEffectProperties& Props, EDamageType Type, const float Damage)
{
bool bCriticalHit = false;
float NewDamage = Damage;
if (Props.SourceASC)
{
bool bFound = false;
const float EffectiveCriticalHitChance = Props.SourceASC->GetGameplayAttributeValue(UCHeroAttributeSet::GetCriticalStrikeChanceAttribute(), bFound);
if (bFound)
{
bFound = false;
bCriticalHit = FMath::RandRange(1, 100) < EffectiveCriticalHitChance;
if (bCriticalHit)
{
const float CriticalStrikeDamage = Props.SourceASC->GetGameplayAttributeValue(UCHeroAttributeSet::GetCriticalStrikeDamageAttribute(), bFound);
if (bFound)
{
NewDamage *= (1.f + CriticalStrikeDamage / 100.f);
// UE_LOG(LogTemp, Warning, TEXT("暴击"))
}
}
}
}
const float NewHealth = GetHealth() - NewDamage;
SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth()));
// UE_LOG(LogTemp, Log, TEXT("NewDamage: %f"), NewDamage)
// 如果生命小于等于0触发死亡
if (NewHealth <= 0.f)
{
// 触发死亡被动
OnDeadAbility(Props);
}
ShowFloatingText(Props,NewDamage, bCriticalHit, Type);
}
我还修改了一下死亡的传递值以及伤害的发送,为此,游戏控制器也要修改
static void ShowFloatingText(const FEffectProperties& Props, float Damage, bool IsCriticalHitE, EDamageType Type);
// 用于激活角色死亡被动的函数
void OnDeadAbility(const FEffectProperties& Props);
void UCAttributeSet::ShowFloatingText(const FEffectProperties& Props, const float Damage, bool IsCriticalHit, EDamageType Type)
{
// 从技能释放者身上获取PC并显示伤害数字
if(ACPlayerController* PC = Cast<ACPlayerController>(Props.SourceCharacter->Controller))
{
PC->ShowDamageNumber(Damage, Props.TargetCharacter, IsCriticalHit, Type); //调用显示伤害数字
}
// 从目标身上获取PC并显示伤害数字
if(ACPlayerController* PC = Cast<ACPlayerController>(Props.TargetCharacter->Controller))
{
PC->ShowDamageNumber(Damage, Props.TargetCharacter, IsCriticalHit, Type); //调用显示伤害数字
}
}
void UCAttributeSet::OnDeadAbility(const FEffectProperties& Props)
{
FGameplayEventData DeadAbilityEventData;
if (Props.SourceAvatarActor)
{
// UE_LOG(LogTemp, Warning, TEXT("Dead:%s"), *GetOwningActor()->GetName())
DeadAbilityEventData.Target = Props.SourceAvatarActor;
}
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwningActor(),
TGameplayTags::Stats_Dead,
DeadAbilityEventData);
}
到游戏控制器CPlayerController
中修改一下输出数字的函数
UFUNCTION(Client, Reliable)
void ShowDamageNumber(float DamageAmount, ACharacter* TargetCharacter, bool bCriticalHit, EDamageType Type);
void ACPlayerController::ShowDamageNumber_Implementation(float DamageAmount, ACharacter* TargetCharacter, bool bCriticalHit, EDamageType Type)
{
if (!IsValid(TargetCharacter) || !NumberPopComponentClass || !IsLocalController())
return;
// 获取目标Actor上的现有组件
UNumberPopComponent_NiagaraText* DamageText = TargetCharacter->GetComponentByClass<UNumberPopComponent_NiagaraText>();
// 不存在则创建并附加
if (!DamageText)
{
DamageText = NewObject<UNumberPopComponent_NiagaraText>(TargetCharacter, NumberPopComponentClass);
if (!DamageText)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UE_LOG(LogTemp, Error, TEXT("Niagara组件创建失败,内存不足或配置错误"));
#endif
return;
}
DamageText->RegisterComponent(); // 注册组件
}
// TODO:添加各种伤害类型的伤害数字特效
// 设置显示参数
FNumberPopRequest NumberPopRequest;
NumberPopRequest.WorldLocation = TargetCharacter->GetActorLocation() + FVector(0, 0, 200);
NumberPopRequest.bIsCriticalDamage = bCriticalHit;
NumberPopRequest.NumberToDisplay = DamageAmount;
DamageText->AddNumberPop(NumberPopRequest);
}
修改ECC让其适用于多种伤害类型
ECC_AttackDamage
中进行一些伤害计算的调整
// 幻雨喜欢小猫咪
#include "GAS/Executions/ECC_AttackDamage.h"
#include "GAS/Core/CAttributeSet.h"
#include "GAS/Core/CHeroAttributeSet.h"
struct FDamageStatics
{
// FGameplayEffectAttributeCaptureDefinition
// 物理基础伤害
DECLARE_ATTRIBUTE_CAPTUREDEF(BaseAttackDamage);
// 魔法基础伤害
DECLARE_ATTRIBUTE_CAPTUREDEF(BaseMagicDamage);
// 真实基础伤害
DECLARE_ATTRIBUTE_CAPTUREDEF(BaseTrueDamage);
// 护甲穿透
DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetration);
// 护甲穿透百分比
DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetrationPercent);
// 法术穿透
DECLARE_ATTRIBUTE_CAPTUREDEF(MagicPenetration);
// 法术穿透百分比
DECLARE_ATTRIBUTE_CAPTUREDEF(MagicPenetrationPercent);
// 伤害加深
DECLARE_ATTRIBUTE_CAPTUREDEF(DamageAmplification);
// 敌方的物理防御
DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);
// 敌方的法术抗性
DECLARE_ATTRIBUTE_CAPTUREDEF(MagicResistance);
// 伤害减免
DECLARE_ATTRIBUTE_CAPTUREDEF(DamageReduction);
FDamageStatics()
{
// 参数:1.属性集 2.属性名 3.目标还是自身 4.是否设置快照(true为创建时获取,false为应用时获取)
DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, BaseAttackDamage, Source, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, BaseMagicDamage, Source, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, BaseTrueDamage, 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);
DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, Armor, Target, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UCAttributeSet, MagicResistance, Target, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UCHeroAttributeSet, DamageReduction, Target, false);
}
};
// 静态数据访问函数(单例模式)
static FDamageStatics& DamageStatics()
{
static FDamageStatics Statics;
return Statics;
}
UECC_AttackDamage::UECC_AttackDamage()
{
// 将属性添加到捕获列表中
RelevantAttributesToCapture.Add(DamageStatics().BaseAttackDamageDef);
RelevantAttributesToCapture.Add(DamageStatics().BaseMagicDamageDef);
RelevantAttributesToCapture.Add(DamageStatics().BaseTrueDamageDef);
RelevantAttributesToCapture.Add(DamageStatics().ArmorPenetrationDef);
RelevantAttributesToCapture.Add(DamageStatics().ArmorPenetrationPercentDef);
RelevantAttributesToCapture.Add(DamageStatics().MagicPenetrationDef);
RelevantAttributesToCapture.Add(DamageStatics().MagicPenetrationPercentDef);
RelevantAttributesToCapture.Add(DamageStatics().DamageAmplificationDef);
RelevantAttributesToCapture.Add(DamageStatics().ArmorDef);
RelevantAttributesToCapture.Add(DamageStatics().MagicResistanceDef);
RelevantAttributesToCapture.Add(DamageStatics().DamageReductionDef);
}
void UECC_AttackDamage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams,
FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
// 获取游戏效果规范和上下文
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 DamageAmp = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().DamageAmplificationDef, EvaluateParameters, DamageAmp);
// 获取敌方的伤害减免
float DamageReduction = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().DamageReductionDef, EvaluateParameters, DamageReduction);
// 计算基础物理伤害值
float BaseAttackDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().BaseAttackDamageDef,
EvaluateParameters,
BaseAttackDamage
);
// 物理伤害的处理
if (BaseAttackDamage > 0.0f)
{
// 获取护甲穿透百分比
float ArmorPenetrationPercent = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorPenetrationPercentDef, EvaluateParameters, ArmorPenetrationPercent);
// 获取护甲穿透
float ArmorPenetration = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorPenetrationDef, EvaluateParameters, ArmorPenetration);
// 获取目标护甲
float TargetArmor = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().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 = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().BaseMagicDamageDef,
EvaluateParameters,
BaseMagicDamage
);
if (BaseMagicDamage > 0)
{
// 获取法术穿透百分比
float MagicPenetrationPercent = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().MagicPenetrationPercentDef,
EvaluateParameters, MagicPenetrationPercent);
// 获取法术穿透
float MagicPenetration = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().MagicPenetrationDef,
EvaluateParameters, MagicPenetration);
// 获取目标法抗
float TargetMagicResistance = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().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 = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
DamageStatics().BaseTrueDamageDef,
EvaluateParameters,
BaseTrueDamage
);
if (BaseTrueDamage > 0.0f)
{
// 计算伤害减免
BaseTrueDamage *= (1.0f - FMath::Min(DamageReduction/100.0f, 1.0f));
// 应用伤害加深(百分比提升)
BaseTrueDamage *= (1.0f + DamageAmp / 100.0f);
OutExecutionOutput.AddOutputModifier(
FGameplayModifierEvaluatedData(
UCAttributeSet::GetTrueDamageAttribute(), //获取到伤害属性
EGameplayModOp::Override,
BaseTrueDamage //伤害
));
}
}
伤害的配置
伤害多个好像会被夺舍
或许多弄几个特效组件UNiagaraComponent
就好了,一种伤害一个组件
我试着操作了一手(不建议这么搞,因为我感觉很抽象)
// 幻雨喜欢小猫咪
#pragma once
#include "CoreMinimal.h"
#include "Components/ControllerComponent.h"
#include "GAS/Core/CGameplayAbilityTypes.h"
#include "NumberPopComponent_NiagaraText.generated.h"
// 定义一个结构体,表示一个数字弹出请求的数据
USTRUCT(BlueprintType)
struct FNumberPopRequest
{
GENERATED_BODY()
// 弹出数字的位置(世界坐标)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lyra|Number Pops")
FVector WorldLocation;
// 要显示的数字
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lyra|Number Pops")
int32 NumberToDisplay = 0;
// 是否是“致命”伤害(@TODO: 应该使用标签来代替)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lyra|Number Pops")
bool bIsCriticalDamage = false;
// 构造函数,初始化默认值
FNumberPopRequest()
: WorldLocation(ForceInitToZero)
{
}
};
class UNiagaraSystem;
class UNiagaraComponent;
/**
*
*/
UCLASS(Blueprintable)
class UNumberPopComponent_NiagaraText : public UControllerComponent
{
GENERATED_BODY()
public:
UNumberPopComponent_NiagaraText(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/**
* 添加一个数字弹出到列表中以进行可视化展示
* @param NewRequest 新的数字弹出请求数据
*/
UFUNCTION(BlueprintCallable, Category = Foo)
void AddNumberPop(const FNumberPopRequest& NewRequest, EDamageType Type);
void AddAttackNumber(const FNumberPopRequest& NewRequest);
void AddMagicNumber(const FNumberPopRequest& NewRequest);
void AddTrueNumber(const FNumberPopRequest& NewRequest);
UPROPERTY(EditDefaultsOnly, Category="DamagePop")
FName NiagaraArrayName;
UPROPERTY(EditDefaultsOnly, Category="DamagePop")
TObjectPtr<UNiagaraSystem> TextNiagara;
protected:
TArray<int32> DamageNumberArray;
UPROPERTY(EditDefaultsOnly, Category = "Number Pop|Style")
TObjectPtr<UNiagaraComponent> NiagaraComp;
UPROPERTY(EditDefaultsOnly, Category = "Number Pop|Style")
TObjectPtr<UNiagaraComponent> MagicNiagaraComp;
UPROPERTY(EditDefaultsOnly, Category = "Number Pop|Style")
TObjectPtr<UNiagaraComponent> TrueNiagaraComp;
};
// 幻雨喜欢小猫咪
#include "Player/NumberPopComponent_NiagaraText.h"
#include "NiagaraComponent.h"
#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
UNumberPopComponent_NiagaraText::UNumberPopComponent_NiagaraText(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SetIsReplicatedByDefault(true);
// bReplicates = true;
}
void UNumberPopComponent_NiagaraText::AddNumberPop(const FNumberPopRequest& NewRequest, EDamageType Type)
{
switch (Type)
{
case EDamageType::PhysicalDamage:
{
AddAttackNumber(NewRequest);
break;
}
case EDamageType::MagicDamage:
{
AddMagicNumber(NewRequest);
break;
}
case EDamageType::TrueDamage:
{
AddTrueNumber(NewRequest);
break;
}
default:
{
break;
}
}
}
void UNumberPopComponent_NiagaraText::AddAttackNumber(const FNumberPopRequest& NewRequest)
{
int32 LocalDamage = NewRequest.NumberToDisplay;
//Change Damage to negative to differentiate Critial vs Normal hit
// 如果是致命伤害,则将数值设为负数以区分普通伤害
if (NewRequest.bIsCriticalDamage)
{
LocalDamage *= -1;
}
// 如果没有 Niagara 组件,则创建一个
if (!NiagaraComp)
{
NiagaraComp = NewObject<UNiagaraComponent>(GetOwner());
if (TextNiagara != nullptr)
{
NiagaraComp->SetAsset(TextNiagara); // 设置 Niagara 资源
NiagaraComp->bAutoActivate = false; // 不自动激活
}
NiagaraComp->SetupAttachment(nullptr); // 不附加到任何物体
check(NiagaraComp);
NiagaraComp->RegisterComponent(); // 注册组件以便更新和渲染
// NiagaraComp->SetReplicate(true);
}
NiagaraComp->Activate(false); // 手动激活 Niagara 粒子效果
NiagaraComp->SetWorldLocation(NewRequest.WorldLocation); // 设置弹出位置
UE_LOG(LogTemp, Log, TEXT("DamageHit location : %s"), *(NewRequest.WorldLocation.ToString()));
//Add Damage information to the current Niagara list - Damage informations are packed inside a FVector4 where XYZ = Position, W = Damage
// 获取 Niagara 数组中的 FVector4 列表(XYZ 表示位置,W 表示伤害值)
TArray<FVector4> DamageList = UNiagaraDataInterfaceArrayFunctionLibrary::GetNiagaraArrayVector4(NiagaraComp, NiagaraArrayName);
// 添加新伤害信息到数组中
DamageList.Add(FVector4(
NewRequest.WorldLocation.X,
NewRequest.WorldLocation.Y,
NewRequest.WorldLocation.Z,
LocalDamage));
// 将更新后的数组写回 Niagara 组件UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(NiagaraComp, NiagaraArrayName, DamageList);
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(NiagaraComp, NiagaraArrayName, DamageList);
}
void UNumberPopComponent_NiagaraText::AddMagicNumber(const FNumberPopRequest& NewRequest)
{
int32 LocalDamage = NewRequest.NumberToDisplay;
//Change Damage to negative to differentiate Critial vs Normal hit
// 如果是致命伤害,则将数值设为负数以区分普通伤害
if (NewRequest.bIsCriticalDamage)
{
LocalDamage *= -1;
}
// 如果没有 Niagara 组件,则创建一个
if (!MagicNiagaraComp)
{
MagicNiagaraComp = NewObject<UNiagaraComponent>(GetOwner());
if (TextNiagara != nullptr)
{
MagicNiagaraComp->SetAsset(TextNiagara); // 设置 Niagara 资源
MagicNiagaraComp->bAutoActivate = false; // 不自动激活
}
MagicNiagaraComp->SetupAttachment(nullptr); // 不附加到任何物体
check(MagicNiagaraComp);
MagicNiagaraComp->RegisterComponent(); // 注册组件以便更新和渲染
// NiagaraComp->SetReplicate(true);
}
MagicNiagaraComp->Activate(false); // 手动激活 Niagara 粒子效果
MagicNiagaraComp->SetWorldLocation(NewRequest.WorldLocation); // 设置弹出位置
UE_LOG(LogTemp, Log, TEXT("DamageHit location : %s"), *(NewRequest.WorldLocation.ToString()));
//Add Damage information to the current Niagara list - Damage informations are packed inside a FVector4 where XYZ = Position, W = Damage
// 获取 Niagara 数组中的 FVector4 列表(XYZ 表示位置,W 表示伤害值)
TArray<FVector4> DamageList = UNiagaraDataInterfaceArrayFunctionLibrary::GetNiagaraArrayVector4(MagicNiagaraComp, NiagaraArrayName);
// 添加新伤害信息到数组中
DamageList.Add(FVector4(
NewRequest.WorldLocation.X,
NewRequest.WorldLocation.Y,
NewRequest.WorldLocation.Z,
LocalDamage));
// 将更新后的数组写回 Niagara 组件UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(NiagaraComp, NiagaraArrayName, DamageList);
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(MagicNiagaraComp, NiagaraArrayName, DamageList);
}
void UNumberPopComponent_NiagaraText::AddTrueNumber(const FNumberPopRequest& NewRequest)
{
int32 LocalDamage = NewRequest.NumberToDisplay;
// 如果是致命伤害,则将数值设为负数以区分普通伤害
if (NewRequest.bIsCriticalDamage)
{
LocalDamage *= -1;
}
// 如果没有 Niagara 组件,则创建一个
if (!TrueNiagaraComp)
{
TrueNiagaraComp = NewObject<UNiagaraComponent>(GetOwner());
if (TextNiagara != nullptr)
{
TrueNiagaraComp->SetAsset(TextNiagara); // 设置 Niagara 资源
TrueNiagaraComp->bAutoActivate = false; // 不自动激活
}
TrueNiagaraComp->SetupAttachment(nullptr); // 不附加到任何物体
check(TrueNiagaraComp);
TrueNiagaraComp->RegisterComponent(); // 注册组件以便更新和渲染
}
TrueNiagaraComp->Activate(false); // 手动激活 Niagara 粒子效果
TrueNiagaraComp->SetWorldLocation(NewRequest.WorldLocation); // 设置弹出位置
UE_LOG(LogTemp, Log, TEXT("DamageHit location : %s"), *(NewRequest.WorldLocation.ToString()));
//Add Damage information to the current Niagara list - Damage informations are packed inside a FVector4 where XYZ = Position, W = Damage
// 获取 Niagara 数组中的 FVector4 列表(XYZ 表示位置,W 表示伤害值)
TArray<FVector4> DamageList = UNiagaraDataInterfaceArrayFunctionLibrary::GetNiagaraArrayVector4(TrueNiagaraComp, NiagaraArrayName);
// 添加新伤害信息到数组中
DamageList.Add(FVector4(
NewRequest.WorldLocation.X,
NewRequest.WorldLocation.Y,
NewRequest.WorldLocation.Z,
LocalDamage));
// 将更新后的数组写回 Niagara 组件UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(NiagaraComp, NiagaraArrayName, DamageList);
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector4(TrueNiagaraComp, NiagaraArrayName, DamageList);
}
对于这几个添加数字的特效的函数也可以添加类型,具体操作我也不会特效,就将就这么放着