“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>();
}

 

  

posted on 2026-02-05 11:41  juaa  阅读(5)  评论(0)    收藏  举报