97. UE5 GAS RPG 实现闪电链技能(二)

news2024/11/25 17:21:15

书接上回,如果没有查看上一篇文章的同学推荐先看上一章,我们接着实现闪电链技能。
在上一章最后,我们实现了闪电链的第一条链,能够正确显示特效,接下来,我们先实现它的音效和一些bug修复。

在这里插入图片描述
我们在多端网络里,只能查看到角色播放了闪电链开始攻击,但是没有持续攻击动画,原因是因为在角色身上的变量没有设置可复制,我们修改一下即可。

添加音效

接下来,我们添加在持续释放闪电链时播放的音效,我们在设置Niagara 系统之后,附加一个音效节点,设置其不会被自动销毁,并将其生成的组件作为变量保存下来,并在取消技能时方便销毁。
在这里插入图片描述
接着就是在停止闪电链技能时,设置销毁
在这里插入图片描述
我们还可以使用fadeOut来实现声音慢慢变小停止
在这里插入图片描述
使用这个时,注意自动销毁要开启,它会在音频停止时自动销毁组件
在这里插入图片描述

为技能增加新通道

我们为了将移动和技能拾取分开,所以增加一个新的通道
在引擎-碰撞这里增加一个新通道
在这里插入图片描述
默认设置阻挡
在这里插入图片描述
它是第二个,所以我们可以在代码中获取
在这里插入图片描述
代码中设置默认通道就是18个,和我们 可以自定义18个匹配
在这里插入图片描述
在鼠标拾取这里,修改为新建的通道
在这里插入图片描述
记得将一些不可让技能拾取的对象相应关掉
在这里插入图片描述
还有我们之前的碰撞拾取溶解效果的问题,会出现来回闪的问题,我们可以通过添加简易碰撞,并设置项目默认来解决。
在这里插入图片描述
项目默认是在引擎-物理这里修改
在这里插入图片描述

设置获取第一个闪电命中

接下来,我们实现获取第一个命中结果,因为鼠标选中的中间有可能被阻挡,我们需要考虑这个因素,所以,将其判断出来,如果鼠标拾取和武器发射位置中间有其它敌人被阻挡,那么,我们将更新它的目标位置。
首先我们在闪电链技能里增加一个新的函数,用于获取闪电链命中的第一个敌人

	/**
	 * 拾取闪电链命中的第一个目标
	 * @param BeamTargetLocation 鼠标点击目标位置
	 * @note 有可能中间会被阻挡,拾取的目标不是鼠标选中的目标
	 */
	UFUNCTION(BlueprintCallable)
	void TraceFirstTarget(const FVector& BeamTargetLocation);

接着,我们实现此函数

void URPGBeamSpell::TraceFirstTarget(const FVector& BeamTargetLocation)
{
	//确保所有者继承了战斗接口
	if(OwnerCharacter && OwnerCharacter->Implements<UCombatInterface>())
	{
		//获取到武器
		if(USkeletalMeshComponent* Weapon = ICombatInterface::Execute_GetWeapon(OwnerCharacter))
		{
			TArray<AActor*> ActorsToIgnore; //当前需要忽略的对象数组
			ActorsToIgnore.Add(OwnerCharacter);// 将自身忽略掉
			
			const FVector SocketLocation = Weapon->GetSocketLocation(FName("TipSocket")); //获取技能发射位置

			FHitResult HitResult; //命中结果的纯粹对象

			//通过武器发射位置和命中位置生成一条球形线,获取第一个命中的结果
			UKismetSystemLibrary::SphereTraceSingle(
				OwnerCharacter,
				SocketLocation,
				BeamTargetLocation,
				10.f,
				TraceTypeQuery1,
				false,
				ActorsToIgnore,
				EDrawDebugTrace::ForDuration, //如果需要debug,将其设置ForDuration,如果关闭设置为None
				HitResult,
				true);

			//如果有命中的结果,修改拾取结果
			if(HitResult.bBlockingHit)
			{
				MouseHitLocation = HitResult.ImpactPoint;
				MouseHitActor = HitResult.GetActor();
			}
		}
	}
}

