62. UE5 RPG 近战攻击获取敌人并造成伤害

在上一篇,我们实现了通过AI行为树控制战士敌人靠近攻击目标触发近战攻击技能,并在蒙太奇动画中触发事件激活攻击的那一刻的伤害判断,在攻击时,我们绘制了一个测试球体,用于伤害范围。
在之前实现的火球术中,我们实现的是一个单体伤害技能,在近战中,我们想实现对范围内的敌人判断,并造成伤害。这也是RPG游戏的通常做法。
所以,我们将在这篇中,实现角色的死亡逻辑,然后接着实现一个函数去获取一定范围内的敌人并造成伤害。

实现死亡逻辑

在敌人接口这里,我们增加两个函数,一个用于判断角色是否死亡,另一个获取角色的Avatar,它们都被设置了BlueprintNativeEvent,它会通过蓝图初始化虚函数,并且可以在蓝图中覆写(如果在蓝图中覆写,C++版本的实现将会失效)

	UFUNCTION(BlueprintNativeEvent, BlueprintCallable)bool IsDead() const; //获取当前角色是否死亡UFUNCTION(BlueprintNativeEvent, BlueprintCallable)AActor* GetAvatar(); //获取当前角色

由于每个角色都需要此功能,我们将在角色基类上面去修改它,首先覆盖它们的父类函数

	/* ICombatInterface战斗接口 */virtual UAnimMontage* GetHitReactMontage_Implementation() override;virtual FVector GetCombatSocketLocation_Implementation() const override;virtual bool IsDead_Implementation() const override;virtual AActor* GetAvatar_Implementation() override;virtual void Die() override;/* ICombatInterface战斗接口 结束 */

创建一个变量,来记录当前是否死亡

protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;bool bDead = false; //当前角色死亡状态

实现前面创建的两个函数

bool ARPGCharacter::IsDead_Implementation() const
{return bDead;
}AActor* ARPGCharacter::GetAvatar_Implementation()
{return this;
}

我们现在能获取了,还需要一个设置死亡的地方,我们在实现角色死亡这里实现了一个死亡函数,它在内部调用触发每个客户端都会运行此函数,我们可以把设置逻辑写到此函数中。
在这里插入图片描述
在函数底部增加设置死亡的变量
在这里插入图片描述

实现获取范围内的敌人函数

我们现在需要实现一个函数来获取一定范围内的敌人。所以,在我们函数库增加一个新的函数用于获取。
要实现这个功能,我们可以查找以下引擎的库里面是否包含此类型的函数,稍微修改一下,在GameplayStatics文件中,有个名为ApplyRadialDamageWithFalloff函数,它和我们所需的类型差不多,创建一个球的配置项,然后忽略掉一些Actor,然后进行查询,获取到对应的Actors,跟我们所需的效果一致,我们只需要返回即可。
在这里插入图片描述
我们创建一个蓝图库函数,可以通过此函数获取到所有攻击位置的Actor

	//获取到攻击位置半径内的所有动态ActorUFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|GameplayMechanics")static void GetLivePlayersWithinRadius(const UObject* WorldContextObject, TArray<AActor*>& OutOverlappingActors, const TArray<AActor*>& ActorsToIgnore, float Radius, const FVector& SphereOrigin);

在实现这里,仿造官方库的写法,获取到所有与设置的碰撞体碰撞的数组,然后对数据遍历,将所需的对象返回

void URPGAbilitySystemBlueprintLibrary::GetLivePlayersWithinRadius(const UObject* WorldContextObject,TArray<AActor*>& OutOverlappingActors, const TArray<AActor*>& ActorsToIgnore, float Radius,const FVector& SphereOrigin)
{FCollisionQueryParams SphereParams; //创建一个碰撞查询的配置SphereParams.AddIgnoredActors(ActorsToIgnore); //添加忽略的ActorTArray<FOverlapResult> Overlaps; //创建存储检索到的与碰撞体产生碰撞的Actorif (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) //获取当前所处的场景,如果获取失败,将打印并返回Null{//获取到所有与此球体碰撞的动态物体World->OverlapMultiByObjectType(Overlaps, SphereOrigin, FQuat::Identity, FCollisionObjectQueryParams(FCollisionObjectQueryParams::InitType::AllDynamicObjects), FCollisionShape::MakeSphere(Radius), SphereParams);for(FOverlapResult& Overlap : Overlaps) //遍历所有获取到的动态Actor{//判断当前Actor是否包含战斗接口   Overlap.GetActor() 从碰撞检测结果中获取到碰撞的Actorconst bool ImplementsCombatInterface =  Overlap.GetActor()->Implements<UCombatInterface>();//判断当前Actor是否存活,如果不包含战斗接口,将不会判断存活(放置的火堆也属于动态Actor,这样保证不会报错)if(ImplementsCombatInterface && !ICombatInterface::Execute_IsDead(Overlap.GetActor())) {OutOverlappingActors.AddUnique(Overlap.GetActor()); //将Actor添加到返回数组,AddUnique 只有在此Actor未被添加时,才可以添加到数组}}}
}

