42. UE5 RPG 实现火球术伤害

news/2024/5/19 16:24:42

上一篇,我们解决了火球术于物体碰撞的问题,现在火球术能够正确的和攻击目标产生碰撞。接下来,我们要实现火球术的伤害功能,在火球术击中目标后,给目标造成伤害。
实现伤害功能的思路是给技能一个GameplayEffect,在击中时,给目标应用GE。首先,我们在GameplayAbility(GA)身上增加一个设置GE的类,在创建火球时,在火球身上创建一个GE的实例,在火球击中目标后,使用GE的实例通过目标的ASC应用GE并造成伤害。
当然,这篇内容主要是为了测试,实际项目中药比这复杂,比如GE造成伤害时需要应用多少伤害。敌人生成时血量,防御,攻击力等等

创建GE

首先,我们创建一个新的GameplayEffect,在GE里面,我们用于测试,先不要实现复杂的伤害计算,直接固定减少20血量。
在这里插入图片描述

首先,我们要在火球的类里面增加一个变量,用于承载技能里面创建的GE
这里我们创建了一个GE实例的句柄,我们通过句柄可以获取到GE的实例等一些信息。
我们并将其设置为了蓝图可读写,并且在创建时,可以设置它在创建时可以设置其属性

	UPROPERTY(BlueprintReadWrite, meta=(ExposeOnSpawn = true)) //蓝图可读写,创建时需要将接口暴露出来方便设置FGameplayEffectSpecHandle DamageEffectHandle;

然后在我们之前做的技能发射器类里面,我们有一项TUDO项,就是给火球添加GE,接下来我们在技能里面实现GE实例的创建。
在ProjectileSpell文件中,我们先增加一个可以设置GE的参数,只能在蓝图中设置,可以在蓝图中读写。

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)TSubclassOf<UGameplayEffect> DamageEffectClass;

然后在生成火球这里,获取到技能的ASC,通过ASC创建它的SpecHandle,并设置给我们生成的Projectile

//创建一个GE的实例,并设置给投射物
const UAbilitySystemComponent* SourceASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetAvatarActorFromActorInfo());
const FGameplayEffectSpecHandle SpecHandle = SourceASC->MakeOutgoingSpec(DamageEffectClass, GetAbilityLevel(), SourceASC->MakeEffectContext());
Projectile->DamageEffectHandle = SpecHandle;

然后我们回到Projectile类里面,在它的碰撞体触发重叠事件时,如果当前客户端对Projectile类有绝对控制权,将获取到目标身上的ASC,然后应用我们设置的DamageEffect。ApplyGameplayEffectSpecToSelf需要的是GE的实际引用,而DamageEffectHandle是对GE的句柄,它的Data是对GE实例的弱指针,我们通过Get()函数获取到GE实例的实际指针,然后在前面加上*代表获取引用。

void AProjectile::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{//播放击中特效PlayImpact();//在重叠后,销毁自身if(HasAuthority()){//为目标应用GEif(UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OtherActor)){TargetASC->ApplyGameplayEffectSpecToSelf(*DamageEffectHandle.Data.Get());}Destroy();}else{//如果对actor没有权威性,将bHit设置为true,证明当前已经播放了击中特效bHit = true;}
}

测试效果

实际代码我们已经书写完成,接下来,我们要测试这样使用后,有没有效果。
我们打开AttributeSet属性集,在属性值变动后,使用UE_LOG打印对应的数据,可以查看到底谁掉血了

void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{Super::PostGameplayEffectExecute(Data);FEffectProperties Props;SetEffectProperties(Data, Props);if(Data.EvaluatedData.Attribute == GetHealthAttribute()){SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));UE_LOG(LogTemp, Warning, TEXT("%s 的生命值发生了修改,当前生命值:%f"), *Props.TargetAvatarActor->GetName(), GetHealth());}if(Data.EvaluatedData.Attribute == GetManaAttribute()){SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana()));}
}

