89. UE5 RPG 实现伤害 冷却 消耗技能描述

news2025/1/12 6:15:25

在上一篇文章里,我们能够通过富文本显示多种格式的文字,并显示技能描述。在这一篇文章里,我们继续优化技能描述,将技能说需要显示的内容显示出来。

实现火球术的基础描述

首先,我们现实现火球术的基础描述,它属于投掷物类型的技能,触发技能会发射多个投掷物。我们实现原理就是覆写积累的获取技能描述的函数,来实现定义火球术的描述。

public:

	virtual FString GetDescription(int32 Level) override; //获取投射技能描述
	virtual FString GetNextLevelDescription(int32 Level) override; //获取投射技能下一等级描述

然后实现,如果需要换行,我们在字符串里是通过\n来实现切换一行

FString UProjectileSpell::GetDescription(int32 Level)
{
	const int32 ScaledDamage = DamageTypes[FRPGGameplayTags::Get().Damage_Fire].GetValueAtLevel(Level); //根据等级获取技能伤害
	
	if(Level == 1)
	{
		return FString::Printf(TEXT("<Title>火球术</>\n<Small>等级:1</>\n\n<Default>发射 1 颗火球,在发生撞击时产生爆炸,并造成</> <Damage>%i</> <Default>点火焰伤害,并有一定几率燃烧。</>"), ScaledDamage);
	}
	
	return FString::Printf(TEXT("<Title>火球术</>\n<Small>等级:%i</>\n\n<Default>发射 %i 颗火球,在发生撞击时产生爆炸,并造成</> <Damage>%i</> <Default>点火焰伤害,并有一定几率燃烧。</>"), Level, FMath::Min(Level, NumProjectiles), ScaledDamage);
}

FString UProjectileSpell::GetNextLevelDescription(int32 Level)
{
	const int32 ScaledDamage = DamageTypes[FRPGGameplayTags::Get().Damage_Fire].GetValueAtLevel(Level + 1); //根据等级获取技能伤害
	return FString::Printf(TEXT("<Title>下一等级</>\n<Small>等级:%i</>\n\n<Default>发射 %i 颗火球,在发生撞击时产生爆炸,并造成</> <Damage>%i</> <Default>点火焰伤害,并有一定几率燃烧。</>"), Level, FMath::Min(Level, NumProjectiles), ScaledDamage);
}

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

创建火球术类

为了能够保证火球术文本不影响其它投掷物技能,我们要基于投掷物技能类创建一个它的子类,这样,我们修改火球术的技能描述,只会应用给火球术。
在这里插入图片描述
接着,我们将火球术的GA的父类修改为我们新创建的子类
在这里插入图片描述

获取冷却和技能消耗

为了能够在技能描述里显示技能冷却时间和技能的消耗,我们需要是现对应的函数获取
我们在技能基类里增加两个函数,用于获取冷却和消耗,它们是保护性的,只有它或者派生类才可以调用

protected:

	float GetManaCost(float InLevel = 1.f) const; //获取技能蓝量消耗
	float GetCooldown(float InLevel = 1.f) const; //获取技能冷却时间

GAS框架给我们封装了获取对应的GE的函数,我们可以直接通过函数获取,并且从修改项种获取对应的修改属性进行判断,并获取对应的等级的结果。

float URPGGameplayAbility::GetManaCost(const float InLevel) const
{
	float ManaCost = 0.f;
	//获取到冷却GE
	if(const UGameplayEffect* CostEffect = GetCostGameplayEffect())
	{
		//遍历GE修改的内容
		for(FGameplayModifierInfo Mod : CostEffect->Modifiers)
		{
			//判断修改的属性是否为角色蓝量属性
			if(Mod.Attribute == URPGAttributeSet::GetManaAttribute())
			{
				//通过修饰符获取到使用的FScalableFloat并计算传入等级的蓝量消耗,FScalableFloat是受保护性的属性,无法直接获取,只能通过函数
				Mod.ModifierMagnitude.GetStaticMagnitudeIfPossible(InLevel, ManaCost);
				break; //获取到了就结束遍历
			}
		}
	}
	return ManaCost;
}

float URPGGameplayAbility::GetCooldown(const float InLevel) const
{
	float Cooldown = 0.f;
	//获取到技能冷却GE
	if(const UGameplayEffect* CooldownEffect = GetCooldownGameplayEffect())
	{
		//获取到当前冷却时间
		CooldownEffect->DurationMagnitude.GetStaticMagnitudeIfPossible(InLevel, Cooldown);
	}
	return Cooldown;
}

