90. UE5 RPG 实现技能的装配

在上一篇里,我们实现了在技能面板,点击技能能够显示出技能的相关描述以及下一级的技能的对应描述。
在这一篇里,我们实现一下技能的装配。
在之前,我们实现了点击按钮时,在技能面板控制器里存储了当前选中的技能的相关信息,有了这个信息以后,我们在实现装配时,可以使用这个数据进行处理。
在这里插入图片描述
当选中技能后,我们接着点击下面的技能插槽时,如果符合装配条件,我们将实现装配到对应的插槽。
在这里插入图片描述

添加技能类型配置

在技能装配这里,我们分了主动技能和被动技能,不同类型的技能无法装配。所以,我们需要在技能数据里设置对应的类型,我们还是使用类型标签设置
我们在技能数据结构体内增加一个配置项,用于设置类型
在这里插入图片描述
然后在标签管理这里,增加三项,主动技能,被动技能和空

	FGameplayTag Abilities_Type_Offensive; //技能类型 主动技能FGameplayTag Abilities_Type_Passive; //技能类型 被动技能FGameplayTag Abilities_Type_None; //技能类型 空 受击等技能设置

并且注册到标签管理器

	/** 当前技能类型标签*/GameplayTags.Abilities_Type_Offensive = UGameplayTagsManager::Get().AddNativeGameplayTag(FName("Abilities.Type.Offensive"),FString("主动技能"));GameplayTags.Abilities_Type_Passive = UGameplayTagsManager::Get().AddNativeGameplayTag(FName("Abilities.Type.Passive"),FString("被动技能"));GameplayTags.Abilities_Type_None = UGameplayTagsManager::Get().AddNativeGameplayTag(FName("Abilities.Type.None"),FString("啥也不是"));

接着打开UE,在技能配置里,设置技能的对应类型
在这里插入图片描述

添加技能装配事件触发

我们也注意到,技能分为两大类型,主动技能和被动技能,为了区分它们,我们需要设置对应的类型标签。
首先在装配按钮这里添加可设置的类型变量,注意把眼睛打开。
在这里插入图片描述
然后在外部设置它对应的类型
在这里插入图片描述
被动技能也设置
在这里插入图片描述
然后给按钮绑定一个点击事件
在这里插入图片描述

添加技能装配处理逻辑

我们需要在技能面板控制器里增加一个点击按钮,用来实现点击事件

	UFUNCTION(BlueprintCallable)void EquipButtonPressed(const FGameplayTag& SlotTag, const FGameplayTag& AbilityType); //装配技能按钮按下事件

在实现这里,我首先判断条件是否达成,然后调用ASC里的实际处理技能装配的逻辑

void USpellMenuWidgetController::EquipButtonPressed(const FGameplayTag& SlotTag, const FGameplayTag& AbilityType)
{const FRPGGameplayTags GameplayTags = FRPGGameplayTags::Get();//获取装配技能的类型const FGameplayTag& SelectedAbilityType = AbilityInfo->FindAbilityInfoForTag(SelectedAbility.Ability).AbilityType;if(!SelectedAbilityType.MatchesTagExact(AbilityType)) return; //类型不同无法装配//获取装配技能的输入标签const FGameplayTag& SelectedAbilityInputTag = GetRPGASC()->GetInputTagFromAbilityTag(SelectedAbility.Ability);if(SelectedAbilityInputTag.MatchesTagExact(SlotTag)) return; //如果当前技能输入和插槽标签相同,证明已经装配,不需要再处理//调用装配技能函数,进行处理GetRPGASC()->ServerEquipAbility(SelectedAbility.Ability, SlotTag);
}

接着,我们在ASC里增加多个函数,用于实现这个逻辑,为什么在ASC里,因为GA是属于GAS系统的,GAS相关的内容就放在GAS相关的类里实现处理

	UFUNCTION(Server, Reliable) //在服务器处理技能装配,传入技能标签和装配的技能标签void ServerEquipAbility(const FGameplayTag& AbilityTag, const FGameplayTag& Slot); UFUNCTION(Client, Reliable) //在客户端处理技能装配void ClientEquipAbility(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot);void ClearSlot(FGameplayAbilitySpec* Spec); //清除技能装配插槽的技能void ClearAbilitiesOfSlot(const FGameplayTag& Slot); //根据输入标签,清除技能装配插槽的技能static bool AbilityHasSlot(FGameplayAbilitySpec* Spec, const FGameplayTag& Slot); //判断当前技能实例是否处于目标技能装配插槽

