Unreal Python 骨骼 Socket 自动化工具

本文章转载自 智伤帝的个人博客 - 原文链接

前言

  自动添加 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<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 抽象接口。

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<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 获取骨骼的数量和骨骼的名称。

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++ 游戏才行~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352