接着我们在技能类里面,调用创建的此函数,中心设置为在角色上面设置的攻击位置,将自身传入忽略的数组,设置好半径,然后将返回的数组遍历,绘制测试图形,记得在遍历结束后,结束此技能。
在这里插入图片描述
接着查看攻击效果,可以看出,攻击时,不但可以在玩家角色身上绘制测试图形,也可以在敌人身上绘制。
在这里插入图片描述

应用GE

现在我们能够获得到需要造成伤害的Actor,那么接下来,我们将实现伤害的应用。按照我们之前的经验 ,我们需要一个GameplayEffect去对攻击目标造成伤害,在前面的章节49. UE5 RPG 使用Execution Calculations处理对目标造成的最终伤害我们实现了一个造成伤害的GE。并且在54. UE5 RPG 增加伤害类型 增加了技能可以造成多种属性的伤害,我们可以给近战攻击技能直接使用这个GE。
我们需要做的就是创建GE的实例,然后使用SetByCaller将技能的伤害数值传递给GE的实例,接下来,我们将在技能蓝图中通过连连看实现此功能,然后在c++中实现我们实际需要使用的函数。

首先,我们先配置技能的造成伤害的GE,并设置当前技能造成的伤害,我们这里设置的造成物理伤害,并且我们需要在曲线表格中新增加一条伤害曲线使用。
在这里插入图片描述
伤害我们设置了从1级到40级的伤害
在这里插入图片描述
完成了准备工作,接下来,我们就可以修改蓝图的逻辑,在获取到攻击目标后,我们遍历攻击目标数组,从伤害类型中,获取到所有的Keys,然后遍历获取到对应的Value(在蓝图无法直接遍历),我这个现在写的还有问题,就是给每个类型的伤害创建的一个Spec,如果只设置了一种类型的伤害,那么效果是一致的。这里主要也是给展示一下如何实现。
在这里插入图片描述
根据等级获取技能伤害节点是通过代码实现的节点,卸载蓝图函数库中
在这里插入图片描述
那么接下来,我们在C++中实现这个逻辑,这样在蓝图中只需要调用一个方法即可。
我们在RPGDamageGameplayAbility.h类里面增加一个函数,用于给目标应用伤害

	UFUNCTION(BlueprintCallable)void CauseDamage(AActor* TargetActor);

在cpp文件中实现它

void URPGDamageGameplayAbility::CauseDamage(AActor* TargetActor)
{//创建GEFGameplayEffectSpecHandle DamageSpecHandle = MakeOutgoingGameplayEffectSpec(DamageEffectClass, 1.f);//通过SetByCaller设置属性伤害for(auto Pair : DamageTypes){const float ScaleDamage = Pair.Value.GetValueAtLevel(GetAbilityLevel());UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(DamageSpecHandle, Pair.Key, ScaleDamage);}//将GE应用给目标GetAbilitySystemComponentFromActorInfo()->ApplyGameplayEffectSpecToTarget(*DamageSpecHandle.Data.Get(),UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor));
}

编译打开UE,在蓝图中修改成我们创建的节点,针对每个目标调用一次函数即可。
在这里插入图片描述

处理玩家角色被敌人攻击不显示伤害数字的问题

我们在被敌人攻击后,无法在角色身上显示伤害的数字,我们去查看一下在AttributeSet里面如何创建的伤害数字的显示。
在显示伤害数字函数中,我们是通过获取到角色的PlayerController,然后通过调用PlayerController身上的函数来实现的。如果是敌人攻击玩家角色,我们是无法在敌人身上获取到PlayerController的。
在这里插入图片描述
所以这里的修改可以修改为,如果从SourceCharacter无法获取到PlayerController,那么我们判断一下目标角色身上获取,然后从目标角色身上获取PlayerController进行调用显示,这样,和这次技能有关的角色都会显示对应的伤害。

