spine-ue 运行时文档

Licensing

将官方的Spine运行时整合到你的应用程序之前, 请仔细阅读 Spine运行时许可页面.

开始使用

安装运行时

要在虚幻引擎项目中使用spine-ue运行时, 请:

  1. 下载并安装虚幻引擎. *目前与 UE 4.27+ 兼容. 虚幻引擎5.3用户请参阅下文中的升级至UE 5.3一节!
  2. 根据操作系统选择下载并安装 Visual Studio 2015 / 2017 / 2019 Community 或最新版本的 Xcode. 如果不确定Visual Studio应安装哪一版本, 请参考该指南. 当安装和设置Visual Studio时, 请遵循指南全新安装Visual Studio时的选项(Options for a New Visual Studio Installation)一节.
  3. 在虚幻引擎编辑器中创建一个新的空的C++代码项目. 虽然spine-ue运行时兼容于C++或Blueprints, 但插件需要使用C++代码项目.
  4. 克隆 spine-runtimes Git 代码库. 果你不想使用Git, 请下载最新版本ZIP压缩包并解压之.
  5. 在你的项目文件夹中, 创建一个名为Plugins的子文件夹.
  6. spine-runtimes/spine-ue/Plugins/下的内容复制到项目的Plugins/文件夹中.
  7. spine-runtimes/spine-cpp/spine-cpp文件夹复制到项目的Plugins/SpinePlugin/Source/SpinePlugin/Public/文件夹中.
  8. 在项目的Build.cs文件中的PublicDependencyModuleNames中加入SpinePlugin.

完整示例可查看该处的示例 Build.cs 文件.

您可以通过在虚幻引擎编辑器中打开项目并选择File -> Open Visual StudioFile -> Open Xcode来检视您的游戏代码和spine-ue运行时的C++代码.

示例项目

spine-runtimes/spine-ue目录是一个可以在虚幻引擎编辑器中打开的项目.

要检视spine-ue运行时示例, 请:

  1. 下载并安装虚幻引擎. 目前支持 UE 4.27+ .

  2. 根据操作系统选择下载并安装 Visual Studio 2015 / 2017 / 2019 Community 或最新版本的 Xcode. 如果不确定Visual Studio应安装哪一版本, 请参考该指南. 当安装和设置Visual Studio时, 请遵循指南全新安装Visual Studio的选项(Options for a New Visual Studio Installation)一节.

  3. 克隆 spine-runtimes Git 代码库. 果你不想使用Git, 请下载最新版本ZIP压缩包并解压之.

  4. spine-runtimes/spine-cpp/spine-cpp文件夹复制到项目的Plugins/SpinePlugin/Source/SpinePlugin/Public/文件夹中.

  5. 在虚幻引擎编辑器中打开 spine-runtimes/spine-ue/SpineUE.uproject 项目. 由于该项目还没有在虚幻项目浏览器中的Recent Projects下列出, 因此请选择More, 然后点击Browse并指定SpineUE.uproject文件.

  6. 查看内容浏览器中Content/GettingStarted文件夹中的不同示例地图. 每张地图都包含如何运行的文字说明和可见物件设置的描述.

    注意: 为使编辑器能够渲染你的skeleton, 请在视口中打开实时模式

您可以通过在虚幻引擎编辑器中打开项目并选择File -> Open Visual StudioFile -> Open Xcode来检视您的游戏代码和spine-ue运行时的C++代码.

更新 spine-ue 运行时

在更新项目中的spine-ue运行时前, 请提前阅读Spine编辑器和运行时版本管理指南.

一旦你确定要更新到最新的spine-ue运行时, 请按以下步骤操作:

  1. 通过Git从spine-runtimes Git 代码库拉取最新更改, 或下载最新版本的ZIP文件并解压之.
  2. 关闭虚幻引擎编辑器和Visual Studio/Xcode.
  3. 删除你虚幻引擎项目中的Plugins/SpinePlugin文件夹.
  4. spine-runtimes/spine-ue/Plugins/下的内容复制到项目的Plugins/文件夹中.
  5. spine-runtimes/spine-cpp/spine-cpp文件夹复制到项目的Plugins/SpinePlugin/Source/SpinePlugin/Public/文件夹中.
  6. 在虚幻引擎编辑器中打开该项目, 然后在Windows系统应选择File -> Generate Visual Studio project files而在macOS中则选择File -> Generate Xcode project files.
  7. 重新编译您项目的源代码以及插件的源代码.

