当前位置: 首页 > news >正文

UE5多人MOBA+GAS 番外篇:移植Lyra的伤害特效(没用GameplayCue,因为我失败了┭┮﹏┭┮)

文章目录

  • Lyra里面的样子和需要的东西
  • 开始编写需要的组件内容
  • 让特效显露而出
  • 大功告成


Lyra里面的样子和需要的东西

学习lyra的伤害奶瓜,奶瓜有关的组件都在这里
在这里插入图片描述
迁移一下奶瓜的资产
在这里插入图片描述
Lyra中GC的一些详细样子
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其中他的暴击是通过tag的判断的
在这里插入图片描述
在这里插入图片描述

开始编写需要的组件内容

直接搜索这个类会发现没有
在这里插入图片描述
在这里插入图片描述
需要开启ModularGamePlay插件才可以
在这里插入图片描述
一搜就出来了
在这里插入图片描述
直接一步到位,因为那个Mesh的lyra里面并没有实现,没得抄,索性直接NumberPopComponent_NiagaraText
在这里插入图片描述
在cs文件中添加三个模块
在这里插入图片描述

在父类中最主要的核心就是这个结构体
在这里插入图片描述
但是在接收方有两个对这个数字奶瓜来说是没用的

// 幻雨喜欢小猫咪#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;
};
// 幻雨喜欢小猫咪#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学的)(我又觉得内存会有问题一测果然,我就想到了,小兵对象池的内存管理,但是只是在这种双方都是对象池管理的情况下比较友好,要是对方会死掉呢,如此一来我又得添加一个委托,当对方销毁的适合移除对象池)

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);
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组件创建失败,内存不足或配置错误"));
#endifreturn;}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);        // 从容器中移除}}
}

属性中添加一个函数,用来调用通过玩家控制器调用输出

static void ShowFloatingText(AActor* TargetActor, float Damage, bool IsCriticalHit);
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里操作更是轻而易举呢,我们直接操作
在这里插入图片描述

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组件创建失败,内存不足或配置错误"));
#endifreturn;}DamageText->RegisterComponent(); // 注册组件}// 设置显示参数FNumberPopRequest NumberPopRequest;NumberPopRequest.WorldLocation = TargetActor->GetActorLocation() + FVector(0, 0, 200);NumberPopRequest.bIsCriticalDamage = bCriticalHit;NumberPopRequest.NumberToDisplay = DamageAmount;DamageText->AddNumberPop(NumberPopRequest);
}

操作完也是蛮ok的
在这里插入图片描述

如果有路过的大佬又更优解,教教┭┮﹏┭┮

http://www.dtcms.com/a/289217.html

相关文章:

  • 均值漂移累积监测算法(MDAM):原理、命名、用途及实现
  • C++现代编程之旅:从基础语法到高性能应用开发
  • JavaScript 对象操作、继承与模块化实现
  • 深度学习图像分类数据集—八种贝类海鲜食物分类
  • UDP中的单播,多播,广播(代码实现)
  • #SVA语法滴水穿石# (014)关于链式蕴含的陷阱
  • python生成密钥
  • FreeSwitch编译部署
  • 去中心化协作智能生态系统
  • DataFrame数据的常用方法
  • LeetCode 刷题【8. 字符串转换整数 (atoi), 9. 回文数】
  • 国产HMC7044调试记录
  • 【Elasticsearch】合适的锅炒合适的菜:性能与成本平衡原理公式解析
  • 拓展面试题之-rabbitmq面试题
  • MySQL笔记3
  • 试用SAP BTP 02:试用SAP HANA Cloud
  • 【机器学习【9】】评估算法:数据集划分与算法泛化能力评估
  • 图的表示法以及实现
  • 【人工智能99问】长短期记忆网络(LSTM)的结构和原理是什么?(12/99)
  • ROS_INFO_STREAM, 模拟cout流输出机制的ROS输出接口
  • 《棒球知识科普》NBA、MLB选秀状元·棒球1号位
  • Everything介绍
  • 《杜甫传》读书笔记与经典摘要(一)
  • 事务的传播行为,分别在spring和mysql中讲解
  • PyTorch模型Ensemble实现
  • 14.多播与广播
  • Vue3.6 无虚拟DOM模式
  • 基于本机如何建立一个vue项目
  • Linux主机 ->多机器登录
  • Openlayers 面试题及答案180道(121-140)