我们将生成闪电链的函数折叠为了一个函数,在调用GameplayCue之前,调用此函数
在这里插入图片描述
然后查看效果
在这里插入图片描述
接下来,我们想实现如果命中的是角色,我们让闪电链结束的位置是目标角色的位置,这样可以放置发生偏斜。
我们修改技能的生成闪电链的逻辑代码,先将GameplayCue的代码创建出来,然后判断命中的第一个actor是否继承战斗接口。
如果继承战斗接口,那么目标就是敌人,我们将GameplayCue的所有者设置为敌人,在敌人死亡时,Cue也会被销毁。如果不是,那就是目标是场景,我们还是使用技能的所有者。
在这里插入图片描述
接着修改技能结束时,我们需要销毁掉GameplayCue,由于根据目标是否继承战斗接口,GameplayCue的所有者也不同,所以我们要通过所有者和配置项进行删除。
这里我们上面将是否继承战斗接口的布尔值给保存了下来,如果继承,就从敌人身上删除,如果不是,我们将从所有者身上删除。
在这里插入图片描述
接着,我们修改GC,在GC里,我们在While Active里首先从配置项里获取到所需的资源。
在这里插入图片描述
然后我们生成粒子系统
在这里插入图片描述
接着对SourceObject进行判断,在技能里,如果目标是敌人,SourceObject就是敌人,如果不是它就没有继承战斗接口,我们可以以此为依据,来设置闪电链特效的终点位置。
在这里插入图片描述
至此,完成了对目标的修改。运行查看,如果攻击到目标,闪电链能准确的攻击敌人的位置
在这里插入图片描述

实现获取附近最近的几个目标

要实现闪电链,我们要获取到攻击目标最近的几个目标,然后再生成闪电链,所以,我们要增加一个函数来获取。
我们增加一个蓝图可调用函数,来获取返回获取到的目标

	/**
	 * 通过技能命中目标获取扩散的敌人目标
	 * @param OutAdditionalTargets 返回获取到的最近的目标数组
	 */
	UFUNCTION(BlueprintCallable)
	void StoreAdditionalTargets(TArray<AActor*>& OutAdditionalTargets);

定义闪电链最多可以攻击的敌人数量

	UPROPERTY(EditDefaultsOnly, Category="FireBolt")
	int32 MaxNumShockTargets = 5; //最大散射的闪电链数

我们在函数里,通过内置函数,来获取范围内的所有目标,然后再遍历这些目标,获取到最近的几个,这里我设置的调试代码

void URPGBeamSpell::StoreAdditionalTargets(TArray<AActor*>& OutAdditionalTargets)
{
	TArray<AActor*> ActorsToIgnore; //遍历时忽略的数组
	ActorsToIgnore.Add(GetAvatarActorFromActorInfo()); //忽略自身
	ActorsToIgnore.Add(MouseHitActor); //忽略鼠标命中的的敌人
	
	TArray<AActor*> OverlappingActors; //存放遍历结果的数组

	//通过封装的函数获取到技能范围内散射的目标
	URPGAbilitySystemLibrary::GetLivePlayersWithinRadius(
		GetAvatarActorFromActorInfo(),
		OverlappingActors,
		ActorsToIgnore,
		850.f,
		MouseHitActor->GetActorLocation());

	//int32 NumAdditionalTargets = FMath::Min(GetAbilityLevel() - 1, MaxNumShockTargets);
	int32 NumAdditionalTargets = 5;

	//通过自定义函数来获取最近的几个目标
	URPGAbilitySystemLibrary::GetClosestTargets(NumAdditionalTargets, OverlappingActors, OutAdditionalTargets, MouseHitActor->GetActorLocation());
}