然后我们在伤害技能类里(所有具有伤害的技能类都继承至它)添加一个根据伤害类型获取伤害数值的函数,伤害类型是我们通过标签添加注册

float GetDamageByDamageType(float InLevel, const FGameplayTag& DamageType); //根据伤害类型获取伤害

然后我们根据配置的配置里,获取对应的标签的曲线图表,来获取对应等级的伤害

float URPGDamageGameplayAbility::GetDamageByDamageType(const float InLevel, const FGameplayTag& DamageType)
{
	checkf(DamageTypes.Contains(DamageType), TEXT("技能 [%s] 没有包含 [%s] 类型的伤害"), *GetNameSafe(this), *DamageType.ToString());
	return DamageTypes[DamageType].GetValueAtLevel(InLevel); //根据等级获取技能伤害
}

需要的数值都能够获取到,接着,我们在火球技能里覆写获取描述的函数

UCLASS()
class RPG_API URPGFireBolt : public UProjectileSpell
{
	GENERATED_BODY()

public:
	// FString GetDescriptionAtLevel(int32 INT32, const char* Str);
	virtual FString GetDescription(int32 Level) override; //获取投射技能描述
	virtual FString GetNextLevelDescription(int32 Level) override; //获取投射技能下一等级描述

	FString GetDescriptionAtLevel(int32 Level, const FString& Title); //获取对应等级的技能描述
};

由于函数的重复代码太过,我就增加了一个通过等级获取技能描述的函数,并且可以自定义标题,当前等级和下一等级的技能描述的标题不同。
这里需要注意的点是,字符串也可以多个拼接,并且你如果输入的是浮点数,可以通过设置%.1f这样的写法来设置它的分段,防止有太小的数值出现。

FString URPGFireBolt::GetDescription(const int32 Level)
{
	return GetDescriptionAtLevel(Level, L"火球术");
}

FString URPGFireBolt::GetNextLevelDescription(const int32 Level)
{
	return GetDescriptionAtLevel(Level, L"下一等级");
}

FString URPGFireBolt::GetDescriptionAtLevel(const int32 Level, const FString& Title)
{
	const int32 Damage = GetDamageByDamageType(Level, FRPGGameplayTags::Get().Damage_Fire);
	const float ManaCost = GetManaCost(Level);
	const float Cooldown = GetCooldown(Level);
	
	return FString::Printf(TEXT(
		// 标题
		"<Title>%s</>\n"

		// 细节
		"<Small>等级:</> <Level>%i</>\n"
		"<Small>技能冷却:</> <Cooldown>%.1f</>\n"
		"<Small>蓝量消耗:</> <ManaCost>%.1f</>\n\n"//%.1f会四舍五入到小数点后一位

		// 技能描述
		"<Default>发射 %i 颗火球,在发生撞击时产生爆炸,并造成</> <Damage>%i</> <Default>点火焰伤害,并有一定几率燃烧。</>"),

		// 动态修改值
		*Title,
		Level,
		Cooldown,
		ManaCost,
		FMath::Min(Level, NumProjectiles),
		Damage);
}

接着,编译打开项目测试效果。
在这里插入图片描述

实现技能取消选中功能

接下来,我们再实现一个小功能,就是在第二次点击技能的时候,取消技能选中状态。
这个功能的实现,需要我们取消选中的时候,要取消掉技能显示的升降级按钮和等级显示。并且要将技能描述里的内容清空。
我们在技能面板控制器增加一个新的蓝图调用函数,用于技能按钮取消选中时调用

	UFUNCTION(BlueprintCallable)
	void GlobeDeselect(); //取消按钮选中处理

在函数实现这里,重置缓存的内容,并广播清空技能描述的内容。

void USpellMenuWidgetController::GlobeDeselect()
{
	const FRPGGameplayTags GameplayTags = FRPGGameplayTags::Get();
	SelectedAbility.Ability = GameplayTags.Abilities_None;
	SelectedAbility.Status = GameplayTags.Abilities_Status_Locked;
	SelectedAbility.Level = 0;

	SpellDescriptionSignature.Broadcast(FString(), FString());
}

有了此函数,打开技能按钮的蓝图在触发取消选中时,调用自身取消状态,并调用刚添加的函数清空技能描述,播放一个取消选中音效。
在这里插入图片描述

防止未添加标签显示技能描述内容

我发现在选中锁定按钮,并锁定按钮没有设置对应的数据时,还会显示技能在多少等级后解锁,为了解决这个问题,我们在ASC函数获取技能描述时,添加判断,如果技能标签未设置,或设置为空,则返回空内容

