80. UE5 RPG 实现UI显示技能冷却进度功能

在上一篇文章里,我们实现了通过GE给技能增加资源消耗和技能冷却功能。UI也能够显示角色能够使用的技能的UI,现在还有一个问题,我们希望在技能释放进去冷却时,技能变成灰色,并在技能冷却完成,技能可以再次使用。
为了实现这个功能,我们首先要实现一个能够监听技能进入冷却的方法,然后在技能被使用后,将UI的颜色修改,并在技能冷却完成后,将技能UI恢复到可释放状态。

创建异步任务来监听技能冷却

为了实现能够监听,我们创建一个新的类用来监听技能冷却。
我们创建一个新的c++类,继承至BlueprintAsyncActionBase类
在这里插入图片描述
修改命名,我们将其命名为监听冷却修改
在这里插入图片描述
接下来,我们将实现类,如果不想看实现过程,请略过,实现完成,我会贴上完整的实现代码。

首先我们创建一个委托宏,用于设置委托类型,返回一个参数

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCooldownChangeSignature, float, TimeRemaining);

在类里创建两个委托,用于触发在技能进入冷却时触发,然后在技能冷却时触发

	UPROPERTY(BlueprintAssignable)
	FCooldownChangeSignature CooldownStart; //冷却触发开始委托

	UPROPERTY(BlueprintAssignable)
	FCooldownChangeSignature CooldownEnd; //冷却结束委托

然后创建两个保护性的变量参数,用于实例类时,存储ASC和需要监听的冷却标签

protected:

	UPROPERTY()
	TObjectPtr<UAbilitySystemComponent> ASC;

	FGameplayTag CooldownTag; //记录监听的冷却标签

接着我们创建一个实例函数,用于创建类的实例设置meta=(BlueprintInternalUseOnly=“true”)为了防止开发mod或者玩家能够使用到此函数。

	UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true"))
	static UListenCooldownChange* ListenForCooldownChange(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayTag& CooldownTag);

然后增加一个函数,用于结束任务,防止内存泄露

	UFUNCTION(BlueprintCallable)
	void EndTask();

在创建实例函数中,我们首先实例化类,并将参数设置上去

