spine-unity 运行时文档

Licensing

将官方的Spine Runtime整合到你的应用程序中需要有效的 Spine 许可证, 但我们欢迎你对Spine Runtimes进行评估.

入门篇

安装

要在你的Unity项目中使用spine-unity运行时, 你需要:

  1. 下载并安装 Unity.
  2. 在Unity Editor创建一个空白的新项目.
  3. 下载 最新的 spine-unity 组件包. 你也可以如下所述方法通过Git获得最新版.
  4. 导入这个Unity组件包 (双击它就可以让Unity打开).

通过Git而非unity组件包获取最新运行时

  1. 克隆 spine-runtimes Git repository.
  2. spine-runtimes/spine-unity/Assets/ 里的文件拷贝到你项目里的 Assets/ 文件夹里.
  3. spine-runtimes/spine-csharp/src 文件夹拷贝到你项目里的 Assets/Spine/Runtime/spine-csharp 文件夹中.

通过软件包管理器安装spine-unity运行时

使用Unity的包管理器-'Add package from git URL'而非直接打开Unity组件包(unitypackage)
如果你的Unity版本比较新, 且你的Unity包管理器提供了 Add package from git URL... 选项, 你也可以直接使用下方的三个URL来添加这三个包:

  1. https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-csharp/src#4.1
  2. https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-unity/Assets/Spine#4.1
  3. https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-unity/Assets/Spine Examples#4.1

要打开Spine Examples包的示例场景请使用操作系统的文件管理器(ExplorerFinder), 应将场景文件从包目录复制到你的 Assets目录. 因为如果你试图直接打开通过git URL下载的包中的场景, Unity中的一个bug会导致出现"Opening scene in read-only package!"错误: Opening scene in readonly package

在Unity中打开 Package Manager (点击Unity的 Window > Package Manager 即可打开). 要安装这三个包, 请选择 + 图标, 选择 Add package from git URL..., 然后输入上文的三条git URL(或从spine-unity下载页面复制URL). URL中的 #4.1 指定了分支版本, 也可以将其改为为具体的git commit哈希值, 比如#5e8e4c21f11603ba1b72c220369d367582783744, 这样能确保您的项目中的每个人都有相同的软件包版本. Window - Package Manager Add Package from git URL Enter git URL 这时 Package Manager 窗口将列出已新增的条目. Listed spine-unity packagesProject 面板的 Packages 下可以看到 spine-unity Runtimespine-unity Runtime Examples 条目. Project panel lists packages 如果条目还未在 Project 面板中列出, 你可能需要重启一下Unity.

2D Toolkit兼容性

spine-unity 支持 2D Toolkit 可以使用TK2D的texture atlas系统渲染Spine骨架. 要启用2D Toolkit兼容性, 请通过 Edit - Preferences (Mac上是 Unity - Preferences) 打开Unity的 Preferences, 在 Spine 部分选择 Define TK2D - Enable.

可选的UPM插件包

spine-unity运行时无需额外的插件即可工作. 一些可选的功能,如timeline或URP支持, 是通过单独的Unity Package Manager(UPM)扩展包提供的.

为何使用独立的插件包

Unity已将大量的可选插件迁移至新的Unity Package Manager(UPM)生态系统. 例如,其基于Universal Render Pipeline的着色器文件也是包含在 Universal RP 包中通过UPM提供, 而非直接内建于每个新的Unity项目里.

如果Unity的 Universal RP 包没有安装, 那么spine-unity运行时中引入的Universal RP Spine shaders会导致错误信息和不必要的配置. 把Universal RP Spine shaders打包为UPM包, 可自动解决这种Unity的Universal RP包尚未引入项目中的依赖性, 使这种附加功能更容易使用.

安装

方法 A - 下载 UPM 包
  1. spine-unity下载页面中找到所需的UPM包, 或者在Git仓库的spine-unity/Modules子目录下找到它们.
  2. 若已打开了某个Unity项目, 建议你关闭当前全部包含了Spine组件的场景(例如, 打开一个新的空场景来执行安装).
  3. 在解压或克隆源文件后, 你可以通过以下两种方式之一使用该软件包:
  • a) 复制到你的项目中
    把包的内容复制到你的项目中的 Packages 目录. Unity会自动加载它.
  • b) 用UPM包管理器导入
    把包的内容复制到 Assets 目录以外的任何地方, 然后在Unity中打开 Package Manager (Window > Package Manager), 选择 + 图标, 单击 Add package from disk..., 定位并选择 package.json 文件. Window - Package Manager Add Package from Disk Select package.json 现在 Package Manager 将列出一个 Spine Lightweight RP Shaders 条目: Listed LWRP package 现在可以在 Project 面板中的 Packages 下方找到一个 Spine Lightweight RP Shaders 条目 : Project panel lists package 如果该条目没有在 Project 面板中列出, 你可能需要重启一下Unity.

方法 B - 从git URL添加包

如果你的Unity版本比较新, 且你的Unity包管理器提供了 Add package from git URL... 选项, 你也可以直接使用git URL来添加包.

重要提示: 当从git URL安装URP Shaders UPM package时, 也必须要通过Unity包管理器安装spine-csharpspine-unity包, 而不能用unitypackage安装到Assets文件夹. 后者会导致URP Shaders UPM package中着色器的include路径会指向一个不存在的spine-unity包目录, 从而导致着色器编译出现错误.

  1. spine-unity下载页面中找到所需UPM软件包的git URL.
  2. 若已打开了某个Unity项目, 建议你关闭当前全部包含了Spine组件的场景(例如, 打开一个新的空场景来执行安装).
  3. 打开Unity的 Package Manager (点击 Window > Package Manager), 选择 + 图标, 点击 Add package from git URL..., 然后输入从spine-unity下载页面找到的git URL. 比如 https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-unity/Modules/com.esotericsoftware.spine.urp-shaders#4.1. URL中的 #4.1 指定了分支版本, 也可以将其改为为具体的git commit哈希值, 比如#5e8e4c21f11603ba1b72c220369d367582783744, 这样能确保您的项目中的每个人都有相同的软件包版本. Window - Package Manager Add Package from git URL Enter git URL 现在 Package Manager 将列出一个 Spine Lightweight RP Shaders 条目: Listed LWRP package 现在可以在 Project 面板中的 Packages 下方找到一个 Spine Lightweight RP Shaders 条目 : Project panel lists package 如果该条目没有在 Project 面板中列出, 你可能需要重启一下Unity.

要打开Spine Examples包的示例场景请使用操作系统的文件管理器(ExplorerFinder), 应将场景文件从包目录复制到你的 Assets目录. 因为如果你试图直接打开通过git URL下载的包中的场景, Unity中的一个bug会导致出现"Opening scene in read-only package!"错误: Opening scene in readonly package

示例场景

要探索spine-unity运行时的附带示例场景你可以:

  1. 下载并安装 Unity.
  2. 在Unity编辑器中创建一个新的空项目.
  3. 下载 最新的 spine-unity 组件包. 你也可以如下所述方法通过Git获得最新版.
  4. 导入这个Unity组件包 (双击它就可以让Unity打开).
  5. 在Unity 编辑器中打开项目, 然后在Project面板中查看 Spine Examples/Getting Started 文件夹中的各个示例场景. 每个示例场景都有关于如何运行的文字说明和描述.

通过Git而不是unitypackage获得最新更新

  1. 克隆 spine-runtimes Git 仓库.
  2. spine-runtimes/spine-unity/Assets/ 下的文件复制到你项目的 Assets/ 文件夹下.
  3. spine-runtimes/spine-csharp/src 文件夹复制到你项目的 Assets/Spine/Runtime/spine-csharp 文件夹下.

通过软件包管理器和git URL从UPM包进行安装
你也可以选择通过Unity软件包管理器从git UPM软件包中安装spine-unity运行时和spine-unity示例, 使用Add package from git URL选项即可. 更多信息请参见使用包管理器安装一节.

你可以在Unity编辑器中打开项目并选择 Assets -> Open C# Project 来查看和修改示例和spine-unity运行时的C#代码. 于示例场景的其他信息, 请参见 场景示例部分.

更新spine-unity运行时

在更新项目中的spine-unity运行时之前, 请参考我们的 Spine编辑器和运行时版本管理指南.

注意: 从Spine 4.0或更早的版本导出的Json和二进制skeleton数据文件 不能 被spine-unity 4.1运行时读取! skeleton数据文件需要使用Spine 4.1重新导出.

如果你有很多项目, 我们建议按照 该文章的方法来自动导出项目文件.
例如, 可以使用该脚本来导出所有的Spine示例项目并创建texture atlas: export.sh

从spine-unity 3.6、3.7、3.8或4.0升级到最新版本时, 请参考如下升级指南:

建议遵循以下步骤来防止出现潜在问题:

  1. 和Unity的更新一样, 建议在执行运行时更新前备份整个Unity项目.
  2. 在更新你的Spine运行时之前, 一定要与你的主程和TA做好确认. Spine运行时是开源的, 并允许用户根据不同的项目需求进行修改. 你项目的Spine运行时可能已经被程序员修改过了. 在这种情况下, 更新到最新的运行时也需要将这些修改重新应用于新版本运行时中.
  3. 阅读unitypackage中包含的CHANGELOG.md 文件或 github. 当过时的函数方法被新方法所取代时, 可以在这里找到必要的描述文档.

一旦你确定要更新到最新的spine-unity运行时:

  1. 下载 最新的 spine-unity unitypackage. 你也可以通过Git从 spine-runtimes Git 仓库 拉取最新的改动.
  2. 关闭Unity编辑器和 Visual Studio/Xcode.
  3. 当进行大版本或小版本更新 (比如从 3.7 升级到 3.8)时, 应从项目中删除旧版spine-unity安装目录的 Assets/SpineAssets/Spine Examples 目录.
  4. 在Unity编辑器中打开该项目. 如果你删除了旧版的spine-unity运行时, 请忽略控制台留存的报错.
  5. 导入最新的unitypackage (双击它就可以让Unity打开).
  6. 当进行大版本或小版本更新, 在项目面板上选择 right-click - Reimport All 来重新导入skeleton资产. 也可以不重新导入所有资产, 在项目面板中找到你想重新导入的包含Spine skeleton资产的文件夹, 右击该文件夹, 然后选择 Reimport 也可达到相同效果.

通过Git而不是unitypackage获得最新更新

  1. spine-runtimes Git 仓库拉取最新的改动来获得最新的spine-unity 运行时.
  2. 当进行大版本或小版本更新 (比如从 3.7 升级到 3.8)时, 应从项目中删除旧版spine-unity安装目录的 Assets/SpineAssets/Spine Examples 目录.
  3. spine-runtimes/spine-unity/Assets/ 中的文件复制到你的项目的 Assets/ 文件夹下.
  4. spine-runtimes/spine-csharp/src 文件夹复制到你项目的 Assets/Spine/Runtime/spine-csharp 文件夹下.

注意: spine-unity运行时基于通用的 spine-csharp 运行时. 请务必在 GitHub上关注spine-unity和spine-csharp运行时的更新.

通过Unity包管理器的 Add package from git URL 选项更新

如果你已经用 Add package from git URL 选项从远程git URL添加了spine-unity运行时, 那么也可以用Unity软件包管理器来更新这些软件包. 在Unity中打开 Package Manager(点击 Window > Package Manager), 选中 spine-csharp Runtime, spine-unity Runtimespine-unity Runtime Examples 三个包, 点击 Update 按钮即可从git的指定分支中重新下载最新版本. 注意, 如果你的URL以 #4.1 结尾, 如https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-csharp/src#4.1, 它将从git仓库的4.1分支版本中下载最新版本.

更新UPM插件包

当需要升级 可选UMP插件包时:

方法 A - 就地更新(通过下载.zip文件或git更新)

  1. 如果你打开了Unity项目,建议你 a) 关闭Unity, 或者 b) 关闭任何包含Spine组件的场景(例如, 另开一个空场景).
  2. 将新的UPM包的zip文件或git目录的内容复制到现有的目录下. 根据你安装UPM包的不同方式,目录可能是你项目中的 project_root/Packages/package_name 目录, 或者是 Assets 目录外的任意目录, 可以通过 Add package from disk..来加载它.
  3. 如果你已经关闭了Unity, 那请在Unity中再次打开你的项目.
  4. Unity将会导入新的资产并显示一个加载进度条.

方法 B - 使用 Add package from git URL 选项

如果你已经用 Add package from git URL 选项从远程git URL添加了spine-unity运行时, 那么也可以用Unity软件包管理器来更新这些软件包. 在Unity中打开 Package Manager(点击 Window > Package Manager), 选中需要升级的软件包, 然后点击 Update 按钮即可从git的指定分支中重新下载最新版本. 注意, 如果你的URL以 #4.1 结尾, 如https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-csharp/src#4.1, 它将从git仓库的4.1分支版本中下载最新版本.

在Unity中进行编码

如果你不熟悉C#编程和Unity的使用, 我们建议先看看 官方的Unity教程. 他们的界面基础初级编程 都是很好的入门主题. 请注意, 动画主题并不直接适用于spine-unity, 因为Spine使用的是自有的动画工作流.

使用spine-unity运行时

概览

spine-unity运行时是一个Unity插件,用于回放和操纵Spine创建的动画. spine-unity 运行时是用C#编写基于通用的spine-csharp 运行时的运行时. 运行时包装了spine-csharp的结构体和函数并将它们作为Unity组件暴露. 此外,spine-unity运行时导入从Spine Editor导出的文件,并将其存储在自定义的Unity资产类型中.

你可以查阅Spine 运行时指南了解Spine Runtime架构的详细概述.

资产管理

为Unity导出Spine资产

导出指南

你可以在《Spine用户指南》中找到完整的步骤, 说明如何来:

  1. 导出skeleton和animation数据
  2. 导出包含skeleton图像的texture atlases

从Spine导出文件到Unity

下面的步骤展示了为Unity导出Spine资产的一个简单方法.

  1. 在你创建了你的skeleton和animations后, 单击 Spine Menu>Export... (CTRL+E). 这会打开 Export window.

  2. 选择导出窗口左上角的 JSON .

  3. 勾选 Texture AtlasPack 复选框. (也推荐新手们勾上 Nonessential data, Pretty print). 确保 Animation cleanup 没有启用,否则pose对应的关键帧(keys)将不会被导出.

  4. 单击 Pack 复选框下面的 Pack Settings . 这会打开 Texture Packer Settings 窗口.

  5. 左下角的 Atlas extension 文本框内容要设置为 .atlas.txt.

  6. 这样就已完成了texture打包器的设置. 点击 OK 即可关闭.

  7. Export window 中选择输出文件夹的位置. (推荐: 创建一个新的空文件夹来存放.)

  8. 单击 Export.

  9. 将会输出三个如下文件:

  10. skeleton-name.jsonskeleton-name.skel.bytes, 包含了skeleton和animation数据.

  11. skeleton-name.atlas.txt, 包含了texture atlas的信息.

  12. 一个或多个 .png 文件, 每个文件代表了texture atlas中的一页, 而texture atlas包含了skeleton所需的全部图片.

注意: 对于 2D Toolkit 用户来说, 步骤 3 (打包 .png.atlas.txt) 是没有必要的. 在SkeletonDataAsset中填入适当字段, 以指定对 tk2dSpriteCollectionData 的引用. 关于如何启用2D Toolkit支持, 请参考本页面的 安装 部分.

用于Unity的二进制导出

以二进制格式而不是JSON格式导出,会使文件体积更小,加载更快.

以下步骤展示了为spine-unity导出二进制Spine资产的一个简单方法.

  1. 在导出窗口的左上角选择 Binary 而非 JSON.
  2. Extension 选为 .skel.bytes.

注意: 无法加载扩展名为 .skel 的文件. 务必使用扩展名 .skel.bytes.

高级 - 单页Texture Atlas文件的导出和SkeletonGraphic

一般来说,建议尽可能使用单texture(单页texture) atlas, 以减少由额外的子网格导致的绘制调用(draw call)次数. 对于 SkeletonGraphic来说尤其如此. 由于Unity所使用的 CanvasRenderer 的限制, SkeletonGraphic 在默认情况下被限制为单texture. 你可以在 SkeletonGraphic 的组件检查器中启用 Advanced - Multiple CanvasRenderers 为每个子网格生成一个子 CanvasRenderer GameObject 来提高texture上限. 出于性能方面的考虑, 最好尽可能地避免这样做. 这意味着在UI中使用的Skeletons应被打包成一个单texture(单页)atlas, 而非多页atlas.

如果它们不适合单页atlas, 你可以 按文件夹分组打包texture atlas页. 这样你可以确保每个皮肤只需要一个atlas页.

当图像被放置在各自的文件夹中时, 你可以通过以下步骤导出skeleton:

  1. Ctrl+E 或在下拉菜单中选择 Export...
  2. 启用 Texture Atlas Pack 并选择 Image Folder 而不是右侧的 Attachments.
  3. (可选步骤) 确保 Pack SettingsOptions 右下方的 Flatten PathsCombine Subdirectories 没有启用(默认为此).
  4. 点击导出.

高级 - Premultiplied 和 Straight Alpha 导出

Spine的Texture Packer对于如何导出texture atlas有两种不同工作流:

  1. Premultiplied alpha (默认为此, 在Gamma色彩空间中进行premultiplied)
  2. Straight alpha

Premultiplied alpha工作流相较于straight alpha有一些优势, 主要的两个优势是: additive-blended附件不会引起额外的绘制调用(draw call)和更佳的mip-map生成.

正确地匹配导出和导入设置是非常重要的, 请参阅 高级 - Premultiplied 和 Straight Alpha 导入一节, 了解Unity中的正确导入设置.

在Unity中导入Spine 资产

  1. 在你的Unity编辑器中打开Unity项目. 它应该已经有一个正常运行的spine-unity运行时, 如安装部分所述 .
  2. 打开导出文件的文件夹. (.json, .atlas.txt.png)
  3. 将导出的文件(或包含它们的文件夹)复制到你项目的 Assets 文件夹中. 你可以通过从资源管理器/搜索器窗口将导出的文件拖到Unity的项目面板中你想要的文件夹中来完成这一操作.

spine-unity运行时在检测到添加的文件后会自动生成必要的额外Unity资产.

以下资产均为自动生成:

  1. _Atlas 文件是texture atlas文件 (.atlas.txt). 它包含对material和 .atlas.txt 文件的引用.
  2. _Material 包含了各个 texture atlas页 (.png). 它包含对着色器和 .png texture的引用.
  3. _SkeletonData 存储了skeleton数据 (.json, .skel.bytes). 它包含了对 .json.skel.bytes 文件以及生成的 _Atlas 资产的引用. 它还为skeleton提供了自定义的导入和动画设置, 详见 Skeleton Data 资产部分.

高级 - Premultiplied 和 Straight Alpha 导入

如前文 高级 - Premultiplied 和 Straight Alpha 导出所述, Spine提供了两种texture导出的基本工作流:

  1. Premultiplied alpha (默认为此, 在Gamma色彩空间中进行premultiplied)
  2. Straight alpha
用于正确导入的 Atlas Texture Settings 性能相关设置参数

重要提示: 非常重要的一点是, 只要在atlas texture导出时启用 Premultiply alpha Unity中Material的 Straight Alpha Texture 参数和Texture的 Alpha Is Transparency 设置都要禁用, 反之亦然. spine-unity运行时在Unity的 Preferences 窗口中有个 Spine 配置 页面, 可以通过Preferences 下的 Edit - Preferences (Mac上是 Unity - Preferences )访问. 它里面有一个 Atlas Texture Settings 设置参数, 在新导入的Atlas textures中可以自动应用适当的texture和material导入设置. 在这里你可以找到 Additive Material, Multiply Material and Screen Material 的material模板. 当切换到Straight Alpha工作流时, 也应调整其material模板引用.

Spine Preferences - Atlas Texture Settings Spine Preferences PMA Preset Selection

Spine Preferences - Blend Mode Materials Spine Preferences Blend Mode Material Selection

当从Spine导出启用了 Premultiply alpha (默认)的atlas textures时, 可以把 Atlas Texture Settings 设置留为 PMATexturePreset, 在 SkeletonPMAAdditive, SkeletonPMAMultiplySkeletonPMAScreen 中选择blend 模式的 materials. 如果禁用了 Premultiply alpha, 则应将 Atlas Texture Settings 置为 StraightAlphaTexturePreset 且在 SkeletonStraightAdditive, SkeletonStraightMultiplySkeletonStraightScreen 中选择blend模式的 materials. 你也可以创建你自己的 TextureImporter Preset 资产和blend模式material模板, 并将之分配于此处. 当你自定义的时候, 建议在名字中带上 PMAStraight 字样, 以提示你自己所使用的blend模式.

如果你看到透明区域周围有深色的边框, 或者图片附件周围有彩色条带, 这很可能是因为导入设置不正确导致的.

正确的Texture打包器导出和Texture & Material导入设置:
  1. Premultiplied Alpha Texture 打包器启用 Premultiply alpha ,
    Unity Texture 设置中禁用 sRGB (Color Texture)Alpha Is Transparency ,
    Unity Material 参数中禁用 Straight Alpha Texture .