bool URPGAbilitySystemComponent::GetDescriptionByAbilityTag(const FGameplayTag& AbilityTag, FString& OutDescription, FString& OutNextLevelDescription)
{
	//如果当前技能处于锁定状态,将无法获取到对应的技能描述
	if(FGameplayAbilitySpec* AbilitySpec = GetSpecFromAbilityTag(AbilityTag))
	{
		if(URPGGameplayAbility* RPGAbility = Cast<URPGGameplayAbility>(AbilitySpec->Ability))
		{
			OutDescription = RPGAbility->GetDescription(AbilitySpec->Level);
			OutNextLevelDescription = RPGAbility->GetNextLevelDescription(AbilitySpec->Level + 1);
			return true;
		}
	}

	//如果技能是锁定状态,将显示锁定技能描述
	const UAbilityInfo* AbilityInfo = URPGAbilitySystemBlueprintLibrary::GetAbilityInfo(GetAvatarActor());
	if(!AbilityTag.IsValid() || AbilityTag.MatchesTagExact(FRPGGameplayTags::Get().Abilities_None))
	{
		OutDescription = FString();
	}
	else
	{
		OutDescription = URPGGameplayAbility::GetLockedDescription(AbilityInfo->FindAbilityInfoForTag(AbilityTag).LevelRequirement);
	}
	OutNextLevelDescription = FString();
	return  false;
	
}

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

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

相关文章

kettle将Excel数据导入oracle

Excel数据导入Oracle示例 Kettle将Excel数据导入Oracle过程记录如下&#xff1a; 3、编辑转换 &#xff08;1&#xff09;Excel输入控件 双击Excel输入&#xff0c;重命名控件名称get_data&#xff0c;在文件选项卡浏览选择Excel文件&#xff08;若不能识别Excel文件&#…

关系,条件和逻辑操作符详解

1关系操作符 C 语言用于比较的表达式&#xff0c;称为 “关系表达式”&#xff08;relational expression&#xff09;&#xff0c;里面使用的运算符就称 为“关系运算符”&#xff08;relational operator&#xff09;&#xff0c;主要有下面6个。 • > 大于运算符 • &…

C++之模版初阶

目录 前言 1.泛型编程 2.函数模版 2.1函数模版概念 2.2函数模版格式 2.3函数模版的原理 2.4函数模版的实例化 2.5模版参数的匹配原则 3.类模版 3.1类模版的定义格式 3.2类模版的实例化 结束语 前言 前面我们学习了C的类与对象和内存管理&#xff0c;接下来我们继续学习…

docker资源控制与数据卷

docker数据卷 容器和宿主机之间数据共享---------挂载卷------------容器内的目录和宿主机的目录进行挂载。实现数据文件共享。 容器的生命周期有限&#xff0c;一旦重启所有对容器内部文件数据修改以及保存的数据都会被初始化&#xff0c;为了防止数据的丢失&#xff0c;重要…

uni-app发布安卓app打包时必须在APP模块配置中选中需要的模块

最近尝试开发一个语音对话的app&#xff0c;在调试阶段没有选中“record录音”模块&#xff0c;安卓基座运行在雷电模拟器上是没有问题的&#xff0c;但是直接云打包在手机上运行就不行了&#xff0c;语音输入没有反应。 经过试验发现是manifest.json没有勾选APP模块配置中的“…

在本地电脑部署属于你的AI大模型

前言&#xff1a; 现在的ai很多&#xff0c;让我看得有些眼花缭乱&#xff0c;随着ai的发展&#xff0c;现在已经有很多ai的大模型已经支持开源&#xff0c;所以现在支持部署一个大模型在自己电脑上&#xff0c;数据私人化。 如果不知道自己是否需要或者是否合适配置可以跳到…

Unity(2022.3.38LTS) - 初步学习C#

目录 一. C#简介 二. 配置代码编辑器 三. C#基本语法 四. 创建脚本 创建C#脚本的方式 1. 在文件夹新建 2. 直接在物体组件创建 五. 例子 扩展物体旋转的方式: 一. C#简介 Unity 中使用的 C# 是一种面向对象的编程语言&#xff0c;具有强大的功能和广泛的应用。 特点…

【笔记】MSPM0G3507移植RT-Thread——MSPM0G3507与RT_Thread(二)

一.创建新工程 找到"driverlib\empty"空白工程&#xff0c;CTRLC然后CTRLV复制副本 重命名为G3507_RTT 打开KEIL工程 双击empty.syscfg&#xff0c;然后打开SYSCONFIG 我的不知道为啥没有48pin选项&#xff0c;如果你也一样&#xff0c;可以跟着我做&#xff0c;如果…