注意: spine-ue运行时是基于通用spine-cpp运行时开发的. 请密切关注GitHub上的spine-ue和spine-cpp运行时更改. 如上文(5.)所述的忘记同时更新(复制)插件的spine-cpp部分是非常典型的更新出错原因.

升级到 Unreal Engine 5.3

从UE 5.3开始, Epic更改了引擎处理UAssets的方式. 导入的.skel.json.atlas文件不再共用文件名前缀. 例如,skeleton.skelskeleton.atlas这样的名称将无法工作. 然而skeleton-data.skelskeleton.atlas 工作正常.

对于使用UE 5.3 之前的版本创建的项目, 无法自动升级已导入的资产. 若将此类项目升级到UE 5.3, Epic 在此引入的机制将使内容浏览器将无法显示导入的skeleton data和atlas资源. 将项目安全升级至UE 5.3 的唯一方法是手动重新命名、重导入并重新连接所有skeleton data和 .atlas 文件.

我们在UE 5.3 发布后立即向 Epic 提交了一份错误报告, 但并未收到对方的回复. 因此我们认为 Epic 将不会修复此错误, 且上述变通方法和使用限制将长期存在.

使用 spine-ue 运行时

概览

spine-ue运行时是一个虚幻引擎插件, 它可以回放并操作由Spine创建的动画. spine-ue运行时是用C++编写的, 它基于通用的spine-cpp运行时. spine-ue运行时包装了spine-cpp的结构体和函数, 且同时以代码和UE蓝图的形式公开. 此外spine-ue运行时可导入从Spine编辑器中导出的文件, 并将它们存储在自定义的虚幻引擎资产文件中.

请查阅Spine 运行时指南以详细了解Spine运行时架构概览.

资产管理

导出虚幻引擎可用的资产

请按照Spine用户指南的指示进行操作, 以便顺利地:

  1. 导出 skeleton & 动画数据
  2. 导出包含图像文件的skeleton texture atlases

一份导出的Skelton数据和texture atlases将包含以下文件:

  1. skeleton-name.jsonskeleton-name.skel文件, 其包含skeleton和动画数据.
  2. skeleton-name.atlas文件, 包含关于texture atlases的信息.
  3. 一个或多个.png文件, 每个文件代表texture atlases的一个页, 包含了Skeleton所使用的打包图像.

请务必确保你的 资产文件名前缀不同. 例如:

skeleton.skel
skeleton.atlas

这样地名称是无法正常导入的, 因为这两个文件名前缀相同skeleton. 不过你可以将它们重命名为:

skeleton-data.skel
skeleton.atlas

如此spine-ue运行时便可将这些文件导入为虚幻引擎自有的资产文件格式.

注意: spine-ue运行时目前还不支持使用预乘alpha导出的atlas.

将资产导入虚幻引擎

  1. 在虚幻引擎编辑器中打开您的虚幻引擎项目
  2. 在内容浏览器中点击 Import
  3. 选择您从Spine编辑器中导出的.json.skel 文件, 并同时选中 .atlas

资产导入器将为skeleton数据和texture atlases自动创建虚幻引擎资产.

文件导入流程会:

  1. 根据skeleton数据文件(.json, .skel)创建一个Spine skeleton数据资产
  2. 根据texture atlases文件创建Spine texture atlases资产(.atlas)
  3. 创建每个texture atlases页的texture资产(.png), 它们将被存储在texture atlases资产旁的一个名为Textures的文件夹中

更新Spine资产

在开发过程中可能需要时常更新Spine skeleton数据和texture atlases文件. 对此可以从Spine编辑器中重新导出数据再直接覆盖源文件(.json, .skel, .atlas, .png)即可.

虚幻引擎编辑器将检测到这些源文件的更改并提示重新导入资产. 在重新导入后, 所有对以前导入的Spine资产的引用均保持完整, 且使用最新的导出数据.