获取最近目标的函数是我封装在函数库里的函数

	/**
	 * 获取距离目标位置最近的几个目标
	 * @param MaxTargets 获取最大目标的数量
	 * @param Actors 需要计算的目标数组
	 * @param OutClosestTargets 返回获取到的最近的目标
	 * @param Origin 计算的位置
	 */
	UFUNCTION(BlueprintCallable, Category="RPGAbilitySystemLibrary|GameplayMechanics")
	static void GetClosestTargets(int32 MaxTargets, const TArray<AActor*>& Actors, TArray<AActor*>& OutClosestTargets, const FVector& Origin);

它通过使用一个while循环,直到获取到所有的所需的目标停止

void URPGAbilitySystemLibrary::GetClosestTargets(int32 MaxTargets, const TArray<AActor*>& Actors, TArray<AActor*>& OutClosestTargets, const FVector& Origin)
{
	//如果数量过于少,直接返回原数组
	if(Actors.Num() <= MaxTargets)
	{
		OutClosestTargets = Actors;
		return;
	}

	TArray<AActor*> ActorsToCheck = Actors; //没有引用就是复制,复制一份用于遍历
	int32 NumTargetFound = 0; //当前已经遍历出最近距离的个数

	//循环遍历,直到获得足够数量的目标时停止
	while (NumTargetFound < MaxTargets)
	{
		if(ActorsToCheck.Num() == 0) break; //如果没有可遍历内容,将跳出循环
		double ClosestDistance = TNumericLimits<double>::Max(); //记录中心于目标的位置,如果有更小的将被替换,默认是最大
		AActor* ClosestActor; //缓存当前最近距离的目标
		for(AActor* PotentialTarget : ActorsToCheck)
		{
			//获取目标和中心的距离
			const double Distance = (PotentialTarget->GetActorLocation() - Origin).Length();

			//比对当前计算的位置是否小于缓存的位置
			if(Distance < ClosestDistance)
			{
				//如果小于,将替换对应信息
				ClosestDistance = Distance;
				ClosestActor = PotentialTarget;
			}
		}
		
		ActorsToCheck.Remove(ClosestActor); //从遍历数组中删除缓存的对象
		OutClosestTargets.AddUnique(ClosestActor); //添加到返回的数组中
		++ NumTargetFound; //递增数量
	}
}

接着,我们编译打开蓝图,在技能结束时,通过节点获取附近的目标,并打印它的位置
在这里插入图片描述
正确显示
在这里插入图片描述
接着,我们在结束时,打印拾取的范围,并增加敌人数量
在这里插入图片描述
然后查看是否能够正确查找到目标
在这里插入图片描述

实现闪电弧效果

我们先实现闪电链的第一形态,闪电弧,在生成闪电链的函数里,我们设置完成命中目标的以后,先对命中目标判断,如果命中的是场景,将不实现扩散效果。然后去获取最近的几个目标,生成扩展的链
在这里插入图片描述

AddShockLoopCueToAdditionalTarget函数是我们将获取的每个目标生成和命中目标的一条闪电链,在节点函数里,我们创建一个Map将目标和配置项存储起来,方便技能结束时清除
在这里插入图片描述
在函数里,我们将创建配置项,并添加到映射中,并生成GameplayCue
在这里插入图片描述
在技能结束之前,我们将通过目标和配置项将GameplayCue清除,并在最后清空Map映射
在这里插入图片描述
RemoveAdditionalCue函数里,我们用到了之前保存的数组,并在Map映射里找到对应的配置项,进行清除。
在这里插入图片描述
接着运行查看效果,发现攻击到敌人时,能够正确生成闪电弧
在这里插入图片描述
在攻击地面时,也能正确表现
在这里插入图片描述

设置伤害和消耗冷却

我们实现了闪电弧的效果,接下来,我们将设置技能的相关伤害,技能蓝量消耗和冷却
首先我们在技能伤害数据表里增加一条闪电链的伤害
在这里插入图片描述
设置到技能上,并设置好伤害类型
在这里插入图片描述
然后创建两个GameplayEffect
在这里插入图片描述
设置给技能
在这里插入图片描述
在消耗这里,我们设置修改蓝量消耗,并设置对应的表格对蓝量进行消耗
在这里插入图片描述
在表格中增加不同等级的蓝量消耗
在这里插入图片描述
添加对应的冷却标签
在这里插入图片描述
这个标签在激活技能时,应用到释放者身上。
在这里插入图片描述