UListenCooldownChange* UListenCooldownChange::ListenForCooldownChange(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayTag& InCooldownTag)
{
	UListenCooldownChange* ListenCooldownChange = NewObject<UListenCooldownChange>();
	ListenCooldownChange->ASC = AbilitySystemComponent;
	ListenCooldownChange->CooldownTag = InCooldownTag;

然后判断传入的两个参数是否存在,如果未存在,将结束此任务

	//如果参数有一项未设置,我们将结束此任务
	if(!IsValid(AbilitySystemComponent) || !InCooldownTag.IsValid())
	{
		ListenCooldownChange->EndTask();
		return nullptr;
	}

我们接下来增加两个回调函数,用于实现对技能冷却的开始和结束的广播

	//监听冷却标签回调函数
	void CooldownTagChanged(const FGameplayTag InCooldownTag, int32 NewCount);

	//监听ASC激活GE的回调
	void OnActiveEffectAdded(UAbilitySystemComponent* TargetASC, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveEffectHandle);

使用对标签的监听来实现技能冷却结束的监听

	//监听冷却标签的变动,并绑定回调,用于获取冷却结束
	AbilitySystemComponent->RegisterGameplayTagEvent(InCooldownTag, EGameplayTagEventType::NewOrRemoved)
	.AddUObject(ListenCooldownChange, &UListenCooldownChange::CooldownTagChanged);

对于技能进入冷却状态,我们采用监听应用冷却GE实现

	//监听GE应用回调,获取冷却激活,用于获取技能开始进入冷却
	AbilitySystemComponent->OnActiveGameplayEffectAddedDelegateToSelf.AddUObject(ListenCooldownChange, &UListenCooldownChange::OnActiveEffectAdded);

对于冷却的开始的参数设置,我们可以查看ASC源码,这个可以在客户端和服务器都获取到对应的委托回调来监听有时效性的GE
在这里插入图片描述
宏的定义时,是传入了三个参数
在这里插入图片描述
设置完成后,我们就可以返回,每次调用,我们可以创建一个监听实例

return ListenCooldownChange;

为了防止内存泄露,我们需要实现EndTask函数,在实例不需要时,对其进行销毁,并进行资源回收。AbilitySystemComponent->OnActiveGameplayEffectAddedDelegateToSelf是由ASC创建的,所以不需要我们去对其进行销毁

void UListenCooldownChange::EndTask()
{
	//判断ASC是否存在
	if(!IsValid(ASC)) return;
	
	//取消对冷却标签的变动监听
	ASC->RegisterGameplayTagEvent(CooldownTag, EGameplayTagEventType::NewOrRemoved).RemoveAll(this);

	SetReadyToDestroy(); //设置此对象可以被销毁,如果此对象不再被引用,将可以被销毁掉
	MarkAsGarbage(); //标记此实例为垃圾资源,可以被回收
}

接着,我们还需要实现两个广播的处理,首先是对冷却结束的广播,我们对冷却标签进行获取,如果标签数量小于1,那么,技能将不处于冷却状态,我们广播冷却结束即可

void UListenCooldownChange::CooldownTagChanged(const FGameplayTag InCooldownTag, int32 NewCount) const
{
	//如果计数为0,代表冷却标签已经不存在,技能不处于冷却状态
	if(NewCount == 0)
	{
		//广播冷却结束委托
		CooldownEnd.Broadcast(0.f);
	}
}

然后就是进入冷却的广播函数广播,我们首先获取这个应用的GE是否为设置了冷却标签,为了防止设置错误,我们获取了设置自身和设置给Actor的标签容器,判断容器内是否拥有我们设置的冷却标签。然后创建查询冷却标签的查询器对象,通过此对象去查找剩余的冷却时间,从中获取到最大冷却时间将时间广播出去。

void UListenCooldownChange::OnActiveEffectAdded(UAbilitySystemComponent* TargetASC, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveEffectHandle) const
{
	//获取设置到自身的所有标签
	FGameplayTagContainer AssetTags;
	SpecApplied.GetAllAssetTags(AssetTags);

	//获取到GE设置给Actor的标签
	FGameplayTagContainer GrantedTags;
	SpecApplied.GetAllGrantedTags(GrantedTags);

	//判断应用的GE是否设置了此冷却标签
	if(AssetTags.HasTagExact(CooldownTag) || GrantedTags.HasTagExact(CooldownTag))
	{
		//创建一个查询对象,用于查询包含所有标签容器标签的GE
		FGameplayEffectQuery GameplayEffectQuery = FGameplayEffectQuery::MakeQuery_MatchAllOwningTags(CooldownTag.GetSingleTagContainer());
		//返回查询到的所有包含此冷却GE的剩余时间的GE
		TArray<float> TimesRemaining = ASC->GetActiveEffectsTimeRemaining(GameplayEffectQuery);
		if(TimesRemaining.Num() > 0)
		{
			//获取最高的冷却时间
			float TimeRemaining = TimesRemaining[0];
			for(int32 i=0; i<TimesRemaining.Num(); i++)
			{
				if(TimeRemaining < TimesRemaining[i]) TimeRemaining = TimesRemaining[i];
			}
			//广播初始时间
			CooldownStart.Broadcast(TimeRemaining);
		}
	}
}

接下来就是完整代码,不想看代码解析的,自己复制代码去修改名称运行即可
ListenCooldownChange.h

// 版权归暮志未晚所有。

#pragma once

#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "AbilitySystemComponent.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "ListenCooldownChange.generated.h"

struct FGameplayAbilitySpec;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCooldownChangeSignature, float, TimeRemaining);

/**
 * 
 */
UCLASS(BlueprintType, meta = (ExposedAsyncProxy="AsyncTask"))
class RPG_API UListenCooldownChange : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

public:

	UPROPERTY(BlueprintAssignable)
	FCooldownChangeSignature CooldownStart; //冷却触发开始委托

	UPROPERTY(BlueprintAssignable)
	FCooldownChangeSignature CooldownEnd; //冷却结束委托

	UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true"))
	static UListenCooldownChange* ListenForCooldownChange(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayTag& InCooldownTag);

	UFUNCTION(BlueprintCallable)
	void EndTask();

protected:

	UPROPERTY()
	TObjectPtr<UAbilitySystemComponent> ASC;

	FGameplayTag CooldownTag; //记录监听的冷却标签

	//监听冷却标签回调函数
	void CooldownTagChanged(const FGameplayTag InCooldownTag, int32 NewCount) const;

	//监听ASC激活GE的回调
	void OnActiveEffectAdded(UAbilitySystemComponent* TargetASC, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveEffectHandle) const;
};

ListenCooldownChange.cpp

// 版权归暮志未晚所有。


#include "AbilitySystem/AsyncTasks/ListenCooldownChange.h"

#include "AbilitySystemComponent.h"