注意: 虽然本文档以前建议启用 sRGB (Color Texture) , 但当同时启用 Generate Mip Maps 后这组设置可能会导致附件出现白缝(seams). 为避免该类问题且如上设置在Gamma空间中亦是无往不利, 因此现在建议在使用PMA texture时禁用 sRGB (Color Texture).

  1. Straight Alpha Texture 打包器禁用 Premultiply alpha , 启用 Bleed ,
    Unity Texture 设置启用 sRGB (Color Texture)Alpha Is Transparency ,
    Unity Material 参数中启用 Straight Alpha Texture .

Texture 打包器的默认设置是使用 Premultiply alpha. 运行时中的所有Spine着色器也都默认使用 Premultiply alpha 工作流, 其 Straight Alpha Texture 参数都是默认禁用的.

在某些情况下你可能需要使用straight alpha工作流. 比如:

  1. 当你使用了 线性色彩空间, 那么就 必须使用straight alpha. 在导出时, Spine是在Gamma色彩空间中进行premultiplication的, 因此在导入时转换为线性色彩空间时将导致色彩超限. 当在material里检测到这种设置组合时, 控制台会给你一个警告消息.
  2. 当你想使用非Spine着色器时.
    一般的着色器默认用于straight alpha的texture, 这将导致图像附件周围出现错误的黑色边框.

在切换到straight alpha工作流时, 请确保对所有textures和materials进行如上配置. 你可以通过 Project Settings - Player - Other Settings - Color Space来检查或修改当前的颜色空间.

导入的故障排除步骤

当你把Skeleton导入Unity时遇到问题, 可能源自许多不同的原因, 从不正确的Spine导出设置到Unity中的错误设置均有可能. 以下视频将帮助你更好地了解导入过程中出现的许多常见问题应如何正确设置.

若内嵌视频无法播放, 请移步该视频的Bilibili播放页观看.

高级- 将Unity SpriteAtlas 作为 Atlas Provider

注意: 我们推荐遵循正常的Spine工作流,使用Spine创建的sprite atlases而非Unity的 Sprite Atlas 资产. Spine atlases提供了卓越的包装,支持多atlas页且健壮性更佳. 因此只有当你不能使用正常的Spine工作流时, 再使用Unity的 Sprite Atlas 资产作为atlas provider. 你还可以在运行时将收集到的皮肤中使用到的texture 区域重打包为一个atlas texture.

你可以使用Unity的 SpriteAtlas 作为atlas provider 以替代skeleton数据文件对应的 .atlas.txt.png 文件. 导入是通过一个特殊的 Spine SpriteAtlas Import 工具窗口处理的, 可以通过 Window - Spine - SpriteAtlas Import访问.

Spine SpriteAtlas Import Window

准备 Sprite Atlas 的步骤如下:

  1. 通过 Assets - Create - Sprite Atlas 创建 Sprite Atlas.
  2. Sprite Atlas 检查器中, 将包含所需Sprites的文件夹作为附件添加到 Objects for Packing 中.
  3. (a) 在Unity 2018.2以前的版本中, 请手动禁用 Tight Packing 并启用 Read/Write Enabled.
    (b) 在Unity 2018.2 及后续版本中, 这些参数会被自动设置. Spine SpriteAtlas Import Window
  4. 在新的 Spine SpriteAtlas Import 窗口中把 Sprite Atlas 赋值给 Sprite Atlas 属性. Unity会自动生成附加资产. Spine SpriteAtlas Import Window
  5. 点击 Load regions by entering Play mode 进入游玩模式然后退出该模式, 这样可以加载区域信息. 现在你的 Sprite Atlas 就可作为Spine atlas使用了.

要在 .json.skel.bytes skeleton 资产上使用atlas:

  1. .json.skel.bytes 新建的atlas资产放在同一目录下.
  2. 将其赋值给 Spine SpriteAtlas Import 窗口中的 Skeleton json/skel file 属性. Spine SpriteAtlas Import Window
  3. 点击 Import Skeleton 生成使用 Sprite Atlas 资产的 _SkeletonData 文件.

Spine 首选项

spine-unity运行时在Unity的 Preferences 窗口中添加了一个 Spine 页面, 可通过 Edit - Preferences 访问(Mac上是 Unity - Preferences ). 在这里, 你可以设置skeleton导入和实例化时使用的默认值, 以及自定义spine-unity运行时的外观和更新行为.

Spine Preferences Window

  • Show Hierarchy Icons. 层次结构面板中带有Spine组件的 GameObjects 旁显示相关图标.

  • Auto-reload scene components. 每当场景中的skeleton组件的SkeletonDataAsset被修改时, 都会自动重新加载. 当你的场景有大量的 SkeletonRendererSkeletonGraphic 组件时, 自动加载操作可能会很慢.

  • Reload SkeletonData after Play. 当启用该选项时, 在退出Play模式后, 运行时将(从.json或.skel.bytes文件)重新加载活动场景中所有Skeleton的共享SkeletonData. 这可能会延长项目的加载等待时间, 但可以防止在Play模式下对共享的SkeletonData进行的修改, 其影响被(意外地)带入到后续游玩中.

  • Auto-Import Settings

    • Default Mix. 设置新导入的SkeletonDataAssets的 Default Mix Duration.
    • Default SkeletonData Scale. 设置新导入的SkeletonDataAssets的默认 Scale 值.
    • Default Shader. 设置新导入的skeleton atlas textures创建materials时使用的默认着色器.
    • Apply Atlas Texture Settings. 对下方指定的texture导入器应用参考的 Atlas Texture Settings .
    • Atlas Texture Settings. 在新导入的atlas textures 和 materials上应用选定的texture导入设置. 当从Spine导出atlas textures并启用 Premultiply alpha (默认为此)时, 你可以将其保持为 PMATexturePreset. 如果你已经禁用了 Premultiply alpha, 请将其设置为 StraightAlphaTexturePreset. 你也可以新建自己的 TextureImporter Preset 资产并分配于此处.
    • Additive Material. 设置槽位blend模式为 Additive 的Material模板. 具体参见 SkeletonData Blend 模式 Materials.
    • Multiply Material. 设置槽位blend模式为 Multiply 的Material模板. 具体参见 SkeletonData Blend 模式 Materials.
    • Screen Material. 设置槽位blend模式为 Screen 的Material模板. 具体参见 SkeletonData Blend 模式 Materials.
  • Warnings

    • Atlas Extension Warning. 每当发现一个 .atlas 文件时, 均在日志中给出警告和建议.
    • Texture Settings Warning. 每当检测到texture导入设置可能导致不佳效果(例如白边伪影)时, 均在日志中给出警告和建议.
    • Component & Material Warning. 当组件和Material设置不兼容时, 输出警告和建议.
    • SkeletonDataAsset no file Error. 当从 SkeletonDataAsset 查找 SkeletonData 时, 若没有正确赋值json或二进制文件对象则输出一个错误.
  • Editor Instantiation

  • Mecanim Bake Settings

    • Include Folder Name in Event. 当启用该项时, Mecanim事件将调用名为 "FolderNameEventName" 的方法; 当禁用时则调用 "EventName" 的方法.
  • Handles and Gizmos

    • Editor Bone Scale. 设置场景视图中显示的骨骼和gizmo元素的尺寸.
  • Prefabs

    • Fix Prefab Overr. MeshFilter. 该项管理了同名skeleton组件检查器里 Advanced 面板参数的全局设置. 当组件参数被设置为 Use Global Settings 时将会使用这里的参数设置.
    • Optimize Preview Meshes. 启用后将在预构建步骤中移除Spine prefab预览网格以减少构建的空间占用. 这将延长构建时间, 因为项目中的所有prefabs都将被处理.
  • Timeline Extension - 该选项将影响 Timeline UPM插件包

    • Default Mix Duration. 设置新创建的 Spine Animation State Clips的默认 Default Mix Duration 参数.
    • Use Blend Duration. 设置新创建的 Spine Animation State Clips的默认 Use Blend Duration 参数.

更新Spine资产

在开发过程中,可能经常需要更新你的Spine skeleton数据和texture atlas文件. 直接覆盖这些文件可以完成更新 (.json, .skel.bytes, .atlas.txt, .png). 从Spine编辑器中重新导出资产,并将再次导出的文件复制到你的项目的 Assets 文件夹的子文件夹中, 覆盖现有文件即可.

Unity将检测这些文件的变化,并自动重新导入修改后的资产.重新导入后,所有以前导入的Spine资产的引用都是完整并使用最新数据的.

注意: Unity有时不能识别文件的变化.在这种情况下,在Unity的项目面板中找到你想重新导入的包含Spine资产的文件夹,右击该文件夹,然后从上下文菜单中选择 Reimport .

Skeleton Data资产

Skeleton data 资产 (名字后缀为 _SkeletonData) 储了skeleton的层次结构、槽位、绘制顺序、动画和其他构成skeleton的数据信息. 由spine-unity运行时提供的其他组件引用并共享skeleton data 资产, 以实现skeleton的动画及显示.

要检查或修改一个skeleton data资产, 在Unity的项目面板中选择它. 检查器面板将显示该skeleton数据的所有属性以供查看和修改.

Skeleton Data

SkeletonData设置块包含了skeleton的通用导入设置.

  • Scale. 设置你自定义的导入缩放值,它会影响所有引用该数据资产的skeleton实例.改变该值将立即影响该skeleton的所有实例.

注意: 当你想精确匹配 - 如32px的图像到1个游戏单位时 (在Spine中没有缩放附件图像), 可以把这个 Scale 参数设置为 1/px_per_unit. 所以对于 32px/unit 的情况, 可以将 Scale 设置为 1/32 = 0.03125.

  • SkeletonData Modifiers. 在完成从 .json.skel.bytes 文件的加载后,为skeleton data资产进行额外处理提供了一种方法. 请参考下文的 SkeletonData 修改器资产 部分, 以了解更多信息.

  • Blend Modes - Upgrade. 将过时的 BlendModeMaterialAsset 升级为下方可显示的原生的Blend Mode Materials属性.

  • Blend Mode Materials. 具有特殊blend modes的Skeleton槽位需要额外的materials. 这些materials在导入时会自动设置, 除非使用了旧版Unity和spine-unity的 BlendModeMaterialAsset . 建议通过上面的 Blend Modes - Upgrade 按钮升级该 BlendModeMaterialAsset 这样可以防止在较新的Unity版本中出现问题. 你可以在 Spine Preferences 中设置每个blend modes的material模板.

    • Apply Additive Material. 启用后,也将为 Additive blend模式的槽位生成材质. 当使用 Straight alpha 工作流时, 启用这个参数. 当使用 PMA 工作流时则无需启用, 因为 NormalAdditive 槽位可以使用同样的PMA material绘制.
    • Additive Materials, Multiply Materials, Screen Materials. 这些列表显示每个blend mode当前使用的blend mode materials.

Atlas

skeleton使用Atlas引用来解析导出的图片名称, 该名称是对应用以渲染的图像区域的引用.

在导入时,每个Atlas资产(以 _Atlas 结尾的资产)将会被自动地一一填入Atlas Assets资产数组中.

如果spine-unity未能自动填入所有需要的atlas资产,你可将 Atlas Assets Size 改为所需的atlas资产数量,并手动按Element0 - ElementN将所需的atlas资产填入.

Mix设置

Skeleton data资产中可以设置 动画 mix times.

Default Mix Duration 设置动画的默认mix时长, 单位为秒.

可以通过点击资产的 Custom Mix Durations 部分的 Add Custom Mix 按钮来定义某两个动画的mix, 并设置特定的mix时长,这将会覆盖上方的默认mix时长值.

使用skeleton data资产的组件(如skeleton动画组件)在播放动画时会使用这些mix时间设置.

预览页面

Skeleton Data资产的预览页面中可以检查资产中包含的所有骨骼(bone)、槽位、动画、皮肤和事件.

你可以通过每个动画左边的播放按钮来回放动画,并在 Slots 部分的 Show Attachments 来查看对应槽位的实时更新.所有时间轴事件以紫色标记显示.在播放时将鼠标悬停在标记上,就能显示事件名称.

点击 Create Animation Reference Assets 按钮即可为skeleton的所有动画生成引用资产. 一个 AnimationReferenceAsset 就像一个引用了 Spine.Animation 的Unity资产, 它也可以在检查器中赋值给组件属性.

Skeleton Mecanim

如果你想使用Unity的Mecanim动画系统不是Spine的默认动画系统, 你可以通过 Generate Mecanim Controller 按钮生成并赋值一个Mecanim控制器.

Skeleton Baking

注意:烘焙是一个专用工具, 在spine-unity中 不推荐通过这种方式 来使用Skeletons! 它不适用于 SkeletonMecanim, SkeletonAnimationSkeletonGraphic (UI) 组件! 它将把一个Skeleton烘焙成一个不太灵活的, 包含带有 MeshRenderers 的, Transforms 层次结构固定的Prefab, 兼容animation clips. Spine的大量功能在Unity的动画系统中无法识别无法转换, 导致这些功能在烘焙后就被丢弃.

点击 SkeletonDataAsset 检查器右上方的齿轮图标选择 Skeleton Baking 就能打开 Skeleton Baking Window.

SkeletonBaker.cs中可以找到功能的支持性详细列表.

注意: 烘焙没有使用最近新增的Unity 2D动画系统,而是采用了基于 MeshRenderer 的3D动画系统.

Texture Atlas 资产

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

你可以通过双击 Materials 数组中的material资产来查看texture atlas页的material.

注意: 你可以修改texture atlas资产所引用的material和texture. 在修改texture的时候, 要确保UV texture坐标是有效的. SkeletonRenderer 组件使用普通的 MeshRenderer 渲染, 所以会像分配给cube或quad那样正常应用material和texture. 所以你大可按需在Texture Atlas资产的material处替换或预处理texture.

Set Mipmap Bias to -0.5 按钮是为高级用户准备的, 它可以修正对atlas texture启用 Generate Mip Maps 时的模糊问题.

按下 Apply Regions as Texture Sprite Slices 按钮会为atlas中每个图像元素生成sprites. 生成的sprites引用了texture atlas图像( png 文件)的区域, 且可以作为任何Unity sprite资产使用.

SkeletonData 修改器资产

在从 .json.skel.bytes 文件加载skeleton后, SkeletonData 修改器可以为skeleton data资产进行额外处理.

SkeletonDataAsset的检查器提供了一个 Skeleton Data Modifiers 列表, 可以在此添加修改器资产.

内置的SkeletonData修改器 - BlendModeMaterialsAsset (已废弃)

注意: spine-unity现在为每个SkeletonDataAsset 提供了 原生的槽位blend模式, 包括: AdditiveMultiplyScreen, 在新导入skeleton资产时将自动设置. BlendModeMaterialsAsset 现已废弃并由原生属性 SkeletonDataAsset 所替代. SkeletonData 资产 检查器提供了全新的 Blend Modes - Upgrade 按钮, 用以将过时的 BlendModeMaterialsAsset 升级为原生的Blend模式属性. 该升级操作将在Unity 2020.1及后续版本的导入和重导入资产中自动进行, 以防止这些Unity版本中出现 BlendModeMaterialAsset 错误. spine-unity 4.1及后续版本亦将自动执行这一升级, 且升级无视Unity版本.

BlendModeMaterialsAsset is a SkeletonData modifier asset class included in spine-unity. It holds references to materials that can be used to render attachments within slots that have the Additive, Multiply and Screen blend modes assigned to them in the Spine editor.

The Material references stored in BlendModeMaterials assets are used as templates to generate new Materials that use the appropriate texture needed by the loaded attachments.

The spine-unity runtime comes packaged with a ready-to-use BlendModeMaterialsAsset named Default BlendModeMaterials. Using this included asset allows the attachments in slots with special blend modes to use the included default Multiply and Screen shaders: Spine/Blend Modes/Skeleton PMA Multiply and Spine/Blend Modes/Skeleton PMA Screen.

If you need to use different Materials or shaders or Materials with different settings, you can create new BlendModeMaterialsAssets using Create -> Spine -> SkeletonData Modifiers -> Blend Mode Materials. Then assign your Material templates to the created asset.

Writing a custom SkeletonDataModifierAsset class

You can write your own custom SkeletonDataModifierAsset class to add additional processing to skeleton data assets after loading from a .json or .skel.bytes file. SkeletonDataModifierAsset is an abstract ScriptableObject class where you can derive your own classes from.

  1. Create a new class derived from SkeletonDataModifierAsset and implement the void Apply (SkeletonData skeletonData) method. Add the CreateAssetMenu class attribute to list an entry for your class in the Asset -> Create menu.
C#
[[CreateAssetMenu(menuName = "TopMenu/Submenu/SubSubmenu", order = 200)]
public class BlendModeMaterialsAsset : SkeletonDataModifierAsset {

public override void Apply (SkeletonData skeletonData) {
...
}
}
  1. Create an instance of your new class by selecting your desired folder in the Project panel and selecting your newly created Asset -> Create menu entry. Assign the created asset at an element of the Skeleton Data Modifiers list of your SkeletonData asset.

Apply(skeletonData) will be called after loading data from the .json or .skel.bytes file has been completed.

主要组件

spine-unity运行时提供了一组显示、动画、跟踪和修改从Spine导出的skeletons的组件. 这些组件将引用按上文所述导入的skeleton data和texture atlas资产.

将Skeleton添加到场景中

在Unity项目中快速添加Spine的skeleton, 需要:

  1. 按前文所述来导入skeleton data和texture atlas.
  2. _SkeletonData 资产拖入Scene视图或Hierarchy面板然后选择 SkeletonAnimation. 这将实例化一个带有所需Spine组件的新GameObject.

注意: 步骤2的另一个实现方法是从头开始创建这个GameObject:

  1. GameObject -> Create Empty 菜单创建一个新的空GameObject.
  2. 在检查器中选择GameObject然后点击 Add Component, 然后选择 SkeletonAnimation. 这会自动为GameObject添加 MeshRendererMeshFilter 组件.
  3. SkeletonAnimation 组件中, 把所需的 _SkeletonData 资产赋值给 Skeleton Data Asset 属性.

注意: 为防在场景视图中只能看见无附件图的skeleton骨骼(bones), 你可能需要在皮肤的 Initial Skin 属性中切换为 default 以外的皮肤.

你现在可以使用组件的C# API来控制skeleton动画, 对动画触发的事件做出反应等等. 更多细节请参考下文的组件文档.

SkeletonAnimation的一种替代 - SkeletonGraphic(UI)和SkeletonMecanim

将skeleton实例化为 SkeletonAnimation 是在Unity中使用Spine skeleton的推荐方法, 因为它的功能在三种实现方法中最为完善.

三种实例化skeleton的方法为:

  1. SkeletonAnimation - 使用Spine定制的动画和事件系统, 提供最高的可定制性. 渲染使用的是 MeshRenderer, 可以像Unity sprite一样与 SpriteMask 等遮罩进行交互. 在Unity中推荐以这种方式使用Spine skeleton.
  2. SkeletonGraphic (UI) - 与Unity Canvas一起作为UI元素使用. 像内置的Unity UI元素一样渲染并与UI遮罩(如 RectMask2D )交互. 动画和事件行为与 SkeletonAnimation相同.
  3. SkeletonMecanim - 使用Unity的Mecanim动画和事件系统来启动、mix和过渡动画. 与SkeletonAnimation相比,提供的动画mix和过渡选项较少.当使用 SkeletonMecanim 时, 无法保证动画过渡看起来和Spine Editor中的预览一致.

高级 - 运行时实例化

注意: 推荐通过正常的工作流将skeleton添加到场景中, 并将其存储于prefabs内, 而非从实例池中取出池化对象来复用. 这样会使得修改和调整更加容易.

虽然不是推荐的工作流, 但spine-unity API支持运行时从SkeletonDataAsset中实例化SkeletonAnimationSkeletonGraphic GameObjects, 甚至直接从这三种导出资产来实例化. 只有当你无法依赖常规Unity导入流程来自动创建 "SkeletonDataAsset "和 "SpineAtlasAsset "资产时, 才建议直接通过导出资产来实例化.

C#
// instantiating a SkeletonAnimation GameObject from a SkeletonDataAsset
SkeletonAnimation instance = SkeletonAnimation.NewSkeletonAnimationGameObject(skeletonDataAsset);

// instantiating a SkeletonGraphic GameObject from a SkeletonDataAsset
SkeletonGraphic instance
= SkeletonGraphic.NewSkeletonGraphicGameObject(skeletonDataAsset, transform, skeletonGraphicMaterial);
C#
// instantiation from exported assets without prior import
// 1. Create the AtlasAsset (needs atlas text asset and textures, and materials/shader);
// 2. Create SkeletonDataAsset (needs json or binary asset file, and an AtlasAsset)
SpineAtlasAsset runtimeAtlasAsset
= SpineAtlasAsset.CreateRuntimeInstance(atlasTxt, textures, materialPropertySource, true);
SkeletonDataAsset runtimeSkeletonDataAsset
= SkeletonDataAsset.CreateRuntimeInstance(skeletonJson, runtimeAtlasAsset, true);
// 3. Create SkeletonAnimation (needs a valid SkeletonDataAsset)
SkeletonAnimation instance = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSkeletonDataAsset);