接下来就是实现在敌人身上设置对应的属性值,方便我们测试,现在我们还没制作敌人的属性,正常游戏项目里面会使用数据驱动的方式,方便策划制作,不会像英雄身上的属性这样复杂,敌人的属性尤其是小怪的属性都是固定的。当时为了方便测试,我们这是使用角色的设置GE来实现敌人身上属性值的设置。
角色身上的属性值的设置是通过三个GE的设置

  1. 主要属性,为设置角色基础属性
  2. 次级属性,基于主要或其他次级属性设置自身属性
  3. 至关重要的属性,主要是血量和蓝量,在角色属性设置完成,出生时将血量和蓝量填满。
    在这里插入图片描述
    这初始化角色属性的函数现在是书写在角色基类里的,我们只需要在敌人的基类里面调用这个InitializeDefaultAttributes函数,然后在蓝图里面设置上对应的GE,就可以实现测试属性的添加。
    我们在敌人初始化ASC后,进行函数调用
void AEnemyBase::InitAbilityActorInfo()
{AbilitySystemComponent->InitAbilityActorInfo(this, this);Cast<UAbilitySystemComponentBase>(AbilitySystemComponent)->AbilityActorInfoSet();//通过GE初始角色的属性InitializeDefaultAttributes();
}

现在准备工作完成了,接下来编译UE,我们首先在火球术的技能上,设置创建的GE
在这里插入图片描述
然后在敌人的蓝图上面设置初始化属性的GE,我们创建了敌人的蓝图基类,可以在基类上面直接设置,那么,它的子类都会将此作为默认值。这里也犯懒了,懒得再创建新的了,直接使用英雄测试的GE来测试。
在这里插入图片描述
这里可以看到,我们一共攻击了BP_Goblin_Slingshot_C_3两次,每次减少20血
在这里插入图片描述
下面我将技能和技能创建的火球的源码列下来
ProjectileSpell