void URPGAttributeSet::ShowFloatingText(const FEffectProperties& Props, const float Damage, bool IsBlockedHit, bool IsCriticalHit)
{//调用显示伤害数字if(Props.SourceCharacter != Props.TargetCharacter){//从技能释放者身上获取PC并显示伤害数字if(ARPGPlayerController* PC = Cast<ARPGPlayerController>(Props.SourceCharacter->Controller)){PC->ShowDamageNumber(Damage, Props.TargetCharacter, IsBlockedHit, IsCriticalHit); //调用显示伤害数字}//从目标身上获取PC并显示伤害数字if(ARPGPlayerController* PC = Cast<ARPGPlayerController>(Props.TargetCharacter->Controller)){PC->ShowDamageNumber(Damage, Props.TargetCharacter, IsBlockedHit, IsCriticalHit); //调用显示伤害数字}}
}

接着运行查看效果。
在这里插入图片描述

处理多人模式下的问题

我们将运行模式修改,数量修改为两人
在这里插入图片描述
运行起来后,发现敌人被攻击后,会报错,原因是因为AIController为null
在这里插入图片描述
我们加个条件判断即可,判断AIController和黑板组件有没有被设置,如果存在才可以调用。

void ARPGEnemy::HitReactTagChanged(const FGameplayTag CallbackTag, int32 NewCount)
{bHitReacting = NewCount > 0;GetCharacterMovement()->MaxWalkSpeed = bHitReacting ? 0.f : BaseWalkSpeed;//设置黑板键的值if(RPGAIController && RPGAIController->GetBlackboardComponent()){RPGAIController->GetBlackboardComponent()->SetValueAsBool(FName("HitReacting"), bHitReacting);}// GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, FString::Printf(TEXT("Hit React bool: %i"), bHitReacting));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339790.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAC2.0全生命周期防护,助力企业构建安全闭环

5月29日&#xff0c;CACTER邮件安全团队凭借多年的邮件安全防护经验&#xff0c;在“防御邮件威胁-企业如何筑起最后防线”直播分享会上展示了构建安全闭环的重要性&#xff0c;并深入介绍了全新CAC 2.0中的“威胁邮件提示”功能。 下滑查看更多直播精彩内容 构建安全闭环的必要…

【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器)

文章目录 前言一、JWT1. 什么是JWT2. 使用场景3. 结构3.1 Header3.2 Payload3.3 Signature 4. 使用 二、案例1.引入库2.JwtUtils3. UserController14. ArticleController 三、拦截器1. 定义拦截器2. 注册拦截器 四、测试1. 登录2. 无token3. 有token4. 全局配置 总结 前言 前面…

JAVAEE之网络初识_协议、TCP/IP网络模型、封装、分用

前言 在这一节我们简单介绍一下网络的发展 一、通信网络基础 网络互连的目的是进行网络通信&#xff0c;也即是网络数据传输&#xff0c;更具体一点&#xff0c;是网络主机中的不同进程间&#xff0c;基于网络传输数据。那么&#xff0c;在组建的网络中&#xff0c;如何判断到…

遥感之特征选择-禁忌搜索算法

各类智能优化算法其主要区别在于算法的运行规则不同&#xff0c;比如常用的遗传算法&#xff0c;其规则就是变异&#xff0c;交叉和选择等&#xff0c;各种不同的变体大多是在框架内的实现细节不同&#xff0c;而本文中的禁忌算法也是如此&#xff0c;其算法框架如下进行介绍。…

【IDEA】-使用IDEA查看类之间的依赖关系

1、父子类的继承、实现关系 1.1、使用CTRL Alt U 选择 java class 依据光标实际指向的类位置 用实心箭头表示泛化关系 是一种继承的关系&#xff0c;指向父类 可以提前设置需要显示的类的属性、方法等信息 快捷键 Ctrl Alt S &#xff0c;然后搜索 Diagrams 1.2、使用…

鸿蒙开发接口资源调度:【@ohos.backgroundTaskManager (后台任务管理)】

后台任务管理 本模块提供后台任务管理能力。 当应用或业务模块处于后台&#xff08;无可见界面&#xff09;时&#xff0c;如果有需要继续执行或者后续执行的业务&#xff0c;可基于业务类型&#xff0c;申请短时任务延迟挂起&#xff08;Suspend&#xff09;或者长时任务避免…

C语言学习笔记之结构体(一)

目录 什么是结构体&#xff1f; 结构体的声明 结构体变量的定义和初始化 结构体成员的访问 结构体传参 什么是结构体&#xff1f; 在现实生活中的很多事物无法用单一类型的变量就能描述清楚&#xff0c;如&#xff1a;描述一个学生&#xff0c;需要姓名&#xff0c;年龄&a…

Lua的几个特殊用法