UListenCooldownChange* UListenCooldownChange::ListenForCooldownChange(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayTag& InCooldownTag)
{
	UListenCooldownChange* ListenCooldownChange = NewObject<UListenCooldownChange>();
	ListenCooldownChange->ASC = AbilitySystemComponent;
	ListenCooldownChange->CooldownTag = InCooldownTag;

	//如果参数有一项未设置,我们将结束此任务
	if(!IsValid(AbilitySystemComponent) || !InCooldownTag.IsValid())
	{
		ListenCooldownChange->EndTask();
		return nullptr;
	}

	//监听冷却标签的变动,并绑定回调,用于获取冷却结束
	AbilitySystemComponent->RegisterGameplayTagEvent(InCooldownTag, EGameplayTagEventType::NewOrRemoved)
	.AddUObject(ListenCooldownChange, &UListenCooldownChange::CooldownTagChanged);

	//监听GE应用回调,获取冷却激活,用于获取技能开始进入冷却
	AbilitySystemComponent->OnActiveGameplayEffectAddedDelegateToSelf.AddUObject(ListenCooldownChange, &UListenCooldownChange::OnActiveEffectAdded);

	return ListenCooldownChange;
}

void UListenCooldownChange::EndTask()
{
	//判断ASC是否存在
	if(!IsValid(ASC)) return;
	
	//取消对冷却标签的变动监听
	ASC->RegisterGameplayTagEvent(CooldownTag, EGameplayTagEventType::NewOrRemoved).RemoveAll(this);

	SetReadyToDestroy(); //设置此对象可以被销毁,如果此对象不再被引用,将可以被销毁掉
	MarkAsGarbage(); //标记此实例为垃圾资源,可以被回收
}

void UListenCooldownChange::CooldownTagChanged(const FGameplayTag InCooldownTag, int32 NewCount) const
{
	//如果计数为0,代表冷却标签已经不存在,技能不处于冷却状态
	if(NewCount == 0)
	{
		//广播冷却结束委托
		CooldownEnd.Broadcast(0.f);
	}
}

void UListenCooldownChange::OnActiveEffectAdded(UAbilitySystemComponent* TargetASC, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveEffectHandle) const
{
	//获取设置到自身的所有标签
	FGameplayTagContainer AssetTags;
	SpecApplied.GetAllAssetTags(AssetTags);

	//获取到GE设置给Actor的标签
	FGameplayTagContainer GrantedTags;
	SpecApplied.GetAllGrantedTags(GrantedTags);

	//判断应用的GE是否设置了此冷却标签
	if(AssetTags.HasTagExact(CooldownTag) || GrantedTags.HasTagExact(CooldownTag))
	{
		//创建一个查询对象,用于查询包含所有标签容器标签的GE
		FGameplayEffectQuery GameplayEffectQuery = FGameplayEffectQuery::MakeQuery_MatchAllOwningTags(CooldownTag.GetSingleTagContainer());
		//返回查询到的所有包含此冷却GE的剩余时间的GE
		TArray<float> TimesRemaining = ASC->GetActiveEffectsTimeRemaining(GameplayEffectQuery);
		if(TimesRemaining.Num() > 0)
		{
			//获取最高的冷却时间
			float TimeRemaining = TimesRemaining[0];
			for(int32 i=0; i<TimesRemaining.Num(); i++)
			{
				if(TimeRemaining < TimesRemaining[i]) TimeRemaining = TimesRemaining[i];
			}
			//广播初始时间
			CooldownStart.Broadcast(TimeRemaining);
		}
	}
}

接着,我们在UI的事件图标中搜索名称,查看是否能够找到对应的节点
在这里插入图片描述
注意,我们搜索的名称是函数名称
在这里插入图片描述

测试代码

上面我们编写了对应的代码,首先做的就是快速测试一下,防止出现问题。
我们快速连一套测试节点,用来检测是否能够获取到对应事件
在这里插入图片描述
查看打印,发现事件确实成功触发,也有一些问题,比如触发了多次。触发多次的原因是因为所有的技能UI都是在监听这一个冷却标签
在这里插入图片描述

处理无法在蓝图调用的问题

我们当前无法在蓝图中获取对象进行调用销毁事件,所以需要一个方法,获取对象,我们在头文件设置,将其作为一个可获取参数,并设置命名"AsyncTask"

UCLASS(BlueprintType, meta = (ExposedAsyncProxy="AsyncTask"))

编译运行,可以查看到对象类型
在这里插入图片描述
我们可以将其设置为变量,避免没有销毁掉
在这里插入图片描述

设置冷却标签

