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

news2024/9/21 11:12:51

在上一篇文章里,我们实现了通过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
在这里插入图片描述

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

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

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

相关文章

【算法】代码随想录之数组

文章目录 前言 一、二分查找法&#xff08;LeetCode--704&#xff09; 二、移除元素&#xff08;LeetCode--27&#xff09; 三、有序数组的平方&#xff08;LeetCode--977&#xff09; 四、长度最小的子数组&#xff08;LeetCode--209&#xff09; 五、螺旋矩阵II&#x…

LLM:tokenizer分词器概念

在做nlp时&#xff0c;首先要对文本进行分词&#xff0c;也就是给定一个句子&#xff0c;将其归到词表中的一系列token上&#xff0c;token有对应的数字&#xff08;token_id&#xff09;。 上图是bert的分词器的json内容&#xff0c;可以看到词表是一个字典&#xff0c;key为t…

电脑显示MSVCR110.dll丢失怎么办,总结5种靠谱的解决方法

在使用电脑过程中&#xff0c;部分用户可能会遇到MSVCR110.dll文件丢失的问题&#xff0c;具体表现为弹出提示窗口&#xff0c;指出缺失该文件&#xff0c;导致部分程序无法正常启动。小编将为您介绍多种解决该问题的指南。 一、找不到MSVCR110.dll是什么情况 MSVCR110.dll是M…

2.线性回归

简化的房价模型 假设1&#xff1a;影响房价的关键因素时卧室个数&#xff0c;卫生间和居住面积&#xff0c;记为 x 1 , x 2 , x 3 x_1,x_2,x_3 x1​,x2​,x3​ 假设2&#xff1a;成交价时关键因素的加权和&#xff1a; y w 1 x 1 w 2 x 2 w 3 x 3 b y w_1x_1w_2x_2w_3x…

在 IVS 2024 加密盛会上开拓创新:CESS 引领去中心化云存储和 CD²N 之发展

作为基于区块链的去中心化云存储网络和 CDN 基础设施网络&#xff0c;CESS 在日本年度加密创业大会 IVS 2024 上成为焦点&#xff01;此次活动在历史悠久的京都举办&#xff0c;围绕“跨越边界”这一主题&#xff0c;吸引了超过15,000 名参会者&#xff0c;其中包括 3,000 创始…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥证明介绍及算法规格】

密钥证明介绍及算法规格 HUKS为密钥提供合法性证明能力&#xff0c;主要应用于非对称密钥的公钥的证明。 基于PKI证书链技术&#xff0c;HUKS可以为存储在HUKS中的非对称密钥对的公钥签发证书&#xff0c;证明其公钥的合法性。业务可以通过系统提供的根CA证书&#xff0c;逐级…

Python数据分析-Excel和 Text 文件的读写操作

1.Excel和 Text 文件的读写操作 1. Text 文件读写包 import sys print(sys.argv[0]) print(__file__) print(sys.path[0]) qopen(sys.path[0] "\out.txt","w",encodingutf-8) q.write(这个是测试一下) q.close() print(done)open 语句可以打开的创建text…

DR模式介绍

DR模式 lvs的三种模式 nat&#xff1a;地址转换 DR模式&#xff1a;直接路由模式 tun&#xff1a;隧道模式 DR模式的特点 调度器在整个lvs集群当中是最重要的&#xff0c;在nat模式下&#xff0c;即负责接受请求&#xff0c;同时根据负载均衡的算法转发流量&#xff0c;响…

OceanBase 社区版,搭建,安装,部署

这是阿里的数据库&#xff0c;因为我目前在杭州&#xff0c;所以企业用这个的比较多。 我看的版本是V4.3.1 官网概述解读 我看的是社区版 他是分布式关系型数据库&#xff0c;支持事务。兼容mysql语法&#xff0c;支持水平扩展&#xff0c;LSM数据压缩。 社区版&#xff0…

ROS的TF系统

一、SLAM 1、SLAM全称是Simultaneous Localization And Mapping&#xff0c;即同时定位与地图构建 2、SLAM软件包Hector_Mapping&#xff0c;←建图可参考链接所示文章 二、机器人定位 1、假设机器人开始建图的位置是地图坐标系的原点 2、则机器人在建图过程中的位置可以描…

qml:Material风格下改变控件的颜色

用过Material的都知道&#xff0c;所有控件的默认颜色是玫红色&#xff0c;如果要改变颜色&#xff0c;只需要加一句代码即可&#xff1a;Material.accent: "blue" 你可以将这句写在顶层&#xff0c;那就是所有颜色都改了&#xff0c;如果你单独写在某个控件下&…

【linux】Shell 脚本中的 For 循环详细用法教程

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

Elasticsearch详细介绍

B站对应视频&#xff1a; Elasticsearch01-01.为什么学习elasticsearch_哔哩哔哩_bilibili 大多数日常项目&#xff0c;搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 首先&#xff0c;查询效率较低。 由于数据…

类和对象(初)

目录 一、面向过程和面向对象初步认识 二、类的引入 命名规则 三、类的定义 四、访问限定符 五、类的作用域 六、类的实例化 七.类对象模型 一、面向过程和面向对象初步认识 1.C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;…

开发个人Ollama-Chat--5 模型管理 (二)

开发个人Ollama-Chat–5 模型管理 (二) ChatGPT 这是该项目的最终效果&#xff0c;使用ollama的open-webui进行人与机器的对话功能&#xff0c;对话的后端服务则完全对接自己开发的Go项目。 如何实现呢&#xff1f;则通过这篇文章&#xff0c;一一给大家剖析后端的原理及功能…

【进阶篇-Day8:JAVA中递归、异常的介绍】

目录 1、递归的介绍和使用1.1 递归的介绍1.2 案例案例一&#xff1a;案例二&#xff1a;案例三&#xff1a;案例四&#xff1a; 1.3 总结 2、异常的介绍和使用2.1 异常的介绍&#xff1a;&#xff08;1&#xff09;能够看懂异常&#xff08;2&#xff09;异常的体系接口和分类&…

fortran快速排序算法,示例对一维数组进行排序

fortran快速排序算法&#xff0c;示例对一维数组进行排序 0. 引言1. 快速排序方法(QuickSqrt)代码实现2. 结语 0. 引言 快速排序&#xff08;QuickSort&#xff09;是一种常用的排序算法&#xff0c;采用分治策略实现。它的基本思想是通过一趟排序将待排序的数据分割成独立的两…

[笔试训练](三十六)106:提取不重复的整数107:哈夫曼编码108:abb

目录 106:提取不重复的整数 107:哈夫曼编码 108:abb 106:提取不重复的整数 题目链接:提取不重复的整数_牛客题霸_牛客网 (nowcoder.com) 题目: ​ 题解: #include <iostream> #include <string> using namespace std; int n0; int cnt[10]; int ret0; int mai…

MySQL CONCAT函数的简单使用

CONCAT函数用于将mysql中查询多列的值拼成一列显示&#xff0c; 使用示例&#xff1a; SELECT CONCAT(attr_name,"&#xff1a;",attr_value) FROM pms_sku_sale_attr_value WHERE sku_id1; 上面SQL语句使用CONCAT函数将attr_name、attr_value两列的值拼成一列&am…

【代码随想录】【算法训练营】【第62天】 [卡码108]冗余连接 [卡码109]冗余连接II

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 62&#xff0c;又是一个周一&#xff0c;ding~ 题目详情 [卡码108] 冗余连接 题目描述 卡码108 冗余连接 LeetCode类似题目684 冗余连接 解题思路 前提&#xff1a; 思路&#xff1a; 重点&a…