你可以查看示例场景Spine Examples/Other Examples/Instantiate from Script以及示例脚本SpawnFromSkeletonDataExample.csRuntimeLoadFromExportsExample.csSpawnSkeletonGraphicExample.cs以获得更多信息.

SkeletonAnimation组件

SkeletonAnimation 组件是三种在Unity中使用Spine skeleton的方式之一. 它们是: SkeletonAnimation, SkeletonMecanimSkeletonGraphic (UI).

SkeletonAnimation 组件是spine-unity运行时的核心. 通过它你可以将一个Spine skeleton添加到一个GameObject上,播放动画、对动画事件作出反应等等.

设置Skeleton Data

SkeletonAnimation 组件需要一个对skeleton data资产的引用,它可以从中获得skeleton的骨骼层次、槽位等信息.

如果您通过拖放将一个skeleton添加到场景中, skeleton data资产会被自动赋值.

如果你有一个已经设置好的GameObject, 而突然想把skeleto改为不同的资产, 你可以通过检查器属性手动执行更改.

要设置或改变skeleton data, 需要:

  1. 选中 SkeletonAnimation 的GameObject
  2. 在检查器中的 SkeletonData Asset 属性处赋值一个_SkeletonData 资产.

设置初始皮肤和动画

SkeletonAnimation 检查器公开暴露了以下参数:

  1. Initial Skin. 该皮肤将在动画开始时显示.注意:如果你看到的skeleton没有附加任何图像,可以切换到 default 以外的皮肤来显示皮肤.
  2. Animation Name. 该动画将在开始时播放.
  3. Loop. 定义初始动画是循环播放还是只播放一次.
  4. Time Scale. 设置时间比例来减慢或加快动画的播放速度.
  5. Unscaled Time. 当置为true时, 更新将根据Time.unscaledDeltaTime时间而非Time.deltaTime进行. 这对于不适用慢动作效果的动画UI元素来说非常有用.

启用 Root Motion

SkeletonAnimationSkeletonGraphic (UI) 组件的root motion是通过一个单独的 SkeletonRootMotion 组件实现的. The SkeletonAnimation 检查器中的 Root Motion Add Component 按钮可以快速添加合适的组件到skeleton GameObject上.

设置高级参数

展开 SkeletonAnimation 检查器的 Advanced 部分就能显示高级配置参数.

SkeletonAnimation 公开暴露了以下高级参数:

  • Initial Flip X, Initial Flip Y. 这两个选项在动画开始时水平或垂直地翻转skeleton. 他们会把翻转后的skeleton的 ScaleXScaleY 置为 -1 .
  • Animation Update. 设置是以普通的Update(默认) 还是基于物理的 FixedUpdate 模式来更新动画, 也可以通过用户调用来手动更新动画. 当使用SkeletonRootMotion组件并分配了 RigidbodyRigidbody2D 时, 推荐将更新模式设置为In FixedUpdate, 其他情况则建议设置为 In Update.
  • Update When Invisible. 当MeshRenderer不可见时的更新模式.当网格再度可见时,更新模式会自动重置为 UpdateMode.FullUpdate..
  • Use single submesh. 该选项选项用于简化子网格的生成, 它假设skeleton只使用了一个Material且只需一个子网格. 这将禁用多种materials、渲染分离(render separation)和自定义槽位materials.
  • Fix Draw Order. 仅适用于使用3个以上子网格的情况(顺序交错的多个 materials,例如 "A B A").如果为 "true",MaterialPropertyBlocks会被赋值到每个material中,以防止LWRP等渲染器对子网格进行主动(aggressive) batching, 从而导致绘制顺序错误(例如,"A1 B A2 "变为"A1 A2 B").当绘制正确时,可以禁用该参数来避免额外的性能开销.
  • Immutable triangles. 启用该参数可以优化附件图片可见性固定不变时skeletons的渲染. 若启用则三角形将不会被更新. 如果skeleton的附件未使用swapping或hiding, 或者未使用绘制顺序键(keys), 则应启用此选项来进行优化. 否则设置为false可能会导致渲染时出现错误.
  • Clear State on Disable. 当该组件或其GameObject被禁用时,清除渲染和skeleton的状态.这可以避免它们在再次启用时之前的状态被保留.当pooling skeleton时,将其设置为"true"可能会有所裨益.
  • Fix Prefab Override MeshFilter. 该选项能修复了Prefab总是被标记为已更改的问题(将MeshFilter 的隐藏标志置为DontSaveInEditor), 但会导致其他组件丢失对 MeshFilter 的引用. 当设置为 Use Global Settings, 运行时将使用 Spine Preferences 中保存的设置.
  • Separator Slot Names. 设置渲染分离时的槽位名称. SkeletonRenderSeparator 等组件将使用该选项,以便在不同的GameObjects上用两个独立的渲染器来渲染skeleton.
  • Z-Spacing. skeleton渲染器组件在X/Y平面上对附件图片自后向前渲染. 每个附件都可以在Z轴上自定义z-spacing值, 以避免 z-fighting 现象.

  • PMA Vertex Colors. 将顶点色彩RGB值与顶点色彩alpha值相乘. 如果用于渲染的着色器是Spine着色器(即使使用了 Straight Alpha Texture )或者使用了PMA additive blend模式 Blend One OneMinusSrcAlpha 的第三方着色器, 则应启用该参数. 对于使用常规blend模式 Blend SrcAlpha OneMinusSrcAlpha 的普通着色器, 则禁用此参数. 当禁用时, 它可以防止additive槽位的single-batch渲染, 且可能增多绘制调用.
  • Tint Black (!). 为网格添加黑色tint顶点数据. 黑色tinting要求着色器将UV2和UV3解释为黑色tint.你可以使用附带的 Spine/Skeleton Tint Black 着色器. 如果你只需要tint整个skeleton而非个别部分, 建议使用 Spine/Skeleton Tint 着色器, 这样效率更高, 并且可以通过MaterialPropertyBlock改变或在动画中使用 _Black material属性. 更多信息请参见着色器部分. 为了保证对多个tint skeletons的不同batching能力, 建议 使用Skeleton.R .G .B .A进行tint.
  • Add Normals. 启用后网格生成器会在输出网格中添加法线. 如果你的着色器需要顶点法线请启用之. 为了更佳性能和更低的内存占用, 推荐使用可以接收(assume)所需法线的着色器, 比如 Spine/Skeleton Lit 着色器. 注意, Spine/Sprite 着色器也可以被配置为接收 Fixed Normal.
  • Solve Tangents. 一些通常用于应用法线贴图的lit着色器需要顶点切线(tangents). 启用后, 每一帧都会计算切线并添加进输出网格.
  • Physics Inheritance. 项控制Transform移动如何应用于skeleton的PhysicsConstraints.
    • Position. 当该值置为非零值时, 将把计入了X和Y缩放因子的变换(Transform)的位移量应用于skeleton的物理约束. 几个 (X,Y) 的典型值是:
      (1,1) 表示正常应用 XY 移动,
      (2,2) 说明应用了双倍移动量,
      (1,0) 表示仅应用水平移动, 而
      (0,0) 表示完全无任何位移.
    • Rotation. 当该值置为非零值时, 将把计入了X和Y缩放因子的变换(Transform)的位移量应用于skeleton的物理约束. 几个典型值为:
      1 表示正常旋转,
      2 表示二倍速旋转, 而
      0 代表完全无任何旋转.
    • Movement relative to. 欲应用Transform移动(诸如父变换等)的基准点, 该项值为计算移动的参考变换. 将此项置为None即使用绝对世界位置(默认值).
  • Add Skeleton Utility. 该按钮可以用来添加一个用于跟踪或覆盖骨骼(bone)位置的 SkeletonUtility 组件到GameObject中. 更多信息请参见 SkeletonUtility .
  • Debug. 有时你可能想在游戏运行时了解槽位当前的颜色或骨骼比例.按下 Debug 按钮可以打开为此而建的 Skeleton Debug 窗口. 你可以在这里检查skeletion当前的骨骼状态、槽位、约束、绘制顺序、事件和统计信息.

生命周期


SkeletonAnimation 组件中, AnimationState保存了所有当前播放的和队列中的动画状态.每一次 Update, AnimationState都会被更新,从而使动画在时间上向前推进. 然后, 新一帧作为一个新的pose被应用于Skeleton上.

你的脚本可以在SkeletonAnimation的 Update 之前或之后运行. 如果你的代码在SkeletonAnimation的更新之前获取Skeleton或骨骼的值, 你的代码会读取上一帧而不是当前帧的值.

该组件将事件回调委托暴露为属性, 这样就能在计算所有骨骼的世界transforms之前和之后介入该生命周期. 你可以绑定这些委托来修改骨骼的位置和其他方面, 而无需关注角色和组件的更新顺序.

SkeletonAnimation Update 回调:

  • SkeletonAnimation.BeforeApply 事件在应用该帧动画之前被引发. 当你想在动画应用到skeleton上之前改变skeleton的状态时, 请使用这个回调.
  • SkeletonAnimation.UpdateLocal 事件在该帧动画更新完成并应用于skeleton的局部值之后被引发. 如果你需要读取或修改骨骼的局部值, 请使用它.
  • SkeletonAnimation.UpdateComplete 在计算完Skeleton中所有骨骼的世界值后被引发. SkeletonAnimation在这之后不会在Update中做进一步的操作. 如果你只需要读取骨骼的世界值, 就使用它. 如果有代码在SkeletonAnimation的Update之后修改了这些值, 这些值可能仍会发生变化.
  • SkeletonAnimation.UpdateWorld 是在计算了Skeleton中所有骨骼的世界值后引发的. 若订阅了该事件则将二次调用 skeleton.UpdateWorldTransform. 如果你的skeleton复杂或正在执行其他计算, 那这将是多余甚至是浪费的行为. 如果需要根据骨骼的世界值来修改骨骼的局部值, 请使用该事件. 它在Unity代码中实现自定义约束很有用.

C#
// your delegate method
void AfterUpdateComplete (ISkeletonAnimation anim) {
// this is called after animation updates have been completed.
}

// register your delegate method
void Start() {
skeletonAnimation.UpdateComplete -= AfterUpdateComplete;
skeletonAnimation.UpdateComplete += AfterUpdateComplete;
}

SkeletonRenderer Update 回调

  • OnRebuild 在skeleton成功初始化后触发.
  • OnMeshAndMaterialsUpdated 会在更新完网格和所有material后, 于LateUpdate()结束时触发.

C#
// your delegate method for OnMeshAndMaterialsUpdated
void AfterMeshAndMaterialsUpdated (SkeletonRenderer renderer) {
   // this is called after mesh and materials have been updated.
}

// your delegate method for OnRebuild
void AfterRebuild (SkeletonRenderer renderer) {
   // this is called after the Skeleton is successfully initialized.
}

// register your delegate method
void Start() {
   skeletonAnimation.OnMeshAndMaterialsUpdated -= AfterMeshAndMaterialsUpdated;
   skeletonAnimation.OnMeshAndMaterialsUpdated += AfterMeshAndMaterialsUpdated;

   skeletonAnimation.OnRebuild -= AfterRebuild;
   skeletonAnimation.OnRebuild += AfterRebuild;
}

另一种做法是改变脚本的执行顺序, 在SkeletonAnimation的Update方法之后运行.

关于Unity中MonoBehaviour的生命周期的更多信息请见: docs.unity3d.com/Manual/ExecutionOrder

C#

通过代码与skeleton互动需要访问SkeletonAnimation组件. 如同其他Unity组件一样, 建议对引用查询一次后将其存储起来以便复用.

C#
...
using Spine.Unity;

public class YourComponent : MonoBehaviour {

SkeletonAnimation skeletonAnimation;
Spine.AnimationState animationState;
Spine.Skeleton skeleton;

void Awake () {
skeletonAnimation = GetComponent<SkeletonAnimation>();
skeleton = skeletonAnimation.Skeleton;
//skeletonAnimation.Initialize(false); // when not accessing skeletonAnimation.Skeleton,
       // use Initialize(false) to ensure everything is loaded.
animationState = skeletonAnimation.AnimationState;
}

Skeleton

SkeletonAnimation 组件通过 SkeletonAnimation.Skeleton 属性提供对底层skeleton的访问. 一个Skeleton存储了对一个skeleton data资产的引用,而skeleton data又引用了一个或多个atlas资产.

通过Skeleton可以设置皮肤、附件、重置骨骼为setup pose、比例以及翻转整个skeleton.

设置附件

设置一个附件需要提供槽位和附件名称.

C#
bool success = skeletonAnimation.Skeleton.SetAttachment("slotName", "attachmentName");
C#
// using properties
[SpineSlot] public string slotProperty = "slotName";
[SpineAttachment] public string attachmentProperty = "attachmentName";
...
bool success = skeletonAnimation.Skeleton.SetAttachment(slotProperty, attachmentProperty);

请注意,上述代码中的 [SpineSlot][SpineAttachment]该节中描述的 String Property Attributes.

重置为Setup Pose

程序性动画有时需要将骨骼和/或槽位重置为其setup pose. 在设置完皮肤后, 你可如设置皮肤一节中的代码所示来调用Skeleton.SetSlotsToSetupPose.

C#
skeleton.SetToSetupPose();
skeleton.SetBonesToSetupPose();
skeleton.SetSlotsToSetupPose();

设置皮肤

一个Spine skeleton可以有多个定义了哪张附件用于哪个槽位的皮肤. skeleton组件提供了一个简单的方法来切换皮肤.

C#
bool success = skeletonAnimation.Skeleton.SetSkin("skinName");
skeletonAnimation.Skeleton.SetSlotsToSetupPose(); // see note below
C#
// using properties
[SpineSkin] public string skinProperty = "skinName";
...
bool success = skeletonAnimation.Skeleton.SetSkin(skinProperty);
skeletonAnimation.Skeleton.SetSlotsToSetupPose(); // see note below

若不想让之前设置的附件影响当前附件的可见性, 则应在更改皮肤后调用Skeleton.SetSlotsToSetupPose. 详情请参见此处的文档.

组合皮肤

可以组合Spine 皮肤,比如从一个个衣物皮肤组成一个完整的角色皮肤.更多细节请参见新的皮肤API文档.

C#
var skeleton = skeletonAnimation.Skeleton;
var skeletonData = skeleton.Data;
var mixAndMatchSkin = new Skin("custom-girl");
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("skin-base"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("nose/short"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("eyelids/girly"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("eyes/violet"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("hair/brown"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("clothes/hoodie-orange"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("legs/pants-jeans"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("accessories/bag"));
mixAndMatchSkin.AddSkin(skeletonData.FindSkin("accessories/hat-red-yellow"));
skeleton.SetSkin(mixAndMatchSkin);
skeleton.SetSlotsToSetupPose();
skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton); // skeletonMecanim.Update() for SkeletonMecanim
运行时重打包

在组合皮肤时会有多个material累积, 这将引发额外的绘制调用. 使用Skin.GetRepackedSkin() 方法可以在运行时将收集到的皮肤中使用过的texture区域合并为一个texture.

C#
using Spine.Unity.AttachmentTools;

// Create a repacked skin.
Skin repackedSkin = collectedSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, out runtimeMaterial, out runtimeAtlas);
collectedSkin.Clear();

// Use the repacked skin.
skeletonAnimation.Skeleton.Skin = repackedSkin;
skeletonAnimation.Skeleton.SetSlotsToSetupPose();
skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton); // skeletonMecanim.Update() for SkeletonMecanim

// You can optionally clear the cache after multiple repack operations.
AtlasUtilities.ClearCache();

重要提示: 如果重打包失败或产生意外问题, 可能源于以下原因:

  1. 禁用了读/写. 你可能需要在并入重打包texture的源textures上设置 Read/Write Enabled 参数.
  2. 启用了压缩: 需要确保源texture的Texture导入设置中 CompressionNone , 而不是 Normal Quality.
  3. 质量层使用了half/quarter分辨率的textures: 这个是Unity的bug, 当使用half/quarter分辨率的texture时会复制错误区域. 确保项目设置中的所有质量层都使用全分辨率texture.
  4. 源texture不是2次texture但Unity将其放大为了最接近次数:a)从Spine导出时启用 Pack Settings 中的 Power of two, 或者b)确保Unity的Atlas Texture导入设置中将 Non-Power of Two 置为 None.

可以查看示例场景Spine Examples/Other Examples/Mix and MatchSpine Examples/Other Examples/Mix and Match Equip 以及 MixAndMatch.cs 示例脚本来加深理解.

高级 – 使用法线贴图的运行时重打包

你也可以同时重打包主texture的法线贴图和其他附加texture层. 只需将 int[] additionalTexturePropertyIDsToCopy = new int[] { Shader.PropertyToID("_BumpMap") }; 传入为 GetRepackedSkin() 的参数, 就能重打包主texture和法线贴图层.

C#
Material runtimeMaterial;
Texture2D runtimeAtlas;
Texture2D[] additionalOutputTextures = null;
int[] additionalTexturePropertyIDsToCopy = new int[] { Shader.PropertyToID("_BumpMap") };
Skin repackedSkin = prevSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, out runtimeMaterial, out runtimeAtlas,
additionalTexturePropertyIDsToCopy : additionalTexturePropertyIDsToCopy, additionalOutputTextures : additionalOutputTextures);

// Use the repacked skin.
skeletonAnimation.Skeleton.Skin = repackedSkin;
skeletonAnimation.Skeleton.SetSlotsToSetupPose();
skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton); // skeletonMecanim.Update() for SkeletonMecanim

注意: 通常情况下, 法线贴图属性名后缀为 "_BumpMap". 当使用自定义着色器时, 一定要使用相应的属性名. 这个名字是着色器中的属性名, 而非检查器中 "Normal Map" 标签里显示的字符串.

改变Skeleton比例和翻转Skeleton

垂直或水平翻转一个skeleton可以复用动画,例如一个面向左边的行走动画可以回放为右朝向.

C#
bool isFlippedX = skeleton.ScaleX < 0;
skeleton.ScaleX = -1;
bool isFlippedY = skeleton.ScaleY < 0;
skeleton.ScaleY = -1;

skeleton.ScaleX = -skeleton.ScaleX; // toggle flip x state

手动获取和设置骨骼Transforms

注意: 该做法仅适用于极特殊的场合. Spine的 BoneFollowerSkeletonUtilityBone 组件是一种与骨骼交互的更简单可靠方式.

通过Skeleton可以设置和获取骨骼的transform值, 以便实现IK地形跟随或者让其他角色和组件(如粒子系统)跟随skeleton中的骨骼.

注意: 务必要通过订阅 SkeletonAnimation.UpdateWorld 委托在更新世界transform的生命周期中获取和应用新的骨骼位置. 否则你的修改会在读取时可能会晚一帧, 要么会在设置时被动画覆盖.

C#
Bone bone = skeletonAnimation.Skeleton.FindBone("boneName");
Vector3 worldPosition = bone.GetWorldPosition(skeletonAnimation.transform);
// note: when using SkeletonGraphic, all values need to be scaled by the parent Canvas.referencePixelsPerUnit.

Vector3 position = ...;
bone.SetPositionSkeletonSpace(position);

Quaternion worldRotationQuaternion = bone.GetQuaternion();

动画 - AnimationState

生命周期

SkeletonAnimation 组件实现了 Update 方法,它根据delta时间更新底层 AnimationState, 将 AnimationState 应用于skeleton上, 并更新skeleton上所有骨骼的世界transforms.

skeleton动画组件通过 SkeletonAnimation.AnimationState 属性暴露了AnimationState API. 本节假定你熟悉轨道、轨道项、mix时间或动画队列等概念, 具体可见通用Spine运行时指南中Applying Animations一节所述.

时间比例(Scale)

你可以设置skeleton动画组件的时间比例, 以减慢或加快动画的播放速度. 用于推进动画的delta时间会乘上时间比例来决定动画播放速度, 例如时间比例为0.5时, 动画会减慢到正常速度的一半, 时间比例为2时, 动画会加速为正常速度的两倍.

C#
float timeScale = skeletonAnimation.timeScale;
skeletonAnimation.timeScale = 0.5f;

设置动画

要设置一个动画,需要确定轨道索引(index)、动画名称和是否循环动画.

C#
TrackEntry entry = skeletonAnimation.AnimationState.SetAnimation(trackIndex, "walk", true);
C#
// using properties
[SpineAnimation] public string animationProperty = "walk";
...
TrackEntry entry = skeletonAnimation.AnimationState.SetAnimation(trackIndex, animationProperty, true);

你也可以传入 AnimationReferenceAsset 作为参数而非动画名字.

C#
// using AnimationReferenceAsset
public AnimationReferenceAsset animationReferenceAsset; // assign a generated AnimationReferenceAsset to this property
...
TrackEntry entry = skeletonAnimation.AnimationState.SetAnimation(trackIndex, animationReferenceAsset, true);
动画队列

要排队动画, 需要提供轨道索引、动画名称、是否循环播放该动画, 以及该动画在轨道上开始播放的延迟时间(以秒计).

C#
TrackEntry entry = skeletonAnimation.AnimationState.AddAnimation(trackIndex, "run", true, 2);
C#
// using properties
[SpineAnimation] public string animationProperty = "run";
...
TrackEntry entry = skeletonAnimation.AnimationState.AddAnimation(trackIndex, animationProperty, true, 2);

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

