UE5-GAS:读取Excel数据在蓝图创建并更新GE类

写了很多期关于GAS的文章,突然想到,实际的项目中,维护很多的蓝图GE会很麻烦,策划往往会更偏好于使用Excel,于是想着是不是可以写一个工具,将Excel中的GE数据导出然后生成相对应的蓝图GE类,效果如下图所示。相关代码只是初步实现了构想,只是提供一个可行的思路。相似的,这个功能也可以用于GA,可以将技能的属性配置转移到Excel中去配置,比如技能范围,技能效果等等。

Excel中的GE数据

导出的蓝图GE

一边看比赛一边写的文章,woc,bin!

确定了需求以后,首先分析一下,实现需求所需要的功能。

  1. 在编辑器中,我们会用DT去维护GE表,DT可以通过Json进行导入,那么需要实现功能:将excel转为UE的DataTable所需的Json数据,这个部分我会使用python作为脚本代码实现。
  2. 编辑器可以写Editor Utility代码,在其中实现功能将Json导入填充DataTable数据,然后遍历DT,依据表数据创建和更新GE类Asset
  3. 实现创建蓝图资产的代码,即编辑器中的Asset
  4. GE类Asset创建以后,实现功能可以读取DT配置的数据,更新GE类的数据

直接展示一下最后的成果,依据表中数据,创建了对应的GE_Test类Asset,并依据表中数据更新了类的属性,包括AssetTags,Duration和Modifiers等

创建Asset资产

接下来是代码实现,首先跳过一,两步,从第三步开始,如何通过C++代码创建指定类的Asset资产,即我们在蓝图编辑器里看到的后缀为.uasset的东东

创建Editor插件

因为相关代码只在编辑器中使用,不会打包到development或者shipping包中,因此就不写在Source代码中了,首先在项目代码中创建一个新的module,命名随意,我这里叫AnnaEditor,作为存放编辑器代码的地方

找到对应的uproject文件,将Type改为Editor

创建Library

创建BlueprintFunctionLibrary的派生类

然后创建函数CreateBlueprint,功能是在指定的位置创建指定类的派生类uasset,函数的核心代码是调用引擎代码FKismetEditorUtilities::CreateBlueprint,作用是创建Blueprint类

代码实现

  1. 判断输入的路径是否已经存在object,如果存在则报错
  1. 调用KismetEditorUtilities函数库中的函数CanCreateBlueprintOfClass,判断是否可以基于输入的ParentClass生成蓝图类,判断的条件包括:Class是否有效、Class是否可以派生蓝图类等
  1. 创建Package,包的概念比较抽象,我个人的理解是引擎内的任何资源(object)都是一个package,以统一的格式,按二进制的形式进行存储,读取以后再转为Class, Texture, Material等引擎资源,而存储的形式就是通过package。(如果回答的不对,请大佬指教,这一块刚刚开始学习)

篮子悠悠:UE4 C++基础 - 资源常见名词解释153 赞同 · 2 评论文章

  1. 输入ParentClass获取对应的蓝图类,和生成的蓝图类。C++类创建蓝图派生类为什么还需要生成一个蓝图类,这一块我了解的并不深入,如果有大佬可以帮我解答一下,非常感谢。
  1. 创建蓝图资产
  1. 注册并保存蓝图资产
  1. 测试,首先创建EBP,命名为EBP_GenGE

重写Run函数,调用刚才实现的CreateBlueprint函数

右键创建的EBP_GenGE,在最上面可以看到RunEditorUtilityBlueprint,点击以后会调用run函数

点击以后,就可以看到编辑器在指定的位置创建了GameplayAbility的类,命名为Test的蓝图资产,创建成功!

读取DT数据写入GE类

现在我们已经可以创建蓝图类了,因此已经可以在指定位置创建GE类了,那么接下来需要实现的就是,可以根据表中的数据,修改GE类的参数

  1. 创建DT行结构体的派生类,命名为FAnnaEditorGenGameplayEffect,其中记录了决定GE类属性所需要的数据,我这里只是写了一部分可能可以用到的数据作为参考。ParentClass为创建的GE的父类,DurationPolicy和Duration应该不需要解释,决定了GE的持续时间规则,AssetTags也不言自明,ModifyAttributes为GE的Modifiers中需要修改的数据是什么
  1. 创建函数CreateOrUpdateGEBlueprint
  1. 实现如下,第一步判断BlueprintPath的位置是否已经存在GE类,如果不存在,则需要调用上一步实现的CreateBlueprint函数生成蓝图资产
  1. 调用CreateBlueprint创建GE类资产
  1. 判断创建的GE类是否有效,如果无效则报错
  1. 接下来开始给GE类赋值,首先赋值Duration,如果GE的持续时间类型为HasDuration,则写入持续时间
  1. 给GE类添加对应的AssetTags,从5.3开始,GE的属性开始由GEComponent来决定,所以如果要给GE添加Asset Tags,首先需要添加对应的AssetTagsGameplayEffectComponent。其它类似GrantAbility,Imunity等功能,也需要通过添加GEComponent去实现
  1. 添加GE所需要修改的属性

