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.rhkb.cn/news/403925.html

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

相关文章

阿里云搜索专家实操:如何高效构建企业级 AI 搜索?

作者:来自阿里云搜索产品专家牛俊 本文由阿里云搜索产品专家牛俊在【AI 搜索 TechDay】上的分享【阿里云 AI 搜索 Demo 展示和动手实践】整理而成。 阿里云 AI 搜索的产品能力与业务价值 阿里云 AI 搜索的方案,基于阿里云 Elasticsearch Inference API…

Java巅峰之路---进阶篇---面向对象(一)

static关键字 介绍 static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量。 其中,被static修饰的成员变量,叫做静态变量;被static修饰的成员方法,叫做静态方法。 静态变量 调用…

牛客面经学习笔记(四)

这种拨码开关在PLC里面很是常用: 这种弧型线就很漂亮: 这个白色按键很漂亮: 快恢复保险丝: 继电器电路: 这里的续流二极管很重要,因为继电器是感性元件: 【【必考】5招搞清楚!单点接…

【轻松拿捏】Java中ArrayList 和 LinkedList 的区别是什么?

ArrayList 和 LinkedList 的区别是什么? 1. ArrayList 2. LinkedList 3.总结 🎈边走、边悟🎈迟早会好 ArrayList 和 LinkedList 都是 Java 中常用的 List 接口的实现类,但它们在内部结构和操作性能上有所不同。 1. ArrayLis…

SpringBoot+Vue实现大文件上传(分片上传)

SpringBootVue实现大文件上传(分片上传) 1 环境 SpringBoot 3.2.1,Vue 2,ElementUI 2 问题 前几篇文章,可以用于较小文件的上传,对于较大文件来说,为了提高上传效率和可靠性,可以采…

【1-4】设计模式概述

目录 一.设计模式产生背景 二.软件设计模式的概念 三.学习设计模式的必要性 四.设计模式分类 一.设计模式产生背景 二.软件设计模式的概念 软件设计模式,又称设计模式,是一套被反复使用、多人知晓的、经过分类编目的、代码设计经验的总结。它描述了…

Python爬虫使用实例

IDE:大部分是在PyCharm上面写的 解释器装的多 → 环境错乱 → error:没有配置,no model 爬虫可以做什么? 下载数据【文本/二进制数据(视频、音频、图片)】、自动化脚本【自动抢票、答题、采数据、评论、点…

深度学习入门:卷积神经网络 | CNN概述,图像基础知识,卷积层,池化层(超详解!!!)

目录 🍔 前言 🍔 图像基础知识 1. 像素和通道的理解 2. 小节 🍔 卷积层 1. 卷积计算 2. Padding 3. Stride 4. 多通道卷积计算 5. 多卷积核卷积计算 6. 特征图大小 7. PyTorch 卷积层 API 7. 小节 🍔 池化层 1. 池…

代码随想录算法训练营第十六天(二叉树 四)

力扣题部分: 513.找树左下角的值 题目链接:. - 力扣(LeetCode) 题面: 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 思路(层序遍历): 应该是这道题最简单的方法了&#xff0…

C++ 设计模式——建造者模式

建造者模式 建造者模式组成部分建造者模式使用步骤1. 定义产品类2. 创建具体产品类3. 创建建造者接口4. 实现具体建造者5. 创建指挥者类6. 客户端代码 建造者模式 UML 图建造者模式 UML 图解析建造者模式的优缺点建造者模式的适用场景完整代码 建造者模式 建造者模式&#xff…

C语言—指针(1)

目录 一、内存和地址 (1.1)内存 (1.2)编址的理解 二、指针变量和地址 (2.1)取地址操作符(&) (2.2)指针变量和解引用操作符 (2.2.1&…

XSS复现

目录 XSS简单介绍 一、反射型 1、漏洞逻辑: 为什么有些标签可以触发,有些标签不能触发 可以触发的标签 不能触发的标签 为什么某些标签能触发而某些不能 二、DOM型 1、Ma Spaghet! 要求: 分析: 结果: 2、J…

Unity项目优化记录

背景:测试反馈项目组游戏存在内存泄露,来找到中台这边协调排查。好家伙,跑了两次看了内存快照,再看资源组织和管理方式,存在的问题确实比较多。 1、修复内存泄露:结算界面由于资源引用丢失导致整个面板不会…

44.【C语言】指针(重难点)(G)

目录 19.字符指针变量 *定义 *简单说明 *如果是字符串 *像数组一样指定访问常量字符串的字符 *练习 20.数组指针变量 *定义 *格式 *例子 问题1 问题2 *利用指针打印 21.二维数组传参的本质 *回顾 往期推荐 19.字符指针变量 *定义 指向字符的指针变量,用于存储字符…

使用Python实现B站自动答题机器人

文章目录 1. 写在前面2. 接口分析3. 点选验证分析4. Python程序实现 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长…

什么是OpenTiny?

OpenTiny 是一套企业级的 Web 前端开发解决方案,提供跨端、跨框架的 UI 组件库和低代码引擎,帮助开发者高效构建 Web 应用 。企业运用开发中,可以利用 OpenTiny 的以下核心组件和优势: TinyVue 组件库:一个丰富的组件库…

C/C++实现蓝屏2.0

🚀欢迎互三👉:程序猿方梓燚 💎💎 🚀关注博主,后期持续更新系列文章 🚀如果有错误感谢请大家批评指出,及时修改 🚀感谢大家点赞👍收藏⭐评论✍ 前…

【机器学习-监督学习】逻辑斯谛回归

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,…

使用Python制作贪吃蛇小游戏

引言 贪吃蛇游戏是一款经典的电子游戏,玩家通过控制一条不断增长的蛇在格子内移动,并吃掉随机出现的食物来获得分数。随着分数的增加,蛇的身体也会越来越长,游戏的难度也随之提升。在本文中,我们将详细介绍如何使用Py…

基于django的双选宠物托管服务平台/python宠物托管系统

摘 要 伴随着社会以及科学技术的发展,互联网已经渗透在人们的身边,网络慢慢的变成了人们的生活必不可少的一部分,紧接着网络飞速的发展,系统管理这一名词已不陌生,越来越多的双选宠物托管服务等机构都会定制一款属于…