skeleton动画组件还提供了设置空动画、队列空动画、清除一个或全部轨道的方法. 这些功能需求都与上文的队列动画的方法和参数类似.

C#
TrackEntry entry = skeletonAnimation.AnimationState.SetEmptyAnimation(trackIndex, mixDuration);
entry = skeletonAnimation.AnimationState.AddEmptyAnimation(trackIndex, mixDuration, delay);
skeletonAnimation.AnimationState.ClearTrack(trackIndex);
skeletonAnimation.AnimationState.ClearTracks();

轨道条目(Entry)

所有的方法都会返回一个 TrackEntry 对象,可以通过它来进一步定制具体动画的回放,以及定制绑定到轨道条目的具体事件的委托.详见下文的 处理AnimationState事件 一节.

注意: 返回的轨道条目只在底层动画状态中删除了对应动画之前有效. Unity的垃圾收集器会自动释放它们. 在接收到轨道条目的销毁事件(dispose event)后, 它就无法再被存储或访问.

C#
TrackEntry entry = ...
entry.EventThreshold = 2;
float trackEnd = entry.TrackEnd;

处理AnimationStates事件

底层的 AnimationState 回放动画时,将引发下列事件来通知监听器(listener):

  1. 动画已开始(started) .
  2. 动画被中断(interrupted), 例如清空了一条轨道或设置了一个新的动画.
  3. 动画无中断地播放已完成(completed) , 如果是循环动画, 该事件可能会多次出现.
  4. 动画已停止(ended) .
  5. 动画和其所处的 TrackEntry 已被销毁(disposed) .
  6. 触发了用户定义的事件(event).

注意: 当设置了一个新动画时, 如果中断了之前的动画, 将不会引发完成(complete)事件,而会引发中断(interrupt)和停止(end)事件.

Skeleton动画组件提供了C#代码可以绑定的委托, 以便对全部轨道上全部队列中的全部动画的事件做出反应. 监听器也可以只与特定 TrackEntry 的委托绑定.所以你可以注册到如 AnimationState.Complete 事件来处理任何后续的动画 Complete 事件的事件回调(event callbacks),或者注册到 TrackEntry.Complete 事件来处理由某个入队动画引发的 Complete 事件.

C#

在处理 AnimationState 事件的类中添加欲监听事件的委托:

C#
SkeletonAnimation skeletonAnimation;
Spine.AnimationState animationState;

void Awake () {
skeletonAnimation = GetComponent<SkeletonAnimation>();
animationState = skeletonAnimation.AnimationState;

// registering for events raised by any animation
animationState.Start += OnSpineAnimationStart;
animationState.Interrupt += OnSpineAnimationInterrupt;
animationState.End += OnSpineAnimationEnd;
animationState.Dispose += OnSpineAnimationDispose;
animationState.Complete += OnSpineAnimationComplete;

animationState.Event += OnUserDefinedEvent;

// registering for events raised by a single animation track entry
Spine.TrackEntry trackEntry = animationState.SetAnimation(trackIndex, "walk", true);
trackEntry.Start += OnSpineAnimationStart;
trackEntry.Interrupt += OnSpineAnimationInterrupt;
trackEntry.End += OnSpineAnimationEnd;
trackEntry.Dispose += OnSpineAnimationDispose;
trackEntry.Complete += OnSpineAnimationComplete;
trackEntry.Event += OnUserDefinedEvent;
}

public void OnSpineAnimationStart(TrackEntry trackEntry) {
// Add your implementation code here to react to start events
}
public void OnSpineAnimationInterrupt(TrackEntry trackEntry) {
// Add your implementation code here to react to interrupt events
}
public void OnSpineAnimationEnd(TrackEntry trackEntry) {
// Add your implementation code here to react to end events
}
public void OnSpineAnimationDispose(TrackEntry trackEntry) {
// Add your implementation code here to react to dispose events
}
public void OnSpineAnimationComplete(TrackEntry trackEntry) {
// Add your implementation code here to react to complete events
}


string targetEventName = "targetEvent";
string targetEventNameInFolder = "eventFolderName/targetEvent";

public void OnUserDefinedEvent(Spine.TrackEntry trackEntry, Spine.Event e) {

if (e.Data.Name == targetEventName) {
// Add your implementation code here to react to user defined event
}
}

// you can cache event data to save the string comparison
Spine.EventData targetEventData;
void Start () {
targetEventData = skeletonAnimation.Skeleton.Data.FindEvent(targetEventName);
}
public void OnUserDefinedEvent(Spine.TrackEntry trackEntry, Spine.Event e) {

if (e.Data == targetEventData) {
// Add your implementation code here to react to user defined event
}
}

更多详情请参考 Spine API文档 .

在回调中更改AnimationState或游戏状态

虽然你可以通过在AnimationState事件回调中调用AnimationState.SetAnimation()等方式修改AnimationState, 但需要将一些时点问题也纳入考量. 该问题同样影响着通过事件回调更改游戏状态的情况.

  1. 当在 SkeletonAnimation.Update() 中应用动画后, SkeletonAnimation.LateUpdate() 更新网格之前, 会触发 AnimationState 事件回调.
  2. 如果你在 AnimationState.End回调中调用了AnimationState.SetAnimation(), 将会在同一帧中触发 AnimationState.Start事件.
  3. 在从一个动画mix过渡到另一个动画时, 第二个动画的 Start 事件会在第一个动画的 End 事件前触发. 这是一个修改游戏状态时会遇到的常见问题.

如果需要将事件回调中的触发推迟到下一个 Update() 循环, 可以使用 StartCoroutine:

C#
trackEntry.End += e => {
StartCoroutine(NextFrame(() => {
YourCode();
}));
};

...

IEnumerator NextFrame (System.Action call) {
yield return 0;
if (call != null)
call();
}
Coroutine Yield 命令

spine-unity运行时提供了几个用于Unity Coroutines的yield指令. 如果你不熟悉Unity Coroutines,那么Coroutine教程 and Unity Coroutine文档值得你阅读一番的.

以下是可供使用的yield指令:

  1. WaitForSpineAnimation. 等待, 直到 Spine.TrackEntry 引发一个具体事件.
C#
var track = skeletonAnimation.state.SetAnimation(0, "interruptible", false);
var completeOrEnd = WaitForSpineAnimation.AnimationEventTypes.Complete |
                      WaitForSpineAnimation.AnimationEventTypes.End;
yield return new WaitForSpineAnimation(track, completeOrEnd);
  1. WaitForSpineAnimationComplete. 等待, 直到 Spine.TrackEntry 引发一个 Complete 事件.
C#
var track = skeletonAnimation.state.SetAnimation(0, "talk", false);
yield return new WaitForSpineAnimationComplete(track);
  1. WaitForSpineAnimationEnd. 等待, 直到 Spine.TrackEntry 引发一个 End 事件.
C#
var track = skeletonAnimation.state.SetAnimation(0, "talk", false);
yield return new WaitForSpineAnimationEnd(track);
  1. WaitForSpineEvent. 等待, 直到 Spine.AnimationState 引发了一个自定义的 Spine.Event 事件(在Spine编辑器中命名该事件).
C#
yield return new WaitForSpineEvent(skeletonAnimation.state, "spawn bullet");
// You can also pass a Spine.Event's Spine.EventData reference.
Spine.EventData spawnBulletEvent; // cached in e.g. Start()
..
yield return new WaitForSpineEvent(skeletonAnimation.state, spawnBulletEvent);

注意:和Unity内置的yield指令一样, spine-unity的yield的实例也可以重复使用, 以避免额外的内存分配.

教程页面

你可以在 这里 找到关于spine-unity事件的教程.

在编码中使用字符串属性特性(String Property Attributes)

在检查器中手动输入动画名称等字符并不方便. 因此, spine-unity为提供了一种将字符串参数作为下拉(popup)字段的方式. 可以在 string 属性前加上下文列出的属性特性(property attribute), 以将其自动转为一个可下拉选择选择的字段, 比如下拉列出一个skeleton上的所有可用动画. 如果你在Spine组件中看到过某个字段, 那么也可以在你自定义的组件中通过属性特性下拉相同字段. 下表展示了可用的属性特性:

C#
[SpineBone] public string bone;
[SpineSlot] public string slot;
[SpineAttachment] public string attachment;
[SpineSkin] public string skin;
[SpineAnimation] public string animation;
[SpineEvent] public string event;
[SpineIkConstraint] public string ikConstraint;
[SpineTransformConstraint] public string transformConstraint;
[SpinePathConstraint] public string pathConstraint;

更多内容请查看spine-unity包中附带的 场景示例 中的字符串属性特性.

SkeletonMecanim组件

SkeletonMecanim 组件是三种在Unity中使用Spine skeleton的方式之一. 它们是: SkeletonAnimation, SkeletonMecanimSkeletonGraphic (UI).

The SkeletonMecanim 组件是 SkeletonAnimation组件的Mecanim版本, 它使用Unity的Mecanim动画系统进行高级控制而Spine的动画系统负责skeleton的姿态和设置. 该组件使用Mecanim系统来确定应播放哪些Spine动画, 每个动画的轨道时间和透明度(alpha). 然后将对应的Spine动画应用于Spine skeleton.

SkeletonAnimation 组件一样, SkeletonMecanim也可以将Spine skeleton附加到一个GameObject上, 为它动起来, 并处理动画事件等等. 在应用动画之后到绘制skeleton之前的这段时间里, 你可以像用SkeletonAnimation而非SkeletonMecanim一样对skeleton进行修改. 相较于 SkeletonAnimation, 它有一些限制和额外的要求:

注意:SkeletonAnimation组件相比, 如果要从前一动画的时间轴状态平滑地过渡(动画混合), SkeletonMecanim 强制要求后续动画的时间轴首帧为关键帧. 更多信息请参见下文的额外关键帧需求一节.

注意: TrackEntry.AttachmentThreshold 和类似的mix阈值功能在 SkeletonMecanim 上不可用.

SkeletonMecanim 组件提供的参数与SkeletonAnimation组件类似, 请查阅SkeletonAnimation部分以了解更多信息.

额外关键帧需求

为了平滑地将一个时间轴状态(如骨骼旋转)从一个动画mix out到下一个动画, 后一个动画在setup pose时, 需要在首帧添加一个关键帧.否则前一个动画的时间轴状态不会被清除. 相较于 SkeletonAnimation, 这是 SkeletonMecanim 的一个缺点.

小示例: 一个 idle 动画应平滑地跟在前一个 jump 动画后.假设 jump 动画结束时, 骨骼 bone1bone2 的关键帧在非setup-pose位置上, 那么为了正确地mix out到 jump 状态, 必须在 idle 动画的首帧上为 bone1bone2 添加关键帧(设置为setup pose或其他你需要的pose).

Auto Reset 这一参数会重置动画状态, 但会在动画过渡结束时粗糙地进行mix out, 无法实现平滑的过渡效果.

另外, 在将skeleton导出为 .json.skel.bytes 时, 一定要禁用 Animation cleanup , 否则将无法导出setup pose对应的关键帧!

动画的blending控制参数

SkeletonMecanim 暴露了以下参数:

  • Mecanim Translator

    • Auto Reset. 当其置为 true, 在一个动画结束时, skeleton状态会mix out为setup pose. 当一个动画改变了附件的可见性状态时, 这就特别重要了:当mix out时, 附件可见性将变为setup pose状态, 否则当前的附件状态将保持到另一个动画的关键帧再度设置这个附件.

    • Custom MixMode. 当禁用时会根据图层的混合模式使用推荐的 MixMode. 而当启用时, 会在下方显示 MixMode 设置让你单独为每个Mecanim图层指定 MixMode.

    • Mix Modes. 当上文的 Custom MixMode 设置启用时显示. 该设置决定了连续动画和跨图层动画间的mix模式.

      • Mix Next (推荐用于 Base LayerOverride 图层)
        应用于前一条轨道, 然后按照Mecanim过渡权重mix in到下一条轨道.
      • Always Mix (推荐用于 Additive 图层)
        淡出前一条轨道(当Auto Reset启用时可能是淡出到setup pose), 并使用Mecanim过渡权重mix in到下一条轨道. 请注意, 在基础图层(base layer)上使用时可能会造成动画出现非预期的浸润(dipping)效果.
      • Hard (以前叫 Spine Style)
        立即应用于下一条轨道.

Auto Reset 和图层 Mix Mode 参数的结果

当动画有过渡时有四种pose - current state last frame, setup pose, previous clip posenew clip pose - 其顺序组合结果如下:

  1. 开始于 current state last frame (或者在SkeletonMecanim的更新前对改帧的修改).
  2. 应用 setup pose:
  • Auto Reset 启用, setup pose 会取代 current state last frame.
  • Auto Reset 禁用, setup pose 将不参与mix.

  1. 应用 previous clip pose:
  • 当mix模式置为 Always Mix 时, the previous clip pose 会与当前状态mix(所以当 Auto Reset 启用时会与 setup pose 混合).
  • 当mix模式置为 HardMix Next 时, previous clip pose 将按100%权重应用, 完全覆盖当前状态( 所以它会覆盖 setup pose).

  1. 应用 new clip pose:
  • 当mix模式置为 Always MixMix Next, new clip pose 将与当前状态混合. 因此启用 Auto ResetAlways Mix 是对 setup pose, previous clip posenew clip pose 三者的mix.
  • 当mix模式置为 Hard, new clip pose 将按100%权重应用, 完全覆盖已应用的pose.

下表显示了都修改了相同的时间轴值(例如相同的骨骼旋转)的当前剪辑片段(Clip) P 和新剪辑片段 N 的各种mix结果. S 代表启用了 Auto Reset 时的setup pose, 如果禁用了 Auto Reset , 则代表当前状态(例如前一帧). 变量 w 表示过渡权重(过渡开始时为0, 过渡结束时为1). 每种图层blend模式的(推荐)mix模式以粗体标出.

Always Mix Mix Next Hard
Base Layer lerp(lerp(S, P, 1-w), N, w) lerp(P, N, w) N
Override lerp(
lerp(lower_layers_result,
P, (1-w) * layer_weight),
N, w * layer_weight)
lerp(
lerp(lower_layers_result,
P, layer_weight),
N, w * layer_weight)
lerp(lower_layers_result,
N,
layer_weight)
Additive lower_layers_result +
layer_weight * lerp(P, N, w))
counts as Always Mix lower_layers_result +
layer_weight * N
缩写 含义
S Setup pose
P Previous clip pose
N New clip pose
w Transition weight
lerp(a, b, weight) Linear interpolation from a to b by weight.

SkeletonMecanim的控制器(Controller)和动画器(Animator)

SkeletonMecanim 组件使用控制器(Controller)资产的方式和Unity Mecanim的 Animator 组件一样. 当通过拖放将一个skeleton实例化为 SkeletonMecanim 时会自动生成并添加控制器资产.

注意: 当启用 Apply Root Motion 时, SkeletonMecanimRootMotion 组件会自动添加到 SkeletonMecanim GameObject中.

你可以将Spine动画拖放到Animator面板来向控制器的动画状态机添加动画. 添加后的动画会显示在控制器资产下.

SkeletonDataAsset 中设置的Mix duration值将被 SkeletonMecanim 忽略. 而Mecanim是通过Animator面板的过渡时间来设置的.

SkeletonMecanim事件

当使用 SkeletonMecanim 时, 事件被存储在每个 AnimationClip 中, 并像其他Unity动画事件一样被引发.例如, 如果你在Spine中把事件命名为 "Footstep", 那么需要在 SkeletonMecanim 的GameObject上添加一个 MonoBehaviour 脚本, 使用名为 Footstep() 的方法来处理该事件.当在Spine中使用文件夹时, 方法名则是文件夹名和动画名的拼接.例如, 若前文中的事件被放在一个名为 Foldername 的文件夹中时, 方法名将为 FoldernameFootstep().

C#
public class YourComponentReceivingEvents : MonoBehaviour {
// to capture event "Footstep" when it's placed outside of folders
void Footstep() {
Debug.Log("Footstep event received");
}

// to capture event "Footstep" when it's placed in a folder named "Foldername"
void FoldernameFootstep () {
Debug.Log("Footstep (in folder Foldername) received");
}
}

关于Unity Mecanim事件的更多信息, 请参见 Unity的Animation Events文档.

SkeletonGraphic组件

SkeletonGraphic 组件是三种在Unity中使用Spine skeleton的方式之一. 它们是: SkeletonAnimation, SkeletonMecanimSkeletonGraphic (UI).

SkeletonGraphic 组件是 SkeletonAnimation 组件的平替, 它使用Unity的UI系统进行布局、渲染和遮罩交互.与 SkeletonAnimation 组件一样, 你可以将Spine skeleton添加到GameObject中, 将其制成动画, 响应动画事件等等.

为何要使用这一UI组件

Unity UI(UnityEngine.UI)使用 CanvasCanvasRenderers 系统来分类和管理其可渲染对象.内置的可渲染的UI组件——如 Text, Image, 和 RawImage ——都依赖于CanvasRenderer 来正常工作.将 MeshRenderers (如默认的Cube对象)或 SpriteRenderers (如Sprite)等对象置入UI, Unity将不会在 Canvas 中渲染它们. SkeletonAnimation 使用的是 MeshRenderers , 因此行为方式也是一样的.因此, spine-unity运行时提供了 SkeletonAnimation 的平替组件 SkeletonGraphic, 它是使用 CanvasRenderers 组件进行渲染的 UnityEngine.UI.MaskableGraphic 的子类.

重要须知-Material

只能在 SkeletonGraphic 组件上使用与 CanvasRenderer 兼容的特殊着色器的material, 例如默认的 Spine/SkeletonGraphic* 着色器. 不要在 SkeletonGraphic 组件上使用如 Spine/Skeleton 的URP、LWRP或常规着色器. 没有肉眼可见的视觉错误并不意味着该着色器可以与 SkeletonGraphic 一起正常工作. 我们已知这种的情况: 它在移动设备上的渲染不正确, 然而在Unity编辑器中的渲染却没有任何问题. 和其他UI组件一样, SkeletonGraphic 使用 CanvasRenderer 而非 MeshRenderer, 后者使用了一条不同的渲染管线.

当把 SkeletonDataAsset 实例化为 SkeletonGraphic时, 在 SpineAtlasAsset 处指定的常规material将被忽略, 只使用其texture. 你可以使用SkeletonGraphicCustomMaterials组件来覆盖 SkeletonGraphic组件的materials.

CanvasGroup的alpha问题

当使用带有CanvasGroupSpine/SkeletonGraphic*着色器时, 你会发现随着CanvasGroup Alpha值的减小, skeleton却如下图所示地变得更亮.

这是由于Unity在后台运算中修改了顶点颜色的alpha值, 而且这一机制与spine-unity运行时的premultiplied alpha着色器无法兼容.

当带有Spine/SkeletonGraphic着色器的material置于带alpha淡出的CanvasGroup下面时, 该material必须启用CanvasGroup Compatible参数. 最好是不要修改原始material, 而是创建一个共享的SkeletonGraphicDefault material的副本, 并将其重命名为如SkeletonGraphic CanvasGroup这样的名字. 然后把这个material分配给CanvasGroup下的某个SkeletonGraphic组件, 而不要使用原始material. 任何使用这种CanvasGroup兼容material的SkeletonGraphic组件都需要禁用Advanced - PMA Vertex Colors选项, 以避免半透明的部分出现成倍变暗的问题. 然而这一举措会阻碍additive槽位的single-batch, 并可能导致绘制调用的数量上升.

当带有Spine/SkeletonGraphic TintBlack着色器的material置于带alpha淡出的CanvasGroup下面时, 也需要进行类似的设置, 但additive槽位不受影响. 做法如上所述: 创建一个共享的SkeletonGraphicTintBlack material的副本, 并将其重命名为如SkeletonGraphicTintBlack CanvasGroup的名字, 不要去修改原始材质. 随后将此material分配给CanvasGroup下的某个 SkeletonGraphic组件,不要使用原始material. 使用这种CanvasGroup兼容material的SkeletonGraphic组件均应启用 Advanced - Canvas Group Tint Black选项. 但不必在组件上禁用Advanced - PMA Vertex Colors, 保持你的原设置即可.

重要提示: 由于Unity CanvasRenderer 的限制, SkeletonGraphic 默认情况下限制为单texture. 你可以在 SkeletonGraphic 组件的检查器中启用 Advanced - Multiple CanvasRenderers 来为每个子网格生成一个子 CanvasRenderer GameObject 以突破texture限制. 出于性能方面的考虑, 最好在尽可能避免这样做. 这意味着UI中使用的Skeletons应被打包成一个单texture(单页)atlas而非多页atlas. 关于如何将附件打包成单页atlas texture, 请参见高级 – 单页Atlas Texture导出与SkeletonGraphic部分.

调整RectTransform区间来实现正确可见性

SkeletonGraphic 的可见性是通过其 Canvas 的边界决定的. 当Skeleton通过拖放实例化为Canvas GameObject的子对象时, RectTransform 边界会自动匹配于initial pose.你也可以点击 Match RectTransform with Mesh 按钮手动将RectTransform置为适合当前pose的边界. 重要的是, RectTransform的边界不能小于网格, 否则如 RectMask2D 这样的组件会当作RectTransform完全位于网格外部而在绘制时忽略skeleton, 即使部分应被渲染的网格仍在RectTransform内部. 当 RectTransform 工具 五种Transform模式之一激活时, 当前的RectTransform边界会显示在场景视图中.