首先,我们看一下后面三个函数,他们是为了处理技能实现的函数。

首先是AbilityHasSlot,需要传入一个技能实例和一个输入标签(插槽标识),用来判断技能是否属于这个插槽

bool URPGAbilitySystemComponent::AbilityHasSlot(FGameplayAbilitySpec* Spec, const FGameplayTag& Slot)
{for(FGameplayTag Tag : Spec->DynamicAbilityTags){if(Tag.MatchesTagExact(Slot)){return true;}}return false;
}

然后就是清除掉技能的装配的插槽,其实就是清除掉GA的输入标签

void URPGAbilitySystemComponent::ClearSlot(FGameplayAbilitySpec* Spec)
{const FGameplayTag Slot = GetInputTagFromSpec(*Spec);Spec->DynamicAbilityTags.RemoveTag(Slot);MarkAbilitySpecDirty(*Spec);
}

然后就是根据输入标签(插槽)清除掉所有技能的对应的插槽,这个会用到上面的两个函数。

void URPGAbilitySystemComponent::ClearAbilitiesOfSlot(const FGameplayTag& Slot)
{FScopedAbilityListLock ActiveScopeLock(*this);for(FGameplayAbilitySpec& Spec : GetActivatableAbilities()){if(AbilityHasSlot(&Spec, Slot)){ClearSlot(&Spec);}}
}

接下来就是装配函数,我们先获取到需要装配的技能实例,获取到当前装配的插槽和当前的技能的状态标签,然后将需要装配到的目标插槽的的技能清除掉,并将技能自身的插槽清除,并将对应的标签修改掉。然后触发客户端的调用,并及时将技能的修改复制到客户端(当前执行只在服务器运行,客户端不会运行,只需要将结果复制到即可)