// 版权归暮志未晚所有。#pragma once#include "CoreMinimal.h"
#include "AbilitySystem/Abilities/GameplayAbilityBase.h"
#include "ProjectileSpell.generated.h"class AProjectile;
/*** */
UCLASS()
class AURA_API UProjectileSpell : public UGameplayAbilityBase
{GENERATED_BODY()protected:virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;UFUNCTION(BlueprintCallable, Category="Projectile")void SpawnProjectile(const FVector& ProjectileTargetLocation);UPROPERTY(EditAnywhere, BlueprintReadOnly)TSubclassOf<AProjectile> ProjectileClass;UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)TSubclassOf<UGameplayEffect> DamageEffectClass;
};
// 版权归暮志未晚所有。#include "AbilitySystem/Abilities/ProjectileSpell.h"#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "Actor/Projectile.h"
#include "Interaction/CombatInterface.h"void UProjectileSpell::ActivateAbility(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,const FGameplayEventData* TriggerEventData)
{Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}void UProjectileSpell::SpawnProjectile(const FVector& ProjectileTargetLocation)
{const bool bIsServer = GetAvatarActorFromActorInfo()->HasAuthority(); //判断此函数是否在服务器运行if (!bIsServer) return;if (ICombatInterface* CombatInterface = Cast<ICombatInterface>(GetAvatarActorFromActorInfo())){const FVector SocketLocation = CombatInterface->GetCombatSocketLocation();FRotator Rotation = (ProjectileTargetLocation - SocketLocation).Rotation(); //将方向转为旋转Rotation.Pitch = 0.f; //设置Pitch为0,转向的朝向将平行于地面FTransform SpawnTransform;SpawnTransform.SetLocation(CombatInterface->GetCombatSocketLocation());SpawnTransform.SetRotation(Rotation.Quaternion());//SpawnActorDeferred将异步创建实例,在实例创建完成时,相应的数据已经应用到了实例身上AProjectile* Projectile = GetWorld()->SpawnActorDeferred<AProjectile>(ProjectileClass,SpawnTransform,GetOwningActorFromActorInfo(),Cast<APawn>(GetOwningActorFromActorInfo()),ESpawnActorCollisionHandlingMethod::AlwaysSpawn);//创建一个GE的实例,并设置给投射物const UAbilitySystemComponent* SourceASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetAvatarActorFromActorInfo());const FGameplayEffectSpecHandle SpecHandle = SourceASC->MakeOutgoingSpec(DamageEffectClass, GetAbilityLevel(), SourceASC->MakeEffectContext());Projectile->DamageEffectHandle = SpecHandle;//确保变换设置被正确应用Projectile->FinishSpawning(SpawnTransform);}
}
// 版权归暮志未晚所有。#pragma once#include "CoreMinimal.h"
#include "GameplayEffectTypes.h"
#include "GameFramework/Actor.h"
#include "Projectile.generated.h"class UNiagaraSystem;
class UProjectileMovementComponent;
class USphereComponent;UCLASS()
class AURA_API AProjectile : public AActor
{GENERATED_BODY()public:	// Sets default values for this actor's propertiesAProjectile();UPROPERTY(VisibleAnywhere)TObjectPtr<UProjectileMovementComponent> ProjectileMovement;UPROPERTY(BlueprintReadWrite, meta=(ExposeOnSpawn = true)) //蓝图可读写,创建时需要将接口暴露出来方便设置FGameplayEffectSpecHandle DamageEffectHandle;protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;virtual void Destroyed() override;UFUNCTION()void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
private://此物体的存在时间UPROPERTY(EditDefaultsOnly)float LifeSpan = 15.f;void PlayImpact() const;bool bHit;//碰撞球UPROPERTY(VisibleAnywhere)TObjectPtr<USphereComponent> Sphere;//击中粒子特效UPROPERTY(EditAnywhere)TObjectPtr<UNiagaraSystem> ImpactEffect;//击中音效UPROPERTY(EditAnywhere)TObjectPtr<USoundBase> ImpactSound;//移动循环音效UPROPERTY(EditAnywhere)TObjectPtr<USoundBase> LoopingSound;//储存循环音效的变量,后续用于删除UPROPERTY()TObjectPtr<UAudioComponent> LoopingSoundComponent;
};
// 版权归暮志未晚所有。#include "Actor/Projectile.h"#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Aura/Aura.h"
#include "Components/AudioComponent.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"// Sets default values
AProjectile::AProjectile()
{// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = false;bReplicates = true; //服务器负责计算并更新Actor的状态,然后通过网络将这些更新复制到所有连接的客户端上。//初始化碰撞体Sphere = CreateDefaultSubobject<USphereComponent>("Sphere");SetRootComponent(Sphere); //设置其为根节点,Sphere->SetCollisionObjectType(ECC_PROJECTILE); //设置发射物的碰撞类型Sphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly); //设置其只用作查询使用Sphere->SetCollisionResponseToChannels(ECR_Ignore); //设置其忽略所有碰撞检测Sphere->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap); //设置其与世界动态物体产生重叠事件Sphere->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap); //设置其与世界静态物体产生重叠事件Sphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap); //设置其与Pawn类型物体产生重叠事件//创建发射组件ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMovement");ProjectileMovement->InitialSpeed = 550.f; //设置初始速度ProjectileMovement->MaxSpeed = 550.f; //设置最大速度ProjectileMovement->ProjectileGravityScale = 0.f; //设置重力影响因子,0为不受影响
}// Called when the game starts or when spawned
void AProjectile::BeginPlay()
{Super::BeginPlay();//设置此物体的存在时间SetLifeSpan(LifeSpan);Sphere->OnComponentBeginOverlap.AddDynamic(this, &AProjectile::OnSphereOverlap);//添加一个音效,并附加到根组件上面,在技能移动时,声音也会跟随移动LoopingSoundComponent = UGameplayStatics::SpawnSoundAttached(LoopingSound, GetRootComponent());
}void AProjectile::Destroyed()
{//如果没有权威性,并且bHit没有修改为true,证明当前没有触发Overlap事件,在销毁前播放击中特效if(!bHit && !HasAuthority()){//播放击中特效PlayImpact();}Super::Destroyed();
}void AProjectile::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{//播放击中特效PlayImpact();//在重叠后,销毁自身if(HasAuthority()){//为目标应用GEif(UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OtherActor)){TargetASC->ApplyGameplayEffectSpecToSelf(*DamageEffectHandle.Data.Get());}Destroy();}else{//如果对actor没有权威性,将bHit设置为true,证明当前已经播放了击中特效bHit = true;}
}void AProjectile::PlayImpact() const
{//播放声效UGameplayStatics::PlaySoundAtLocation(this, ImpactSound, GetActorLocation(), FRotator::ZeroRotator);//播放粒子特效UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, ImpactEffect, GetActorLocation());//将音乐停止后会自动销毁if(LoopingSoundComponent) LoopingSoundComponent->Stop();
}