参数

SkeletonGraphic 提供了与SkeletonAnimation组件类似的参数, 请参考SkeletonAnimation部分以获得更多信息.

SkeletonGraphic 检查器暴露了如下参数:

  • PMA Vertex Colors (适用于CanvasGroup alpha的额外规则). 将顶点颜色的RGB与顶点颜色的alpha值相乘. 如果用于渲染的着色器是Spine/SkeletonGraphic*着色器(即使带有 Straight Alpha Texture)或使用了PMA additive blend模式Blend One OneMinusSrcAlpha的第三方着色器, 则应启用该参数. 若是使用了带有CanvasGroup alpha"Spine/SkeletonGraphic"着色器的特殊情况, 或在使用带有常规blend模式Blend SrcAlpha OneMinusSrcAlpha的普通着色器时, 则应禁用该参数. 当禁用时, 它可以防止additive槽位的单batch渲染, 且可能会使得绘制调用的数量增加.

  • Freeze. 当置为 true 时, SkeletonGraphic 将不再被更新.

  • Layout Scale Mode. SkeletonGraphic支持基于其RectTransform边界的自适应缩放. 默认值None表示保持以前的行为. 将该参数设置为Width Controls HeightHeight Controls Width, Fit In ParentEnvelope Parent即启用自动缩放. 若需要修改参考布局边界, 点击Edit Layout Bounds按钮进入编辑模式, 即可调整skeleton的边界框. 此时Skeleton将被自动缩放, 以适应参考布局边界和对象的RectTransform.

  • Edit Layout Bounds 用于修改上文中Layout Scale Mode所使用的参考布局边界, 点击该按钮进入编辑模式. 然后就可以手动调整边界或者点击Match RectTransform with Mesh按钮自动适配当前pose. 当完成调整后再次点击Edit Layout Bounds按钮即可退出编辑模式.

  • Match RectTransform with Mesh. 你可以通过点击 Match 按钮使 SkeletonGraphic 的RectTransform匹配其当前pose. 应关注的重点是RectTransform的边界尺寸不能小于网格尺寸, 否则RectMask2D组件会认为RectTransform完全位于网格外部, 而在绘制时忽略skeleton, 即使应被渲染的网格仍然有某些部分位于内部. 仅当Layout Scale Mode被置为None, 或者Edit Layout Bounds开关置为了编辑模式时, 该按钮可用.

  • 高级参数

    • Use Clipping. 当置为 false 时, 会忽略所有的Spine clipping attachments.

    • Canvas Group Tint Black. 只在使用 SkeletonGraphic Tint Black 着色器时应被启用. 当 Tint Black 被禁用时不产生效果. 在 CanvasGroup 下的 SkeletonGraphic 槽位中使用 Additive blend模式时启用之. 启用后, Additive Alpha值将存储在 uv2.g 而非 color.a 中, 以便跟踪 CanvasGroupcolor.a 的修改.

    • Multiple CanvasRenderers. 当置为 true, SkeletonGraphic 不再使用单个 CanvasRenderer , 而是为每个绘制调用(submesh)自动创建所需的子 CanvasRenderer GameObjects.这可以用来突破单txture限制, 但会产生额外的性能开销.

      • Blend Mode Materials. 可以为每个槽位的Blend模式指定不同的 SkeletonGraphic 材料. 该处仅支持 "Spine/SkeletonGraphic *" 或其他与 CanvasRenderer 兼容的Materials.

      • 选择 Assign Default 来设置默认的blend模式material SkeletonGraphicAdditive, SkeletonGraphicMultiplySkeletonGraphicScreen.

        注意: 要确保 SkeletonDataAssetBlend Mode Materials 设置正确, 因为 SkeletonGraphic 的blend模式material的依赖于 SkeletonDataAsset的 material. 当启用 PMA Vertex Colors 时, Additive Material 将被忽略.

    • Animation Update. 设置是以普通的Update(默认) 还是基于物理的 FixedUpdate 模式来更新动画, 也可以通过用户调用来手动更新动画. 当使用SkeletonRootMotion组件并分配了 RigidbodyRigidbody2D 时, 推荐将更新模式设置为In FixedUpdate, 其他情况则建议设置为 In Update.

      • Update When Invisible. 设置当MeshRenderer不可见时使用何种更新模式. 当网格再次可见时更新模式会自动重置为UpdateMode.FullUpdate.

      • Separator Slot Names. 设置当 Enable Separation 置为 true 时渲染分离位置的槽位名称. 关于渲染分离的信息, 请参考SkeletonRenderSeparator部分, 但注意, 使用 SkeletonGraphic 时无需额外组件.

      • Enable Separation. 可以在此处直接启用渲染分离, 无需任何额外组件(如 SkeletonRenderSeparatorSkeletonRendererSkeletonPartsRenderer 组件). 启用后将自动创建额外的分离部件GameObjects, 也会附加相应的 CanvasRenderer GameObjects到层级架构中.分离部件的 GameObjects 可以根据你的要求在层次结构中移动并重新设置层级关系, 以便保证在 Canvas 中的绘制顺序.

      • Update Part Location. 当启用后, 将更新分离部件GameObject的位置以匹配 SkeletonGraphic 的位置. 这在将部件re-parenting到不同的GameObject时会有帮助.

      • Physics Inheritance. 项控制Transform移动如何应用于skeleton的PhysicsConstraints.

        • Position. 当该值置为非零值时, 将把计入了X和Y缩放因子的变换(Transform)的位移量应用于skeleton的物理约束. 几个 (X,Y) 的典型值是:
          (1,1) 表示正常应用 XY 移动,
          (2,2) 说明应用了双倍移动量,
          (1,0) 表示仅应用水平移动, 而
          (0,0) 表示完全无任何位移.
        • Rotation. 当该值置为非零值时, 将把计入了X和Y缩放因子的变换(Transform)的位移量应用于skeleton的物理约束. 几个典型值为:
          1 表示正常旋转,
          2 表示二倍速旋转, 而
          0 代表完全无任何旋转.
      • Movement relative to. 欲应用Transform移动(诸如父变换等)的基准点, 该项值为计算移动的参考变换. 将此项置为None即使用绝对世界位置(默认值).

示例场景

可以浏览示例场景 Spine Examples/Getting Started/6 Skeleton Graphic 来了解基本用法. 也可以在 Spine Examples/Other Examples/SkeletonRenderSeparator 中找到高级示例场景, 它展示了如何设置分离器(separator)槽位和修改绘制顺序.

SkeletonRenderer组件

SkeletonRenderer 组件负责绘制skeleton的当前状态. 它是 SkeletonAnimationSkeletonMecanim 的基类.

注意: 多数时候均应使用SkeletonAnimationSkeletonMecanimSkeletonGraphic (UI). 这些组件提供了丰富的动画控制方法. 该组件只有在手动应用无过渡动画(如UI仪表元素中)时可能有用.

渲染是通过 MeshRenderer 组件中更新procedural网格进行的. 该组件使用由 SkeletonDataAsset 引用的texture atlas资产来查找绘制skeleton附件所需的textures和materials. 你可以查阅 SkeletonAnimation 部分以了解更多信息.

也可以查看示例场景 Spine Examples/Other Examples/SpineGauge 来了解如何直接使用 SkeletonRenderer 组件.

Utility 组件

SkeletonRootMotion

spine-unity的三种Spine skeleton组件均支持root motion. SkeletonRootMotion 组件可以附加到SkeletonAnimationSkeletonGraphic (UI) GameObjects上, 且单独为SkeletonMecanim提供了 SkeletonMecanimRootMotion 组件.附加这个root motion组件类似于在Unity Mecanim Animator 组件上启用 Apply Root Motion 参数. 当启用后, 会根据动画中指定的 Root Motion Bone 的运动来驱动角色位置.

注意: SkeletonMecanimRootMotion 组件仅用于 SkeletonMecanim 对象. 当它用于 SkeletonAnimationSkeletonGraphic (UI) 组件时 SkeletonRootMotion 将无法正常工作.

参数

  • Root Motion Bone. 指定其运动用于root motion的目标骨骼.
  • X. 启用后, 沿局部X轴的运动被用于root motion.
  • Y. 启用后, 沿局部Y轴的运动被用于root motion.
  • Root Motion Scale (X). 应用于水平root motion delta的比例. 可用于delta补偿, 例如将一个跳跃动画的位移拉伸到所需的距离.
  • Root Motion Scale (Y). 应用于垂直root motion delta的比例.可用于delta补偿, 例如将一个跳跃动画的位移拉伸到所需的高度.
  • Animation Tracks. 可以指定哪些动画轨道应包含在root motion的计算中.

可选参数

  • Rigidbody2D. 当添加了 Rigidbody2D 组件时, 运动将通过物理运动应用于给定的 Rigidbody2D 而非 Transform 组件. 然后建议将skeleton动画组件在检查器里的 Advanced - Animation Update 参数设置为 In FixedUpdate.
  • Rigidbody. 当添加了 Rigidbody 组件时, 运动将通过物理运动应用于给定的 Rigidbody 而非 Transform 组件.然后建议将skeleton动画组件在检查器里的 Advanced - Animation Update 参数设置为 In FixedUpdate.

注意: SkeletonRootMotion 类提供了 AdjustRootMotionToDistance() 和其他方法来进行Delta补偿. Delta补偿可以用于特定场景(例如拉伸一个跳跃动画到一个给定的距离). Root motion可以在动画开始时调整, 也可以通过 skeletonRootMotion.AdjustRootMotionToDistance(targetPosition - transform.position, trackIndex); 在某一帧上调整.

SkeletonMecanimRootMotion

该组件是 SkeletonRootMotion 组件的SkeletonMecanim版, 与 SkeletonMecanim 组件配合使用.

当Unity Mecanim AnimatorApply Root Motion 参数被启用时, SkeletonMecanimRootMotion 组件会自动添加到skeleton的GameObject上. 要移除 SkeletonMecanimRootMotion 组件, 必须首先禁用Animator的 Apply Root Motion 参数.

参数

  • Root Motion Bone. 指定其运动用于root motion的目标骨骼.
  • X. 启用后, 沿局部X轴的运动被用于root motion.
  • Y. 启用后, 沿局部Y轴的运动被用于root motion.
  • Root Motion Scale (X). 应用于水平root motion delta的比例. 可用于delta补偿, 例如将一个跳跃动画的位移拉伸到所需的距离.
  • Root Motion Scale (Y). 应用于垂直root motion delta的比例. 可用于delta补偿, 例如将一个跳跃动画的位移拉伸到所需的高度.
  • Mecanim Layers. 允许你指定哪些Mecanim图层应包括在root motion计算中.

可选参数

  • Rigidbody2D. 当添加了 Rigidbody2D 组件时, 运动将通过物理运动应用于给定的 Rigidbody2D 而非 Transform 组件.
  • Rigidbody. 当添加了 Rigidbody 组件时, 运动将通过物理运动应用于给定的 Rigidbody 而非 Transform 组件.

注意: SkeletonMecanimRootMotion 类提供了 AdjustRootMotionToDistance() 和其他方法来进行Delta补偿. Delta补偿可以用于特定场景(例如拉伸一个跳跃动画到一个给定的距离).Root motion可以在动画开始时调整, 也可以通过 skeletonRootMotion.AdjustRootMotionToDistance(targetPosition - transform.position, trackIndex); 在某一帧上调整.

BoneFollower

该组件引用 SkeletonAnimation 组件的骨骼, 并在每次 Update 时将自己的transform置为该骨骼的transform.

注意: SkeletonGraphic 对象有自己专用的 BoneFollowerGraphic 组件.

SkeletonUtilityBone 组件相反, BoneFollower 可以作为一个单独的GameObject使用, 没有任何父级骨骼对象.

使用它可以让粒子系统等对象跟随skeleton上的某个骨骼.

你可以在示例场景 Spine Examples/Getting Started/4 Object Oriented Sample 中查看如何设置 BoneFollower 组件.

BoneFollowerGraphic

该组件为 Bone Follower 组件的 SkeletonGraphic 版本, 和SkeletonGraphic 组件搭配使用.

SkeletonUtilityBone 组件不同的是, BoneFollowerGraphic 可以作为一个单独的GameObject使用, 没有任何父级骨骼对象.

使用它可以让粒子系统等对象跟随skeleton上的某个骨骼.

你可以在示例场景 Spine Examples/Getting Started/6 Skeleton Graphic 中查看如何设置BoneFollowerGraphic 组件.

PointFollower

这个组件类似于 Bone Follower 组件, 但是它跟随的是一个 PointAttachment 而非一个骨骼.

SkeletonUtilityBone 组件不同的是, PointFollower 可以作为一个单独的GameObject使用, 没有任何父级骨骼对象.

BoundingBoxFollower

这个组件用于在skeleton的槽位中匹配一个边界框(bounding box). 它会获取边界形状并将其传递给 PolygonCollider2D , 且在每一帧启用或禁用它以匹配当前动画.

注意: 骨骼位置不会被自动跟踪, 这就是为什么它通常与 BoneFollower 组件一起使用. 你可以在 BoundingBoxFollower 检查器中使用 Add Bone Follower 按钮来创建和设置一个 BoneFollower 组件.

注意: 顶点变形动画(随着时间推移, Bounding Box的顶点也会移动的动画)不会被跟随, 组件只能覆盖其skeleton的初始形状.

更多信息请参见 Bone Follower.

SkeletonUtilityBone

有时你需要在运行时以编码的方式修改骨骼位置, 以以便对物理运动或用户输入做出反应.

SkeletonUtilityBone 组件提供了一个方便的接口, 让GameObjects跟随一个骨骼位置, 通过手动或2D/3D物理来覆盖一个骨骼的位置.它可以被配置为 跟随(follow) 本地骨骼位置或在每次 Update覆盖(override) 它.当设置为Override时, 该组件将在SkeletonAnimation组件更新世界transform之前设置骨骼位置.

重要提示: SkeletonUtilityBone 使用局部transform值.它依赖于SkeletonUtilityBone GameObjects的层次结构, 所以反映了skeleton的骨骼层次结构. 快速创建SkeletonUtilityBone 层次结构的推荐方法是使用下文中的SkeletonUtility 组件.

SkeletonUtilityBone 检查器还提供了一个接口来(选择性或递归性地)创建额外的子骨骼或创建2D 和3D铰链(hinge chain).

一旦你创建了SkeletonUtilityBones 的层次结构, 层次结构面板会在SkeletonUtilityBone GameObject旁边显示不同的图标, 取决于它的设置为

  • Follow: Follow
  • 或是 Override: Override

示例用例

Override 模式下使用 SkeletonUtilityBone , 例如让用户拖动skeleton上的某块骨骼.

如果一个GameObject只跟随一个骨骼的位置, 你可以使用 BoneFollower 组件来节省资源.

示例场景

你可以在 Spine Examples/Other Examples/SkeletonUtility Animated Physics 中找到一个演示 SkeletonUtilityBone 用法的示例场景. 它展示了 SkeletonUtilityBones 节点是如何 Follow 骨骼位置, 成为 Override 节点的必要父层节点的.

创建2D和3D的物理铰链(Hinge Chains)

Hinge Chain Demo

你可能想为你的角色的披风添加物理效果, 让角色拖拽重物或者给它加个流星锤. spine-unity运行时可以从现有的 SkeletonUtilityBone 层次结构(见创建SkeletonUtilityBones的层次结构)中生成 HingeJointHingeJoint2D 元素的physics rig.

Hinge Chain Setup

选中第一个 SkeletonUtilityBone 链元素, 在检查器中选择 Create 3D Hinge ChainCreate 2D Hinge Chain 来生成physics rig. 被选中的元素和其全部 SkeletonUtilityBone 子元素都会转换为一个铰链. 然后你可以调整Rigidbody的阻力(drag)和质量(mass)参数来调整效果. 提高阻力值将使Rigidbody移动得更慢, 可以创造出物体沉重或被空气阻碍的效果.

请注意, 链根节点不再以skeleton的骨骼为父节点, 而是放置在场景的最高层级.这是Unity中使用动量的一个要求.不要把链根节点再置入skeleton骨骼的层级中, 否则链元素将不再受到skeleton运动的影响!

3D铰链
  1. 创建一个SkeletonUtilityBone层级结构.
  2. 在场景面板中选中第一个链元素, 在检查器中选择 Create 3D Hinge Chain 来创建3D铰链rig. Create Hinge Chain 3D
  3. 这将从先前的父级(在本例中为 cape-root)移除链GameObjects, 并在场景的最高层放置一个全新的 HingeChain Parent GameObject. 如前文所述, 千万不要将这个GameObject重新放回到skeleton下面去! Hinge Chain 3D Hierarchy
  4. 调整链元素Rigidbody的阻力和质量参数来调整效果.

当skeleton翻转时, HingeChain Parent GameObject将自动旋转180度来把铰链调整到翻转后的骨骼位置上.

2D铰链
  1. 创建一个SkeletonUtilityBone层级结构.
  2. 在场景面板中选中第一个链元素, 在检查器中选择 Create 2D Hinge Chain 来创建2D铰链rig. Create Hinge Chain 2D
  3. 这将从先前的父级(在本例中为 cape-root)移除链GameObjects, 并在场景的最高层放置一个全新的 HingeChain Parent GameObject. 如上文所述, 千万不要将这个GameObject重新放回到skeleton下面去! Hinge Chain 2D Hierarchy
  4. 调整链元素Rigidbody2D的阻力和质量参数来调整效果.

要注意这个GameObject包含两个子对象: Hinge ChainHinge Chain FlippedX. 当翻转skeleton时, 这些GameObjects将自动启用和停用, 分别代表了各自朝向的铰链动画.

SkeletonUtility

创建SkeletonUtilityBones的层次结构

SkeletonUtility 组件提供了一个快速创建 SkeletonUtilityBone GameObjects层次结构的方法, 该层级结构它反映了skeleton的骨骼层次结构.

要添加一个 SkeletonUtility 组件: 选中 SkeletonAnimation 组件, 在检查器中展开 Advanced 部分并点击 Add Skeleton Utility. 一旦创建了该组件, Add Skeleton Utility 按钮将消失, SkeletonUtility 组件将被添加到GameObject中.

SkeletonUtility 组件提供了一个 Spawn Hierarchy 按钮, 点击后会出现以下选项:

  1. Follow all bones 为层次结构中的所有骨骼创建 SkeletonUtilityBone GameObject, 设置为 Follow 模式
  2. Follow (Root Only) 只创建root SkeletonUtilityBone GameObject, 设置为 Follow 模式
  3. Override all bones 为层次结构中的所有骨骼创建 SkeletonUtilityBone GameObjects, 设置为 Override 模式
  4. Override (Root Only) 只创建root SkeletonUtilityBone GameObject, 设置为 Override 模式

然后可以单独配置每个 SkeletonUtilityBone, 以便在必要时覆盖skeleton的骨骼位置.

注意: 你可以在以后通过 SkeletonUtilityBone 检查器添加额外的 SkeletonUtilityBone GameObjects, Spawn Hierarchy 功能可以做好大致的铺垫. 你也可以删除多余的 SkeletonUtilityBone GameObjects来节省资源. 只需记住一定要保持父子关系不变, 所以不要在层次结构里删除GameObjects或改变其父对象.

SkeletonUtilityConstraint

C#

它是skeleton utility约束子类的基类.它在父类 SkeletonUtility 处自动注册并进行相应的更新.

关于如何编写你自己的约束类, 请参见示例约束类 SkeletonUtilityGroundConstraintSkeletonUtilityEyeConstraint.

示例场景

spine-unity运行时附带了演示上述约束的示例场景. 你可以在 Spine Examples/Other Examples/SkeletonUtility GroundConstraintSpine Examples/Other Examples/SkeletonUtility Eyes 中找到它们.

SkeletonRendererCustomMaterials

你可能想覆盖某个skeleton实例甚至某个槽位的material. 该组件提供了一个检查器接口, 用于为SkeletonRenderer(包括子类SkeletonAnimationSkeletonMecanim)设置自定义material覆盖.

右击 SkeletonRenderer(或子类SkeletonAnimationSkeletonMecanim), 并选择 Add Basic Serialized Custom Materials 来将此组件添加到渲染器中. 在 Custom Material Overrides 数组中添加条目可覆盖某个槽位的material, 或 在Custom Material Overrides数组中添加条目, 可以将整个skeleton的当前material替换为另一material. 请确保禁用 Override Disabled, 这样才能启用各条目中设置的material覆盖.

注意: 该组件设计初衷里不包括通过代码进行交互.要通过代码为你的SkeletonRenderer动态地设置material, 应直接通过 SkeletonRenderer.CustomMaterialOverride 来访问material覆盖数组, 或者 SkeletonRenderer.CustomSlotMaterials 来访问槽位material覆盖.

SkeletonGraphicCustomMaterials

该组件为SkeletonGraphic专用, 是 SkeletonRendererCustomMaterialsSkeletonGraphic 版本. 该组件也提供了检查器接口, 用于为 SkeletonGraphic 设定自定义material和texture覆盖.