&#xff1a;/.的区别 详细可以参考https://zhuanlan.zhihu.com/p/651619116。最重要的不同就是传递默认参数self。 通过.调用函数&#xff0c;传递self实例 通过 &#xff1a; 调用函数&#xff0c;传递self (不需要显示的传递self参数&#xff0c;默认就会传递&#xff0c;但…

旧衣回收小程序带来的收益优势,小程序有哪些功能?

随着互联网的快速发展&#xff0c;大众对旧衣回收市场也越来越了解&#xff0c;对于闲置的旧衣物也有了适合的处理方式。旧衣回收也符合了当下资源回收利用&#xff0c;因此&#xff0c;旧衣回收市场获得了爆发式增长&#xff0c;市场规模不断扩大。同时市场中还吸引了越来越多…

C++入门5——C/C++动态内存管理(new与delete)

目录 1. 一图搞懂C/C的内存分布 2. 存在动态内存分配的原因 3. C语言中的动态内存管理方式 4. C内存管理方式 4.1 new/delete操作内置类型 4.2 new/delete操作自定义类型 1. 一图搞懂C/C的内存分布 说明&#xff1a; 1. 栈区&#xff08;stack&#xff09;&#xff1a;在…

在github上创建(上传、关联)自已的项目

目录 创建一个github项目并进行开发。 1.github创建空项目 2. git clone 项目 3. 将项目关联 创建一个github项目并进行开发。 1.github创建空项目 右边的New 然后按步创建就行 2. git clone 项目 复制这个连接 然后在终端&#xff1a;git clone [刚才复制的连接] 3. 将…

MySQL -- SQL笔试题相关

1.银行代缴花费bank_bill 字段名描述serno流水号date交易日期accno账号name姓名amount金额brno缴费网点 serno: 一个 BIGINT UNSIGNED 类型的列&#xff0c;作为主键&#xff0c;且不为空。该列是自动增量的&#xff0c;每次插入新行时&#xff0c;都会自动递增生成一个唯一的…

simulink基础学习笔记

写在前面 这个笔记是看B站UP 快乐的宇航boy 所出的simulink基础教程系列视频过程中记下来的&#xff0c;写的很粗糙不完整&#xff0c;也不会补。视频教程很细跟着做就行。 lesson1-7节的笔记up有&#xff0c;可以加up的群&#xff0c;里面大佬挺活跃的。 lesson8 for循环 For …

C++之map

1、标准库的map类型 2、插入数据 #include <map> #include <string> #include <iostream>using namespace std;int main() {map<string, int> mapTest;// 插入到map容器内部的元素是默认按照key从小到大来排序// key类型一定要重载小于号<运算符map…

CTFHUB-密码口令-弱口令

目录 题干介绍 密码字典 找flag过程 尾声 题干介绍 通常认为容易被别人&#xff08;他们有可能对你很了解&#xff09;猜测到或被破解工具破解的口令均为弱口令。 密码字典 下载地址&#xff1a;GitHub - NepoloHebo/Commonly-used-weak-password-dictionary: 常用弱密码字…

QT_UI设计

mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE //命名空间 namespace Ui { class MainWindow; } //ui_MainWindow文件里定义的类&#xff0c;外部声明 QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_O…

深度神经网络——什么是梯度下降?

如果对神经网络的训练有所了解&#xff0c;那么很可能已经听说过“梯度下降”这一术语。梯度下降是提升神经网络性能、降低其误差率的主要技术手段。然而&#xff0c;对于机器学习新手来说&#xff0c;梯度下降的概念可能稍显晦涩。本文旨在帮助您直观理解梯度下降的工作原理。…

python用tanh画图

用tanh函数画图 圆形 import numpy as np import matplotlib.pyplot as plt# 创建一个二维网格 xx np.linspace(-1, 1, 1000) yy np.linspace(-1, 1, 1000) x_i, y_i np.meshgrid(xx, yy)# 圆的半径和中心 r 0.4 center_x, center_y 0, 0 # 假设圆心在(0, 0)# 计算每个网…

构建智慧监控系统的功能架构,保障安全与便利

智慧监控系统作为现代城市安全管理的重要工具&#xff0c;不仅能够提供有效的安防监控&#xff0c;还能为人们的生活带来更多的便利。本文将探讨智慧监控系统的功能架构&#xff0c;以实现安全和便利的双重目标。 ### 1. 智慧监控系统背景 随着城市化进程的加速&#xff0c;人…

Mybatis实现树形结构方式

1&#xff0c;三级分类树形结构查询 /*** DDD(Domain-Driven Design): 领域驱动设计** 三级分类树形结构&#xff1b;* 支持无限层级&#xff1b;* 当前项目只有三级*/ Data public class CategoryTreeTo {private Long categoryId; //1private String categoryName;private …