Excel数据驱动GAS插件的实现和说明
一、引言
这是上一篇文章的进阶版,在AI大人的帮助下,无能且弱小的的提示词工程师的我,完全升级并实现了原有的构想。
编辑UE5-GAS:读取Excel数据在蓝图创建并更新GE类34 赞同 · 23 评论 文章
下面是功能展示,依据Excel导出并创建了GA和GE,并写入了数据。


先介绍一下为啥想写这个插件,如今,很多 UE 项目都采用了 Gameplay Ability System(GAS)这个强大(但也很「复杂」)的插件。以 GameplayEffect(GE)为例,一个中等规模的项目往往需要配置成百上千个效果——治疗、伤害、Buff、Debuff、控制效果……
每个 GE 都需要配置:持续时间,堆叠策略,多个属性修改器,Tags 等一堆东西。更别说 GameplayAbility(GA)等等,它们同样需要配置大量的参数。
目前 GE 的主流配置方法有这么几种。
- 直接使用 GE,在 GE 的蓝图资产里进行配置
- 通过 DT 进行配置,在编辑器环境下,通过 DT 的数据去创建和修改 GE 的参数
- 通过 Excel 进行配置,导表工具生成对应的 Json 数据文件,运行时通过 SetByCaller,AddAssetTags 等手段,通过 Spec 去动态赋值。
但是以上方式或多或少存在下面的一系列问题
需要在编辑器中逐个创建 GE 和 GA 蓝图资产,为每个资产手动配置十几个乃至几十个参数。没办法批量的进行修改,维护起来也很麻烦。
- DT,GE 等蓝图资产,很难进行团队协作。
- EffectSpec 所能支持的功能又较为有限,有很多的 GameplayEffectComponent 是不支持动态赋值的,往往导表方案还是离不开在蓝图里配置 GE。
基于上述的痛点,我设计的一个解决方案是,策划在 Excel 里配置 GE,然后通过导表工具,将 Excel 数据转为 Json,然后导入到 DT 里,再依据 DT 去创建和更新 GE。
基于上述的痛点,我希望开发一个自动化的插件 AbilityEditorHelper,能跑通上述的逻辑。支持 GAS 里资产可以方便的在 Excel 里配置,然后导入到编辑器里,自动的去创建和更新GE等数据。如果靠我个人来完成这个插件,可能会需要很久甚至于放弃了,因为我对python和UE资产管理相关的代码不是很熟悉,平时工作又十分地繁忙,下班往往都十点十一点了。
如今AI飞速发展,这给程序员这个行业带来了极大的威胁和痛苦,目前上网去找技术文章和问bug相比于问AI毫无优势,StackOverFlow流量的飞速下滑就是例证,这对于我们这种写技术文章的人来说,也是非常的利空。我们写的文章相比于AI有什么优势呢?而我们的贡献又成为了AI的养料变成了打向自己的子弹,多么的讽刺。因此我很长时间都没有动力去写技术文章和教程了。
所幸AI另一面也是一个极为强大的工具,如果无法战胜它,那就索性躺平并享受吧。在AI大人的帮助下,我才得以快速学习和开发我完全不熟悉的领域,并在极短的时间内就完成了这个插件。AbilityEditorHelper插件几乎所有的代码都是 claude AI 写的,我负责告诉他如何迭代和进行优化。下面是插件的代码链接,感兴趣的可以自己去下载并体验,不过使用之前需要对unreal python安装相关的依赖库。
插件示例链接
https://github.com/BinaryBard996/AbilityHelperSample