你可以右击 SkeletonGraphic 并选择 Add Basic Serialized Custom Materials 来将这个组件添加到GameObject中.在 Custom Texture Overrides 数组中添加条目可以给整个skeleton替换一种texture.在 Custom Material Overrides 数组中添加条目, 可以用另一种material替换原始(替换前的)texture中使用的material.请确保启用 Override Enabled 以启用各条目中的texture或material覆盖.

注意: 该组件设计初衷里不包括通过代码进行交互.要通过代码为你的SkeletonGraphic动态设置material, 应直接通过 SkeletonGraphic.CustomMaterialOverride 来访问material覆盖数组, 或通过 SkeletonGraphic.CustomTextureOverride 来访问texture覆盖.

SkeletonRenderSeparator

有时可能需要在角色的各个部分之间显示其他的GameObjects, 例如: 让你的角色爬到一棵树上, 在树干前面显示一条腿在树干后面显示另一条. SkeletonRenderSeparator 组件让你可以将 SkeletonRenderer (或其子类SkeletonAnimationSkeletonMecanim)分为两个或更多个SkeletonPartsRenderers, 且可自定义其图层顺序.

注意: SkeletonGraphic 组件直接在 SkeletonGraphic 检查器的 Advanced 部分提供了渲染分离功能, 它不无需任何额外组件.

注意: 通常情况下, Spine渲染器组件会使用一个渲染器来显示整个skeleton的网格.然而这会导致无法在其部件(parts)之间插入其他 UnityEngine.Renderers (SpriteRenderer, MeshRenderer, ParticleSystem 等等)组件.

设置步骤

  1. 确定Skeleton的绘制顺序. 找出你想用哪个槽位来将Skeleton渲染分为多个部件.为方便起见, 在导出skeleton之前应清晰地标注该槽位.

  2. 添加SkeletonRenderSeparator组件 选中Spine GameObject. 在检查器中右键点击SkeletonAnimationSkeletonRenderer. 选择 Add Skeleton Render Separator 后将把 SkeletonRenderSeparator 组件添加到GameObject中.

"Steps 2 to 4"

  1. 分配Separator槽位 检查器现在会显示一个警告, 因为separator列表是空的.在 Separator Slot Names 下选择所需的槽位.你可以点击 + 按钮来添加额外的separator槽位.

注意: 这个字段在SkeletonRenderer(或SkeletonAnimationSkeletonMecanim 子类)组件处被序列化, SkeletonRenderSeparator 只是为它提供了接口.

  1. 添加部件(Parts)渲染器 检查器现在会显示一个警告:你没有足够的部件渲染器.
    点击 Add the missing renderers (n) 按钮, 用 SkeletonPartsRenderer 组件创建所需的GameObjects. 这些GameObjects将被自动分配到上面的 Parts Renderers 列表中.

注意: SkeletonRenderSeparator 会根据当前的绘制顺序来检测当前所需的部件渲染器的数量. 如果在运行时修改了绘制顺序, 可能会发现渲染器需要更多的部件渲染器. 在这种情况下, 可以通过点击 Add Parts Renderer 按钮, 手动添加一到两个额外的部件渲染器.

  1. 设置 Sorting LayerOrder in Layer 每个 SkeletonPartsRenderers 都在检查器中提供了Sorting LayerOrder in Layer属性.你可以在每个 SkeletonPartsRenderer 中设置顺序属性值, 值更大的部件渲染器越靠前.

注意: SkeletonPartsRenderer GameObjects不必是Spine GameObject的子对象. SkeletonRenderSeparator 会保持引用, 所以你可以根据需要组织层级结构.

示例场景

你可以在Spine Examples/Other Examples/SkeletonRenderSeparator中找到 SkeletonPartsRendererSkeletonRenderSeparator 用法的示例场景.

C#

启用和禁用

默认情况下, SkeletonRenderSeparator 将禁用 SkeletonRenderer 并接管其网格渲染任务. 所以如果你禁用了 SkeletonRenderSeparator之后, SkeletonRenderer 将重新接管渲染工作.

你可以像操作其他组件一样启用和禁用 SkeletonRenderSeparator:

C#
skeletonRenderSeparator.enabled = true; // separation is enabled.
skeletonRenderSeparator.enabled = false; // separation is disabled.
改变分离点

分离点并不存储在 SkeletonRenderSeparator 中.
它是由 SkeletonRenderer (或其子类SkeletonAnimationSkeletonMecanim)中的separator槽位存储的. 如果你想在运行时操纵separator槽位, 可以通过代码访问 SkeletonRenderer.separatorSlots 列表, 并像操作普通的列表一样调用其Add, Remove or Clear方法.

C#
Spine.Slot mySlot = skeletonAnimation.Skeleton.FindSlot("MY SPECIAL SLOT");
skeletonAnimation.separatorSlots.Clear();
skeletonAnimation.separatorSlots.Add(mySlot);
在运行时添加SkeletonRenderSeparator

可以使用静态方法 SkeletonRenderSeparator.AddToSkeletonRenderer 来添加和初始化一个新的 SkeletonRenderSeparator 组件.

C#
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.SeparatorSlots.Add(mySlot); // see above

// Add the SkeletonRenderSeparator.
SkeletonRenderSeparator skeletonRenderSeparator = SkeletonRenderSeparator.AddToSkeletonRenderer(skeletonAnimation);

默认情况下, 它将添加当前必要的 SkeletonPartsRenderers. 它也为高级用例提供了一些可选的参数, 详情请查看编码文档.

渲染

着色器

spine-unity 运行时包含多种不同着色器.默认情况下, 一个新导入的skeleton material用的是 Spine/Skeleton 着色器. 你也可以通过Material的着色器参数来更改着色器. 下文为Spine着色器列表.

重要提示:SkeletonGraphic 组件中只支持 CanvasRenderer 兼容的特殊着色器. 详见SkeletonGraphic - Important Material Requirements.

重要提示: spine-unity 运行时软件包只提供了 Built-In Render Pipeline (内置渲染管线)着色器, 不兼容于 Universal Render Pipeline (通用渲染管线)项目. 如果你使用的是通用渲染管线, 请使用 Spine URP着色器UPM插件包 中提供的 URP 着色器.

注意: Spine着色器还不支持延迟着色渲染路径.

  1. Spine/Skeleton (默认着色器)
    Unlit 透明着色器. 不写z-buffer.
  2. Spine/Skeleton Graphic (Default shader for SkeletonGraphic)
    用于SkeletonGraphic的Unlit透明着色器. 不写Z-buffer. 当与 CanvasGroup 一起使用时, 不支持 Additive blend模式. 如果有这类需求应使用 Spine/Skeleton Graphic Tint Black 代替. 由于 CanvasRenderer 的限制, 只能使用单texture.
  3. Spine/Skeleton Lit
    Simple lit 透明着色器, 不支持法线贴图. 不写 z-buffer.
  4. Spine/Skeleton Lit ZWrite
    Simple lit 透明着色器, 不支持法线贴图. 写z-buffer.
  5. Spine/Skeleton Fill
    可自定义颜色叠加(color overlay)的Unlit 透明着色器. 不写 z-buffer. FillColor 决定叠加颜色, FillPhase 决定叠加强度.
  6. Spine/Skeleton Tint
    可定制双色tint的Unlit 透明着色器, 将深色与浅色的tinting独立, 称为tint black. 不写z-buffer.

texture的浅色被tint成 Tint Color, texture的深色tint成 Black Point. 与普通的多色混合(multiply color blending)相比, tint的texture比其原颜色更亮.当把 Tint ColorBlack Point 设置为同一颜色时, 得到的是一个纯色叠加.当把 Tint Color 设置为黑色而 Black Point 设置为白色时, texture则会反色.

  1. Spine/Skeleton Tint Black

可为每个动画槽位tint black的unlit透明着色器.不写z-buffer.
Spine为槽位提供了 Tint Black 功能, 可以动画black tint.

所需额外的设置步骤(用于tint color顶点数据):

  1. Spine/Skeleton Tint Black Additive
    可为每个动画槽位tint black的unlit透明着色器. 使用additive blend模式. 不写z-buffer.
  2. Spine/SkeletonGraphic Tint Black
    Spine/Skeleton Tint Black 着色器的SkeletonGraphic版本. 当与 CanvasGroup 一起使用时支持 Additive blend模式.

所需的额外设置步骤(用于tint color顶点数据):

  1. SkeletonAnimation的检查器中的Advanced 部分启用Tint Black.
  2. SkeletonGraphic 的material设置为 Spine/Runtime/spine-unity/Materials 文件夹下的 SkeletonGraphicTintBlack material.
  3. 选中父Canvas并在 Additional Shader Channels 中启用 TexCoord1TexCoord2.

CanvasGroup中设置Additive blend模式的步骤:

  1. SkeletonGraphic 检查器的 Advanced 部分中启用 Canvas Group Tint Black.
  2. 在着色器中启用 CanvasGroup Compatible.
  3. Spine/Sprite
    复杂的可配置着色器, 支持比 Spine/Skeleton Lit 着色器更高级的照明. 可以在实例场景 Spine Examples/Other Examples/Sprite Shaders 中找到关于 Spine/Sprite/Vertex Lit 着色器的演示.
  4. Spine/Sprite/Unlit
    unlit着色器, 包含可配置的blend模式、覆盖颜色、色调(hue)、饱和度(saturation)和亮度(brightness)调整. 可配置为写入z-buffer.支持fog.
  5. Spine/Sprite/Vertex Lit
    可配置blend模式的复杂vertex-lit着色器.
    支持法线贴图、次级漫反射(secondary albedo)、金属性(metallic)和自发光(emission)贴图.
    用于cel-shaded look的可配置color ramp和基于法线的边缘照明(rim lighting).
    可配置颜色叠加、色调、饱和度和亮度调整.
    可配置为写入z-buffer. 支持fog.
  6. Spine/Sprite/Pixel Lit
    Spine/Sprite/Vertex Lit 着色器的Pixel-lit版. 它是唯一一个接受逐像素(per-pixel)实时阴影的着色器. 这个着色器总是写入z-buffer(启用了 ZWrite, 因为它使用了 ForwardAdd pass).
  7. Spine/Special
  8. Spine/Special/Skeleton Grayscale
    可定制灰度渲染强度的Unlit透明着色器. 不写z-buffer.
  9. Spine/Special/Skeleton Ghost
    SkeletonGhost 组件使用的特殊着色器, 用于踪迹(trail)渲染.
  10. Spine/Blend Modes
    适用于在Spine编辑器中的槽位中配置了 Additive, MultiplyScreen blend 模式的情况. 建议使用BlendModeMaterials SkeletonData Modifier资产, 以便在导入时自动设置blend模式material.
  11. Spine/Blend Modes/Skeleton PMA Additive
    Unlit透明着色器. 使用additive blend模式. 不写z-buffer.
  12. Spine/Blend Modes/Skeleton PMA Multiply
    Unlit透明着色器. 使用multiply blend模式. 不写z-buffer.
  13. Spine/Blend Modes/Skeleton PMA Screen
    Unlit透明着色器. 使用screen blend模式. 不写z-buffer.
  14. Spine/Outline
    上述所有着色器都提供了 Outline 参数, 可用它来切换不同版本的 Spine/Outline 着色器, 它可以在skeleton的边缘上额外绘制一个彩色轮廓. 你可以在示例场景 Spine Examples/Other Examples/Outline Shaders 中找到 Spine/Outline 着色器的演示.
  15. Spine/Outline/OutlineOnly-ZWrite 一个特殊的, 只渲染网格轮廓的单pass着色器. 写入z-buffer, 以便在重叠的(overlapping)附件上实现合适的轮廓遮蔽(outline occlusion).也可以用于需要渲染多种材质的skeleton, 在这种情况下, 普通的轮廓着色器会错误地渲染出每个子网格的轮廓, 而非整个skeleton的外轮廓. 例如, 你可以用 RenderCombinedMesh 组件来重渲染拼装的skeleton网格, 并使用此着色器在skeleton后面勾勒轮廓.

后处理效果

景深(DoF, Depth of Field)等一些后处理效果需要着色器写入z缓冲区(更准确地说是写入深度pre-pass深度缓冲区). 某些Spine着色器在材质中提供了深度写入(Depth Write) (又称ZWrite)参数开关, 而其他着色器则默认写入z缓冲区. 欲知哪些 Spine 着色器会写入 z 缓冲, 请参阅本文档前文.

根据项目的图形设置(或渲染管线资产设置)不同, 着色器写入z缓冲的操作可能并不会生效, 例如将URP-HighFidelity作为渲染管线资产时就会这样. 这时还需将材质的Render Queue参数从Transparent改为AlphaTest.

如果你觉得Spine所提供着色器限制了创作, 你也可以创建自己的 Spine 着色器副本, 并将(每个Pass部分代码里)任何读取ZWrite Off的行改为 ZWrite On, 并将标记"Queue"="Transparent"改为"Queue"="AlphaTest". 同时别忘记更改着色器文件第一行中的着色器名称, 以避免命名冲突.

URP 着色器 - UPM插件包

通用渲染管线(URP)着色器包含于一个单独的UPM(Unity Package Manager)包. 关于如何下载和安装UPM包, 请参见可选UPM插件包一节; 关于如何更新扩展UPM包, 请参见更新UPM插件包.

URP Shaders UPM包提供了专门为Unity通用渲染管线构建的着色器, 包括2D渲染器功能.

注意: 不要在 SkeletonGraphic 组件上使用URP着色器. 详见 SkeletonGraphic - Important Material Requirements.

注意: Spine URP着色器尚未支持新添加的URP 延迟渲染路径功能.

用于2D渲染器的URP版着色器 (切勿用于URP (3D) 前向渲染器)

  1. Universal Render Pipeline/2D/Spine/Skeleton
    Spine/Skeleton 着色器的URP 2D渲染器版.
  2. Universal Render Pipeline/2D/Spine/Skeleton Lit
    Spine/Skeleton Lit 着色器的URP 2D渲染器版.
  3. Universal Render Pipeline/2D/Spine/Sprite
    Spine/Sprite/Vertex LitPixel Lit 着色器的URP 2D渲染器版.

用于3D渲染器的URP版着色器 (切勿用于 URP 2D 渲染器)

  1. Universal Render Pipeline/Spine/Skeleton
    Spine/Skeleton 着色器的URP版.
  2. Universal Render Pipeline/Spine/Skeleton Lit
    Spine/Skeleton Lit 着色器的URP版.
  3. Universal Render Pipeline/Spine/Sprite
    Spine/Sprite/Vertex LitPixel Lit 着色器的URP版. 接受逐像素(per-pixel)实时阴影.
  4. Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly
    Spine/Outline 着色器的URP版. URP不允许一个着色器有多个pass, 所以它需要自己的material. 你可以考虑使用RenderExistingMesh 组件, 详见包中的示例场景 com.esotericsoftware.spine.URP-shaders/Examples/Outline Shaders URP. 如果你的skeleton需搭配多种材质, 建议使用RenderCombinedMesh组件而非RenderExistingMesh组件.

这些着色器可以附加于material, 并且会受到 Project Settings - Graphics 中设置的 UniversalRenderPipelineAsset 的影响.

你可以UPM包中的com.esotericsoftware.spine.URP-shaders/Examples目录下的示例场景中找到以下三个URP着色器的演示:
3D/URP 3D Shaders.unity, 2D/URP 2D Shaders.unityOutline Shaders URP.unity.

LWRP Shaders - Extension UPM Package

轻量渲染管线(LWRP)着色器包含于一个单独的UPM(Unity Package Manager)包. 关于如何下载和安装UPM包, 请参见可选UPM插件包一节; 关于如何更新扩展UPM包, 请参见更新UPM插件包.

LWRP Shaders UPM包提供了Unity的轻量渲染管线专用的着色器:

注意: 不要将LWRP着色器用于 SkeletonGraphic 组件. 详见 SkeletonGraphic - Important Material Requirements.

  1. Lightweight Render Pipeline/Spine/Skeleton
    Spine/Skeleton 着色器的LWRP版.
  2. Lightweight Render Pipeline/Spine/Skeleton Lit
    Spine/Skeleton Lit 着色器的LWRP版.
  3. Lightweight Render Pipeline/Spine/Sprite
    Spine/Sprite/Vertex LitPixel Lit着色器的LWRP版.

这些着色器可以附加于material, 并且会受到 Project Settings - Graphics中设置的 LightweightRenderPipelineAsset 的影响.

你可以在示例场景 com.esotericsoftware.spine.lwrp-shaders-3.8/Examples/LWRP Shaders.unity 中找到LWRP着色器的演示.

Shader Graph

目前还没有官方的Shader Graph Spine着色器或着色器节点可用.请注意, 当你从Spine导出texture且使用Straight alpha时, 你可以使用任何非Spine着色器.如果你想在其中复刻Spine着色器专用功能, 请参见论坛上的帖子[1], [2]. 如果你有任何问题, 欢迎随时来Spine论坛的Unity板块上发帖.

Amplify Shader Editor

虽然官方没有为Amplify着色器编辑器提供着色器模板, 但用户 Hana 已经在论坛上热情地分享了他的模板代码.

编写自己的着色器

请先熟悉一下为Unity编写自定义着色器的大概方法. 特别是教程: 顶点和片元(fragment)编程提供了一个很好的概述, 让你更容易理解spine-unity着色器的各个部分在做什么.

从现有的spine-unity着色器开始编写自己的着色器

强烈建议从现有的spine-unity 着色器副本着手.然后, 你可以逐步修改一个已可用的着色器, 调整它来达到你想要的效果.例如, 你可以在着色器返回最终颜色结果之前添加自己的颜色处理代码.下面的代码展示了一个简短的示例, 说明了如何创建一个添加了灰度(grayscale)功能的修改版 SkeletonGraphic 着色器:

Properties
{
   _GrayIntensity("Intensity", Range(0, 1)) = 1 // this line was added to provide a Material property
   [..]
}
sampler2D _MainTex;
float _GrayIntensity; // this parameter was added
..
fixed4 frag (VertexOutput IN) : SV_Target
{
   ..
   color.rgb = lerp(color.rgb, dot(color.rgb, float3(0.3, 0.59, 0.11)), _GrayIntensity); // this line was added
   return color;
}
使用非Spine着色器或可视化着色器编辑器时的附加说明

务必考虑以下spine-unity着色器和其他非Spine着色器间的区别:

  1. 在渲染任何Spine skeleton时, 必须设置 Cull Off禁用Backface culling .
  2. Spine着色器通常不需要法线, 因此在使用lit着色器时, 可能需要在组件处启用 Advanced - Add Normals.
  3. Spine着色器通常不需要tangents, 因此在使用法线贴图时, 可能需要在组件中启用 Advanced - Solve Tangents.
  4. Spine着色器默认使用Premultiply Alpha textures, 所以要么
    a) 将atlas textures导出为Straight alpha, 或者
    b) 将着色器的blend模式改为PMA blend模式 Blend One OneMinusSrcAlpha.
  5. Spine顶点颜色通常是PMA顶点颜色.当使用透明槽位或 Additive 槽位时, 你可以
    a) 将着色器的blend模式改为PMA blend模式 Blend One OneMinusSrcAlpha, 并使用PMA atlas textures, 或者 b) 在组件中禁用 Advanced - PMA Vertex Colors(此后将不渲染 Additive 槽位).

UI和非UI着色器的一般准则是:

  1. 不要在 SkeletonAnimationSkeletonMecanim上使用UI着色器.
  2. 不要在 SkeletonGraphic 上使用非UI着色器.
Spine/Skeleton 着色器的解析

下文提供了对Spine/Skeleton 着色器的解析. 在导入Spine texture atlas后生成Material时, 默认会使用该着色器. Spine/Skeleton 着色器相当精简且经典, 它具有以下特点:

  • Premultiply Alpha (PMA) blending
  • No depth buffer writing
  • No lighting
  • No backface culling
  • No fog
  • 通过乘以顶点颜色来对texture进行tint
  • 可选择使用straight alpha而非PMA textures
  • 有一个 "ShadowCaster" pass, 因此它可以投射实时阴影
  • Material属性:
    • _MainTex "Main Texture"
    • _StraightAlphaInput "Straight Alpha Texture"
    • _Cutoff "Shadow alpha cutoff"
    • 高级参数:
      • _StencilRef "Stencil Reference"
      • _StencilComp "Stencil Comparison"
      • Outline parameters (_OutlineWidth "Outline Width", etc.)

详细解释:
  • Premultiply Alpha (PMA) blending

hlsl
Blend One OneMinusSrcAlpha

(Spine-Skeleton.shader:25)

Blending的本质是 result_rgba = frag_output_rgba * src_factor + framebuffer_rgba * dst_factor.
非标准的 Blend One OneMinusSrcAlpha 模式可以让 Additive blend模式的槽位和 Normal blend模式槽位在单次渲染pass中同时绘制. 这是通过在上面这行代码的 SrcFactor 中使用 One 值 (而非 SrcAlpha 值). 这将无修改的frag_output_rgba 值加到被 OneMinusSrcAlpha 加权的 OneMinusSrcAlpha 上:
a) 对于 Normal blending, 片元着色器将 RGB 乘以 A, A 保持不变.
b) 对于 Additive blending, RGB 不乘以 alpha, 且 A 置为 0, 最终结果为 result_rgba = frag_output_rgba + (1-0) * framebuffer_rgba.

