# spine-unity 4.3 模块化组件升级指南 ## 组件架构重构 --- ## 📋 本指南的适用范围 **本文档仅涵盖 spine-unity 模块化组件迁移流程.** 当从 4.2 升级至 4.3 时, spine-csharp 和 spine-unity 中仍包含其他破坏性更改, 在着手模块化组件升级前应先解决这些问题: * **spine-csharp API 变更:** 全新引入的姿态(pose)系统显著地改变了访问骨骼、槽位和约束属性的方式 如需获取涵盖 4.2 至 4.3 所有变更的完整迁移步骤, 请参阅以下官方文档: * 运行时 [CHANGELOG.md](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3-beta/CHANGELOG.md#c-2) 的 C# 和 Unity 一节 * 论坛置顶帖 ["Spine-Unity 4.2 to 4.3升级指南"](https://zh.esotericsoftware.com/forum/d/29263-zh-cnspine-unity-42-to-43%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97) **重要提示:** 请务必优先完成 spine-csharp API 的迁移, 再依据本指南执行模块化组件迁移操作. --- ## ⚠️ 重要提醒: 升级前须知 **本提醒仅用于升级现有项目, 新项目可忽略本节内容.** ### 升级后将发生的变化 组件将**自动升级**并分离为相互独立的动画与渲染组件: * `SkeletonAnimation` → `SkeletonAnimation` + `SkeletonRenderer` 组件 * `SkeletonMecanim` → `SkeletonMecanim` + `SkeletonRenderer` 组件 * `SkeletonGraphic` → `SkeletonAnimation` + `SkeletonGraphic` 组件 所有组件设置与字段将被自动迁移 - 不会造成数据丢失. **但请注意:** 由于变更了组件类型, 项目脚本中对原组件的引用可能失效 (例如, `SkeletonAnimation` 不再继承自 `SkeletonRenderer`). ### 必须****依序****执行的项目升级步骤 1. **🔒 备份项目** 在升级前请完整备份项目. 升级操作将修改场景与预制件文件. 2. **📖 通读本指南全文** 在执行升级操作前, 务必充分理解全部的破坏性变更点. 3. **✏️ 测试并更新你的代码** * **更新代码:** 根据本指南修正所有引用了 Spine 组件的脚本, 由于类成员已迁移至模块化类, 项目代码将可能无法编译. * **测试:** 打开若干测试场景, 检查丢了失哪些引用. 未保存的场景仍会保留旧组件数据, 不妨将其作为安全备份——仅当你保存场景后旧组件才会被替换为模块化组件, 此时旧的组件引用便会失效. **⚠️ 警告:** 小心对待预制件(Prefab)! 在预制件编辑模式中打开预制件的话, 它会因 Unity 的自动保存机制立即触发组件迁移. 请优先在场景范围内测试, 避免直接操作预制体. * **可选操作:** 也可以一个个地手动重新分配丢失的引用, 或编写一套自动化迁移脚本(详见下文)来自动迁移组件. **⚠️ 重要提示:** 自动组件升级仅在 Unity 编辑器打开了场景或预制件时触发. 在构建项目前, 你必须选择 'Upgrade All' 或逐一保存所有场景与预制件来确保没有残留旧组件, 否则可能导致构建中缺失必要组件而失败! **💡 提示:** 若控制台输出信息中包含 "SendMessage cannot be called during Awake, CheckConsistency, or OnValidate" 字样,则表明场景中的旧资产已完成自动升级. 此消息将后附日志信息来确认旧组件已成功迁移至模块化组件. 4. **🔄 选择升级方式** **选项 A - 手动重分配 (推荐小型项目采用):** * 逐一打开各场景与预制件 * 在检查器(Inspector)中手动分配缺失的引用 * 确认无误后保存 **选项 B - 自动化迁移 (推荐含大量场景/预制件的大型项目采用):** * 首先编写迁移代码: * 实现引用迁移模式 (详见下文中的 "防止丢失组件引用" 一节) * 对于 `SkeletonGraphic` 的引用, 请见 "将已有引用迁至 SkeletonAnimation" 一节 * 在少量文件上测试迁移代码 * 使用 `Upgrade All` 按钮: * 打开 `Edit → Preferences → Spine` * 在 "Upgrade Scenes & Prefabs" 中 * 点击 `Upgrade All` 按钮 ### 若仓促升级可能会导致以下后果 * 场景或预制件中的 Spine 组件引用在保存后将丢失 (引用被置null) * 在未能升级全部场景/预制件便匆忙执行构建, 可能导致构建过程中缺失必要组件而失败 **请勿跳过步骤 1-3, 否则项目极有可能遭到破坏.** --- ## 📦 可选方案: 4.2 版的两阶段迁移 若你正从 spine-unity 4.2 升级至最新版, 建议遵循两阶段迁移方案来隔离潜在问题: ### 阶段 1: 升级至 4.3-beta 非模块化组件版本 First upgrade to a 4.3-beta version before the component split changes: * **Commit Hash**: `a07b1de` * **使用 Git URL 添加包**: `https://github.com/EsotericSoftware/spine-runtimes.git?path=spine-csharp/src#a07b1de` * 或使用 **unitypackage**: 这一阶段可以: * 优先解决 4.2 → 4.3 spine-csharp API 变更问题, 同时确保项目正常 * 在升级模块化组件前验证项目稳定性 * 确认项目在 4.3-beta 下稳定后, 有机会再次备份完整项目, 为阶段 2的迁移工作作出充分准备 ### 阶段 2: 升级至最新的 4.3-beta 模块化组件版本 当项目能够在 4.3-beta 非模块化组件版下稳定工作后: * Up升级至最新 4.3-beta 包 * 按照本指南所述执行模块化组件迁移 * 处理独立的动画与渲染组件 这种两阶段策略可有效隔离问题源头: 项目出现故障时, 可以明确地判断出是 4.2 → 4.3 的 spine-csharp API 变更导致的, 还是模块化组件所致. --- ## 📋 简介 spine-unity 4.3 运行时引入了重大架构变更: **相互独立的动画与渲染组件取代原有的继承结构**. 该设计让用户可灵活地选用组件, 例如在项目中让 `SkeletonMecanim` 负责控制动画, 同时采用 `SkeletonGraphic` 执行渲染——这是此前无法实现的. ### 关键更改 * **模块化组件**: 旧组件被拆分为了相互独立的渲染与动画组件. * **独立组件架构**: `SkeletonAnimation` 和 `SkeletonMecanim` 不再继承自 `SkeletonRenderer`, 而是与独立渲染器组件(`ISkeletonRenderer` 的子类)协同工作, 例如 `SkeletonRenderer` 和 `SkeletonGraphic`. * **接口更新**: 新增了命名统一的 `ISkeletonRenderer` 和 `ISkeletonAnimation` 接口. * **设置分组**: 网格生成的相关设置集中到了 `MeshSettings` 属性中. * **自动化迁移**: 当启用 `AUTO_UPGRADE_TO_43_COMPONENTS` (*此版本默认启用*)后, Unity 编辑器将自动升级组件并迁移已废弃的字段. * **升级全部场景和预制件**: 如需一键升级全部场景与预制件, 请打开 `Edit - Preferences - Spine`, 并在 `Automatic Component Upgrade` 中点击 `Upgrade Scenes & Prefabs` - `Upgrade All` 即可. ### 各组件关系 | **旧架构** | **新架构** | |---------------------|---------------------| | `SkeletonAnimation` 继承自 `SkeletonRenderer` | `SkeletonAnimation` + 独立的 `SkeletonRenderer` 组件 | | `SkeletonMecanim` 继承自 `SkeletonRenderer` | `SkeletonMecanim` + 独立的 `SkeletonRenderer` 组件 | | `SkeletonGraphic` 内置 AnimationState | `SkeletonGraphic` + 独立的 `SkeletonAnimation` 组件 | --- ## 适配你的代码 以下为各组件的具体变更说明: ## ▶️ SkeletonRenderer ### 破坏性变更 #### 1. 接口变更 * 接口 `IHasSkeletonRenderer` 中的属性名 `SkeletonRenderer` 改为 `Renderer`, 类型由 `SkeletonRenderer` 改为 `ISkeletonRenderer`. #### 2. 事件变更 * `SkeletonRendererDelegate` 类型不再为 `SkeletonRenderer` 的嵌套类型. 如需修复编译错误, 只需将 `SkeletonRenderer.SkeletonRendererDelegate` 替换为 `SkeletonRendererDelegate`. * `BeforeApply` 委托类型由 `SkeletonRendererDelegate` 更改为 `SkeletonAnimationDelegate`. * `SkeletonRendererDelegate` 委托签名由 `SkeletonRendererDelegate(SkeletonRenderer)` 更改为 `SkeletonRendererDelegate(ISkeletonRenderer)`. 该更改会影响以下事件: `OnRebuild`, `OnMeshAndMaterialsUpdated`. 如需修复编译错误, 只需将方法实参从 `SkeletonRenderer` 更改为 `ISkeletonRenderer`. * 骨骼事件 `UpdateLocal`, `UpdateWorld`, 和 `UpdateComplete` 从 `ISkeletonAnimation` 类 (`SkeletonAnimation`, `SkeletonMecanim`) 迁移至 `ISkeletonRenderer (SkeletonRenderer, SkeletonGraphic)` 类中. 委托类型由 `UpdateBonesDelegate` 改为 `SkeletonRendererDelegate`, 参数由 `ISkeletonRenderer` 改为了 `ISkeletonAnimation`. #### 2. 方法变更 * `LateUpdateMesh()` 改为了 `UpdateMesh()`. * 移除了 `MeshGenerator.TryReplaceMaterials`. #### 3. 执行顺序 * `SkeletonRenderer` 和 `SkeletonGraphic` 组件接受 `DefaultExecutionOrder(1)]` 参数, 以确保其在默认脚本*(即 order=0)*之后执行. 此举保证了即使 `UpdateTiming` 置为 `InLateUpdate`, 动画仍能先于 skeleton 更新. #### 4. 行为变更 * 在启用 `singleSubmesh` 后, 现在也会调用 `generateMeshOverride`. ### 字段与属性迁移 #### 网格生成器设置 | **旧 API** | **新 API** | **说明** | |-------------------|-------------------|-----------| | `skeletonRenderer.zSpacing` | `skeletonRenderer.MeshSettings.zSpacing` | 已迁移至 MeshSettings | | `skeletonRenderer.useClipping` | `skeletonRenderer.MeshSettings.useClipping` | 已迁移至 MeshSettings | | `skeletonRenderer.immutableTriangles` | `skeletonRenderer.MeshSettings.immutableTriangles` | 已迁移至 MeshSettings | | `skeletonRenderer.pmaVertexColors` | `skeletonRenderer.MeshSettings.pmaVertexColors` | 已迁移至 MeshSettings | | `skeletonRenderer.tintBlack` | `skeletonRenderer.MeshSettings.tintBlack` | 已迁移至 MeshSettings | | `skeletonRenderer.addNormals` | `skeletonRenderer.MeshSettings.addNormals` | 已迁移至 MeshSettings | | `skeletonRenderer.calculateTangents` | `skeletonRenderer.MeshSettings.calculateTangents` | 已迁移至 MeshSettings | #### 已更改的小写属性 * `skeletonRenderer.maskInteraction` 改为了 `skeletonRenderer.MaskInteraction`. #### 已弃用的小写属性 * 小写属性 `initialFlipX`, `initialFlipY` 和 `initialSkinName` 已被弃用并将于后续版本中移除. 请转用同名大写属性 `InitialFlipX`, `InitialFlipY` 和 `InitialSkinName`. --- ## ▶️ SkeletonAnimation ### 破坏性变更 #### 1. 组件架构 * **SkeletonAnimation 现为独立于 SkeletonRenderer 的组件**, 不再继承自 SkeletonRenderer. * 如需访问渲染器: `skeletonAnimation.Renderer`. * 如需通过渲染器访问动画: `skeletonRenderer.Animation`. #### 2. 属性变更 * `state` 不再是公开属性. 请转用 `AnimationState` 属性. * 移除了 `valid`. 请转用 `IsValid` 属性. #### 3. 方法变更 * 新增 `UpdateOncePerFrame()` - 仅在该帧未调用 `Update` 时执行更新. 已有的 `Update(float time)` 保持行为不变. * 不再公开无参 `Update()` 方法, 如需仅在该帧无更新时执行更新, 请使用 `UpdateOncePerFrame()`, 而 `Update(0)` 可用于强制执行一次动画更新. ### 字段与属性迁移 由于 `SkeletonAnimation` 现已成为独立组件, 曾由 `SkeletonRenderer` 提供的方法与属性将无法直接通过 `SkeletonAnimation` 实例访问. 请通过 `SkeletonAnimation` 实例的 `ISkeletonRenderer` 访问渲染器属性 `skeletonAnimation.Renderer`, 而 `skeletonRenderer.Animation` 属性则可通过 `SkeletonRenderer` 或 `SkeletonGraphic` 实例中的 `ISkeletonAnimation` 访问. 对于未在 `ISkeletonRenderer` 接口中暴露的成员, 可将 `skeletonAnimation.Renderer` 显式转换为 `SkeletonRenderer` 或 `SkeletonGraphic` 以访问渲染器成员变量. ### 示例代码 ```csharp // 升级前: 直接访问 SkeletonRenderer 属性 skeletonAnimation.zSpacing = 0.1f; skeletonAnimation.AnySkeletonRendererProperty; // 升级后: 通过 Renderer 对象访问 skeletonAnimation.Renderer.MeshSettings.zSpacing = 0.1f; // 访问 ISkeletonRenderer 接口暴露出的成员 var skeletonRenderer = (SkeletonRenderer)skeletonAnimation.Renderer; skeletonRenderer.AnySkeletonRendererProperty; // 访问 ISkeletonRenderer 接口未暴露的成员 ``` --- ## ▶️ SkeletonMecanim ### 破坏性变更 #### 1. 组件架构 * **SkeletonMecanim 现为独立于 SkeletonRenderer 的组件**, 不再继承自 SkeletonRenderer. * 访问方式同 [SkeletonAnimation](#▶️-skeletonanimation). #### 2. 方法变更 * 不再公开 `Update()`. * 请改用 `UpdateIfNecessary()`, 如需强制更新请使用 `Update(0)`. ### 字段与属性迁移 与上文中的 [SkeletonAnimation](#▶️-skeletonanimation) 相同 --- ## ▶️ SkeletonGraphic ### 破坏性变更 #### 1. 组件架构 * **SkeletonGraphic 不再涉及动画逻辑, 请额外添加 SkeletonAnimation 组件以使用动画**. * 如需访问动画: `skeletonGraphic.Animation`. #### 2. 将已有引用迁至 SkeletonAnimation 若你组件仅通过引用 `SkeletonGraphic` 来修改其动画属性, 那么建议将引用类型更改为 `SkeletonAnimation`. 此举便可直接访问动画状态而无需像 `((SkeletonAnimation)skeletonGraphic.Animation).AnimationState` 这样进行显式转换, 例如: `skeletonAnimation.AnimationState`. 请注意, 若希望重命名序列化组件变量, 可在变量声明前添加 `[FormerlySerializedAs("previousName")]` 特性, Unity 将自动将原字段中保存的序列化值迁移至新变量, 从而保留你在场景或预制件中的已有配置. #### 示例代码 ```csharp // Automatically reassign previous serialized values [FormerlySerializedAs("skeletonGraphic")] public SkeletonAnimation skeletonAnimation; // Will maintain the reference after upgrade ``` #### 3. 属性变更 * 已移除属性 `AnimationState` - 请通过 `SkeletonAnimation` 组件查询该属性. * 不再公开 `MeshGenerator` - 请改用 `MeshSettings` 属性和 `SetMeshSettings()` 方法. * `MaterialsMultipleCanvasRenderers` 类型由 `ExposedList` 改为 `Material[]`. #### 4. 创建辅助器(Creation Helper)方法 * `NewSkeletonGraphicGameObject` 现在默认同时创建 `SkeletonGraphic` 和 `SkeletonAnimation`, 以对齐原有行为. 返回类型改为了一个包含了这两个组件引用的结构体. * 已移除 `AddSkeletonGraphicComponent`, 可用替换为: * `AddSkeletonGraphicAnimationComponents` - 同时创建 `SkeletonGraphic` 和 `SkeletonAnimation` 组件. * `AddSkeletonGraphicRenderingComponent` - 仅创建 `SkeletonGraphic` 组件. #### 5. 事件更改 * 委托签名从 `SkeletonRendererDelegate(SkeletonGraphic)` 改为 `SkeletonRendererDelegate(ISkeletonRenderer)`. 该更改会影响以下事件: `OnRebuild`, `OnMeshAndMaterialsUpdated`. 如需修复编译错误, 只需将方法实参从 `SkeletonGraphic` 更改为 `ISkeletonRenderer`. #### 6. 执行顺序 * `SkeletonRenderer` 和 `SkeletonGraphic` 组件现可接收 `DefaultExecutionOrder(1)]` 参数,, 以确保其在默认脚本*(即 order=0)*之后执行. 此举保证了即使 `UpdateTiming` 置为 `InLateUpdate`, 动画仍能先于 skeleton 更新. #### 7. 行为变更 - Material 更新 * 各 `CanvasRenderer` 的 material 不再于每个 `LateUpdate` 中更新, 而仅在以下情况时更新: * a) 当 skeleton 更新需更换 materials, 或者 * b) 代码访问了 `CustomMaterialOverride` 或 `CustomTextureOverride` 并可能修改它们. ### 字段与属性迁移 #### A动画属性 | **旧 API** | **新 API** | **说明** | |-------------------|-------------------|-----------| | `skeletonGraphic.AnimationState` | `((SkeletonAnimation)skeletonGraphic.Animation).AnimationState` | 需显式转换 | | `skeletonGraphic.startingAnimation` | `skeletonAnimation.AnimationName` | 通过 Animation 组件访问 | | `skeletonGraphic.startingLoop` | `skeletonAnimation.loop` | 通过 Animation 组件访问 | | `skeletonGraphic.timeScale` | `skeletonAnimation.timeScale` | 通过 Animation 组件访问 | | `skeletonGraphic.unscaledTime` | `skeletonAnimation.unscaledTime` | 通过 Animation 组件访问 | #### 网格生成器设置 | **旧 API** | **新 API** | **说明** | |-------------------|-------------------|-----------| | `skeletonGraphic.MeshGenerator.settings` | `skeletonGraphic.MeshSettings` | 直接访问设置 | --- ## ⚠️ 其他重要注意事项 ### 防止丢失组件引用 其他组件(例如 `SkeletonRenderSeparator`)若持有对 `SkeletonRenderer` 的引用, 且其目标引用曾为 `SkeletonAnimation` 或 `SkeletonMecanim`, 则升级后该引用将被置为 *null*. 这是由于 `SkeletonAnimation` 与 `SkeletonMecanim` 不再继承自 `SkeletonRenderer`, 因而不再构成有效的类型引用. 对此, 手动解决方案是直接不管, 变量类型保持 `SkeletonRenderer` 不变, 任凭 `SkeletonAnimation` 的引用被置为 *none*. 随后再在各场景与预制件中手动分配丢失的引用. 而半自动方案则是: 将项目中 `SkeletonRenderer` 变量的类型改为 `Component` (保持对象引用避免丢失目标), 新增一个类型为 `SkeletonRenderer`(或 `SkeletonAnimation`)的变量,然后在代码中从 `Component` 类型变量读取目标对象, 并将其赋给新变量. 例如, 在 Unity 编辑器中给组件添加 [ExecuteAlways] 特性标记, 并在 `Awake()` 方法中自动执行此操作. #### 示例代码 ```csharp // 升级前的类代码 public class TestMigrateReferences : MonoBehaviour { public SkeletonRenderer skeletonRenderer; // 赋给这个 SkeletonAnimation 的引用将在升级后丢失. public void Foo () { skeletonRenderer.skeleton.ScaleX = -1; } } // 升级后的类代码 [ExecuteAlways] // 或者用 [ExecuteInEditMode] public class TestMigrateReferences : MonoBehaviour { #if UNITY_EDITOR [SerializeField, HideInInspector, FormerlySerializedAs("skeletonRenderer")] Component previousSkeletonRenderer; // 如此便能捕获原名为 skeletonRenderer 的 `SkeletonAnimation` 引用. #endif public SkeletonRenderer skeletonRenderer; public void Foo () { skeletonRenderer.skeleton.ScaleX = -1; } #if UNITY_EDITOR public void Awake () { AutoUpgradeReferences(); } public void AutoUpgradeReferences () { if (previousSkeletonRenderer != null && skeletonRenderer == null) { skeletonRenderer = previousSkeletonRenderer.GetComponent(); if (skeletonRenderer != null) Debug.Log("Upgraded SkeletonRenderer reference."); } } #endif } ``` ### 组件的启用/禁用 由于 `ISkeletonRenderer` 和 `ISkeletonAnimation` 组件现已一分为二, 任何控制组件启用/禁用的脚本都必须**同时启用/禁用这两个组件**. ### SkeletonUtilityBone 行为更改 在 Override 模式下, `SkeletonUtilityBone` 仅在 `UpdatePhase.Complete` 阶段调整变换(Transform), 不再于 `UpdatePhase.World` 阶段重复更新, 以减少冗余操作. ### 自动化迁移 * 当启用了 `AUTO_UPGRADE_TO_43_COMPONENTS` 后, Unity 编辑器将自动迁移废弃字段. * 如需一键升级全部场景与预制件, 请打开 `Edit - Preferences - Spine`, 并在 `Automatic Component Upgrade` 中点击 `Upgrade Scenes & Prefabs` - `Upgrade All` 即可. * 各类中的 `UpgradeTo43` 和 `TransferDeprecatedFields()` 方法负责其各自的序列化数据的迁移. * 运行时访问仍需手动更新代码. ### 常见变更总结 1. **添加 `.Renderer.`** 前缀便可从动画组件访问渲染属性. 2. **添加 `.MeshSettings.`** 前缀可以访问网格生成设置. 3. 从 SkeletonGraphic 访问 AnimationState 时, 需 **显式类型转换为 SkeletonAnimation**. 4. **更新委托方法签名**: 签名中的具体类型应改为接口类型. 5. 使用上述迁移模式升级后要**重新绑定丢失的引用**. --- ## 禁用自动升级检查 当完成了 Spine 资源、场景和预制件的全部迁移工作后, 可禁用自动升级检查来提升编辑器性能: 1. 转到 `Edit → Preferences → Spine` 2. 在 `Automatic Component Upgrade` 中点击 `Split Component Upgrade` → `Disable` 此操作将停止 Unity 编辑器在加载场景或预制件时的迁移检测. 如有新资源需要迁移, 可随时重启此功能. --- ## 需要帮助? 若在迁移过程中遇到棘手问题, 或发现组件迁移有误, 请前往 [Spine 论坛](https://esotericsoftware.com/forum) 发帖反馈. 我们将全力协助,确保自动迁移过程尽可能顺畅无痛.