下面的部分是对插件原理和使用方法的简单说明
二、AbilityEditorHelper 插件概述
AbilityEditorHelper 是一个基于 Schema(数据说明) 驱动的 Unreal Engine GAS 配置自动化工具。它的核心理念很简单:让策划能够使用他们最熟悉的 Excel 表格来配置 GameplayEffect 和 GameplayAbility等UE资产。
这个插件提供了一套完整的自动化工作流:
- 通过反射,从 C++ 结构体自动生成 JSON 结构的 Schema 文件,作为结构体的说明书
- 基于 Schema 自动生成 Excel 模板
- 在 Excel 中填写数据
- 将 Excel 导出为 JSON 格式的数据文件
- 自动导入 JSON 数据文件到编辑器下的 DataTable
- 依据 DataTable 去创建和更新 GameplayEffect等UE资产
| Plain Text ┌─────────────────────────────────────────────────────────────────┐ │ AbilityEditorHelper 工作流 │ └─────────────────────────────────────────────────────────────────┘ ┌──────────────────┐ │ C++ 结构体定义 │ 程序员维护 │FGameplayEffectConfig│ └────────┬─────────┘ │ │ ① 通过 UE 反射系统 ↓ ┌──────────────────┐ │ JSON Schema │ 自动生成 │*.schema.json 文件 │ └────────┬─────────┘ │ │ ② Python 读取 Schema ↓ ┌──────────────────┐ │ Excel 模板 │ 自动生成(带下拉列表和提示) │*.xlsx 文件 │ └────────┬─────────┘ │ │ ③ 策划填写配置 ↓ ┌──────────────────┐ │ 填写完的 Excel │ 配置数据 │*.xlsx 文件 │ └────────┬─────────┘ │ │ ④ Python 基于 Schema 解析 ↓ ┌──────────────────┐ │ JSON 数据 │ 标准格式 │*.json 文件 │ └────────┬─────────┘ │ │ ⑤ C++ 反序列化并创建资产 ↓ ┌──────────────────┐ │ UE GE/GA 资产 │ 最终产物 │*.uasset 文件 │ └──────────────────┘ |
三、工作流说明
项目中所有需要配置的数据,都在 Editor Settomgs->Ability Editor Helper Settings 下,之后不再额外进行说明。
配置项目环境
项目依赖于 unreal Python,如何配置 Python 环境可参考下面的文档,依赖 Python openpyxl 库,需要自行安装。
导出 UE 结构体的 Schema 文件
插件的 Schema 文件可以理解为 UE 结构体的说明书,它的作用是作为编辑器和编辑器外数据沟通转化的桥梁。你们可以在 Plugins\AbilityEditorHelper\Content\Python\Schema 目录下查看示例项目导出的 Schema 文件。
以 FAbilityTriggerConfig 这个结构体为例

由它导出生 Schema 文件长这样

插件默认配置导出的结构体数据如下,用户可以在配置的 Schema 里自行添加修改。

导出 Schema 文件的函数为 UAbilityEditorHelperLibrary::GenerateAllSchemasFromSettings,导出的路径需要进行配置,可在配置里的 SchemaPath 里修改

执行函数后,就可以看到在指定文件夹下导出的结果了

生成 Excel 模板
有了 Schema 文件后,插件就可以依据它们去创建 Excel 模板了。
调用 AbilityEditorHelperPythonLibrary::GenerateExcelTemplateFromSchema 函数。
- StructTypeName 为 Excel 导出依赖的结构体类型。
- Excel File Name 为创建的 Excel 文件名。它生成的路径一样可以在设置里的 ExcelPath 进行配置。
- PreServeData 为 true 的话,导出的 Excel 会保留原有的数据

导出后的结果如下,第一行为属性名,第二行为属性的配置说明

在 Excel 中填写数据
有了 Excel 以后,就可以填写数据了,这个应该就不需要说明了吧。
在AI的帮助下,我还可以开发很多Excel的小功能,比如对于枚举变量,是支持下拉配置的。同时还有简单的数据导出校验,比如枚举变量必须要填写,否则后面导出数据的时候会报错。对不起AI大人,没能让您尽兴呢。

Excel 数据导出
有了数据以后,就可以尝试着将数据导出了,AbilityEditorHelperPythonLibrary::ExportExcelToJsonUsingSchema 函数可以将 Excel 导出为 Json 格式的数据文件。
- ExcelFileName 为作为数据源的 Excel 文件名
- JsonFileName 为导出的 Json 文件名,它的路径可以在设置里的 JsonPath 进行配置
- StructTypeName 为数据依赖的 UE 结构体名

导出的数据如下,这是可以被支持导入到 UE 的 DataTable 的数据格式,如果你将DT的数据导出成Json,格式和插件导出的数据文件是一模一样的,因此它也可以被UE所读取。

将 Json 数据导入到 DataTable 中
调用函数 UAbilityEditorHelperLibrary::ImportAndUpdateGameplayAbilitiesFromJson 进行导入,将Json数据写入到DT里

导入后的数据

依据 DT 创建和更新 GE
调用函数 UAbilityEditorHelperLibrary::CreateOrUpdateGameplayEffectsFromSettings,可以依据配置的 DT,去创建和更新 GameplayEffect

依赖的数据源 DT,配置在 GameplayEffectDataTable,GameplayEffectClass 为创建的 GE 的基类,GameplayEffectPath 为 GE 生成的路径