注意: 虚幻引擎编辑器有时不能识别源文件的变化. 在这种情况下, 请在内容浏览器中找到您的Spine Skelton数据或texture atlases资产, 双击它, 然后在新打开窗口的菜单中选择Asset -> Reimport.

Skeleton 数据资产

skeleton数据资产存储了骨骼的层次结构、槽位、绘制顺序、动画以及其他构成skeleton的数据. spine-ue运行时提供的其他组件引用并共享该skeleton数据资产, 以便将skeleton作为虚幻引擎Actor实例的一部分进行动画回放和显示.

skeleton数据资产中可以指定动画mix时间. 在内容浏览器中双击该资产, 然后输入默认的mix时间, 或者通过点击资产Mix Data属性上的+号来设置两个特定动画间的mix时间.

使用skeleton数据资产的组件, 如skeleton动画组件, 会在播放动画时使用这些mix时间设置.

skeleton数据资产的详细视图还显示了资产中包含的所有骨骼、槽位、动画、皮肤和事件.

Texture Atlas 资产

texture atlas资产包含了skeleton所用的图片的信息, 即图片存储在texture atlas的哪个页上, 以及它在texture atlas页上的UV坐标.

你可以在内容浏览器中双击资产来查看texture atlas页上的texture.

注意: 你可以修改texture atlas资产所引用的texture. 在执行此操作时请确保其UV坐标有效.

组件

spine-ue运行时提供了一组组件, 可以显示、播放动画并修改从Spine导出的skeleton. 这些组件将引用上文中的Skelton数据和texture atlas资产.

将Skeleton置入场景

要在您的虚幻引擎项目中显示Spine skeleton:

  1. 按上文中的步骤导入skeleton数据和texture atals.
  2. 在您的关卡中创建一个空的Actor.
  3. 在Actor的详情视图中添加一个 Spine Skeleton Animation组件, 并skeleton数据和texture atlas属性设置为导入的资产.
  4. 在Actor中添加一个Spine Skeleton Renderer Component.

现在就可以使用蓝图或组件的C++ API来为skeleton应用动画, 响应动画触发的事件等等. 更多细节请参考下文的组件一节.

注意: 为了让编辑器渲染skeleton, 请在视口中打开实时模式

Skeleton 组件

Skeleton组件存储了对skeleton数据和texture atlas资产的引用, 并将在每个Tick()更新每块骨骼的世界变换. 它可以作为skeleton动画组件或自定义组件的基础组件.

注意: 一般来说还是建议使用下述的skeleton动画组件, 它足够动画化Skelton. 自定义Skelton组件一般基于原装的Skeleton组件构建.

设置 Skeleton 数据 & Texture Atlas

一个Skeleton组件需要引用Skelton数据资产, 它可以从中获得有关Skeleton的骨骼层次、槽位等信息; 此外还需要引用texture atlas资产, 通过它可从中获得Skeleton所使用的图像.

要设置Skeleton数据和texture atlas:

  1. 在actor的详情视图中选择组件
  2. 设置AtlasSkeleton Data属性即可

注意: 必须正确设置Skelton数据和atlas资产的引用组件才能正常运行. 如果两个属性中有任一留空, 组件则将无提示地忽略所有来自C++和蓝图的交互.

生命周期

Skelton组件实现了UActorComponent::Tick()方法, 该方法会更新Skeleton中所有骨骼的世界变换.

该组件将组播委托BeforeUpdateWorldTransformAfterUpdateWorldTransform暴露为了属性, 通过它们可以在计算所有骨骼的世界变换之前和之后介入组件的生命周期. 绑定这些委托即可修改骨骼的位置等skeleton属性, 而无需关心actor和组件的更新顺序.

C++

在欲介入组件生命周期的类中, 向类头文件中添加一个UFUNCTION声明:

UFUNCTION()
void BeforeUpdateWorldTransform(USpineSkeletonComponent* skeleton);

注意: 将该方法标记为UFUNCTION至关重要, 否则它就无法被绑定到委托.

接下来, 在类的.cpp文件中添加该函数的签名:

void MyClass::BeforeUpdateWorldTransform(USpineSkeletonComponent* skeleton) {
... modify the skeleton here ...
}