这里的ConvertStringToAttribute是为了将String转为对应的GameplayAttribute

  1. 标记GE类为Dirty,返回创建并更新的GE类

读取DT创建GE

在前面我们已经实现了3,4步的功能,已经可以依据GE Gen Data在蓝图中创建GE类,并更新GE的属性和效果,那么这一步的目标是读取GE DT,遍历全部的行,依次读取GE Gen Data然后在编辑器蓝图中生成GE类

首先创建RowStructure为FAnnaEditorGenGameplayEffect的DataTable,命名为EDT_GameplayEffect

打开在前面创建的Editor Utility Blueprint-EBP_GenGE

创建函数Generate Gameplay Effects,首先获取表中全部RowNames,再依次读取对应的表数据的结构体

接下来生成GE的路径,路径由两个部分组成,第一个部分为GE的文件夹路径EffectPath,第二个部分为GE类名,组合在一起就是最终的Path

获取生成需要生成的GE类的父类,默认为项目的GE基类,我这里为AnnaGameplayEffect,不过这一步似乎可以移动到C++中去做

调用在第二步实现的函数CreateOrUpdateGEBlueprint函数,创建并更新GE类

蓝图代码展示

最后给创建的DT中添加一些数据,我自己随便写了两个测试

Run调用GenerateGameplayEffects

跑一下,就可以看到在指定的路径生成了GE类,Perfect!

做到这一步,已经可以给提供一个可以维护的表工具了,在编辑器中可以通过配置DT来导出GE,但是DataTable依旧是一个二进制的文件,没有办法合并,同时DT的功能毕竟不如Excel多,有时候没有办法满足策划的需求,用起来没有有着足够沉淀的Excel丝滑。为此,我最后还会试着写一个Excel导表工具,将Excel生成可以导入UE的Json文件。

Excel导表工具

导表工具我是试着用python来写的,python写起来简单,对json和excel的读写支持都很完善,我大概花了一个晚上研究一下就能可以大致的实现了。代码需要用到pandas库,python环境的安装和配置网上一搜就有很多,我这里就不复述了,这里我只把代码贴出来,仅供参考。

第一步,我们首先看看DT导出的Json是长啥样的。有了对应的Json文件,我们就依样画葫芦照着写,让导表生成的Json和UE导出来的格式一样,就可以将导出文件再导入到UE当中了。

然后看一看我的Excel表格,其中第一行为属性名,对应了Json表中的Key键值,即紫色的部分。第二行为数据类型,在基础类型外,我额外支持了list和map。从第三行开始就是GE类的属性了。

首先创建函数item_excel_to_json,它的作用是将Excel单元格的数据转为Json对应的数据,下图所示,Excel中第一行为属性名,第二行为数据类型,在这里我支持了list和map,str,int,float,tag等类型,第三行为数据,对于list和map来说,逗号","为分隔符。

函数的输入分别代表了数据类型和excel中的单元格数据,数据类型是Excel表中第二行绿色的部分

首先考虑list和map两种情况,递归调用item_excel_to_json,依次读取单元格内的子数据并转化为对应的类型

对于枚举类,UE导出的Json如下,只是一个简单的string,但问题是,如何保证在excel中输入的数据是对的呢?

我使用了一个笨方法,创建一个Enum Map,维护表中需要用到的全部枚举数据

如果data_type在枚举字典中存在,那么首先判断excel单元格的数据是否存在,如果不存在则报错,进行一个检查

接下来考虑str,float和int的情况

最后考虑到tag的特殊情况,让生成的json和导出的一致。

对于UE来说,导出的结构体是类似下面的样子,是一个字典,字典的key为property name,value为property value,其实就是一个嵌套的结构,理论上结构体的导出函数也可以用递归的方法获取,但目前我只是写一个最简单的版本,之后有时间再考虑去进行优化吧。

最后实现读Excel导出Json的代码,代码应该蛮简单的,就是一行行遍历excel单元格的数据,将其转为json并添加到json_list中,最后将json_list写入json文件

运行一下python代码,打开查看生成的json文件,和导出的一模一样,完美

写一个批处理文件可以运行导表代码

最后重新打开编辑器的EBP蓝图,在run后调用函数FillDataTableFromJsonFile,它的作用是导入json文件填充DT。填充完数据以后就可以调用生成蓝图GE的代码,至此,神功大成!

最终效果展示

如果感兴趣的人多,我可以考虑抽时间将相关代码写成一个插件

相关推荐
小飞猪Jay36 分钟前
C++面试速通宝典——13
jvm·c++·面试
rjszcb2 小时前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想2 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财2 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh3 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手3 小时前
c++_ 多态
开发语言·c++
郭二哈3 小时前
C++——模板进阶、继承
java·服务器·c++
挥剑决浮云 -3 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
丶Darling.3 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5204 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法