我们需要记录技能的冷却标签,有一个比较好的方法就是在技能数据结构体增加一个配置项
在这里插入图片描述
在数据资产中配置上
在这里插入图片描述
接着我们修改ui的蓝图,在应用了技能数据后,对其绑定技能的回调,为了保证内存泄露,现进行销毁,防止频繁切换ui显示的技能导致频繁触发回调。
在这里插入图片描述
在ui被销毁时,也需要调用
在这里插入图片描述
接着编写代码测试
在这里插入图片描述
经过测试,发现还是有问题,原来是没有判断是否为当前需要监听的技能
和之前的技能一样,我们通过标签判断是否需要执行后续
在这里插入图片描述
这样就实现了事件监听
在这里插入图片描述

创建UI冷却效果

上面,我们实现了技能的冷却进入和退出。
我们接下来,要将冷却效果表现到UI上面,让玩家能够清晰的了解到技能已经进入冷却,无法释放。
我们增加两个函数节点,一个是设置技能UI变暗,并将冷却时间显示出来
在这里插入图片描述
另一个则是恢复默认状态,将冷却节点隐藏,并将技能图标颜色恢复默认
在这里插入图片描述
我们在监听到技能进入冷却后,将冷却时间保存为变量,方便后续使用,并进入冷却状态
在这里插入图片描述
然后我们设置一个定时器,在定时器里面对显示剩余时间进行更新,Time为多次时间更新一次,Looping选中,定时器将循环更新,不勾选将只触发一次。
在这里插入图片描述
并将定时器的引用保存下来,方便在冷却结束后,将其销毁
在这里插入图片描述
然后在自定义事件里面,减去每次调用时间,更新冷却时间,并显示到UI上面
在这里插入图片描述
为了防止出现负数,我们将其限制在最小值为0
在这里插入图片描述

在技能冷却结束后,我们将定时器清除,并恢复默认状态
在这里插入图片描述
接下来运行查看效果
在这里插入图片描述
在技能冷却完成,也能恢复默认
在这里插入图片描述
接下来,截取一张完整的蓝图
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux安全技术与防火墙

一、安全技术和防火墙 1.1 安全技术 入侵检测系统&#xff1a;特点是不阻断网络访问&#xff0c;主要是提供报警和时候报警&#xff0c;不主动介入。 入侵防御系统&#xff1a;透明模式工作&#xff0c;对数据包、网络监控、服务攻击、木马蠕虫、系统漏洞等等进行准确的分析和…

两个视频怎么剪辑成一个视频?3个方法分享

两个视频怎么剪辑成一个视频&#xff1f;将两个视频剪辑成一个视频&#xff0c;是现代数字内容创作中的高频需求&#xff0c;它不仅简化了素材管理&#xff0c;还能通过创意剪辑提升作品连贯性与表现力。通过精心编排&#xff0c;两个视频片段可以无缝融合&#xff0c;讲述更完…

如何通过兔子和窝窝的故事理解“在机器人学习和研究中的获得成本与维护成本”(节选)

获得成本 掌握一门课程&#xff0c;以最为简单的学校成绩过60为例&#xff0c;需要按要求提交材料&#xff0c;包括作业、报告、实验和考试等&#xff0c;依据学分和考核要求的不同&#xff0c;需要对于花费时间和经历进行完成。 维护成本 考完了&#xff0c;如果被动学习那…

Django 删除单行数据

1&#xff0c;添加模型 from django.db import modelsclass Post(models.Model):title models.CharField(max_length200)content models.TextField()pub_date models.DateTimeField(date published)class Book(models.Model):title models.CharField(max_length100)author…

FastAPI 学习之路(四十七)WebSockets(三)登录后才可以聊天

之前我们是通过前端自动生成的token信息&#xff0c;这次我们通过注册登录&#xff0c;保存到本地去实现。首先&#xff0c;我们实现一个登录页面&#xff0c;放在templates目录下。 <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

基于PCIe总线架构的2路1GSPS AD、4路1GSPS DA信号处理平台(100%国产化)

板卡概述 PCIE723-165是基于PCIE总线架构的2通道1GSPS采样率14位分辨率、4通道1GSPS采样率16位分辨率信号处理平台&#xff0c;该板卡采用国产16nm FPGA作为实时处理器&#xff0c;支持2路高速采集以及4路高速数据回放&#xff0c;板载2组DDR4 SDRAM大容量数据缓存&#xff0c;…

gradle 和 java 版本对应关系

文章目录 gradle 和 java 版本对应关系原地址 gradle 和 java 版本对应关系 原地址 https://docs.gradle.org/current/userguide/compatibility.html#compatibility

Python进阶 2024/7/10

