spine-runtimes/spine-unity/Assets/Spine/Documentation/4.3-split-component-upgrade-guide-zh.md

419 lines
20 KiB
Markdown

# 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**: <https://esotericsoftware.com/files/runtimes/unity/spine-unity-4.2-2025-09-26.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>` 改为 `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<SkeletonRenderer>();
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) 发帖反馈. 我们将全力协助,确保自动迁移过程尽可能顺畅无痛.