“Unity 高版本2021的 Prefab(UI界面)导入Unity低版本5.6文件格式不兼容UI丢失”问题解决
Unity 的 Prefab(预制体)文件格式不兼容 。
原因分析
Unity 在 2018.3 版本中引入了 Nested Prefabs(嵌套预制体) 系统,这彻底改变了 Prefab 文件的底层 YAML 数据结构。
- Unity 2021 保存的 Prefab 使用了新版格式。
- Unity 5.6 无法识别新版格式的字段,因此导入后会导致物体丢失、Transform 数据错乱或脚本引用丢失。这是 向前不兼容 的(旧版本无法打开新版本文件)。
解决方案
由于无法直接降级 Prefab,两种选择:
1. 手动重建(最稳妥) :在 Unity 5.6 中参照原效果重新搭建 UI。
2. 使用脚本迁移(推荐) :可以在 2021 中将 UI 结构导出为 JSON 数据,然后在 5.6 中一键重建。
操作步骤:
1. 导出 :在 Unity 2021 中,选中 Canvas 下的根节点 UI 物体,点击菜单 Tools -> Export UI Structure ,保存 JSON 文件。
2. 导入 :将 JSON 文件复制到 Unity 5.6 项目中。点击菜单 Tools -> Import UI Structure ,选择该 JSON 文件,即可自动生成 UI 结构。
代码实现
using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.Collections.Generic; using System.IO; public class UITransferTool : EditorWindow { [MenuItem("Tools/Export UI Structure (Select GameObject)")] public static void ExportUI() { GameObject go = Selection.activeGameObject; if (go == null) { Debug.LogError("请先选择一个 UI 根节点物体!"); return; } UIDataNode rootNode = ExtractNode(go.transform); string json = JsonUtility.ToJson(rootNode, true); string path = EditorUtility.SaveFilePanel("Save UI Structure", "", go.name + "_UI_Structure", "json"); if (!string.IsNullOrEmpty(path)) { File.WriteAllText(path, json); Debug.Log("UI 结构导出成功: " + path); } } [MenuItem("Tools/Import UI Structure (Select JSON)")] public static void ImportUI() { string path = EditorUtility.OpenFilePanel("Select UI Structure JSON", "", "json"); if (string.IsNullOrEmpty(path)) return; string json = File.ReadAllText(path); UIDataNode rootNode = JsonUtility.FromJson<UIDataNode>(json); if (rootNode != null) { // 查找 Canvas,如果没有则创建一个 Canvas canvas = FindObjectOfType<Canvas>(); Transform parent = canvas != null ? canvas.transform : null; if (parent == null) { GameObject canvasGO = new GameObject("Canvas"); canvas = canvasGO.AddComponent<Canvas>(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvasGO.AddComponent<CanvasScaler>(); canvasGO.AddComponent<GraphicRaycaster>(); parent = canvasGO.transform; } ReconstructNode(rootNode, parent); Debug.Log("UI 结构导入重建成功!"); } } // ---------------- 数据提取逻辑 ---------------- private static UIDataNode ExtractNode(Transform trans) { UIDataNode node = new UIDataNode(); node.name = trans.name; node.isActive = trans.gameObject.activeSelf; // 提取 RectTransform RectTransform rt = trans as RectTransform; if (rt != null) { node.anchorMin = rt.anchorMin; node.anchorMax = rt.anchorMax; node.anchoredPosition = rt.anchoredPosition; node.sizeDelta = rt.sizeDelta; node.pivot = rt.pivot; node.localRotation = rt.localRotation; node.localScale = rt.localScale; } // 提取组件信息 (Image, Text, Button) Image img = trans.GetComponent<Image>(); if (img != null) { node.hasImage = true; node.imgColor = img.color; node.imgSpriteName = img.sprite != null ? img.sprite.name : ""; node.imgType = (int)img.type; node.raycastTarget = img.raycastTarget; } Text txt = trans.GetComponent<Text>(); if (txt != null) { node.hasText = true; node.textContent = txt.text; node.textColor = txt.color; node.fontSize = txt.fontSize; node.fontStyle = (int)txt.fontStyle; node.alignment = (int)txt.alignment; node.richText = txt.supportRichText; node.raycastTarget = txt.raycastTarget; // 注意:Font 资源无法直接通过名字完美跨工程迁移,这里仅记录名字供参考 node.fontName = txt.font != null ? txt.font.name : ""; } // 简单处理 Button (不包含复杂的 Transition 设置) Button btn = trans.GetComponent<Button>(); if (btn != null) { node.hasButton = true; node.interactable = btn.interactable; } // 递归子节点 foreach (Transform child in trans) { node.children.Add(ExtractNode(child)); } return node; } // ---------------- 重建逻辑 ---------------- private static void ReconstructNode(UIDataNode node, Transform parent) { GameObject go = new GameObject(node.name); go.SetActive(node.isActive); // 确保是 RectTransform RectTransform rt = go.AddComponent<RectTransform>(); rt.SetParent(parent, false); // worldPositionStays = false // 还原 RectTransform 属性 rt.anchorMin = node.anchorMin; rt.anchorMax = node.anchorMax; rt.anchoredPosition = node.anchoredPosition; rt.sizeDelta = node.sizeDelta; rt.pivot = node.pivot; rt.localRotation = node.localRotation; rt.localScale = node.localScale; // 还原 Image if (node.hasImage) { Image img = go.AddComponent<Image>(); img.color = node.imgColor; img.type = (Image.Type)node.imgType; img.raycastTarget = node.raycastTarget; // 尝试在资源中查找同名 Sprite (简单尝试) if (!string.IsNullOrEmpty(node.imgSpriteName)) { // 注意:在 Editor 下可以使用 AssetDatabase 查找,但比较慢且可能有同名文件 // 这里仅作为辅助,如果找不到需要手动关联 // img.sprite = FindSpriteByName(node.imgSpriteName); } } // 还原 Text if (node.hasText) { Text txt = go.AddComponent<Text>(); txt.text = node.textContent; txt.color = node.textColor; txt.fontSize = node.fontSize; txt.fontStyle = (FontStyle)node.fontStyle; txt.alignment = (TextAnchor)node.alignment; txt.supportRichText = node.richText; txt.raycastTarget = node.raycastTarget; // 默认字体 if (txt.font == null) txt.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); } // 还原 Button if (node.hasButton) { Button btn = go.AddComponent<Button>(); btn.interactable = node.interactable; // 自动关联 Image if (node.hasImage) { btn.targetGraphic = go.GetComponent<Image>(); } } // 递归子节点 foreach (var childNode in node.children) { ReconstructNode(childNode, rt); } } } // ---------------- 数据结构 ---------------- [System.Serializable] public class UIDataNode { public string name; public bool isActive; // RectTransform public Vector2 anchorMin; public Vector2 anchorMax; public Vector2 anchoredPosition; public Vector2 sizeDelta; public Vector2 pivot; public Quaternion localRotation; public Vector3 localScale; // Image public bool hasImage; public Color imgColor; public string imgSpriteName; public int imgType; // Text public bool hasText; public string textContent; public Color textColor; public int fontSize; public int fontStyle; public int alignment; public bool richText; public string fontName; // Common Graphic public bool raycastTarget; // Button public bool hasButton; public bool interactable; public List<UIDataNode> children = new List<UIDataNode>(); }
浙公网安备 33010602011771号