处理消耗冷却和角色死亡时的bug

接下来,我们要实现正确触发技能冷却,并在每次造成伤害时,扣除角色的蓝量。并在目标死亡时,让特效也能够正常显示。我们将在技能的类里,增加两个回调,可以监听角色在死亡时,对不同的角色死亡进行处理。
我们之前实现了对角色死亡时,会触发死亡委托,在释放技能时,我们绑定对应的委托即可。
在这里插入图片描述
然后在RPGBeamSpell中,增加两个函数回调,在蓝图里可以去实现对应的函数,为什么区分两个函数,因为鼠标命中的主要敌人死亡后,无法将技能分散出去,而额外的角色死亡,我们只需要将其相关的处理即可。

	/**
	 * 鼠标命中的敌人死亡处理
	 * @param DeadActor 命中敌人
	 */
	UFUNCTION(BlueprintImplementableEvent)
	void PrimaryTargetDied(AActor* DeadActor);

	/**
	 * 额外的敌人死亡处理
	 * @param DeadActor 额外敌人 
	 */
	UFUNCTION(BlueprintImplementableEvent)
	void AdditionalTargetDied(AActor* DeadActor);

然后在技能命中的第一个角色身上,我们绑定主要角色的死亡回调,这里时通过战斗接口判断,并判断当前是否绑定了对应的
在这里插入图片描述
额外的目标逻辑相同,一样绑定对应的死亡回调
在这里插入图片描述
我们还需要增加一个技能结束时,取消绑定,因为技能结束后,不能再进行对应的函数回调触发。我们增加一个函数,由于额外的目标没有保存,我们调用时需要传入额外的目标

	/**
	 * 技能结束时调用
	 */
	UFUNCTION(BlueprintCallable)
	void OnEndAbility(TArray<AActor*> AdditionalTargets);

在函数里,我们实现对主要目标和额外的目标的死亡委托的取消绑定

void URPGBeamSpell::OnEndAbility(TArray<AActor*> AdditionalTargets)
{
	//取消主要目标的死亡回调
	if(ICombatInterface* CombatInterface = Cast<ICombatInterface>(MouseHitActor))
	{
		CombatInterface->GetOnDeathDelegate().RemoveDynamic(this, &URPGBeamSpell::PrimaryTargetDied);
	}

	//取消额外目标的死亡回调
	for(AActor* TargetActor : AdditionalTargets)
	{
		if(ICombatInterface* CombatInterface = Cast<ICombatInterface>(TargetActor))
		{
			CombatInterface->GetOnDeathDelegate().RemoveDynamic(this, &URPGBeamSpell::AdditionalTargetDied);
		}
	}
}

我们在蓝图中实现对应目标死亡回调,主要目标死亡,我们直接结束技能
在这里插入图片描述
在额外目标死亡时,我们清除对应的表现效果即可,目标死亡后,将无法对其角色进行应用GE
在这里插入图片描述
完成以后,我们将修改闪电链的蓝图。
在触发闪电链时,我们增加对目标是否已死亡的判断,如果角色已经死亡,将结束技能,场景的目标则没有这个问题。如果目标没有死亡,我们将在技能持续阶段,通过定时器,在一定时间一直造成伤害。
在这里插入图片描述
在应用伤害时,分别对主要目标和额外目标进行调用
在这里插入图片描述
然后通过创建GE实例应用给目标,这里对ASC判断,是因为有时目标的ASC不存在会报错。
在这里插入图片描述
接下来就是技能结束时,我们需要设置技能进入冷却,并清除定时器
在这里插入图片描述
在函数PrepareToEndAbility中,我们将角色设置为可移动,并取消角色死亡绑定(要不然技能结束,目标死亡时,也会触发对应回调),并将目标身上的GameplayCue给清除,清除效果表现,并将对应数组清空。
在这里插入图片描述

