本文章转载自 智伤帝的个人博客 - 原文链接
前言
自动添加 Unreal 骨骼 Socket 的功能,我查了一下,发现第三方的 UnrealEnginePython 已经集成了这个功能。 github地址
然而官方的插件在这方面还有很多限制。
比如说官方的 Skeleton 类就根本没有相关的 Sockets 数组可以获取。 文档地址
刚好最近收到了一个需求,需要实现通过 Excel 配置的文档批量自动生成对应骨骼的 Socket 效果。
为了实现这个需求,还是跳不过调用 C++ 的 API ,不过这一次的操作可以借鉴 UnrealEnginePython 插件的实现方法。
C++ 源码查询
这个操作套路还是之前的套路,先通过 UI 显示的名称然后反向去查询源码的路径。
如果右键菜单的名称不好找,可以去找它的 Tooltip , Tooltip 的名称比较长,比较唯一。
image
然后就可以通过 VScode 进行定位。
image
从这个 UI 命令中,可以找到 AddSocket 这个关键字命令。
可以搜索一下这个命令,并且在 UI 命令的模块下可以找到相关联的脚本。
image
于是,按照我之前的经验。
就可以想办法将这个类 FEditableSkeleton 引入的插件里面,然后就可以调用了。
另外这个类是 F 开头的纯 C++ 类,因此这个类是无法作为蓝图的输入和输出类型的,只有继承自 UObject 的对象是可以暴露到 Python 里面。
《大象无形》文档里面也有所提及。
image
同时这个 AddSocket 方法并不是静态方法,因此需要一些特殊的实例化操作,才可以使用。
虚幻的实例化有好几种方法,不同的情况,用不同的方法,具体我也不清楚,但是作为工具人,这些源码功能肯定能在其他的源码里面找到答案。
所以我就去搜索 FEditableSkeleton 这个类
image
首先排除了这个类本身的 cpp 文件和 头文件,剩下的使用参考已经非常清晰了。
应该就是 SkeletonEditorModule.cpp 的实例化方案了。
后面就是复制粘贴抄代码的事情。
解决无法编译的问题
然而经过我一通操作,将代码弄到我的蓝图里面之后,我却发现我居然无法通过编译。
我测试了好多遍,都提示我这个调用方法有某些缺了头文件之类的,无法识别。
我仔细核对了 build.cs 文件以及 C++ 引用的头文件,应该都没有什么缺失,于是我只好再尝试其他的方法。
可能是我这个构造函数的操作方法不对,但是我看了搜索到的构造方法基本都需要 CreateEditableSkeleton 这个方法。
于是我进一步沿着这个思路去解决它。
image
image
于是我尝试用套这个构建方法,结果编译通过了~
顺便到引擎测试一下是不是 Python 可以添加 Socket 了,然后可喜可贺,居然实现了。
USkeletalMeshSocket* UPyToolkitBPLibrary::AddSkeletalMeshSocket(USkeleton* InSkeleton, FName InBoneName)
{
USkeletalMeshSocket* socket = nullptr;
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked("SkeletonEditor");
TSharedRef EditableSkeleton = SkeletonEditorModule.CreateEditableSkeleton(InSkeleton);
socket = EditableSkeleton->AddSocket(InBoneName);
return socket;
}
批量删除 Socket
原本以为只要实现添加 Socket ,我的 Python 工具就万无一失了。
但是考虑到如果使用者添加错了,删除 Socket 还要手动删除就很离谱。
于是还得添加一个清空 Socket 的功能才行。
然后我想直接套用我上面实现的 EditableSkeleton 的方法。
然后才发现,原来上面调用的并不是 FEditableSkeleton 而是 IEditableSkeleton
这两个有啥区别呢?《大象无形》里面没有提到 I 开头的代表啥。
于是网上查了一下 naming convention (还好绑定没有白学~)
然后总算找到了官方文档给出的定义 链接
I 开头属于 abstract interfaces 抽象接口。
image
然后相应地去找 IEditableSkeleton 所在的脚本,其实这个时候可以发现这些脚本都在 SkeletonEditor 这个模块下。
而且 FEditableSkeleton 在 Private 目录,而 IEditableSkeleton 在 Public 目录。
image
然而另外颇为诧异的是,IEditableSkeleton 没有删除 Socket 的相关函数,但是 FEditableSkeleton 里面有 HandleDeleteSockets 的函数。
但是如果直接调用 FEditableSkeleton 根本无法通过编译。
最后实在没办法了,查了 UnrealEnginePython 的插件也没有开窍,Unreal 的 C++ 学艺不精,只好找程序来支援一下。
程序一看就说并不是所有的 API 可以暴露出来使用的,有些没有宏暴露的就不可以。
于是我赶紧查了一下 C++ 的 API 文档,结果发现只能查到 IEditableSkeleton 这个类 链接
SkeletonEditor 的 API 文档里面甚至没有提到 FEditableSkeleton(:з」∠)
那现在该怎么办呢?
程序说不慌,实在不行可以将 FEditableSkeleton 的功能代码抄到蓝图里面🤣
我在仔细看了一下 HandleDeleteSockets 的代码内容,我突然就开窍了。
image
这个 Sockets.Remove 是如此的熟悉,这不就和 UnrealEnginePython 的 Python 操作一个样吗?
于是我赶紧试验了一下传 Skeleton 的操作,果然将对应的 Socket Remove 删除掉就可以了~
不过如果只是Remove操作无法在编辑器面板上更新数据,需要重开面板才能看到 Socket 的确被删除了。
于是我结合上面的 IEditableSkeleton 进行更新就可以实现 Socket 删除的更新~
void UPyToolkitBPLibrary::DeleteSkeletalMeshSocket(USkeleton* InSkeleton, TArray SocketList)
{
InSkeleton->Modify();
for (USkeletalMeshSocket* Socket : SocketList)
{
InSkeleton->Sockets.Remove(Socket);
}
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked("SkeletonEditor");
TSharedRef EditableSkeleton = SkeletonEditorModule.CreateEditableSkeleton(InSkeleton);
EditableSkeleton->RefreshBoneTree();
}
获取骨骼名称
官方的 Python 可以获取到 Skeleton 的 bone_tree 信息。
但是 BoneNode 就没有什么有价值的 API 可供调用了。
查了一下 API 文档,原本是可以通过 BoneNode 获取骨骼名称,但是相关方法已经 Deprecated 了
于是我又参考了 UnrealEnginePython 插件。
可以通过 USkeleton 获取骨骼的数量和骨骼的名称。
int32 UPyToolkitBPLibrary::GetSkeletonBoneNum(USkeleton* InSkeleton)
{
return InSkeleton->GetReferenceSkeleton().GetNum();
}
FName UPyToolkitBPLibrary::GetSkeletonBoneName(USkeleton* InSkeleton,int32 BoneIndex)
{
return InSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
}
这样 Socket 的增删操作就完场了,改和查的操作可以通过 SkeletalMesh的 API 实现。
Python 结合 CSV 模块编写插件界面
后续需要开发一个 CSV 编辑界面,这样可以打通 Excel 文档同时又兼有 文件简单的特点。
这次尝试使用 dayu_widgets 来实现效果。
dayu_widgets 封装了一系列 MVC 框架相关的类。
可以直接调用起来,具体可以参考 dayu_widgets 的 example 链接
相关功能已经集成到了 PyToolkit 仓库里面。
image
总结
这次重新整理了一波 C++ 扩展的开发思路,没有深入搞过 Unreal C++ ,总结就一个字 菜(:з」∠)
只能说上面那些套路算是一种野生的解决方案,可以快速应付项目需求,但是真正要摸透还是得用 Unreal 开发几个 C++ 游戏才行~