文件编码概念 文件的读取操作 打开文件 open&#xff08;&#xff09;打开函数 open&#xff08;name&#xff0c;mode,encoding&#xff09; name:打开的目标文件的字符串&#xff0c;可以包含文件所在的具体路径 mode&#xff1a;访问模式&#xff0c;只读&#xff0c;只…

MySQL修改表名:重命名RENAME

RENAME命令 RENAME命令用于修改表的名称&#xff0c;命令格式&#xff1a;rename table 原表名 to 新表名; 例如&#xff1a;将user表改成user_info rename table user to user_info;使用场景 第一个场景重命名 最常用的场景就是使用rename修改表名。 rename table user t…

js页面跳转

最近&#xff0c;自己学习前端时发现有趣的js跳转页面&#xff0c;下面和大家分享小知识。 网上有许多跳转方法&#xff0c;我只选择了一种。如下代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">…

基于粒子滤波和帧差法的目标跟踪matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 帧差法 4.2 粒子滤波 4.3 粒子滤波与帧差法的结合 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 原重采样方法&#xff1a; 改进重采样方法&#xff1a; 2.算法…

The Web3 社区 Web3 产品经理课程

概述 / 深耕区块链行业 11 年&#xff0c;和很多产品经理都打过交道&#xff1b;遇到过优秀的产品经理&#xff0c;也遇到过比较拉垮的产品经理。多年工作中&#xff0c;曾在某些团队&#xff0c;承载技术兼产品经理的角色&#xff1b;也参与过很多 Web3 外包项目&#xff0c;包…

【原创教程】埃斯顿机器人:弯管机推力解决方式(上)

现的功能及应用的场合 本项目为弯管机设备改造工程,在不破坏设备原有的功能的情况下通过只更换设备原来的永宏PLC,使弯管机能够与埃斯顿机器人进行信号交互,通过机器人对弯管机进行上料、下料动作,即节约了人工成本,又提高了生产效率。 本文所述内容为“弯管机推力”的解决…

从汇编层看64位程序运行——栈帧(Stack Frame)边界

大纲 RBP&#xff0c;RSP栈帧边界总结参考资料 在《从汇编层看64位程序运行——栈帧(Stack Frame)入门》中&#xff0c;我们简单介绍了栈帧的概念&#xff0c;以及它和函数调用之间的关系。如文中所述&#xff0c;栈帧是一种虚拟的概念&#xff0c;它表达了一个执行中的函数的栈…

基于AT89C51单片机GSM模块的家庭防火防盗报警系统设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于AT89C51单片机GSM模块的家庭防火防盗报警系统设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 摘 要 原理图 仿真图 元器件清单 代码 系统论文 参考文献 资源下载…

细说MCU用定时器控制ADC采样频率的实现方法

目录 一、工程依赖的硬件及背景 二、设计目的 三、 建立工程 1.选择时钟源和Debug模式 2.配置系统时钟和ADC时钟 3.配置串口 4.配置ADC 5.设置TIM3 6.设置TIM4 7.配置中断 8.GPIO 四、代码修改 1.重新定义ADC回调函数 2.在主程序中编写数据发送代码 3.使能ADC和…

如何从gitlab删除仓库

嗨&#xff0c;我是兰若姐姐。今天发现gitlab上有些仓库的代码没有用&#xff0c;是个多余的仓库&#xff0c;想要删掉&#xff0c;经过一番操作之后&#xff0c;成功的删除了&#xff0c;git上没有 多余的仓库&#xff0c;看着干净舒服很多&#xff0c;现在把删除的过程分享出…

PostgreSQL 怎样处理数据仓库中维度表和事实表的关联性能?

文章目录 PostgreSQL 中维度表和事实表关联性能的处理 PostgreSQL 中维度表和事实表关联性能的处理 在数据仓库的领域中&#xff0c;PostgreSQL 作为一款强大的关系型数据库管理系统&#xff0c;对于处理维度表和事实表的关联性能是一个关键的问题。维度表和事实表的关联是数据…

数学建模--数据统计类赛题分析~~神经网络引入

1.缺失值的处理 &#xff08;1&#xff09;像在下面的这个表格里面&#xff0c;这个对于缺失的数据&#xff0c;我们需要分情况进行分析&#xff0c;如果这个数据就是一个数值型的数据&#xff0c;我们可以使用平均值进行处理&#xff1b; &#xff08;2&#xff09;对于这个…

Linux--USB驱动开发(二)USB总线程序

一、USB总线驱动程序的作用 a&#xff09;识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 b&#xff09;查找并安装对应的设备驱动程序 c&#xff09;提供USB读写函数 二、USB设备工作流程 由于内核自带了USB驱动,所以我们先插入一个U…