实现技能延迟结束

如果我们直接释放技能并松开时,技能还没有播放就结束,所以,这里我们将实现保证技能最短时间,在技能播放完成攻击并播放特效后结束。
所以,我们增加一个变量,来设置技能最小执行时间
在这里插入图片描述
然后在技能释放健抬起时,我们能够获取到抬起时间,通过和最小时间比对,如果时间没到,我们将延迟到最小时间,再结束技能。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(9)MATLAB瑞利衰落信道仿真2

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、瑞利衰落信道二、瑞利衰落信道建模三、仿真结果二、高斯随机变量和瑞利随机变量后续 前言 本文首先给出瑞利衰落信道模型&#xff0c;并根据瑞利衰落变量估…

乌班图基础设施安装之Mysql8.0+Redis6.X安装

简介&#xff1a;云服务器基础设施安装之 Mysql8.0Redis6.X 安装 Docker安装 # 按照依赖 yum install -y yum-utils device-mapper-persistent data lvm2 Docker Mirror 从去年开始. hub.docker.com[1] 在国内的访问速度极慢. 当时大家主要还是依赖国内的一些镜像源: 如中科…

【JNI】数组的基本使用

在上一期讲了基本类型的基本使用&#xff0c;这期来说一说数组的基本使用 HelloJNI.java&#xff1a;实现myArray函数&#xff0c;把一个整型数组转换为双精度型数组 public class HelloJNI { static {System.loadLibrary("hello"); }private native String HelloW…

AI编程工具的崛起:效率提升的未来在哪里?

你正在使用的编程工具会被淘汰吗&#xff1f;AI编程工具正在改变这一切&#xff01; 在日益忙碌的开发世界里&#xff0c;工具的选择决定了开发者的工作效率。在过去的十年里&#xff0c;从代码编辑器到版本控制工具&#xff0c;各种工具帮助开发者逐步优化了工作流程&#xf…

C/C++逆向:函数逆向分析-调用约定分析

在进行函数逆向分析时&#xff0c;分析其函数调用约定具有非常重要的作用&#xff0c;因为调用约定直接影响了函数的参数传递、返回值、栈管理、寄存器使用等多个方面&#xff0c;不同的编译器和平台可能有不同的默认调用约定&#xff0c;识别调用约定可以帮助判断代码是由哪种…

HTB:Mongod[WriteUP]

连接至HTB服务器并启动靶机 靶机IP&#xff1a;10.129.99.33 分配IP&#xff1a;10.10.16.12 1.How many TCP ports are open on the machine? 使用nmap对靶机进行全端口TCP脚本、服务扫描&#xff1a; nmap -sC -sV -T4 -p- {TARGET_IP} 可以看到靶机共开放TCP端口2个&…

LC108-将有序数组转化为二叉搜索树(二叉平衡树)

文章目录 1 题目2 思路3 ACM完整代码参考 1 题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡二叉搜索树 示例&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1…

最佳语音识别 Whisper-large-v3-turbo 上线,速度更快(本地安装 )

Openai 上线语音模型whisper-large-v3-turbo 在本文中&#xff0c;我们将介绍 whisper-large-v3-turbo 以及 whisper-web&#xff08;一个直接在浏览器中进行ML语音识别的开源项目&#xff09;。 尽管近年来出现了许多音频和多模态模型&#xff0c;但Whisper 仍是生产级自动语音…

类型模板参数与非类型模板参数

在C中&#xff0c;模板参数分为两种类型&#xff1a;类型参数和非类型参数。类型参数是指定模板类型名称的参数&#xff0c;而非类型参数是指定模板整型常量的参数。 模板参数不限定于类型&#xff0c;普通值也可作为模板参数&#xff0c;但这里值的类型只能是整形家族&#x…

