文章目录
Lyra里面的样子和需要的东西
学习lyra的伤害奶瓜,奶瓜有关的组件都在这里
迁移一下奶瓜的资产
Lyra中GC的一些详细样子
其中他的暴击是通过tag的判断的
开始编写需要的组件内容
直接搜索这个类会发现没有
需要开启ModularGamePlay
插件才可以
一搜就出来了
直接一步到位,因为那个Mesh的lyra里面并没有实现,没得抄,索性直接NumberPopComponent_NiagaraText
在cs文件中添加三个模块
在父类中最主要的核心就是这个结构体
但是在接收方有两个对这个数字奶瓜来说是没用的
cpp
// 幻雨喜欢小猫咪
#pragma once
#include "CoreMinimal.h"
#include "Components/ControllerComponent.h"
#include "NumberPopComponent_NiagaraText.generated.h"
// 定义一个结构体,表示一个数字弹出请求的数据
USTRUCT(BlueprintType)
struct FLyraNumberPopRequest
{
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;
// 构造函数,初始化默认值
FLyraNumberPopRequest()
: WorldLocation(ForceInitToZero)
{
}
};
class UNiagaraSystem;
class UNiagaraComponent;
/**
*
*/
UCLASS()
class UNumberPopComponent_NiagaraText : public UControllerComponent
{
GENERATED_BODY()
public:
UNumberPopComponent_NiagaraText(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/**
* 添加一个数字弹出到列表中以进行可视化展示
* @param NewRequest 新的数字弹出请求数据
*/
UFUNCTION(BlueprintCallable, Category = Foo)
void AddNumberPop(const FLyraNumberPopRequest& 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;
};
cpp
// 幻雨喜欢小猫咪
#include "Player/NumberPopComponent_NiagaraText.h"
#include "NiagaraComponent.h"
#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
UNumberPopComponent_NiagaraText::UNumberPopComponent_NiagaraText(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UNumberPopComponent_NiagaraText::AddNumberPop(const FLyraNumberPopRequest& 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->Activate(false); // 手动激活 Niagara 粒子效果
NiagaraComp->SetWorldLocation(NewRequest.WorldLocation); // 设置弹出位置
// UE_LOG(LogLyra, 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);
}
让特效显露而出
角色控制器中添加(具体是不是要这么操作,我也不懂呢,我GameplayCue成为失败的man了,现在用了Aura学的)(我又觉得内存会有问题一测果然,我就想到了,小兵对象池的内存管理,但是只是在这种双方都是对象池管理的情况下比较友好,要是对方会死掉呢,如此一来我又得添加一个委托,当对方销毁的适合移除对象池)
cpp
public:
UFUNCTION(Client, Reliable)
void ShowDamageNumber(float DamageAmount, AActor* TargetActor, bool bCriticalHit);
private:
UPROPERTY(EditAnywhere, Category = "Components")
TSubclassOf<UNumberPopComponent_NiagaraText> NumberPopComponentClass;
// 对象池管理
UPROPERTY()
TArray<TObjectPtr<UNumberPopComponent_NiagaraText>> ActiveNumberPops;
UFUNCTION()
void HandleTargetActorDestroyed(AActor* DestroyedActor);
cpp
void ACPlayerController::ShowDamageNumber_Implementation(float DamageAmount, AActor* TargetActor, bool bCriticalHit)
{
if (!IsValid(TargetActor) || !NumberPopComponentClass || !IsLocalController())
return;
UNumberPopComponent_NiagaraText* DamageText = nullptr;
// 查找可复用组件
for (UNumberPopComponent_NiagaraText* Pop : ActiveNumberPops)
{
if (Pop && Pop->GetOwner() == TargetActor)
{
DamageText = Pop;
break;
}
}
// 创建新组件或复用现有组件
if (!DamageText)
{
DamageText = NewObject<UNumberPopComponent_NiagaraText>(TargetActor, NumberPopComponentClass);
if (!DamageText)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UE_LOG(LogTemp, Error, TEXT("Niagara组件创建失败,内存不足或配置错误"));
#endif
return;
}
DamageText->RegisterComponent();
ActiveNumberPops.Add(DamageText);
TargetActor->OnDestroyed.AddDynamic(this, &ACPlayerController::HandleTargetActorDestroyed);
}
else if (!DamageText->IsRegistered())
{
DamageText->RegisterComponent();
}
// 设置显示参数
FNumberPopRequest NumberPopRequest;
NumberPopRequest.WorldLocation = TargetActor->GetActorLocation();
NumberPopRequest.WorldLocation.Z += 200.f;
NumberPopRequest.bIsCriticalDamage = bCriticalHit;
NumberPopRequest.NumberToDisplay = DamageAmount;
DamageText->AddNumberPop(NumberPopRequest);
}
void ACPlayerController::HandleTargetActorDestroyed(AActor* DestroyedActor)
{
// 遍历所有活跃的 Niagara 组件
for (int32 i = ActiveNumberPops.Num() - 1; i >= 0; i--)
{
UNumberPopComponent_NiagaraText* PopComponent = ActiveNumberPops[i];
if (PopComponent && PopComponent->GetOwner() == DestroyedActor)
{
// 清理组件
PopComponent->UnregisterComponent(); // 解除注册
PopComponent->MarkAsGarbage(); // 标记为垃圾回收
ActiveNumberPops.RemoveAt(i); // 从容器中移除
}
}
}
属性中添加一个函数,用来调用通过玩家控制器调用输出
cpp
static void ShowFloatingText(AActor* TargetActor, float Damage, bool IsCriticalHit);
cpp
void UCAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
// 伤害
if (Data.EvaluatedData.Attribute == GetAttackDamageAttribute())
{
float NewDamage = GetAttackDamage();
SetAttackDamage(0.f);
bool bCriticalHit = false;
UAbilitySystemComponent* SourceASC = Data.EffectSpec.GetContext().GetOriginalInstigatorAbilitySystemComponent();
if (NewDamage > 0.f)
{
if (SourceASC)
{
bool bFound = false;
const float EffectiveCriticalHitChance = SourceASC->GetGameplayAttributeValue(UCHeroAttributeSet::GetCriticalStrikeChanceAttribute(), bFound);
if (bFound)
{
bFound = false;
bCriticalHit = FMath::RandRange(1, 100) < EffectiveCriticalHitChance;
if (bCriticalHit)
{
const float CriticalStrikeDamage = 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)
// 弄出数字
if (AActor* TargetActor = Data.Target.AbilityActorInfo->AvatarActor.Get())
{
ShowFloatingText(TargetActor,NewDamage, bCriticalHit);
}
}
}
}
void UCAttributeSet::ShowFloatingText(AActor* TargetActor, const float Damage, bool IsCriticalHit)
{
for (int32 i = 0; ;++i)
{
if (ACPlayerController* PC = Cast<ACPlayerController>(UGameplayStatics::GetPlayerController(TargetActor,i)))
{
PC->ShowDamageNumber(Damage, TargetActor, IsCriticalHit); //调用显示伤害数字
}else
{
break;
}
}
}
创建一个蓝图版本的NumberPopComponent_NiagaraText
大功告成
客户端运行,也是双方都能看见特效了

对象池管理前,明显看到帧率从60掉到了40左右(如果可以的话选择销毁这个组件也是可以的,就是我懒罢了)
对象池管理后(我把蓝耗改成了1,所有我轰这群小兵轰了这么多次)
主播主播对象池还是太吃操作了,有没有别的方法,有的有的
既然这个是组件,我又觉得对象池这个操作还是有点抽象,这内存有点不该放在每个玩家控制器里,太浪费内存了。
这是lyra在GC里面的操作,他是属于直接获取这个组件,那像这样的操作,我们在cpp里操作更是轻而易举呢,我们直接操作
cpp
void ACPlayerController::ShowDamageNumber_Implementation(float DamageAmount, AActor* TargetActor, bool bCriticalHit)
{
if (!IsValid(TargetActor) || !NumberPopComponentClass || !IsLocalController())
return;
// 获取目标Actor上的现有组件
UNumberPopComponent_NiagaraText* DamageText = TargetActor->GetComponentByClass<UNumberPopComponent_NiagaraText>();
// 不存在则创建并附加
if (!DamageText)
{
DamageText = NewObject<UNumberPopComponent_NiagaraText>(TargetActor, NumberPopComponentClass);
if (!DamageText)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UE_LOG(LogTemp, Error, TEXT("Niagara组件创建失败,内存不足或配置错误"));
#endif
return;
}
DamageText->RegisterComponent(); // 注册组件
}
// 设置显示参数
FNumberPopRequest NumberPopRequest;
NumberPopRequest.WorldLocation = TargetActor->GetActorLocation() + FVector(0, 0, 200);
NumberPopRequest.bIsCriticalDamage = bCriticalHit;
NumberPopRequest.NumberToDisplay = DamageAmount;
DamageText->AddNumberPop(NumberPopRequest);
}
操作完也是蛮ok的
如果有路过的大佬又更优解,教教┭┮﹏┭┮