WebDeveloper:1靶机

端口扫描 靶机ip地址为192.168.153.158 目录扫描 访问80端口 拼接访问 /ipdata 发现了一个流量包 放在 wireshark 查看&#xff0c;找到 账号密码 账号&#xff1a;webdeveloper 密码&#xff1a;Te5eQg&4sBS!Yr$)wf%(DcAd 拼接 /wp-login.php 找到登录框 登录成功 找…

vue3+Element Plus功能组件封装——顶部导航(动态渲染+样式调整)

网页顶部的导航栏&#xff0c;一般由代码动态生成 菜单数据在文件内统一配置&#xff0c;方便增删改查&#xff0c;导入后可自动生成导航菜单 代码如下 1.dom部分&#xff08;简单示例&#xff09; <el-menu mode"horizontal" :default-active"currentPath…

java Spring|day1.Spring基础

框架 Core IoC容器AOP功能数据绑定类型转换等 TestingData AccessWeb Servlet 核心 IOC容器 定义 IoC&#xff08;Inversion of Control&#xff09;是控制反转的意思&#xff0c;这是一种面向对象编程的设计思想。 优点 在不采用这种思想的情况下&#xff0c;我们需要自…

51单片机-动态数码管显示

动态数码管显示就是在数码管中显示多个数字&#xff0c;利用了人眼的视觉差显示了数字&#xff0c;但是存在一个问题&#xff0c;在顺序执行显示数字和数值的设置时候&#xff0c;数码管的显示数据会出现错乱&#xff0c;因此&#xff0c;需要执行消影&#xff0c;每次在数码管…

机器学习速成第二集——监督学习之分类(理论部分)!

目录 分类算法的种类 分类问题的应用场景 模型选择与评估 结论 如何在不同数据集中选择最适合的监督学习分类算法&#xff1f; 监督学习中集成模型与单一模型相比有哪些具体的优势和劣势&#xff1f; 优势&#xff1a; 劣势&#xff1a; 在处理高维稀疏数据时&#xf…

AAAI论文截稿

标题&#xff1a;AAAI2025截稿不足36小时!抓住今年最后的机会&#xff01; 点击上方“会议之眼”关注 重磅干货&#xff0c;第一时间送达 会议之眼 快讯 AAAI&#xff08;Association for the Advancement of Artificial Intelligence&#xff09;全称国际顶级人工智能学术…

Promise 一个基于协程下的任务队列状态管理任务包 解决复杂的异步转同步问题

前言 一直都想写关于Promise的东西&#xff0c;Promise解决的问题特别多&#xff0c;而普通前端就把这东西结合ajax来做一个await request() ,如果仅仅作为这样一种东西使用那就太可惜了。 它是队列的任务包 前端同学应该是没听说过队列&#xff0c;但是大前端同学&#xff0…

Kubernetes-K8S

Kubernetes由于单词太长&#xff0c;省略掉中间8个字母简称为K8S。它介于应用服务和服务器之间。能够通过策略协调和管理多个服务&#xff0c;只需要一个YAML文件配置。定义应用的部署顺序等信息&#xff0c;自动部署应用到各个服务器&#xff0c;还可以自动扩容缩容。 架构原理…

RabbitMQ实现多线程处理接收消息

前言&#xff1a;在使用RabbitListener注解来指定消费方法的时候&#xff0c;默认情况是单线程去监听队列&#xff0c;但是这个如果在高并发的场景中会出现很多个任务&#xff0c;但是每次只消费一个消息&#xff0c;就会很缓慢。单线程处理消息容易引起消息处理缓慢&#xff0…

前程无忧 阿里227滑块 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;有相关问题请第一时间头像私信联系我删…

Qt下使用QtPdfium处理PDF文档

文章目录 前言一、使用QPdfDocument二、使用QtPdfium三、示例完整代码总结 前言 在我之前的文章中&#xff0c;有提到如何生成PDF&#xff0c;这个可以查看文末参考文章。如果要实现在Qt下进行PDF文档的处理&#xff08;读取显示&#xff09;&#xff0c;可以使用自带的PDF库以…

Unity(2022.3.38LTS) - 基础概念

目录 一. 场景 二. 游戏对象 三. 组件 四. 标签 五. 静态游戏对象 六. 保存 一. 场景 Unity 场景是游戏或应用开发中的一个重要概念。 Unity 场景的组成元素&#xff1a; 它通常包含了各种游戏对象&#xff0c;比如 3D 模型、灯光、摄像机、脚本组件、音频源等等。 作用…