前言

  自动添加 Unreal 骨骼 Socket 的功能,我查了一下,发现第三方的 UnrealEnginePython 已经集成了这个功能。 github地址
  然而官方的插件在这方面还有很多限制。
  比如说官方的 Skeleton 类就根本没有相关的 Sockets 数组可以获取。 文档地址

  刚好最近收到了一个需求,需要实现通过 Excel 配置的文档批量自动生成对应骨骼的 Socket 效果。
  为了实现这个需求,还是跳不过调用 C++ 的 API ,不过这一次的操作可以借鉴 UnrealEnginePython 插件的实现方法。

C++ 源码查询

  这个操作套路还是之前的套路,先通过 UI 显示的名称然后反向去查询源码的路径。
  如果右键菜单的名称不好找,可以去找它的 Tooltip , Tooltip 的名称比较长,比较唯一。

alt

  然后就可以通过 VScode 进行定位。

alt

  从这个 UI 命令中,可以找到 AddSocket 这个关键字命令。
  可以搜索一下这个命令,并且在 UI 命令的模块下可以找到相关联的脚本。

alt

  于是,按照我之前的经验。
  就可以想办法将这个类 FEditableSkeleton 引入的插件里面,然后就可以调用了。
  另外这个类是 F 开头的纯 C++ 类,因此这个类是无法作为蓝图的输入和输出类型的,只有继承自 UObject 的对象是可以暴露到 Python 里面。
  《大象无形》文档里面也有所提及。

alt

  同时这个 AddSocket 方法并不是静态方法,因此需要一些特殊的实例化操作,才可以使用。
  虚幻的实例化有好几种方法,不同的情况,用不同的方法,具体我也不清楚,但是作为工具人,这些源码功能肯定能在其他的源码里面找到答案。
  所以我就去搜索 FEditableSkeleton 这个类

alt

  首先排除了这个类本身的 cpp 文件和 头文件,剩下的使用参考已经非常清晰了。
  应该就是 SkeletonEditorModule.cpp 的实例化方案了。
  后面就是复制粘贴抄代码的事情。

解决无法编译的问题

  然而经过我一通操作,将代码弄到我的蓝图里面之后,我却发现我居然无法通过编译。
  我测试了好多遍,都提示我这个调用方法有某些缺了头文件之类的,无法识别。
  我仔细核对了 build.cs 文件以及 C++ 引用的头文件,应该都没有什么缺失,于是我只好再尝试其他的方法。

  可能是我这个构造函数的操作方法不对,但是我看了搜索到的构造方法基本都需要 CreateEditableSkeleton 这个方法。
  于是我进一步沿着这个思路去解决它。

alt
alt

  于是我尝试用套这个构建方法,结果编译通过了~
  顺便到引擎测试一下是不是 Python 可以添加 Socket 了,然后可喜可贺,居然实现了。

1
2
3
4
5
6
7
8
9
USkeletalMeshSocket* UPyToolkitBPLibrary::AddSkeletalMeshSocket(USkeleton* InSkeleton, FName InBoneName)
{
USkeletalMeshSocket* socket = nullptr;

ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
TSharedRef<IEditableSkeleton> EditableSkeleton = SkeletonEditorModule.CreateEditableSkeleton(InSkeleton);
socket = EditableSkeleton->AddSocket(InBoneName);
return socket;
}

批量删除 Socket

  原本以为只要实现添加 Socket ,我的 Python 工具就万无一失了。
  但是考虑到如果使用者添加错了,删除 Socket 还要手动删除就很离谱。
  于是还得添加一个清空 Socket 的功能才行。

  然后我想直接套用我上面实现的 EditableSkeleton 的方法。
  然后才发现,原来上面调用的并不是 FEditableSkeleton 而是 IEditableSkeleton
  这两个有啥区别呢?《大象无形》里面没有提到 I 开头的代表啥。
  于是网上查了一下 naming convention (还好绑定没有白学~)

  然后总算找到了官方文档给出的定义 链接
  I 开头属于 abstract interfaces 抽象接口。

alt


  然后相应地去找 IEditableSkeleton 所在的脚本,其实这个时候可以发现这些脚本都在 SkeletonEditor 这个模块下。
  而且 FEditableSkeleton 在 Private 目录,而 IEditableSkeleton 在 Public 目录。

alt

  然而另外颇为诧异的是,IEditableSkeleton 没有删除 Socket 的相关函数,但是 FEditableSkeleton 里面有 HandleDeleteSockets 的函数。
  但是如果直接调用 FEditableSkeleton 根本无法通过编译。


  最后实在没办法了,查了 UnrealEnginePython 的插件也没有开窍,Unreal 的 C++ 学艺不精,只好找程序来支援一下。
  程序一看就说并不是所有的 API 可以暴露出来使用的,有些没有宏暴露的就不可以。
  于是我赶紧查了一下 C++ 的 API 文档,结果发现只能查到 IEditableSkeleton 这个类 链接
  SkeletonEditor 的 API 文档里面甚至没有提到 FEditableSkeleton(:з」∠)

  那现在该怎么办呢?
  程序说不慌,实在不行可以将 FEditableSkeleton 的功能代码抄到蓝图里面🤣


  我在仔细看了一下 HandleDeleteSockets 的代码内容,我突然就开窍了。

alt

  这个 Sockets.Remove 是如此的熟悉,这不就和 UnrealEnginePython 的 Python 操作一个样吗?
  于是我赶紧试验了一下传 Skeleton 的操作,果然将对应的 Socket Remove 删除掉就可以了~

  不过如果只是Remove操作无法在编辑器面板上更新数据,需要重开面板才能看到 Socket 的确被删除了。
  于是我结合上面的 IEditableSkeleton 进行更新就可以实现 Socket 删除的更新~

1
2
3
4
5
6
7
8
9
10
11
void UPyToolkitBPLibrary::DeleteSkeletalMeshSocket(USkeleton* InSkeleton, TArray<USkeletalMeshSocket*> SocketList)
{
InSkeleton->Modify();
for (USkeletalMeshSocket* Socket : SocketList)
{
InSkeleton->Sockets.Remove(Socket);
}
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
TSharedRef<IEditableSkeleton> EditableSkeleton = SkeletonEditorModule.CreateEditableSkeleton(InSkeleton);
EditableSkeleton->RefreshBoneTree();
}

获取骨骼名称

  官方的 Python 可以获取到 Skeleton 的 bone_tree 信息。
  但是 BoneNode 就没有什么有价值的 API 可供调用了。
  查了一下 API 文档,原本是可以通过 BoneNode 获取骨骼名称,但是相关方法已经 Deprecated 了

  于是我又参考了 UnrealEnginePython 插件。
  可以通过 USkeleton 获取骨骼的数量和骨骼的名称。

1
2
3
4
5
6
7
8
9
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 仓库里面。

alt

总结

  这次重新整理了一波 C++ 扩展的开发思路,没有深入搞过 Unreal C++ ,总结就一个字 菜(:з」∠)
  只能说上面那些套路算是一种野生的解决方案,可以快速应付项目需求,但是真正要摸透还是得用 Unreal 开发几个 C++ 游戏才行~