最后, 将该方法绑定到委托————例如在你的actor或组件的BeginPlay()中. 这里假设你有一个对包含了Skeleton组件的AActor的引用:

AActor* actor = ...
USpineSkeletonComponent* skeletonComponent = static_cast<USpineSkeletonComponent*>(Actor->GetComponentByClass(USpineSkeletonComponent::StaticClass()));
skeletonComponent->BeforeUpdateWorldTransform.AddDynamic(this, &USpineBoneDriverComponent::BeforeUpdateWorldTransform);

绑定AfterUpdateWorldTransform委托方法亦然.

Blueprint

假设你有一个包含Skeleton组件的角色的蓝图

  1. 在蓝图编辑器中打开该蓝图
  2. 在组件视图中, 选择skeleton组件
  3. 在Skeleton组件的详情视图中, 点击Before Update World TransormAfter Update World Transform后的+
  4. 将其他蓝图节点连接到事件的exec端, 以调用你自定义的更新逻辑

绑定AfterUpdateWorldTransform委托方法亦然.

设置皮肤

一个Spine Skeleton可包含多个皮肤, 它定义了哪个附件处于哪个槽位里. Skeleton组件提供了一个便捷的函数来切换皮肤.

C++
bool success = skeletonComponent->SetSkin(FString(TEXT("skinName"));
Blueprint

设置附件

若要设置附件则需提供槽位和附件名称.

C++
bool success = skeletonComponent->SetAttachment(FString(TEXT("slotName")), FString(TEXT("attachmentName"));
Blueprint

重置为Setup Pose

程序化动画有时需要将骨骼和/或插槽重置到其setup pose.

C++
skeletonComponent->SetToSetupPose();
skeletonComponent->SetBonesToSetupPose();
skeletonComponent->SetSlotsToSetupPose();
Blueprint

Skeleton翻转

垂直或水平翻转Skeleton可复用动画, 例如, 一个向左的行走动画可以翻转后作为向右行走的动画.

C++
bool isFlippedX = skeletonComponent->GetScaleX() < 1;
skeletonComponent->SetScaleX(-1);
bool isFlippedY = skeletonComponent->GetScaleY() < 1;
skeletonComponent->SetScaleY(-1);
Blueprint

获取并设置骨骼变换

可通过Skeleton组件设置和获得骨骼变换, 由此可实现IK地形适配或者让其他actor和组件(如粒子系统)跟踪Skeleton中的骨骼. 所有变换均以世界坐标形式给出, 这样可使交互更便利.

注意: 若你需要修改骨骼的本地变换, 请在C++代码中调用spine-cpp运行时的API.

注意: 确保在更新世界变换的生命周期中应用新骨骼位置, 否则你的修改可能被动画覆盖.

注意: Spine骨骼跟随器(bone follower)和Spine骨骼驱动器(bone driver)组件是与骨骼交互的一种更便利方式.

C++
FTransform boneWorldTransform = skeletonComponent->GetBoneWorldTransform(FString(TEXT("boneName"));
skeletonComponent->SetBoneWorldPosition(FString(TEXT("boneName"), FVector(x, y, z));
Blueprint

Skeleton 动画组件

Skeleton 动画组件是spine-ue运行时的核心. 它可以将Spine的Skeleton添加到一个Actor上, 使其动动起来并响应动画事件等等.

注意: Skeleton 动画组件基于Skeleton组件, 因此继承了上文所述的所有Skeleton组件功能!

生命周期

Skeleton动画组件实现了UActorComponent::Tick()方法, 该方法基于delta时间更新底层的AnimationState对象, 将AnimationState应用于Skeleton并更新Skelton上全部骨骼的世界变换.

由于Skeleton动画组件基于Skeleton组件的, 因此亦可循上文所述之法将其绑定到BeforeUpdateWorldTransformAfterUpdateWorldTransform代理.

Skeleton动画组件会将AnimationState API暴露给蓝图和C++. 本节假设你熟悉轨道、轨道条目、mix时间或动画队列等概念, 详细内容请见Spine运行时通用指南中应用动画一节所述.

时间缩放比例(Time Scale)

可以设置Skeleton动画组件的时间缩放比例来减慢或加快动画的播放速度. 推进动画所用的delta时间直接乘以时间缩放比例就是动画播放速度, 例如, 缩放比例为0.5时动画会减慢到正常速度的一半; 而缩放比例为2时, 动画播放会加速为正常速度的2倍.

C++
float timeScale = animationComponent->GetTimeScale();
animationComponent->SetTimeScale(0.5);
Blueprint

设置动画

设置一个动画需要提供: 轨道索引、动画名称并指定是否循环播放动画-

C++
USpineSkeletonAnimationComponent* animationComponent = ... fetch from actor ..
UTrackEntry* entry = animationComponent->SetAnimation(trackIndex, FString(TEXT("walk")), true);
Blueprint

队列动画

要队列一个动画, 需要提供轨道索引、动画名称、该动画在轨道上开始播放的延迟时间(单位为秒)并指定是否循环播放该动画.

C++
UTrackEntry* entry = animationComponent->AddAnimation(trackIndex, FString(TEXT("run")), true, 2);
Blueprint

设置和队列空动画, 清空轨道

Skeleton动画组件还提供了函数和蓝图节点来设置空动画, 排队空动画和清除一根或所有的轨道. 所有这些功能都与上文所示的函数和节点类似.

C++
UTrackEntry* entry = animationComponent->SetEmptyAnimation(trackIndex, mixDuration);
entry = animationComponent->AddEmptyAnimation(trackIndex, mixDuration, delay);
animationComponent->ClearTrack(trackIndex);
animationComponent->ClearTracks();
Blueprint

轨道条目

所有的方法/节点中均会返回TrackEntry, 通过它可以进一步定制某个特定动画的播放, 并可将其绑定到某个轨道条目事件的委托上. 详见下文中的处理动画状态事件一节.

注意: 返回的轨道条目只在动画状态移除了条目对应的动画之前有效. 虚幻引擎的垃圾收集器(GC)会自动释放它们. 在收到一个轨道条目的释放(dipose)事件之后就不应该再存储或访问轨道条目了. 基于基类spTrackEntry的C++包装器可以阻断无效的访问.

C++
UTrackEntry* entry = ...
entry->SetEventThreshold
(2);
float trackEnd = entry->GetTrackEnd();
Blueprint

处理动画状态事件

当基类对象AnimationState播放动画时, 将触发各种事件来通知监听器

  1. 动画播放 开始(started).
  2. 动画播放 中断(interrupted), 例如清空了一条轨道或设置了新的动画.
  3. 动画播放 完成(completed), 如果循环播放动画则该事件会多次触发.
  4. 动画播放 结束(ended), 既可能缘于动画播放中断亦可能是非循环动画播放完成
  5. 动画及其对应TrackEntry已被 释放(disposed)
  6. 触发了 用户自定义的 事件(event)

Skelton动画组件提供了C++代码或蓝图可以绑定的委托, 这些委托可响应所有轨道上的全部队列动画的事件. 监听器也可以只绑定到某个TrackEntry的委托上.

C++

在需响应AnimationState事件的类的头文件中, 为需要监听的事件添加UFUNCTION声明:

UFUNCTION()
void AnimationComplete(UTrackEntry* entry);

UFUNCTION()
void UserDefinedEvent(UTrackEntry* entry, FSpineEvent evt);

接下来, 在类的.cpp文件中添加函数的签名:

void MyClass::AnimationComplete(UTrackEntry* entry) {
... react to complete event here ...
}

void MyClass::UserDefinedEvent(UTrackEntry* entry, FSpineEvent evt) {
... react to user defined event here ...
}

最后, 将该方法绑定到USkeletonAnimationComponent或的UTrackEntry委托:

USpineAnimationComponent* animationComponent = ...
animationComponent->AnimationComplete.AddDynamic(this, &MyClass::AnimationComplete);

UTrackEntry* entry = ...
animationComponet->AnimationEvent.AddDynamic(this, &MyClass::UserDefinedEvent);
Blueprint

要绑定到Skeleton动画组件上的AnimationState事件:

  1. 打开蓝图
  2. 选择Actor上的Skeleton动画组件
  3. 在详情视图中, 点击欲响应事件旁的+按钮

要绑定到一个某个TrackEntry的委托:

  1. Set Animation, Add Animation, Set Empty AnimationAdd Empty Animation等节点的返回值端点拖到蓝图中空白区域.
  2. 在弹出的 Components -> Spine -> TrackEntry类别中选择Assign Animation XXX.
  3. 确保 Bind Event to节点的连接能够被执行.

视口预览

可以使用Skeleton动画组件的Preview AnimationPreview Skin属性在UE编辑器视口预览动画和皮肤. 只要输入动画和皮肤的名称即可.

/img/blog/Unreal-Engine-4-quality-of-life-improvements/preview.gif

将各自属性置空即可重置动画或皮肤.

Skeleton渲染器组件

Skeleton 渲染器(Renderer)组件用于绘制同一角actor的Skeleton或Skeleton动画组件的当前状态. 渲染基于程序化网格进行. 该组件使用Skeleton(或Skeleton动画)组件所引用的texture atlas资产来查找绘制Skeleton附件所需的texture.

材质(Materials)

Skeleton渲染器组件包含4个material(材质)属性, Spine支持的每种blend模式各占其一. 默认情况下, 这4种material被设置为无光照(unlit)material, 它们是spine-ue插件的一部分(见该插件的Contents文件夹).

修改该插件的默认material可更改全部Skeleton渲染器组件所引用的material.

只需创建一个新的material并将其分配给该Skeleton渲染器组件的material属性, 即可修改某个特定的Skeleton渲染器组件所使用的material.

无论何时都必须提供参数化的material, 如此才能让Skeleton渲染器组件设置texture源. 默认情况下, 渲染器组件会假设texture参数名为 "SpriteTexture". 你也可以在Skeleton渲染器组件中自定义参数名.

深度偏移(Depth Offset)

附件是由Skeleton渲染器组件基于x/z平面上从后向前渲染的. 每个附件均可设置各自的Y轴的深度偏移值, 以避免深度冲突(z-fighting)问题. 深度偏移值在C++和蓝图中均可设置.

Skeleton跟随器(Follower)组件

该组件引用一根来自Skeleton(动画)组件的骨骼, 并在每个Tick上将该骨骼的变换设置为该组件的变换.

它可以让粒子系统等对象跟随skeleton上的某块特定骨骼.

Skeleton驱动器(Driver)组件

该组件引用一根来自Skeleton(动画)组件的骨骼, 并在每个Tick上将该骨骼的位置设为该组件的位置. 该组件将在Skeleton(动画)组件更新世界变换前设置骨骼位置.

当玩家需要拖动Skeleton上的骨骼时可使用该组件.

适用于UMG UI的Spine小部件

从Spine 3.8开始, spine-ue运行时提供了Spine小部件(widget), 用于整合Spine Skeleton与UMG UI. 这个小部件与Skeleton动画组件具有相同的接口, 适用于蓝图和C++代码. 小部件也与Skeleton渲染器组件共享一些接口, 即允许你设置附件间的深度偏移, 以及改变用于渲染Skeleton的material. 详情请参考关于这两个组件的章节.

要在你的UMG UI上添加Spine Skeleton, 首先要创建一个新的小部件(widget)蓝图. 打开蓝图, 然后通过菜单为它添加一个Spine小部件.

接下来在Spine小部件上设置atlas和Skeleton数据资产, 并在设计器视口中调整其大小.

Skeleton会根据小部件来自动调整尺寸, 并保持长宽比, 以便尽量填充小部件的内部空间. 可以通过Scale属性来缩放它.

在小部件编辑器窗口的Design切换到Graph编辑模式后, 就可以修改小组件的蓝图. 以下是一个蓝图的示例:

当触发了Construct事件时, 将设置raptor小部件的初始动画. 我们还需要将Tick事件与小部件的Tick函数连接起来, 这样它才会播放动画.

最后, 打开关卡蓝图并将UMG部件添加到视口中:

在蓝图中, 首先创建由其类名指定的部件, 然后将其添加到视口中.

动态模糊后处理

spine-ue的默认material使用了虚幻引擎支持的半透明blend模式. 为使这种blend模式能够适配动态模糊后处理效果, 请在material的详细一项中启用OutputVelocity选项.