http://www.mrgr.cn/p/71477746

相关文章

OBMysql4.3.0.1的升级与备份恢复

OBMysql4.3.0.1的升级与备份恢复吐槽 OBMysql开源版本的升级路线图太操蛋了. 我最开始安装的是 4.2.2.0 的版本 耗费了一个多小时想升级 4.3.0.1 发现总是报错 自己以为是自己的人品有问题. 结果尝试升级 4.2.2.1 就可以升级 现在明显是 升级路线有问题. [ERROR] fail to get …

Redis的性能与CPU主频的关系

Redis的性能与CPU主频的关系背景 最近想验证一下不同主频下 CPU性能相关内容. 本来想开启一个高主频的 虚拟机 但是经过自己一翻 百度 bing 发现大部分资料告知我 虚拟机没法调整主频. WTF 只能用物理机来进行相关的处理. 经过十分钟的努力 得出结论 Redis的性能在相同架构, …

赋能智慧校园!A3D数字孪生可视化,轻量又高效!

放假之后&#xff0c;学生们会逐步返学&#xff0c;大量人员出入校园&#xff0c;安全更是不容忽视&#xff0c;如何在短时间内对大批人员及设施进行智能监管&#xff1f;数字化转型是关键手段&#xff0c;我们可以融合线上线下数据&#xff0c;搭建3D立体的智慧校园&#xff0…

如何从requirements.txt文件中安装pytorch

平时使用requirements.txt文件来安装python的依赖&#xff0c;如下所示&#xff1a; Flask3.0.0 Flask-Cors4.0.0 elastic-transport8.11.0 elasticsearch8.11.1但是如果我们的依赖中包含pytorch依赖&#xff0c;显然是不能简单的通过这个方式来进行的&#xff0c;例如&#x…

centos 7 yum install -y nagios

centos 7 systemctl disable firewalld --now vi /etc/selinux/config SELINUXdisabled yum install -y epel-release httpd nagios yum install -y httpd nagios systemctl enable httpd --now systemctl enable nagios --now 浏览器 IP/nagios 用户名&#xff1a;…

读天才与算法:人脑与AI的数学思维笔记10_混沌

混沌1. 混沌 1.1. 艺术不是再现可见,而是使不可见成为可见 1.1.1. 保罗克利 1.1.2. 绘画是自我发现,每个优秀的艺术家都在描绘他自己的样子 1.2. 滴画 1.2.1. 在作画时,波洛克一边走来走去,一边把颜料泼洒到画布上,最…

Django与mysqlclient链接不成功

先检查自己的python是什么版本&#xff0c;是64位还是32位&#xff0c;这个自己去网上查。 我的是32位的&#xff0c;因为直接pip下载不了&#xff0c;网上也没有32位的whl&#xff0c;所以卸载重装一个64位的3.9.6的python 网上直接搜mysqlclient&#xff0c;找到对应py39也…

图搜索算法详解

图搜索算法详解 图搜索算法是一种常用的算法技术&#xff0c;广泛应用于计算机科学、人工智能、数据挖掘、网络优化等领域。它的主要目的是在图结构中寻找从起点到终点的最优路径&#xff0c;使得搜索过程更加高效、准确。图搜索算法有多种&#xff0c;包括广度优先搜索、深度优…

4.25日学习记录

[HZNUCTF 2023 preliminary]ppppop 对于php反序列化&#xff0c;在之前的学习中有过了解&#xff0c;但是对于序列化字符串的格式不是很了解&#xff0c;刚好接触这题&#xff0c;可以了解一下 序列化字符串的格式&#xff1a; 布尔型&#xff08;bool&#xff09;b&#xf…

芒果YOLOv8改进组合161:动态标签分配ATSS+新颖轻量化非对称多级压缩LADH检测头组合改进,LADH作为原创可以发表SCI顶刊论文,小目标高效涨点