当在SkeletonRendererSkeletonGraphic 组件上启用Advanced - PMA Vertex Colors时, NormalAdditive 槽位混合模式将作为顶点颜色被隐式地传入着色器:

hlsl
struct VertexInput {
    float4 vertexColor : COLOR
}

(Spine-Skeleton.shader:48)

当PMA顶点颜色乘以采样出的PMA texture颜色时, 会自动应用槽位的 NormalAdditive blend模式:

hlsl
return (texColor * i.vertexColor);

(Spine-Skeleton.shader:72)

所以得在你自己的着色器中适当地支持 NormalAdditive PMA blend模式:

  1. 将blend函数定义为 Blend One OneMinusSrcAlpha
  2. 将texture颜色与顶点颜色相乘
  3. 在组件中启用 Advanced - PMA Vertex Colors.

如果你想使用一个使用标准blend模式 Blend SrcAlpha OneMinusSrcAlpha 的着色器, 且不需要 Additive 槽位, 则需要确保从Spine导出的atlas textures为Straight alpha.

  • No depth buffer writing

hlsl
ZWrite Off

(Spine-Skeleton.shader:24)

典型的alpha-blended 2D sprite着色器不写depth buffer. 透明物体将根据Camera.transparencySortMode 自后向前绘制, 而不是按照depth buffer进行深度排序. Spine/Skeleton 与Unity的 Sprites/Default 着色器均有这一特性.

如果你想使用带有深度写入的着色器, 请确保在你的 SkeletonRendererSkeletonGraphic 组件中将 Advanced - Z-Spacing 置为 0 以外的值, 以防止Z-Fighting现象, 特别是当你用了光照时. 请注意, 使用depth buffer可能会在半透明区域周围造成错误效果, 比如边缘锯齿(aliasing effects on edges).

  • No lighting
    Spine/Skeleton 着色器不受放置在场景中的任何灯光的影响, 且将始终以100%的 texColor * i.vertexColor 强度(intensity)进行渲染.

为了在着色器中使用光照, 建议从一个可用的lit着色器开始修改, 来得到你所需的版本. 简单地将一行 Lighting Off 改为 Lighting On 并不能达到预期的效果, 你需要在你的顶点着色器(对于每顶点照明(per-vertex lighting))或片元着色器函数(对于每像素照明(per-pixel lighting))中计算光照, 再乘以颜色强度. 还要注意的是, URP, URP-2D和标准管线着色器的光照计算步骤均不同, 所以参考着色器代码时一定要选择对应的着色器.

  • No backface culling

hlsl
Cull Off

(Spine-Skeleton.shader:23)

渲染Spine skeleton的唯一要求是禁用backface culling, 特别是2D着色器.

因为缩小(scaled negatively)Spine网格的部件, 或是翻转skeleton时, 会导致某些三角形变得不可见, 所以大多数3D着色器都启用了backface culling.

  • No fog
    Spine/Skeleton 着色器不受fog的影响.

在着色器上启用fog需要在着色器代码中添加一些额外的顶点参数和函数调用. 例如 UnityCG.cginc 的代码:

hlsl
multi_compile_fog Will compile fog variants.
UNITY_FOG_COORDS
(texcoordindex) Declares the fog data interpolator.
UNITY_TRANSFER_FOG
(outputStruct,clipspacePos) Outputs fog data from the vertex shader.
UNITY_APPLY_FOG
(fogData,col) Applies fog to color "col". Automatically applies black fog when in forward-additive pass.
Can also use UNITY_APPLY_FOG_COLOR to supply your own fog color.

你可以查阅 Spine/Sprite/Unlit 着色器来了解如何在你的着色器中应用fog:

hlsl
#pragma multi_compile_fog

(SpritesUnlit.shader:70)

hlsl
UNITY_FOG_COORDS(1) // to declare it at the free attribute TEXCOORD1

(SpriteUnlit.cginc:L24)

hlsl
UNITY_TRANSFER_FOG(output,output.pos);

(SpriteUnlit.cginc:46)

  • 通过乘以顶点颜色来对texture进行tint
    请参见前文的Premultiply Alpha (PMA) blending.

  • 可选择使用straight alpha而非PMA textures
    由于 Spine/Skeleton 着色器的blend模式一直被设置为PMA blending, 所以没有premultiplied alpha颜色的输入texture需要在采样后被转换为PMA颜色. 下面几行代码实现了该功能:

// bool Material parameter, enables the _STRAIGHT_ALPHA_INPUT shader keyword when enabled
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
..
// compiles the shader in two variants so that shader keywords can switch between both variants
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
..
// when enabled, multiply texture rgb values by the texture alpha value.
#if defined(_STRAIGHT_ALPHA_INPUT)
texColor.rgb *= texColor.a;
#endif
  • 有一个 "ShadowCaster" pass, 因此它可以投射实时阴影
    带有 Tags { "LightMode"="ShadowCaster" } 的第二pass将被 LightMode 自动识别为阴影投射(shadow caster) pass. ShadowCaster pass不写入任何RGB颜色, 而是将深度信息写入阴影缓冲区(shadow buffer). 因此, 它必须设置为ZWrite On. 由于不能写入半透明的深度, 可以把片元写入深度缓冲区, 也可以丢弃片元(这时不投射阴影). 这是通过调用阈值(thresholding)函数完成的:

hlsl
clip(texcol.a * i.uvAndAlpha.a - _Cutoff);

(Spine-Skeleton.shader:112)

这里的 _Cutoff Material参数定义了alpha的阈值, 若 x < 0 则 clip(x) 丢弃片元.

  • Material属性:
    • _MainTex "Main Texture"
      The main texture.
    • _StraightAlphaInput "Straight Alpha Texture"
      参见上文"使用straight-alpha而非PMA textures".
    • _Cutoff "Shadow alpha cutoff"
      参见上文 "有一个 "ShadowCaster" pass, 因此它可以投射实时阴影".
    • 高级参数:
      • _StencilRef "Stencil Reference"
        用于与遮罩交互.
      • _StencilComp "Stencil Comparison"
        用于与遮罩交互, 将根据 Mask Interaction 属性由SkeletonRendererSkeletonGraphic组件来设置.
      • Outline parameters (_OutlineWidth "Outline Width", etc.)
        在切换到Spine/Outline/Skeleton类的outline着色器时使用, 一般的Spine/Skeleton这类非outline着色器中不使用.

Materials

MeshRenderer的material数组是由 SkeletonRenderer 逐帧管理, 依赖于当前所用的附件和包含这些附件的AtlasAssets.

注意: 直接修改material数组是无效的, 因为它们会再接下来的 LateUpdate() 调用中被覆盖掉, 所以应使用SkeletonRendererCustomMaterialsSkeletonGraphicCustomMaterials 组件来覆盖material. 也可以在 _Atlas 资产处配置不同的material来改变所有实例的material. 修改 _Atlas 资产之后, 都需要在 SkeletonRenderer 组件的 SkeletonData Asset 参数处点击 Reload 来重新加载使用了新的atlas materials的skeleton.

注意: Spine/SkeletonGraphic* 着色器只能在搭配 SkeletonGraphic 组件时才能正常使用materials. 切勿在 SkeletonGraphic 组件中使用URP, LWRP或如 Spine/Skeleton 这类常规着色器. 更多内容详见 SkeletonGraphic - Important Material Requirements.

Material 切换(Switching)和绘制调用

如果附件分布在多个atlas页上, 比如material A和material B, 运行时会根据需要material的绘制顺序来设置material数组.

若请求顺序为:

  1. A的附件
  2. A的附件
  3. B的附件
  4. A的附件

则生成的material数组为:

  1. Material A (满足需求1和2)
  2. Material B (满足需求3)
  3. Material A (满足需求4)

Material数组中的每一个material都对应着一次绘制调用. 因此大量的material切换会削弱性能表现.

Dragon示例展示了这种有很多绘制调用的用例:

因此, 建议将附件打包到尽可能少的atlas页中, 并根据绘制顺序将附件分组置入atlas页以防止多余的material切换. 请参阅Spine Texture Packer: Folder Structure了解如何在你的Spine atlas中编排atlas区域.

更改每个实例的Materials

注意: 直接修改SkeletonRenderer中的material数组是无效的, 因为它们会在随后的LateUpdate() 调用中被覆盖掉. 如果下面的方法都不合适你, 可以使用 SkeletonAnimation.OnMeshAndMaterialsUpdated 手动地逐帧覆盖 MeshRenderer.Materials. 这个回调会在atlas materials分配完毕之后于 LateUpdate() 回调委托结束时调用.

CustomMaterialOverride 和 CustomSlotMaterials

SkeletonRenderer 可以覆盖(override)特定槽位上的materials, 或者覆盖生成的(resulting) materials.

要在运行时替换某个实例中的SkeletonRenderer的material, 使用 SkeletonRenderer.CustomMaterialOverride 属性:

C#
// Use the following code to programmatically query the original material, see note below.
// MeshRenderer.material will not work, MeshRenderer.sharedMaterial might fail in some cases.
if (originalMaterial == null)
originalMaterial = skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial;

skeletonAnimation.CustomMaterialOverride[originalMaterial] = newMaterial; // to enable the replacement.
skeletonAnimation.CustomMaterialOverride.Remove(originalMaterial); // to disable that replacement.

注意: 请勿使用 originalMaterial = skeletonAnimation.GetComponent<MeshRenderer>().material 因为 .material 会返回一个PrimaryMaterial的实例拷贝, 而非材质本身. 同样也不建议使用 originalMaterial = skeletonAnimation.GetComponent<MeshRenderer>().sharedMaterial, 因为还没有给PrimaryMaterial赋值的时候(例如, 当活动帧尚无活动附件, 或者语句被过早调用时), 该语句可能会返回null.

要替换某个槽位上的material, 可以使用 SkeletonRenderer.CustomSlotMaterials:

C#
skeletonAnimation.CustomSlotMaterials[slot] = newMaterial; // to enable the replacement.
skeletonAnimation.CustomSlotMaterials.Remove(slot); // to disable that replacement.
Tinting Skeletons while retaining batching

在skeleton实例上使用不同的 MaterialsMaterialPropertyBlocks 会破坏batching. 如果你只需要tint某个skeleton实例且不修改其他material属性, 可以使用 Skeleton.R .G .B .A 颜色属性. 为了使tinting生效, 必须在SkeletonRenderer检查器中启用Advanced - PMA Vertex Colors.

C#
public Color color = Color.white;
...
skeleton = GetComponent<SkeletonRenderer>().Skeleton;
...
skeleton.R = color.r;
skeleton.G = color.g;
skeleton.B = color.b;
skeleton.A = color.a;

这些skeleton颜色值将置为顶点颜色且不改变material属性.

这也同样适用于tinting某个附件:

C#
slot = skeleton.FindSlot(slotname);
...
slot.R = slotColor.r;
slot.G = slotColor.g;
slot.B = slotColor.b;
slot.A = slotColor.a;

注意: 当你的动画修改附件的颜色值时, 一定要使用诸如 SkeletonAnimation.UpdateComplete 的回调来在动画应用后设置全部槽位的颜色值.

MaterialPropertyBlocks

可以使用 Renderer.SetPropertyBlock 来覆盖单个 MeshRenderer 的material属性值.

C#
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
mpb.SetColor("_FillColor", Color.red); // "_FillColor" is a named property on the used shader.
mpb.SetFloat("_FillPhase", 1.0f); // "_FillPhase" is another named property on the used shader.
GetComponent<MeshRenderer>().SetPropertyBlock(mpb);

// to deactivate the override again:
MaterialPropertyBlock mpb = this.cachedMaterialPropertyBlock; // assuming you had cached the MaterialPropertyBlock
mpb.Clear();
GetComponent<Renderer>().SetPropertyBlock(mpb);

注意:MaterialPropertyBlock 中使用的参数名, 如 _FillColor_FillPhase, 须与对应的着色器变量名匹配. 请注意, 着色器变量名不是显示在检查器中的名称, 例如 Fill ColorFill Phase. 要查看着色器的参数名, 你可以打开 .shader 文件(通过点击material的齿轮图标, 选择 Edit Shader), 查看最前面的 Properties { .. } 这段代码, 那里有全部参数的列表. 在如下这行参数中, 最左边的名字就是参数名 _FillColor:

_FillColor ("Fill Color", Color) = (1,1,1,1)
^^^^^^^^^^

着色器变量名通常以 _ 字符开头, 且从不包含任何空格. 它旁边的字符串 "Fill Color" 就是在检查器中显示的名称.

你可以在示例场景 Spine Examples/Other Examples/Per Instance Material Properties 中找到对每个实例material属性的演示.

优化提示

  • 使用具有不同Material值的Renderer. SetPropertyBlock会破坏渲染器之间的batching. 当MaterialPropertyBlock的参数相等时(例如所有的tint颜色都设置为绿色)那么渲染器间将进行batching.
  • 每当你改变或增加MaterialPropertyBlock的属性值时, 你都需要调用 SetPropertyBlock. 但你可以把MaterialPropertyBlock作为你的类的成员, 这样你就不必在改变某个属性值时不得不实例化出一个新的.
  • 当你需要经常设置某个属性时, 你可以使用静态方法: Shader.PropertyToID(string) 来缓存该属性的int ID, 而不是字符串重载MaterialPropertyBlock的setter.

透明度(Transparency)与绘制顺序

所有的spine-unity着色器都使用alpha blending来干净地绘制附件边缘的半透明过渡.如果没有alpha blending(使用了一个硬的透明度阈值), 会产生尖锐的狗牙状的轮廓, 类似于锯齿(aliasing artifacts)效果.

然而alpha blending有个老问题, 就是无法自动地对z-buffer进行深度排序.所以三角形需要按照从后到前进行渲染, 将各部件堆叠绘制. 每个 SkeletonRenderer 都会生成其对应网格, 按照Spine中定义的槽位绘制顺序绘制三角形. 一次绘制调用即可将一个网格中部件有序的skeleton正确绘制.

在网格之间, spine-unity利用Unity的许多渲染顺序系统来决定哪个网格应该在哪个上面. 按照标准的spine-unity设置, 整个skeleton网格的渲染顺序由以下多个因素决定:

  1. 相机深度(Camera depth). 与多机位的设置有关.
  2. Material.renderQueue. 当它设置后将覆盖着色器的 Queue 标签.
  3. 着色器的 Queue 标签. 与其他sprites一样, 在 Spine 着色器中默认为 "Transparent" 队列.
  4. Sorting Group组件. 当该组件置于MeshRenderer的GameObject或任何一个父GameObject上时.
  5. 渲染器的 SortingLayerlayer内SortingOrder.
  6. 相机距离(distance). 可以把相机配置为 使用平面(planar)距离(正交相机)还是透视距离(透视相机).

如果一个场景的渲染器处于相同的sorting layer, 顺序相同且着色器 Queue 标签也相同, 那么则可以通过与摄像机的距离来控制Spine GameObjects的顺序. 请注意这里的摄像机提供了 transparencySortMode 属性.

Sorting Layer 和 Order in Layer

SkeletonRenderer(或子类SkeletonAnimationSkeletonMecanim)的检查器提供了 Sorting LayerOrder in Layer 属性, 它们本质上是对 MeshRenderersortingLayerIDsortingOrder 属性的修改. 这些属性是存在 MeshRenderer 内的, 而非 SkeletonRenderer 内.

你可以通过代码访问这些属性:

C#
GetComponent<MeshRenderer>().sortingOrder = 1; // Change the Order in Layer to 1.

防止排序错误

在使用正交相机时, 特别容易出现多页atlas的skeleton排序错误的情况. 这个问题可以通过在skeleton的GameObject中添加一个 Sorting Group 组件来解决. 另一个解决方法是将摄像机请为旋转一下, 比如, 将摄像机transform的旋转Y值置为0.001.

在Skeleton部件间渲染对象

你可能想在角色的各个部分之间显示其他的GameObjects, 例如: 让你的角色爬到一棵树上, 在树干前面显示一条腿在树干后面显示另一条.

spine-unity提供了 SkeletonRenderSeparator 组件, 用于将一个skeleton分割成多个部件.

淡入和淡出Skeleton

当降低skeleton的alpha值使其半透明时, alpha blending将导致skeleton后面的部分露出来. 这是一个常见的问题, 因为每次绘制三角形时才会应用透明度.

解决该问题的一个办法是使用过渡中间件RenderTexture.

可以在正常的不透明度下将整个角色渲染到RenderTexture上, 然后按所需的淡化不透明度(fade opacity)将RenderTexture的内容绘制到场景中. 建议使用我们提供的SkeletonRenderTextureSkeletonRenderTextureFadeout两个示例组件来实现. 可以在示例场景 Spine Examples/Other Examples/RenderTexture FadeOut Transparency 中找到这两个组件的演示.

注意, 这只是实现淡出效果的多种方法之一. 可能还有其他更简单的解决方案, 比如用纯色来渐进地tinting skeleton, 或减小比例(scale)等等. 现在的2D游戏可以承担较大的开销, 因为RenderTextures是一种开销很大的方案, 在过去很少使用.

示例组件

spine-unity附带了额外的示例组件, 展示了一些高级用例的解决方案. 下文列出了一些最重要的示例组件.

SkeletonRagdoll

如果需要把一个动画skeleton变成一个类似木偶的布娃娃, 例如在角色死亡时模拟物理跌落. 这可以通过 SkeletonRagdollSkeletonRagdoll2D 示例组件来实现, 它们提供了一个简单的接口来在SkeletonRenderer (或子类 SkeletonAnimationSkeletonMecanim)上创建布娃娃物理组件.

你可以在示例场景 Spine Examples/Other Examples/SkeletonUtility Ragdoll 中可以找到对 SkeletonRagdoll2D 组件的演示.

SkeletonRenderTexture

在某些情况下, 你可能需要将skeleton渲染到RenderTexture上而非直接渲染到屏幕上. 比如可能需要调整透明度来淡出一个skeleton. 该组件可以将skeleton以适当的尺寸和分辨率渲染到RenderTexture, 并显示于匹配的边框中, 这样就可以一对一地无缝替换替换这些skeleton.

下述的SkeletonRenderTextureFadeout组件需要一个SkeletonRenderTexture或一个SkeletonGraphicRenderTexture组件. 在给你的GameObject添加SkeletonRenderTexture组件后, 可以添加SkeletonRenderTextureFadeout组件来处理淡出效果.

重要提示: 相较于直接绘制skeleton到帧缓冲区中, 使用中间件RenderTexture进行渲染是开销较大的操作. 只有在必要之处才应使用该组件. 请保持其禁用直到你真正需要显示目标特效时再启用它. 例如, 只在淡出时启用SkeletonRenderTexture组件, 而在淡出前后均保持禁用.

SkeletonGraphicRenderTexture

SkeletonGraphic版的SkeletonRenderTexture组件.

SkeletonRenderTextureFadeout

你可能想调整透明度来淡出skeleton, 但又想避免这类附件重叠问题. 该组件可以与SkeletonRenderTextureSkeletonGraphicRenderTexture组件(需预先添加)搭配使用, 将一个完全不透明的skeleton渲染到一个临时的RenderTexture上, 然后在场景中以不透明度的渐变来绘制这个RenderTexture上的内容.

SkeletonGhost

用于在角色处渲染一个运动轨迹(motion-trail)或运动模糊(motion-blur)效果来模拟速度或力量.SkeletonGhost 组件可以附加在SkeletonRenderer (或子类 SkeletonAnimationSkeletonMecanim)上, 使用可定制的material来多次绘制skeleton.

SkeletonUtilityKinematicShadow

用于需要让skeleton有些惯性或对其他skeleton的运动做出反应, 比如让披风以一种更真实的方式跟随角色运动.这可以通过 SkeletonUtilityKinematicShadow 组件来实现. 它允许铰链(hinge chains)从父transform位置的变化或无关的刚体中继承到速度矢量.

你可以在实例场景 Spine Examples/Other Examples/SkeletonUtility Animated Physics 中找到 SkeletonUtilityKinematicShadow 组件的演示.

RenderExistingMesh

若想通过在不同位置多次渲染同一个动画skeleton的副本来节省性能, 比如渲染一大群可重复的skeleton. 或者你想用URP着色器比如Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly 在选中网格后在其背后再渲染一个网格. 该组件可用于再次渲染一个已经被动画化和更新的skeleton网格, 以节省动画和计算网格的开销.

RenderCombinedMesh

当skeleton需使用多种材质时, 内附的轮廓着色器会渲染出每个子网格的轮廓而非整个skeleton的外轮廓. 使用该组件可用于合并子网格并将其作为一个整体来渲染, 从而勾勒出正确的skeleton轮廓.

场景示例

spine-unity运行时包含示例场景, 以演示重要组件和C# API在一般场景下的使用.你可以在 Spine Examples 顶层目录中找到它们.每个场景都包括一个描述文本, 可以帮助你快速探索和理解相关部分. 以下视频给出了 spine-unity 运行时中所含示例场景的概述:

若内嵌视频无法播放, 请移步该视频的Bilibili播放页观看.

