前言
在日常工作中,常会遇到一些繁琐的流程,或者重复性很强的任务,而这些重复性,便可以通过提取通用模板,编写生成器,从而在后续中任务中只需要关注差异内容便可。
最常见的便是,在游戏开发中,书写某一类业务,例如:UI,技能等。都是在已存在的框架下,大部分内容都需要遵循框架,只有少部分需要进行业务化差异开发。
UI业务场景
在做UI业务时,常常会使用一些脚本语言,例如Lua,因为一般UI业务面临高度可变性,需要更频繁的更新和修改。
以一般MVC的UI框架,都会生产Model,View,Controller文件,其中会包含一些生命周期,或者常用的函数,而差异可能仅仅只是模块名不同,因此上述三个文件便可提取出模板,将模块名作为差异变量进行生产文件。
核心思路
- 制作模板文件
- 指定模板标识符,例如#Name#来标记此处被占位
- 读取模板文件
- 书写生成器逻辑代码,替换占位符
- 输出生成文件至指定目录结构
示例代码函数
bool UActionWidget::GenExcelTableFiles(const FExcelTableParam& ExcelTableParam)
{
//1. 参数准备
FString TemplateFilePath = ExcelTableParam.TemplateFilePath;
FString TableName = ExcelTableParam.TableName;
FString RowTableId = ExcelTableParam.RowTableId;
FString TargetTsFilePath = ExcelTableParam.TargetTsFilePath;
FString ConMgrFilePath = ExcelTableParam.ConMgrFilePath;
IFileManager& FileManager = IFileManager::Get();
//2. 逻辑代码,替换关键词 替换TableName 与 cid
FString TsResultString;
FArchive* Reader = FileManager.CreateFileReader(*TemplateFilePath);
FFileHelper::LoadFileToString(TsResultString, *Reader);
TsResultString = TsResultString.Replace(TEXT("#TableName#"), *TableName);
TsResultString = TsResultString.Replace(TEXT("#RowId#"), *RowTableId);
if (FileManager.FileExists(*TargetTsFilePath))
{
UE_LOG(LogTemp, Warning, TEXT("GenExcelTableFiles, FileExists:%s"), *TargetTsFilePath);
}
FStringView TsStringView(TsResultString);
//输出ts
FFileHelper::SaveStringToFile(TsStringView, *TargetTsFilePath);
//3. 逻辑代码,修改Mgr
TArray<FString> ConMgrStringResult;
FFileHelper::LoadFileToStringArray(ConMgrStringResult, *ConMgrFilePath);
TArray<FString> TempStringArray;
TArray<FString> FeatureStringArray;
TArray<int> InsertPosArray;
TempStringArray.Add(FString::Printf(TEXT("import {Init%s} from \"./Parse/C%s\";"), *TableName, *TableName));
TempStringArray.Add(FString::Printf(TEXT(" readonly C%s: Map<number, xls.%s> = new Map();"), *TableName, *TableName));
TempStringArray.Add(FString::Printf(TEXT(" Init%s(this);"), *TableName));
for (int i = 0; i < 3; i++)
{
FeatureStringArray.Add(FString::Printf(TEXT("//StartFeature0%d"), i + 1));
InsertPosArray.Add(0);
}
for (int i = 0; i < ConMgrStringResult.Num(); i++)
{
FString StringItem = ConMgrStringResult[i];
for (int j = 0; j < 3; j++)
{
if (StringItem.Contains(FeatureStringArray[j]))
{
InsertPosArray[j] = i;
}
}
}
for (int i = 2; i >= 0; i--)
{
ConMgrStringResult.Insert(TempStringArray[i], InsertPosArray[i]);
}
//修改Mgr后,输出Mgr.ts
return FFileHelper::SaveStringArrayToFile(ConMgrStringResult, *ConMgrFilePath);
}
技能场景业务(GAS)
使用UE5 GAS框架,要制作一个技能
- 一个GA
- 若干GE,常见的效果GE,冷却GE,消耗GE
- 若干GC
- 若干GameTag
等等...
流程优化思路
整个流程作为一个模板,差异为技能名。
当要新制作一个技能时,例如技能名:UseItem。
则生成:GA_UseItem.uasset, GE_UseItem.uasset, GE_UseItem_CD.uasset, GE_UseItem_Cost.uasset, GC_UseItem_Normal.uasset等。
修改DefaultGameplayTags.ini,在末行新增Ability.UseItem标签。
总结
在做模板文件生成流程,解决的痛点是:规范框架下的制作流程,减少重复性工作。
其核心思路:
- 发现常见重复性工作流程
- 抽取模板和差异内容,制定模板与解析规则
- 读取模板,处理逻辑
- 输出目标文件
- 进行差异化开发