💡本篇内容:【芒果YOLOv8改进ATSS标签分配策略|第四集】芒果YOLOv8改进组合161:动态标签分配ATSS+新颖轻量化非对称多级压缩LADH检测头组合改进,小目标高效涨点 💡🚀🚀🚀本博客 标签分配策略ATSS改进+ 新颖轻量化非对称多级压缩LADH检测头组合改进,适用于 YOLOv…

3DTiles特性与内容解析

一篇19年整理的比较老的笔记了。更多精彩内容尽在数字孪生平台。 瓦片种类 3DTiles瓦片有多种类型&#xff1a; b3dm(Batched 3D Model&#xff0c;批量3D模型) b3dm瓦片存储了多个个体&#xff0c;b3dm中的glb代表的实际对象应该具有相同的种类但是可能数据内容不同。b3dm…

flutter笔记-webrtc使用1:依赖本地包socket.io-client

文章目录 1. 示例工程2. yaml 修改3. 使用4. socketio 关于自定义服务器自定义签名的问题封装成async和await方式 本文开始介绍webrtc的使用&#xff0c;阅读本文的前提是假设你已经使用过webrtc&#xff0c;了解webrtc的交互机制&#xff0c;不了解的可以看之前的文章&#xf…

用友政务财务系统 FileDownload 任意文件读取漏洞复现

0x01 产品简介 用友政务财务系统具有多项核心功能,旨在满足各类组织的财务管理需求。首先,它提供了财务核算功能,能够全面管理企业的总账、固定资产、现金、应付应收等模块,实时掌握企业的财务状况,并通过科目管理、凭证处理、报表分析等功能为决策提供有力支持。 0x02 …

分布式文件系统--MinIO

1 MinIO安装(Docker) ●在root目录下新建docker_minio文件夹 ●在docker_minio文件夹下新建config文件夹,data文件夹 ●在root目录下新建docker_compose文件夹,在docker_compose文件夹中添加docker-compose.yaml services:minio:image: quay.io/minio/miniocontainer_name: mi…

【文章转载】Lance Martin的关于RAG的笔记

转载自微博黄建同学 从头开始学习 RAG&#xff0c;看Lance Martin的这篇笔记就行了&#xff0c;包含了十几篇论文和开源实现&#xff01; —— 这是一组简短的&#xff08;5-10 分钟视频&#xff09;和笔记&#xff0c;解释了我最喜欢的十几篇 RAG 论文。我自己尝试实现每个想…

【蓝桥杯省赛真题40】python摘苹果 中小学青少年组蓝桥杯比赛 算法思维python编程省赛真题解析

目录 python摘苹果 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python摘苹果 第十三届蓝桥杯青少年组python编程省赛真题 一、题目要求 &…

一线实战,一次底层超融合故障导致的Oracle异常恢复

背景概述 某客户数据由于底层超融合故障导致数据库产生有大量的坏块&#xff0c;最终导致数据库宕机&#xff0c;通过数据抢救&#xff0c;恢复了全部的数据。下面是详细的故障分析诊断过程&#xff0c;以及详细的解决方案描述&#xff1a; 故障现象 数据库宕机之后&#xff0c…

K8S基础概念

一、MASTER Kubernetes里的Master指的是集群控制节点&#xff0c;在每个Kubernetes集群里都需要有一个Master来负责整个集 群的管理和控制&#xff0c;基本上 Kubernetes的所有控制命令都发给它&#xff0c;它负责具体的执行过程&#xff0c;我们后 面执行的所有命 令基本都…

C# 给图片添加文字水印

目录 应用场景 开发运行环境 方法说明 方法代码 调用示例 小结 应用场景 在某些应用项目&#xff08;如电子档案信息管理&#xff09;中&#xff0c;查看电子图片信息是经常使用到的功能&#xff0c;此时我们就需要给显示在浏览器中的图片添加文字水印版权或提示信息。…

如何设置微信自动回复?教你快速上手!

自动回复对于需要在微信上洽谈业务的人来说&#xff0c;无疑是非常实用的一个功能。 下面就一起来看看微信管理系统的机器人自动回复都有哪些设置吧&#xff01; 1、自动通过好友 只要有新的好友请求发送到你的微信账号&#xff0c;系统会自动通过该请求&#xff0c;无需手动…