如果你是第一次使用spine-unity运行时, 强烈建议查看一下 Spine Examples/Getting Started 中的示例场景.

Spine Examples / Getting Started

Spine Examples/Getting Started 中的示例场景演示了基本组件及基本用例.

1 The Spine GameObject

该场景演示了SkeletonAnimation组件和提供必要数据的SkeletonDataAsset引用 This scene demonstrates the SkeletonAnimation component and the referenced SkeletonDataAsset which provides the necessary data.

2 Controlling Animation

该场景演示了基本的动画C# API代码的使用——开始动画以及对动画事件做出响应.

当按下Play键时, Spineboy将依次播放walk、run、idle和turn(转身)等动画.脚步(Footstep)事件将触发对应的音效.

你可以检查附在spineboy GameObject上的示例脚本 SpineBeginnerTwo 组件. 它演示了 SkeletonAnimation.AnimationState.SetAnimation()SkeletonAnimation.AnimationState.AddAnimation() 的用法. 脚本 HandleEventWithAudioExample 附在 sound GameObject上, 演示了你如何通过在 SkeletonAnimation.AnimationState.Event 注册委托来回调自己的事件方法.

3 Controlling Animation Continued

该场景演示了如何通过使用多个动画轨道同时播放动画. 还展示了如何使用 AnimationReferenceAssets 来替代动画名字符串的使用.

当按下Play键时, 将开始循环播放 walk 动画.同时, 在其timeframe内还会同时播放次要动画 gungrabgunkeep.

你可以检查附在 raptor Skeleton GameObject上的示例脚本 Raptor. 它展示了如何在一个组件上暴露 AnimationReferenceAsset属性, 并通过调用 SkeletonAnimation.AnimationState.SetAnimation()方法在轨道0和1上将其作为动画来赋值.

4 Object Oriented Sample

该场景演示了如何按照Model-View-Controller的面向对象的软件设计模式来设置一个平台游戏角色. 请注意, 虽然这种设置可能不会完美适配你的项目, 但它将启发你的一些游戏设计思路, 说明如何将用户输入、游戏逻辑和可视化部分分离成不同的组件.

当按下Play键时, 你可以用WASD(移动)、空格键(跳跃)和鼠标输入(瞄准和射击)控制Spineboy角色. 另外, 你也可以通过XBOX手柄来控制它.

你可以检查附在 PLAYER INPUT GameObject上, 作为 controller 的示例脚本 SpineboyBeginnerInput. 它修改了附在 PLAYER Spineboy GameObject上代表了 modelSpineboyBeginnerModel 组件的状态. 而状态的可视化是由SpineboyBeginnerView 组件执行的, 它附在了 VIEW Spineboy GameObject上, 作为 view. 它将启动附加于同一个GameObject上的 SkeletonAnimation 组件上的各个动画.

5 Basic Platformer

该场景演示了一个带动画的典型平台游戏用例, 包含如跳、跑、下落、落地的动画, 并配有粒子和声音效果. 它还展示了在Unity中如何用Spine网格来投射阴影.

注意: 如果没有看到任何投射出的阴影, 请通过 Edit - Preferences - Quality - Shadows (Mac上是 Unity - Preferences)来启用阴影.

当按下Play键时, 你可以用WASD(移动)和空格键(跳跃)控制角色. 或者你也可以通过XBOX手柄来控制它.

可以检查附在 Player GameObject上的示例脚本 BasicPlatformerController. 它展示了如何使用Unity input来改变角色的状态, 这些状态保存在一个新建的 CharacterState 属性中. 当状态改变时, 用 SkeletonAnimationHandleExample 示例脚本来过渡到新动画, 而 HeroEffectsHandlerExample 示例脚本则用来播放声音和产生粒子系统.

6 SkeletonGraphic

该场景演示了 SkeletonGraphic组件 的使用以及如何将其集成到现有的Unity UI中. 它还展示了如何使用 BoneFollowerGraphic 组件来附加文本标签以跟随骨骼位置. 可以在 Detached BoneFollowerGraphicChild BoneFollowerGraphic GameObjects中找到它们.

当按下Play键时, 你会看到一个基于Canvas的用户界面, Doi和Spineboy被置入一个可滚动的面板中. 两者都像 SkeletonAnimation 组件一样循环播放动画, 它们同时也是用户界面的一部分.

Timeline UPM插件包

时间轴(Timeline)插件作为一个单独的UPM(Unity Package Manager)包提供. 关于如何下载和安装该包, 请参见可选UPM插件包一节; 关于如何更新UPM插件包, 请查阅更新UPM插件包. 该视频将指导你安装和使用Timeline扩展包的详细步骤.

Spine-Unity Timeline Playables

Spine Timeline目前提供三种类型的时间轴Playables:

  • AnimationState Track (用于 SkeletonAnimation 动画),
  • AnimationState Graphic Track (用于 SkeletonGraphic 动画),
  • Skeleton Flip Track (用于 SkeletonAnimationSkeletonGraphic 剪辑片段(flips)).

注意: 为提高可读性, Timeline的轨道在最新的版本中被已重新命名.
SkeletonAnimation Track 更名为 Spine AnimationState Track,
SkeletonGraphic Track 更名为 Spine AnimationState Graphic Track,
Skeleton Flip Track 更名为 Spine Skeleton Flip Track.

Limitations: 目前只支持 SkeletonAnimationSkeletonGraphic. 时间轴插件目前不支持 SkeletonMecanim.

SkeletonAnimation Track 与 SkeletonGraphic Track

这类型的轨道可用于给 SkeletonAnimationSkeletonGraphic 的AnimationState设置动画. SkeletonAnimation Track 用于 SkeletonAnimation, SkeletonGraphic Track 则用于 SkeletonGraphic.

参数

  • Track Index. 要设置动画的AnimationState轨道索引. 当使用多个时间轴轨道时, 不要忘记相应地设置这个值.

重要提示: 目前需要对时间轴轨道进行排序, 基本轨道在顶部, 叠加轨道在下方, 否则在编辑器预览时将显示错误的结果.

  • Unscaled Time. 每当在这个轨道启动新动画片段时, SkeletonAnimation.UnscaledTime(或SkeletonGraphic.UnscaledTime均)将被置为该值. 这使得你可以按常规游戏速度或无缩放的游戏速度来回放时间轴剪辑片段. 注意, PlayableDirector.UpdateMethod将被这个属性完全替代(其值将被忽略), 这样可对每个时间轴轨道进行更精细的控制.

Spine Animation State Clip

可以通过将 AnimationReferenceAsset 拖拽到时间轴轨道上, 来将 Spine Animation State Clip添加到 SkeletonAnimation Track (或 SkeletonGraphic Track)中. 关于如何给 SkeletonDataAsset 生成 AnimationReferenceAssets , 请参见SkeletonData - Preview一节.

参数
Clip Timing

  • Clip In. 播放此动画时初始本地开始时间的偏移量. 也可以通过拖动clip的左边缘来调整.
  • Blend In Duration. 启用 Use Blend Duration 且禁用 Default Mix Duration 时将使用Blend过渡时长参数. 可以通过把clip叠到前一个clip来调整, 在过渡处会出现一个交叉渐变的三角形.
  • Speed Multiplier. 播放速度的倍率. 当设置为2.0时, 将以两倍速播放动画, 当设置为0.5时, 以半速播放.

Spine Animation State Clip

  • Don't Pause with Director. 若置为true, 当Director暂停时动画将继续播放.
  • Don't End with Clip. 通常情况下, 当时间轴上的clip后面的时间轴为空时, 会在轨道上使用空动画填充. 若此参数置为"true"则继续播放clip的动画.
  • Clip End Mix Out Duration.Don't End with Clip 为false时, 如果clip后面为空白或停止, 就会按 MixDuration 设置一个空动画. 当该值小于0时, clip则会被暂停.

Mixing Setting

  • Default Mix Duration. 当禁用时, Mix Duration 值将作用于上一个动画到当前动画的过渡. 当启用时, 则使用该动画在SkeletonData资产处设置的 Mix Duration值.
  • Use Blend Duration. 当启用时, Mix Duration 值将与时间轴clip的过渡时长 'Ease In Duration' '同步. 启用此值后, 可以把clip叠到前一个clip来调整过渡时长, 这时会在过渡处会出现一个淡入淡出的三角形.
  • Mix Duration. 当禁用 Default Mix Duration 时, 这个mix时长将用于从上一个动画到当前动画的过渡.
  • Event Threshold. 参见 TrackEntry.EventThreshold.
  • Attachment Threshold. 参见 TrackEntry.AttachmentThreshold.
  • Draw Order Threshold. 参见 TrackEntry.DrawOrderThreshold.

已忽略参数

  • Ease Out Duration, Blend Curves. 该参数被忽略, 不产生任何影响.

注意: Default Mix Duration 曾用名为 Custom Duration 且老旧两个变量其值互斥. 为了提高可读性, 已将其重命名.

使用方法

  1. SkeletonAnimationPlayableHandle 组件添加到你的SkeletonAnimation GameObject, 如果是 SkeletonGraphic 则添加 SkeletonGraphicPlayableHandle.
  2. 在当前的Unity Playable Director中, 在Unity Timeline窗口中, 右击左边的一个空白区域, 选择 Spine - SkeletonAnimation Track.
  3. 把SkeletonAnimation或SkeletonGraphic GameObject拖到新的Spine AnimationState Track的空引用属性上.
  4. 要在轨道上添加动画, 可以像处理普通动画clip一样, 将各自的 AnimationReferenceAsset 拖入clips视图(时间轴面板的右侧部分).

请参考spine-unity 运行时文档的Preview一节, 了解如何为每个动画创建 AnimationReferenceAsset.

注意: 你可以使用复制功能(CTRL/CMD + D)在clips视图中复制选定的clips.

轨道行为

  • 每个clip的开头都将根据 AnimationReferenceAsset 调用 AnimationState.SetAnimation().
  • 从Timeline 4.0版本开始, Clip时长变得很关键.

注意: 3.8版认为Clip时长并不重要. 当时间轴上的clip后没有其他内容时也不会清除动画.

  • 空动画: 如果一个clip没有赋值 AnimationReferenceAsset, 它将调用 SetEmptyAnimation 作为替代.
  • 错误处理: 如果动画没有找到其 AnimationReferenceAsset, 它将不产生任何行为(前一动画将继续正常播放).
  • 在Timeline开始播放之前, 正在播放的动画将不会被打断, 直到第一个clip开始播放.
  • 在clip结束和Timeline结束时发生的事情取决于clip设置. 当 Don't End with Clip 启用时, 在clip结束不产生任何行为. 这意味着最后一个clip的 SetAnimation 调用的效果将持续到AnimationState发出其他调用. 如果 Don't End with Clip 禁用, 它将按 Clip End Mix Out Duration 设置的时长mix out到空动画, 如果 Clip End Mix Out Duration 小于0时则暂停播放.
  • Edit模式预览的mix可能看起来与Play模式的mix不同. 效果以Play模式下的结果为准. 请参阅SkeletonAnimation Track中关于预览多个重叠轨道时正确排序轨道的说明.

Skeleton Flip Track

此轨道类型可用于翻转 SkeletonAnimationSkeletonGraphic 的skeleton.

Spine Skeleton Flip Clip

参数

  • Flip X. 在clip内沿X轴翻转skeleton.
  • Flip Y. 在clip内沿Y轴翻转skeleton.

使用方法

  1. SkeletonAnimationPlayableHandle 组件添加到你的 SkeletonAnimation GameObject, 如果使用的是 SkeletonGraphic, 则添加 SkeletonGraphicPlayableHandle.
  2. 在当前的Unity Playable Director中的Unity Timeline窗口中, 右击左边的一个空白区域, 选择 Spine - Skeleton Flip Track.
  3. 将SkeletonAnimation或SkeletonGraphic GameObject拖到新的Spine Skeleton Flip Track的空引用属性上.
  4. 在Timeline dopesheet中的空行上点击右键, 选择 Add Spine Skeleton Flip Clip.
  5. 调整新clip的开始和结束时间, 在检查器顶部为它命名, 并选择所需的FlipX和FlipY值.

轨道行为

  • 指定的skeleton翻转值将用于每个轨道时长内的每一帧.
  • 在timeline结束时, 轨道将把skeleton翻转恢复到它该时间轴播放开始时记录的翻转值.

已知问题

  • 控制台可能会记录一个不正确的、无害的错误: DrivenPropertyManager has failed to register property "m_Script" of object "Spine GameObject (spineboy-pro)" with driver "" because the property doesn't exist.. 这是已知的一个Unity问题. 请在此了解更多信息:

# 常见问题 - FAQ ## 导入问题 当你把Skeleton导入Unity时遇到问题, 可能源自许多不同的原因, 从不正确的Spine导出设置到Unity中的错误设置均有可能. 以下视频将帮助你更好地了解导入过程中出现的许多常见问题应如何正确设置.

若内嵌视频无法播放, 请移步该视频的Bilibili播放页观看.

为什么导入失败, 弹出一个"无法为......自动设置AtlasAsset"的窗口?

请确保你的atlas文件的扩展名为.atlas.txt而非.atlas. 详见从Spine导出文件到Unity.

从来自git的UPM包中打开场景时, 为什么会出现 "Opening scene in read-only package!" 错误 ?

Opening scene in readonly package

如果你试图直接打开从git URL下载的包中的场景, Unity的一个bug会引发错误: "Opening scene in read-only package!". 因此应使用您系统的文件管理器(ExplorerFinder)先将包目录中的场景文件复制到你自己的Assets目录下, 再打开包中包含的示例场景.

视效问题

为什么我的附件图像的透明区域周边出现了暗框?

最可能的情况是你把你的textures导出为了 Premultiply alpha (PMA)但你在Unity中的导入Material或Texture的设置与此不匹配. 详见Premultiplied导入和Straight Alpha导入. 如果你使用的是Linear色彩空间, 请注意它不支持PMA texture.

另一种可能是, 在使用了直接alpha工作流程后, 在texture导入设置中启用了 Generate Mip Maps , 但没有对所有透明像素进行渗色(color bleed)处理. 即使从Spine导出时启用了Bleed选项, 若图像编辑器中再次修改了导出的texture, 仍可能会发生这种情况. 有些软件不能正确地在alpha为0的透明区域重新进行渗色, 而使像素的RGB部分置为黑色或白色。如果导出的texture未经修改能正确显示, 而修改后的texture却不能正确显示, 那么缺失了渗色操作可能就是症结所在.

为什么我的附件图像的透明区域出现了彩纹?

最可能的情况是你把你的textures导出为了straight alpha (Bleed)但你在Unity中的导入Material或Texture的设置与此不匹配. 详见Premultiplied导入和Straight Alpha导入.

为何在启用生成 Mip Maps后我的附件图像周围出现了白色的边框?

很可能你把你的纹理导出为 Premultiply alpha (PMA), 但你在Unity中的导入texture设置却错误地启用了 sRGB (Color Texture). 请参阅 Premultiplied导入和Straight Alpha导入进一步了解详情.

我要给MeshRenderer分配材质却失败了. 尼玛的为什么?

每一帧的Materials均由SkeletonRenderer控制, 详见Materials. 如果你需要替换某个实例或某个槽位的materials, 请阅读该节: 更改每个实例的Materials.

为什么我URP项目里某些附带的Spine着色器显示不正确?

spine-unity 运行时软件包只提供了 Built-In Render Pipeline (内置渲染管线)着色器, 不兼容于 Universal Render Pipeline (通用渲染管线)项目. 如果你使用的是通用渲染管线, 请使用 Spine URP着色器UPM插件包 中提供的 URP 着色器.

为什么我URP项目中的Outline着色器只显示轮廓?

可能的原因有二:

  1. 你在URP目中使用了内置渲染管线着色器, 如Spine/SkeletonSpine/Outline/Skeleton. 内置渲染管线着色器和URP项目并不兼容. 请换用Spine URP着色器UPM插件包中的 URP 着色器.

  2. 如果你使用的是正确的 URP 着色器Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly, 那么它只渲染了轮廓是因为它是一个单pass的着色器. 正常的加轮廓用例是, 你得用 RenderExistingMesh组件来重新渲染skeleton网格, 并调用这个只渲染轮廓的着色器在skeleton后面添加一个轮廓, 看上去就是加了轮廓的角色了.

请查阅Spine URP着色器UPM插件包一节中的示例场景com.esotericsoftware.spine.URP-shaders/Examples/Outline Shaders URP中的示例设置.

为什么轮廓着色器把轮廓渲染在多个skeleton部件的边缘?

当Skeleton需渲染多种材质时, 附带的轮廓着色器会试着渲染每个子网格而非skeleton整体. 最好的解决办法是确保skeleton只使用一种材质. 要确保这一点, 可以对图集分别打包, 或者通过运行时重打包来实现. 如果实在需要多种材质, 你可以使用RenderCombinedMesh组件重渲染组合起来的skeleton网格, 并使用仅轮廓(outline-only)着色器在skeleton后添加轮廓. 可用的仅轮廓着色器有用于内置渲染管线的Spine/Outline/OutlineOnly-ZWrite和用于 URP 的Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly两种.

怎么替换每个槽位的Materials?

详见 更改每个实例的Materials.

我要在skeleton上使用normalmap, 但这小老弟咋看起来不对劲?

请确保你在SkeletonRenderer (或 SkeletonAnimation)组件上启用了Advanced - Solve Tangents选项.

为啥用CanvasGroup alpha淡出UI面板时, SkeletonGraphic会变亮?

详见 SkeletonGraphic 组件 - CanvasGroup alpha一节.

为啥我的skeleton某些部分在alpha淡出时仍会显示?

这是个在绘制重叠的三角形还同时调整透明度时常出现的渲染问题. 详情请参见淡入或淡出Skeleton 一节.

为啥我重打包的皮肤在编辑器中能显示, 但构建的程序里显示的却是白色多边形?

详见runtime repacking一节, 内附的重要须知列出了这个问题的最常见的成因.

不同机器上的程序行为不同

我用git URLs导入的UPM包, 为啥项目在两台机器上的表现不一致?

如果你用的git URL以#4.1结尾, 那么Unity Package Manager就会从4.1分支下载最新版. 当项目第一次安装所需的包时, 它会一次性地下载所需包的最新版. 所以一台早些时间下载了这些包的机器会比现在这台刚下了包的机器上的包版本要老. 你可以在Unity Package Manager中选择该包, 然后点击Update按钮来重新下载包, 以此来更新到最新版本. 你可以把git URL中的#4.1替换为特定的git commit哈希值(如#5e8e4c21f11603ba1b72c220369d367582783744)来避免这个问题, 这样可以确保你项目中的每个人都有相同的软件包版本. 请阅读下文来了解如何获得git commit哈希值. 要注意短的哈希值如#5e8e4c21似乎无法在Unity Package Manager中被识别.

你说的这个git commit哈希值在哪儿能找到?

你可以访问 github commits 页面来获得commit哈希值, 选择需要的分支, 点击commit右侧的Copy按钮(会提示"复制完整的SHA")就能把哈希值复制到剪贴板了.

性能问题

实例化一个skeleton就搞得fps下降或触发了内存GC分配. 怎么优化?

  1. 如果你是以.json形式导出的skeleton, 可以考虑改成以.skel.bytes的形式导出二进制. 详见用于Unity的二进制导出.
  2. 如果你在游戏过程中实例化skeleton prefabs, 可以考虑在关卡加载时加载skeleton, 同时使用对象池而非实时实例化和销毁prefabs. 在非关键的关卡加载时间点上加载多个(比如10个)实例来预热对象池. 启用并重定位对象, 不要即时生成新对象; 禁用(和使用AnimationStateClearTracks)而不要去销毁它们.

我发现一个skeleton上有大量绘制调用/batches/materials. 咋回事?

你多半交替使用了多个atlas页或Slot blend模式在进行绘制. 详见MaterialsMaterial切换和绘制调用. 当多个atlas textures的皮肤或附件组合时, 可以考虑靠运行时重打包将附件重打包到单个atals texture中去(代价就是要进行一次重打包操作).

怎么提高skeleton性能?

  1. 尽量少用剪裁(clipping)多边形, 考虑用Unity的遮蔽(masking)功能来实现. 如果你需要使用剪裁附件, 在剪裁多边形上用尽可能少的顶点. 多边形面积对性能的影响不大, 只有顶点数才重要————举个例子, 一个矩形区域应该考虑用一个大三角形, 而非两个小三角形来覆盖.
  2. 尽量少用网格变形key.
  3. 尽可能少地使用顶点.
  4. 移除不必要的key, 别啥玩意儿都key.
  5. 把Spine Editor的Metrics view中显示的其他指标全给优化了.
  6. 尽可能少地使用atlas page textures.
  7. 当一个skeleton需要多种materials时, 尝试优化Spine中的绘制顺序来尽量减少materials启停次数, 或者直接优化atlas texture导出, 使绘制顺序列表中彼此相邻的附件打包到同一个atlas page texture中去. 详情参见MaterialsMaterial切换和绘制调用两节.
  8. 尽量尝试在多个skeleton之间共享一个较大的atlas texture. 你可以在不同skeleton的SkeletonDataAsset atlas 资产数组中指定同一个AtlasAsset. 详见打包. 你可以考虑按文件夹结构打包或用命令行打包打包.