Qt教程(001):Qt概述与安装

文章目录 一、Qt概述1.1 什么是Qt1.2 Qt优点1.3 Qt发展史1.4 支持的平台1.5 成功案例1.6 下载安装1.7 QtCreator介绍 一、Qt概述 1.1 什么是Qt Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的&…

快乐数(c语言)

1.「快乐数」 定义为&#xff1a;对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&#xff0c;那么这个数就是快乐数。如果 n 是 快乐数 就返…

打卡第三天 P5729 【深基5.例7】工艺品制作

今天是我打卡第三天&#xff0c;做个入门题吧(#^.^#) 题目描述 输入格式 输出格式 输出一个整数表示答案。 输入输出样例 输入 #1 4 4 4 1 1 1 1 2 2 2 输出 #1 56 说明/提示 C&#xff1a; #include<bits/stdc.h> using namespace std; long long a[100][100][1…

【ubuntu】修改用户名、主机名、主文件夹名、登录名、密码

目录 1.他们是什么 2.修改方法 2.1 修改用户密码 2.2 修改主机名 2.2.1 切换到root用户 2.2.2 修改名称 2.3 修改用户名 主文件夹名 登录名 2.2.1 sudoers 2.2.2 passwd 2.2.3 shadow 2.2.4 group 2.2.5 修改主文件夹名 3.重启 1.他们是什么 &#xff08;1&#xf…

初识 C 语言(2)

目录 一、sigined 和 unsigned1. unsigned 二、数据类型的取值范围三、变量的分类1. 局部变量2. 全局变量3. 全局变量和局部变量名称冲突 四、算数操作符1. 加法操作符&#xff08;&#xff09;2. 减法操作符&#xff08;-&#xff09;3. 乘法操作符&#xff08;\*&#xff09;…

认知杂谈96《反人性与顺人性》

内容摘要&#xff1a; 成长常被视为反人性的&#xff0c;因为它意味着要离开舒适区&#xff0c;面对挑战。然而&#xff0c;在与人共事时&#xff0c;顺应人性同样重要&#xff0c;它要求我们理解他人的需求和动机。为了平衡成长与顺应人性&#xff0c;我们应设定清晰目标&…

封装el-upload组件,用于上传图片和视频

使用环境 vue3element-ui plus 需要根据后端返回结构修改的函数&#xff1a;onPreview onRemove onSuccess 组件使用 基本使用 源代码&#xff1a; <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.…

hdfs伪分布式集群搭建

1 准备 vmware 虚拟三台centos系统的节点三台机器安装好jdk环境关闭防火墙&#xff08;端口太多&#xff0c;需要的自行去开关端口&#xff09;hadoop压缩包解压至三台服务器 可在一台节点上配置完成后克隆为三台节点 2 host修改 vi /etc/hosts在每个节点上添加三台机器的i…

Linux环境搭建git服务器和代码自动化部署

在开发过程中&#xff0c;我们经常遇到的问题就是提交代码到测试地址&#xff0c;然后进行线上测试。 要实现Git代码的自动化部署&#xff0c;考虑以下几种方法 FTP提交&#xff1a;可以使用FTP将代码上传到服务器自动化部署工具&#xff1a;如Jenkins、当代码被推送到仓库时…

【JavaWeb实战项目】在线蛋糕商城的设计与实现(附完整源代码)

一、系统介绍 本项目分为前后台&#xff0c;分为管理员与普通用户两种角色&#xff0c;管理员登录后台&#xff0c;普通用户登录前台&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录 商品管理 订单管理 客户管理 类目管理等功能。 用户角色包含以下功能&a…

MySQL 表的操作

温馨提示&#xff1a;非特殊情况不要修改和删除表 创建表 第一种方式 第二种方式 第三种方式 简单查看 查看表 查询当前数据库&#xff1a;select database(); 查询当前数据库中具有的表&#xff1a;show tables; 查看表的简略信息&#xff1a;desc 表名1&#xff1b; 查看表的…