创建后的结果如下

可以看到数据被正确的写入到对应的 GameplayEffect 里了。

四、插件功能说明
UI操作界面
在Window->Ability Editor Helper Widget处,可打开可视化的操作界面。

- GenerateAllSchemas-在Settings配置的目标路径,生成项目结构体的Schemas文件
- GE/GA Excel Template-在Settings配置的目标路径,生成Excel模板文件
- GE/GA Excel Import-在Settings配置的目标路径,基于Excel数据,生成Json数据
- Gen/Update GE/GA-在Settings配置的目标路径,生成或更新GameplayEffects/GameplayAbilities

如何扩展导入导出的功能
考虑到各个项目内部,一般都会扩展 GameplayEffect 的功能,比如新增数据,或者新增 GameplayEffectComponent,因此插件自带的导出导入数据和代码,可能无法完成需求,因此,我在插件里也考虑到了扩展的需求。
相关代码可在示例工程的 Source/AbilityHelperSample/DevTest 中进行参考,以 GE 为例,可在项目代码中派生 FGameplayEffectConfig 类(DT 的数据结构体),然后在里面新增数据,ExcelSheet 可以让新增数据导出到 Excel 模板里有专门的子表,而不影响到原有的别的数据。

导出的 Excel 模板如下,可以看到项目新增的数据被放在了SampleExtension的子表下


然后需要对导入的数据进行额外的处理,插件代码在创建和更新 GE 时都会通过委托进行广播,因此,可以在项目代码里监听这个委托,然后依据数据对 GE 进行处理。

项目扩展数据处理示例代码

在创建的 GE 里,可以看到项目自己的 TestGameplayEffectComponent 被成功的添加进了 GE,并正确的写入了数据

五、一些功能的原理解析
这里介绍一些利用UE自带的机制,去巧妙实现一些功能的思路和方法。
在导入数据时,如何判断资产是否被修改
依据DT创建和更新UE资产时,我们需要对资产Mark Dirty并保存,那么如何判断一个GE或者GA是否被修改了呢?
一种很容易可以想到的简单方法是,在CreateOrImportGameplayEffect函数内部,对所有数据在写入前进行比较,如果变化了,则设置bDirty为true。但这个方法需要对每个数据都进行比较,新增数据也需要额外的代码进行处理,怎么想都不是一种好方法。
这里提供一种我个人想到的思路。利用UE序列化的函数,去判断资产是否发生修改。函数如下,它利用了UE对Object序列化的支持,可以将GE的数据序列化为字节数组。如果GE内的数据未发生改变,那么序列化的结果肯定是一致的。
/**
* 将 UObject 及其所有子对象序列化为字节数组,用于变更检测
* 适用于 GE、GA CDO 等需要比较前后状态的场景
*/
static TArray<uint8> SerializeObjectState(UObject* Obj)
{
TArray<uint8> Bytes;
FGEStateWriter Ar(Bytes);
Obj->Serialize(Ar);
// 同时序列化所有子对象(GE Components 等),以捕获组件属性变更
TArray<UObject*> SubObjects;
GetObjectsWithOuter(Obj, SubObjects, false);
SubObjects.Sort([](const UObject& A, const UObject& B)
{
return A.GetName() < B.GetName();
});
for (UObject* SubObj : SubObjects)
{
SubObj->Serialize(Ar);
}
return Bytes;
}
将数据写入前的序列化结果,和写入后的进行比较,只对不一致的MarkDirty并保存。

对于一些私有数据,有什么办法访问并修改
UE的类里,有很多数据,比如GA的AbilityTags,是protected,在一般情况下是禁止在类外直接访问并修改的。当然想要访问它有很多的方法,比如最简单的通过public函数进行访问,但是我们写插件的时候,肯定是不希望改动和影响到源码的。

