# spine-unity 4.3 分离组件升级指南 ## 组件架构重构 --- ## 📋 本文档范围 **本文档仅涵盖 spine-unity 组件分离迁移。** 从 4.2 版本到 4.3 版本,spine-csharp 和 spine-unity 中还有其他破坏性更改,您需要在处理组件分离之前解决这些问题: - **spine-csharp API 更改:** 影响骨骼、插槽和约束属性的新姿势系统 有关涵盖所有 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 Upgrade Guide"](https://esotericsoftware.com/forum) 以获取全面的迁移指南 **重要:** 首先完成 spine-csharp API 迁移,然后继续执行本文档中描述的组件分离迁移。 --- ## ⚠️ 重要提示:升级前必读 **此警告仅适用于正在升级的现有项目。新项目可以安全地忽略此部分。** ### 将会发生什么 组件将**自动升级**并分离为独立的动画和渲染组件: - `SkeletonAnimation` → `SkeletonAnimation` + `SkeletonRenderer` 组件 - `SkeletonMecanim` → `SkeletonMecanim` + `SkeletonRenderer` 组件 - `SkeletonGraphic` → `SkeletonAnimation` + `SkeletonGraphic` 组件 所有组件设置和字段将自动转移 - 不会丢失任何内容。 **但是:** 由于这些类型更改,您自定义脚本中的现有引用可能会丢失,因为组件类型不再匹配(例如,`SkeletonAnimation` 不再是 `SkeletonRenderer` 的子类)。 ### 必需的升级步骤(按顺序): 1. **🔒 备份您的项目** 在升级之前创建完整备份。这些更改将修改您的场景和预制件。 2. **📖 阅读整个文档** 在继续之前了解所有破坏性更改。 3. **✏️ 测试并更新您的代码** - **更新您的脚本:** 根据本文档修复引用 Spine 组件的自定义脚本,因为当成员被移动到分离的类时,您的代码可能无法编译 - **测试:** 打开一些测试场景以查看哪些组件引用丢失。未保存的场景仍然包含旧的组件数据并作为安全备份 - 只有当您保存时,分离的组件才会替换旧组件并且引用会丢失。 **⚠️ 警告:** 预制件的行为不同!在 Prefab Edit Mode 中打开预制件会由于 Unity 的预制件自动保存功能而自动保存组件迁移。请始终先用场景进行测试,而不是预制件。 - **决定:** 要么手动重新分配丢失的引用,要么编写迁移代码(下面说明)来自动处理 **⚠️ 重要:** 自动组件分离仅在 Unity 编辑器中打开场景/预制件时发生。您必须选择 `Upgrade All` 或保存每个场景/预制件后再构建游戏,否则旧的单一组件不会被分离,构建中可能会缺少一半所需的组件! **💡 提示:** 如果您遇到控制台日志输出 "SendMessage cannot be called during Awake, CheckConsistency, or OnValidate",这表明场景中存在旧的未迁移资产,它们刚刚被自动升级。这些消息之后会跟随一条日志消息,确认旧组件已自动迁移为分离组件。 4. **🔄 选择您的升级路径** **选项 A - 手动重新分配(推荐用于小型项目):** - 逐个打开每个场景和预制件 - 在检查器中手动重新分配丢失的引用 - 满意后保存 **选项 B - 自动迁移(用于具有许多场景/预制件的大型项目):** - 首先编写迁移代码: - 实现引用迁移模式(参见下面的"防止丢失组件引用"部分) - 对于 `SkeletonGraphic` 引用,请参见"将现有引用更改为 SkeletonAnimation"部分 - 在几个文件上测试迁移代码 - 然后使用 `Upgrade All`: - 转到 `Edit → Preferences → Spine` - 在 `Upgrade Scenes & Prefabs` 下 - 点击 `Upgrade All` 按钮 ### 如果您不准备会发生什么破坏: - 一旦您保存场景或预制件,场景或预制件中的组件引用可能会丢失(设置为 null) - 在升级所有场景/预制件之前构建将导致构建中缺少组件 **在完成步骤 1-3 之前不要继续,否则您可能会破坏您的项目。** --- ## 📦 从 4.2 的可选两步迁移 如果您正在从 spine-unity 4.2 迁移,您可能会发现分两步升级更容易隔离潜在问题: ### 步骤 1:升级到 4.3-beta 预分离版本 首先升级到组件分离更改之前的 4.3-beta 版本: - **提交哈希**: `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 正常工作: - 升级到最新的 4.3-beta 包 - 按照上面的组件分离迁移指南 - 处理分离的动画和渲染组件 这种两步方法有助于隔离问题 - 如果出现问题,您将知道是由于 4.2 → 4.3 spine-csharp 更改还是组件分离特定导致的。 --- ## 📋 介绍 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`,动画也会在骨架更新之前应用。 #### 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 | #### 已弃用的小写属性 - 小写属性 `initialFlipX`、`initialFlipY` 和 `initialSkinName` 现已弃用,将在未来的运行时版本中删除。使用添加的相同名称但大写的属性:`InitialFlipX`、`InitialFlipY` 和 `InitialSkinName`。 --- ## ▶️ SkeletonAnimation ### 破坏性更改 #### 1. 组件架构 - **SkeletonAnimation 现在是与 SkeletonRenderer 分离的组件**,不再是子类。 - 通过以下方式访问渲染器:`skeletonAnimation.Renderer`。 - 从渲染器访问动画:`skeletonRenderer.Animation`。 #### 2. 属性更改 - `state` 不再是公共的。使用 `AnimationState` 属性代替。 - `valid` 已移除。使用 `IsValid` 代替。 #### 3. 方法更改 - 添加 `UpdateOncePerFrame()` - 如果本帧尚未调用 `Update` 则更新。现有的 `Update(float time)` 仍然在调用时始终更新。 - 不带参数的 `Update()` 不再是公共的,使用 `UpdateOncePerFrame()` 在本帧未执行更新时更新, 或使用 `Update(0)` 强制动画更新。 ### 字段和属性迁移 由于 `SkeletonAnimation` 现在是一个独立的组件,`SkeletonRenderer` 提供的方法和属性不能再通过 `SkeletonAnimation` 对象直接访问。有一个 `skeletonAnimation.Renderer` 属性可用于从 `SkeletonAnimation` 对象访问关联的 `ISkeletonRenderer`, 以及一个 `skeletonRenderer.Animation` 属性从 `SkeletonRenderer` 或 `SkeletonGraphic` 对象访问关联的 `ISkeletonAnimation`。对于 `ISkeletonRenderer` 接口未公开的成员,您可以将 `skeletonAnimation.Renderer` 转换为 `SkeletonRenderer` 或 `SkeletonGraphic` 以访问渲染器成员变量。 ### 示例代码 ```csharp // 旧的从 SkeletonAnimation 访问 SkeletonRenderer 属性 skeletonAnimation.zSpacing = 0.1f; skeletonAnimation.AnySkeletonRendererProperty; // 新的从 SkeletonAnimation 访问 SkeletonRenderer 属性 skeletonAnimation.Renderer.MeshSettings.zSpacing = 0.1f; // 在 ISkeletonRenderer 接口中公开 var skeletonRenderer = (SkeletonRenderer)skeletonAnimation.Renderer; skeletonRenderer.AnySkeletonRendererProperty; // 未在 ISkeletonRenderer 接口中公开 ``` --- ## ▶️ SkeletonMecanim ### 破坏性更改 #### 1. 组件架构 - **SkeletonMecanim 现在是与 SkeletonRenderer 分离的组件**,不再是子类。 - 与 [SkeletonAnimation](#▶️-skeletonanimation) 相同的访问模式。 #### 2. 方法更改 - `Update()` 不再是公共的。 - 使用 `UpdateIfNecessary()` 或 `Update(0)` 强制更新。 ### 字段和属性迁移 与上面的 [SkeletonAnimation](#▶️-skeletonanimation) 相同。 --- ## ▶️ SkeletonGraphic ### 破坏性更改 #### 1. 组件架构 - **SkeletonGraphic 不再涵盖动画,添加 SkeletonAnimation 作为独立的动画组件**。 - 通过以下方式访问动画:`skeletonGraphic.Animation`。 #### 2. 将现有引用更改为 SkeletonAnimation 如果您的组件仅为了修改其动画属性而持有对 `SkeletonGraphic` 的引用,建议将引用类型更改为 `SkeletonAnimation`。 这样您可以像 `skeletonAnimation.AnimationState` 一样访问动画状态 而不必像 `((SkeletonAnimation)skeletonGraphic.Animation).AnimationState` 那样进行转换。请注意,如果您想更改序列化组件变量的名称,可以在变量定义前使用 `[FormerlySerializedAs("previousName")]` 属性来自动重新分配您现有的序列化值。 #### 示例代码 ```csharp // 自动重新分配先前的序列化值 [FormerlySerializedAs("skeletonGraphic")] public SkeletonAnimation skeletonAnimation; // 升级后将保持引用 ``` #### 3. 属性更改 - 移除属性 `AnimationState` - 改为从 `SkeletonAnimation` 组件查询。 - `MeshGenerator` 不再是公共的 - 使用 `MeshSettings` 属性和 `SetMeshSettings()` 代替。 - `MaterialsMultipleCanvasRenderers` 类型从 `ExposedList` 更改为 `Material[]`。 #### 4. 创建辅助方法 - `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`,动画也会在骨架更新之前应用。 #### 7. 行为更改 - 材质更新 - 每个 `CanvasRenderer` 的材质不再在每个 `LateUpdate` 中更新,而是在以下情况下更新: - a) 更新的骨架需要更改材质,或 - b) 当 `CustomMaterialOverride` 或 `CustomTextureOverride` 被访问并因此可能被修改时。 ### 字段和属性迁移 #### 动画属性 | **旧 API** | **新 API** | **备注** | |-------------------|-------------------|-----------| | `skeletonGraphic.AnimationState` | `((SkeletonAnimation)skeletonGraphic.Animation).AnimationState` | 需要转换 | | `skeletonGraphic.startingAnimation` | `skeletonAnimation.AnimationName` | 通过动画组件 | | `skeletonGraphic.startingLoop` | `skeletonAnimation.loop` | 通过动画组件 | | `skeletonGraphic.timeScale` | `skeletonAnimation.timeScale` | 通过动画组件 | | `skeletonGraphic.unscaledTime` | `skeletonAnimation.unscaledTime` | 通过动画组件 | #### 网格生成器设置 | **旧 API** | **新 API** | **备注** | |-------------------|-------------------|-----------| | `skeletonGraphic.MeshGenerator.settings` | `skeletonGraphic.MeshSettings` | 直接访问设置 | --- ## ⚠️ 其他重要说明 ### 防止丢失组件引用 任何引用 `SkeletonRenderer` 组件并具有 `SkeletonAnimation` 或 `SkeletonMecanim` 目标的其他组件(例如 `SkeletonRenderSeparator`)在升级后将指向 *null*,因为 `SkeletonAnimation` 和 `SkeletonMecanim` 组件不再是 `SkeletonRenderer` 的子类,因此不是有效的引用。手动解决方案是将类型保留为 `SkeletonRenderer` 并丢失对 `SkeletonAnimation` 的引用(将设置为 *none*)。然后您需要在场景和预制件中手动重新分配丢失的引用。半自动替代解决方案如下:将您的 `SkeletonRenderer` 变量的类型更改为 `Component`(以捕获对象引用而不丢失它)并添加类型为 `SkeletonRenderer`(或 `SkeletonAnimation`)的第二个变量,然后以编程方式从 `Component` 变量读取它并将其分配给新添加的变量。例如,您可以在 Unity 编辑器中的 `Awake()` 中自动执行此操作,并向您的组件添加 `[ExecuteAlways]` 标签。 #### 示例代码 ```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.World` 中调整 Transform,仅在 `UpdatePhase.Complete` 中(移除冗余更新)。 ### 自动迁移 - 当定义了 `AUTO_UPGRADE_TO_43_COMPONENTS` 时,Unity 编辑器会自动转移已弃用的字段。 - 要一次升级所有场景和预制件,请转到 `Edit - Preferences - Spine` 并选择 `Upgrade Scenes & Prefabs` - `Upgrade All`。 - 每个类中的 `UpgradeTo43` 和 `TransferDeprecatedFields()` 方法处理序列化数据迁移。 - 运行时访问仍需要手动代码更新。 ### 最常见更改的摘要 1. **添加 `.Renderer.`** 前缀以从动画组件访问渲染属性。 2. **添加 `.MeshSettings.`** 以访问网格生成设置。 3. **转换为 SkeletonAnimation** 当从 SkeletonGraphic 访问 AnimationState 时。 4. **更新委托方法签名** 从具体类型到接口。 5. **重新分配丢失的引用** 使用上述迁移模式升级后。 --- ## 禁用自动升级检查 一旦您完成了所有 Spine 资产、场景和预制件的迁移,您可以禁用自动升级检查以提高编辑器性能: 1. 转到 `Edit → Preferences → Spine` 2. 在 `Automatic Component Upgrade` 下,点击 `Split Component Upgrade` → `Disable` 这将阻止 Unity 编辑器在场景或预制件加载时执行编辑器内检查以确定组件是否需要迁移。如果您需要迁移其他资产,您可以随时重新启用它。 --- ## 需要帮助? 如果您在迁移期间遇到任何意外问题或发现组件属性迁移不正确,请在 [Spine 论坛](https://esotericsoftware.com/forum) 发帖。我们很乐意提供帮助并修复任何问题,使自动迁移尽可能无痛。