void URPGAbilitySystemComponent::ServerEquipAbility_Implementation(const FGameplayTag& AbilityTag, const FGameplayTag& Slot)
{if(FGameplayAbilitySpec* AbilitySpec = GetSpecFromAbilityTag(AbilityTag)){const FGameplayTag& PrevSlot = GetInputTagFromSpec(*AbilitySpec); //技能之前装配的插槽const FGameplayTag& Status = GetStatusTagFromSpec(*AbilitySpec); //当前技能的状态标签//判断技能的状态,技能状态只有在已装配或者已解锁的状态才可以装配const FRPGGameplayTags GameplayTags = FRPGGameplayTags::Get();if(Status == GameplayTags.Abilities_Status_Equipped || Status == GameplayTags.Abilities_Status_Unlocked){ClearAbilitiesOfSlot(Slot); //通过技能的输入标签清除掉插槽的技能ClearSlot(AbilitySpec); //清除掉当前技能的输入标签AbilitySpec->DynamicAbilityTags.AddTag(Slot); //将目标插槽的输入标签添加到技能实例的动态标签容器中//如果状态标签是已解锁,我们需要将其修改为已装配状态if(Status.MatchesTagExact(GameplayTags.Abilities_Status_Unlocked)){AbilitySpec->DynamicAbilityTags.RemoveTag(GameplayTags.Abilities_Status_Unlocked);AbilitySpec->DynamicAbilityTags.AddTag(GameplayTags.Abilities_Status_Equipped);}ClientEquipAbility(AbilityTag, Status, Slot, PrevSlot);MarkAbilitySpecDirty(*AbilitySpec); //立即将其复制到每个客户端}}
}

在客户端,我们只进行一个委托的广播,然后让控制器监听去修改
所以,我们增加一个技能装配后的委托

DECLARE_MULTICAST_DELEGATE_FourParams(FAbilityEquipped, const FGameplayTag& /*技能标签*/, const FGameplayTag& /*技能状态标签*/, const FGameplayTag& /*输入标签*/, const FGameplayTag& /*上一个输入标签*/);
FAbilityEquipped AbilityEquipped; //技能装配更新回调

然后在客户端执行的函数里进行调用

void URPGAbilitySystemComponent::ClientEquipAbility_Implementation(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot)
{AbilityEquipped.Broadcast(AbilityTag, Status, Slot, PreviousSlot); //在客户端将更新后的标签广播
}

在控制器接收技能装配委托

接下来我们要在控制器实现对技能装配委托的监听,考虑到,技能装配后,在技能面板和Overlay里都需要使用它,我们将函数写到基类里,然后在对应的控制器里进行监听绑定。
我们在控制器基类增加一个委托回调函数

	//监听技能装配后的处理void OnAbilityEquipped(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot) const;

然后,在实现里,我们通过传递过来的标签,实现技能数据的广播,然后在UI监听更新

void URPGWidgetController::OnAbilityEquipped(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot) const
{const FRPGGameplayTags GameplayTags = FRPGGameplayTags::Get();//清除旧插槽的数据FRPGAbilityInfo LastSlotInfo;LastSlotInfo.StatusTag = GameplayTags.Abilities_Status_Unlocked;LastSlotInfo.InputTag = PreviousSlot;LastSlotInfo.AbilityTag = GameplayTags.Abilities_None;AbilityInfoDelegate.Broadcast(LastSlotInfo);//更新新插槽的数据FRPGAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(AbilityTag);Info.StatusTag = Status;Info.InputTag = Slot;AbilityInfoDelegate.Broadcast(Info);
}

我们在需要监听的派生类里,添加对其的监听

//监听技能装配的回调GetRPGASC()->AbilityEquipped.AddUObject(this, &USpellMenuWidgetController::OnAbilityEquipped);

编译打开代码,我们在数据接收这里,增加,在判断是否为对应插槽的更新数据,如果技能标签为空,则是清除旧插槽。
在这里插入图片描述
在技能按钮里,还需要多做一步操作就是处理技能冷却的监听处理
在这里插入图片描述

解决降级为0级技能还装配的问题

当技能等级降级为0级以后,技能的装配变为了无法装配,我们需要将此信息同步到技能栏和装配技能栏。
所以我们在降级函数中,将其增加一个委托,再调用客户端更新
在这里插入图片描述

接下来是效果展示
在这里插入图片描述

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

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

相关文章

ZooKeeper工作原理

1. ZooKeeper工作原理 1.1 ZooKeeper角色 领导者(Leader):在Zookeeper集群中,Leader是负责管理集群事务的节点。它负责处理所有的写请求,并将这些请求转化为事务,并提交事务日志。Leader节点还负责发起和决…

用C#写一个随机音乐播放器

form1中namespce里的代码如下 public partial class Form1 : Form {public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){string folder textBox1.Text;string folderPath folder; // 指定音频文件所在的文件夹路径OpenRandomFi…

vue项目上线打包后出现的问题

1、出现空白页 1.1 打包路径: module.exports {publicPath:./, //修改为绝对路径 } 修改完打包路径后build可以展示页面 1.2 路由模式: 项目上线要求是history模式,需要后端做重定向 前端自测可以使用h…

JavaWeb笔记_FilterListener

一.过滤器 1.1 过滤器概述 过滤器主要用来拦截目标资源(静态资源或动态资源)的请求和响应 (类似地铁的安检) 我们访问动态或静态资源都要通过URL访问:http://localhost:8080/... 所以过滤器本质上拦截的是URL 1.2 过滤…

dps或者ppt文件判断是否加密

doc文件是否加密可以通过fib来判断,例如 同样的方法判断ppt也可以,但是在判断wps保存的dps文件时,提示没有加密,文件双击打开时又需要密码,查看ppt格式文档有下面发现 查看文件的二进制发现了加密标识 后面再研究doc x…

OpenCV图像滤波(4)构建图像金字塔函数buildPyramid()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在计算机视觉和图像处理中,构建图像金字塔(Image Pyramid)是一种常用的技术,它生成一系列分辨率逐…

CISAW信息安全保障人员认证是否值得学习?

CISAW信息安全保障人员认证的学习难度因人而异。 如果考生具备足够的学习能力以及丰富的信息安全工作经验,那么考试的难度可能会相对较低。 相反,如果考生缺少这些条件,学习难度可能会相对较高。 1. 从考试内容来看,CISAW以概念…

Neutralinojs教程项目实战初体验(踩坑指南),干翻 electron

Neutralinojs 项目实战初体验(踩坑指南),干翻 electron Neutralinojs 官方文档 卧槽卧槽,!这个年轻人居然用浏览器把电脑关机了_哔哩哔哩_bilibili正是在下 本教程搭建的是纯原生项目,没有和其它前端框架…

简单快捷!Yarn的安装与使用指南

Yarn 是由 Facebook (现 Meta) 开发的包管理工具。 今天,我将介绍如何使用 Yarn。 目录 Yarn 的官方网站 关于安装 版本确认 开始一个新项目(创建 package.json 文件) 安装软件包 升级包 运行脚本 执行包的命令 卸载包 总结 Yarn 的…

低代码平台在采购管理中的革新与应用

引言 随着企业数字化转型的不断推进,传统的企业软件开发模式面临着诸多挑战。开发周期长、成本高、需求变更频繁等问题使得企业在快速变化的市场中难以保持敏捷性。低代码平台作为一种新的开发模式,凭借其“低代码”甚至“零代码”的特性,极大…

APT 安装软件详细教程

文章目录 APT 安装软件详细教程APT 概述APT 的基本命令APT 命令详解安装软件包更新和升级软件包删除软件包搜索和查找软件包管理软件包依赖清理软件包缓存APT 配置软件源配置自定义软件源常见问题及解决方案解决软件包依赖问题处理软件源错误其他常见问题使用 APT 的最佳实践总…

词的向量化和文本向量化

词的向量化和文本向量化 向量化one-hot编码提前准备词表不提前准备词表one-hot缺点 词向量简介词向量的定义和目标word embedding和word vector的区别onehot编码与词向量关系构建 训练方式1(基于语言模型)训练方式2(基于窗口)CBOW…

选择排序思路和算法实现

选择排序 在未排序的数组中,用第一个数去和后面的数比较,找出最小的数,和第一个数交换。第一个数已为已排序的数。 相当于0~7 从0~7中找到最小的数放在0 从1~7中找到最小的数放在1 从2~7中找到最小的数放在2 ...以此类推 从6~7中找到最…

从简单到复杂:9款画底纹软件让设计更生动

底纹不知道怎么画?快来试一试这9款软件吧,可以让你的设计作品更加出彩,相信我,用了就是赚到,分别是即时设计、Adobe Illustrator、CorelDRAW、Photoshop、AutoCAD、Inkscape、GIMP、ZebraDesigner、LabelJoy。 1、即时…

软件定义AI算力:解锁AI算力的新时代

从1964年发明CPU——即IBM System 360发布的那一年算起,已经过去60年,我们已经从传统的CPU算力时代跨越到了以GPU算力为代表的加速计算时代。尤其是随着大模型的广泛应用和AIGC技术的崛起,行业对GPU等AI算力的需求呈现出爆炸式的增长。据华为…

微短剧出海CPS分销推广影视平台系统搭建思维逻辑介绍

随着国内短剧市场的蓬勃发展,其独特的魅力与影响力已跨越国界,成为海外观众的新宠。这一趋势不仅推动了短剧内容的全球化传播,也为海外市场的CPS(按销售分润)分销模式提供了广阔舞台。连接内容创作者、平台运营者、系统…

VMware 的网络模式详解

VMware 的网络模式详解 使用 VMware 创建虚拟机,配置虚拟机网络时,主要有三个选项,分别是桥接模式、NAT 模式、仅主机模式 这三个模式到底有什么含义? VMware 是通过虚拟网络,即虚拟交换机,来连接物理机&…

angular入门基础教程(五)父子组件的数据通信

组件之间的通信是我们业务开发中少不了的,先了解下父子组件的通信 父组件传数据给子组件 前面,我们学会会动态属性的绑定,所以在父组件中给子组件绑定属性,在子组件中就可以使用这个属性了。 父组件中声明然后赋值 export class AppCompon…

智能开关助力酒店管理提升

随着科技的迅猛跃进,智能化浪潮席卷全球,酒店业亦不例外地迎来了智能化转型的新纪元。智能开关,作为这股浪潮中的先锋,凭借其尖端的通信技术和智能控制逻辑,正深刻改变着酒店的运营模式与顾客体验。 它不仅赋予了酒店远…

IP地址证书签发之后可以绑定到指定端口访问吗?

IP地址证书概述 IP地址证书,也称为IP SSL证书,是为互联网协议(IP)地址提供安全认证的一种证书。它包含公钥、所有者信息以及由可信的证书颁发机构(CA)签发的数字签名。通过使用公钥基础设施(PKI),IP地址证书确保了网络实体之间的信任和验证。…