89. UE5 RPG 实现伤害 冷却 消耗技能描述
在上一篇文章里,我们能够通过富文本显示多种格式的文字,并显示技能描述。在这一篇文章里,我们继续优化技能描述,将技能说需要显示的内容显示出来。
实现火球术的基础描述
首先,我们现实现火球术的基础描述,它属于投掷物类型的技能,触发技能会发射多个投掷物。我们实现原理就是覆写积累的获取技能描述的函数,来实现定义火球术的描述。
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;//获取到冷却GEif(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;//获取到技能冷却GEif(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;}
相关文章:
89. UE5 RPG 实现伤害 冷却 消耗技能描述
在上一篇文章里,我们能够通过富文本显示多种格式的文字,并显示技能描述。在这一篇文章里,我们继续优化技能描述,将技能说需要显示的内容显示出来。 实现火球术的基础描述 首先,我们现实现火球术的基础描述࿰…...
el-tree树状控件,定位到选中的节点的位置
效果图 在el-tree 控件加 :render-content"renderContent" 在掉接口的方法中 实际有用的是setTimeout 方法和this.$refs.xxxxxx.setCheckedKeys([industrycodeList]) if(res.data.swindustrylist.length>0){res.data.swindustrylist.forEach(item > {industry…...
YOLO目标检测的单目(多目标测距),使用相机光学模型,支持目标检测模型训练,可输出目标位置和距离信息并可视化
本项目旨在开发一个基于YOLO的目标检测系统,该系统不仅能检测图像中的多个目标,还能利用单目摄像头的图像估计每个目标与摄像头之间的相对距离。系统的核心组成部分包括目标检测、距离估计、模型训练以及结果可视化。 主要功能 目标检测:使用…...
unity简易lua文件迁移工具
一. 了解商业游戏的Lua热更新开发方式 市面上的3种结合Lua热更新的开发方式 1.纯Lua开发(所有的游戏主要逻辑都用Lua实现) 好处:机动性强;坏处:代码效率略差 2.半C#,半Lua开发(核心逻辑C#开发…...
Elasticsearch中的自动补全功能详解与实践
简介 自动补全是现代搜索引擎中的一项重要功能,它能够根据用户的输入提供实时的建议,提高用户体验。Elasticsearch提供了Completion Suggester查询来实现这一功能。本文将详细介绍Elasticsearch中的自动补全功能,并提供详细的配置和查询示例…...
前端如何使用Nginx代理dist网页,代理websocket,代理后端
本文将指导您如何配置Nginx以代理前后端分离的项目,并特别说明了对WebSocket的代理设置。通过本教程,您将能够实现一次性配置,进而使项目能够在任意局域网服务器上部署,并可通过IP地址或域名访问服务。 笔者建议 先速览本文了解大…...
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. 问题解决
问题描述 原来我的服务器docker服务运行正常,但在某次尝试用时, 根据系统的错误提示执行了snap install docker指令之后, 再执行docker ps命令则提示Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running…...
零基础学习Redis(2) -- Redis安装与配置
Redis官方是并不支持Windows系统的,并且现在绝大部分公司都是使用的Linux,所以我们在Linux上进行安装,这里我使用的是Ubuntu 1. 安装步骤 1. 首先使用工具连接到我们的云服务器,然后输入apt指令搜索redis相关的软件包࿱…...
UniApp第一天
一、官网介绍 1.1、 SDK SDK是"Software Development Kit"的缩写,中文意思是“软件开发工具包”。SDK通常是由软件开发者为其他开发者提供的一个软件工具集合,用于帮助开发者快速开发、测试和部署软件应用。SDK通常包含了一系列的开发工具、库…...
TLE4966-3G带方向检测功能的高灵敏度汽车霍尔开关
TLE4966-3G是一款集成电路双霍尔效应传感器,专为使用旋转极轮的高精度应用而设计。通过片上有源补偿电路和斩波器技术实现精确的磁切换点和高温稳定性。 该传感器在Q2提供速度输出,其状态(高或低)与磁场值相对应。对于超过阈值BO…...
Github 2024-08-14 C开源项目日报Top10
根据Github Trendings的统计,今日(2024-08-14统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10Objective-C项目1PHP项目1Python项目1PHP:流行的Web开发脚本语言 创建周期:4710 天开发语言:C, PHP协议类型:OtherStar数量:37340 …...
飞桨Paddle API index_add 详解
index_add paddle.index_add(x, index, axis, value, nameNone)[源代码] 沿着指定轴 axis 将 index 中指定位置的 x 与 value 相加,并写入到结果 Tensor 中的对应位置。这里 index 是一个 1-D Tensor。除 axis 轴外,返回的 Tensor 其余维度大小和输入 …...
后端代码练习1——加法计算器
1. 需求 输入两个整数,点击 “点击相加” 按钮,显示计算结果。 2.准备工作 创建Spring Boot项目,引入Spring Web依赖,把前端代码放入static目录下。 2.1 前端代码 <!DOCTYPE html> <html lang"en"> <h…...
观察者模式和MQ是什么关系
观察者模式(Observer Pattern)和MQ(Message Queue,消息队列)之间的关系主要体现在它们所实现的功能和机制上的相似性,尽管它们在技术实现和应用场景上有所不同。 观察者模式 观察者模式是一种行为型设计模…...
JDK动态代理和CGLIB动态代理案例分析
JDK动态代理和CGLIB动态代理案例分析 JDK动态代理和CGLIB动态代理的实现原理如下: JDK动态代理的实现原理: JDK动态代理是基于Java的反射机制实现的实现一个继承InvocationHandler接口的对象,重写invoke方法,invoke方法中可以在目…...
【数据结构-前缀哈希】力扣1124. 表现良好的最长时间段
给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。 我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。 所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大…...
电商平台产品ID|CDN与预渲染|前端边缘计算
技术实现 都是通过ID拿到属性,进行预渲染html,通过 oss 分发出去 详情页这种基本都是通过 ssr 渲染出来,然后上缓存 CDN 分发到边缘节点来处理,具体逻辑可以参考 淘宝——EdgeRoutine边缘计算(CDNServerless 边缘计算…...
LATTICE进阶篇DDR2--(4)DDR2 IP核总结
一、IP核的时钟框架 1片DDR2的接口是16位,且DDR2是双边沿读取的, 故当DDR2芯片的时钟为200M时,右侧DDR2芯片上的数据吞吐率为200M*2*16b,左侧数据吞吐率为200M*32b,左右两侧数据吞吐量相等。 根据上规律可知…...
windows下php安装kafka
下载zookeeper Kafka 依赖 Zookeeper 进行分布式协调,所以需要下载Zookeeper ,当然你也可以使用kafka包里自带的一个默认配置的 Zookeeper。这里我们单独下载一个 访问Zookeeper官方下载页面在页面中找到最新的稳定版本,点击相应的下载链接…...
【wiki知识库】09.欢迎页面展示(浏览量统计)SpringBoot部分
🍊 编程有易不绕弯,成长之路不孤单! 大家好,我是熊哈哈,这个项目从我接手到现在有了两个多月的时间了吧,其实本来我在七月初就做完的吧,但是六月份的时候生病了,在家里休息了一个月的…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