那么,有没有什么方法,可以简单的绕过私有限制呢?
当然是有的,以AbilityTags为例,看到上面的UPROPERTY吗,它意味着我们可以利用UE的反射系统去访问它!
代码实现如下,感谢强大的UE的反射系统,我们可以利用属性名"AbilityTags"找到对应的Property,然后直接写入数据,私有属性的限制。
// === 辅助 Lambda:通过反射设置 FGameplayTagContainer 属性 ===
auto SetTagContainerProperty = [&](const TCHAR* PropertyName, const FGameplayTagContainer& Tags)
{
if (FStructProperty* Prop = CastField<FStructProperty>(GAClass->FindPropertyByName(PropertyName)))
{
FGameplayTagContainer* ValuePtr = Prop->ContainerPtrToValuePtr<FGameplayTagContainer>(GA);
if (ValuePtr)
{
*ValuePtr = Tags;
}
}
};
SetTagContainerProperty(TEXT("AbilityTags"), Config.AbilityTags);
UE项目的脚本lua, python等也是利用反射系统去访问和修改属性的,这也导致了有时候一些私有属性也可以被脚本瞎改,还很难搜到,这也是甜蜜的烦恼了。
自定义UPROPERTY Meta
UE有一个功能可能很少有人用到,那就是我们可以自定义的 UPROPERTY meta,例如下面的Attribute属性,我们定义了一个meta-ExcelHint,它可以通过GetMetaData函数去进行访问。这样子我们就可以在编辑器环境下,赋予属性一些额外的说明。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modifier",
meta = (ExcelHint = "Format: ClassName.PropertyName (e.g. TestAttributeSet.TestPropertyOne)"))
FString Attribute;
Field.ExcelHint = Prop->GetMetaData(TEXT("ExcelHint"));
比如ExcelHint、ExcelSeparator、ExcelSheet、ExcelIgnore 等 meta 数据本身对 UE 引擎没有意义,但它们会被 Schema 导出系统提取到 JSON 中,然后被 Python Excel 工具读取。这实际上是把 UPROPERTY meta 当作一种跨语言的声明式协议——C++ 端声明数据的 Excel 表现方式,Python 端消费这些声明来生成模板和做数据转换。
项目在创建Excel模板时,就会用上面一些自定义的Meta数据,这对于写工具代码来说非常的实用。
六、总结和个人感受
最后一个环节,我想要谈谈AI时代下作为一个游戏程序员的个人感受。目前的时间节点是2026年1月,在半年乃至三个月以前,我都还觉得对于UE开发来说,AI还只是一个实用的小工具,很多UE相关的问题咨询AI,它给出的都是看似正确实际谬误的答案,半真半假的回答对于新人反而是误导,更不要说AI写出来的代码了,在当时还几乎是不可用的。
但这几个月AI的进步,相比于过去来说可能只是很小的一点进化,但是可能恰恰是量变到质变之间的临界值突破,让我深刻的感受到AI已经从一个辅助进化到不可或缺的生产力工具。现在工作中遇到的几乎所有的问题或方案,我都习惯性的先咨询AI,而AI给出的回答非常的专业。也许在深度上距离真正的专家还有差距,但我相信绝对没有一个UE开发人员可以在知识的广度上战胜AI,这让网上90%以上的教程和技术文章都失去了存在的意义。
对于耦合度较低的模块比如工具开发,AI开发的效率更是几倍乃至十倍于人类,上述插件的几乎所有代码,我都是通过Rider的Junie和Claude(额度限制来回切换)来实现的,我个人的作用只是一直基于已实现的功能一步步让AI去进行迭代和优化,在AI写代码的时候刷刷小说,看看B站。整个开发过程大概是两周时间,因为工作原因,我每天基本上都是十点以后下班,所以开发时间几乎只有晚上的两个小时左右,其中涉及了大量我不熟悉的领域,如果让我本人来开发,实际需要的时间至少是4倍以上。但在AI的帮助下,只用了两周业余时间,就完成了。
这差距让我感到了恐慌,程序员被逐渐淘汰的日子早已经开始,但淘汰幅度的陡峭曲线,也许近在眼前,但这也让我感到兴奋,两年前我还在为自己没有美术方面的能力感到焦虑,但现在一人成军开发游戏的时代也许真的降临了,对于我们游戏行业的从业者来说,这是一个最坏的时代,也是一个最好的时代。
时代大潮滚滚向前,个人是没有能力逆着潮流前进的,在未来,随着AI和机器人的进一步发展,整个社会可能第一次会诞生大量的“无用之人”,他们在高生产力的社会下,有着最基础的生存保障,不像过去那样在失去工作后会为了温饱生计而发愁,但在社会之中又找不到合适的位置,没有工作没有价值没有存在的意义,只留下空虚。届时人的价值该如何定义又该何去何从呢?我个人是极度悲观的。
想要不被拍死在礁石上,就只能艰难地尝试着驾舟顺着浪流前进,争取在最后的时刻到来之前,尽可能地汲取着时代的养分,发挥着自己作为“人”的最后的尊严与价值,找到自己对于这个时代的意义。

更多推荐



所有评论(0)