Unity 模型合并

1.创建一个文件夹 命名为Editor (必须时Editor不能错
在Editor文件夹下创建一个C#脚本命名为:CombinedMeshEditor
脚本内容如下:

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

[CustomEditor(typeof(CombinedMesh))]
public class CombinedMeshEditor : Editor
{
    SerializedProperty PropSaveMeshAsset;
    SerializedProperty PropKeepPosition;
    SerializedProperty PropPivotMode;
    SerializedProperty PropMeshObjects;
    SerializedProperty PropRootNode;

    [MenuItem("Tools/Combined Mesh")]
    static void CreateFracturedObject()
    {
        GameObject combinedMeshObject = new GameObject();
        combinedMeshObject.name = "Combined Mesh";
        combinedMeshObject.transform.position = Vector3.zero;
        combinedMeshObject.AddComponent<CombinedMesh>();

        Selection.activeGameObject = combinedMeshObject;
    }
    void Progress(string strMessage, float fT)
    {
        CombinedMesh combinedMesh = serializedObject.targetObject as CombinedMesh;

        Repaint();

        if (EditorUtility.DisplayCancelableProgressBar("Combining", strMessage, fT))
        {
            combinedMesh.CancelCombining();
        }
    }

    void OnEnable()
    {
        PropSaveMeshAsset = serializedObject.FindProperty("SaveMeshAsset");
        PropKeepPosition = serializedObject.FindProperty("KeepPosition");
        PropPivotMode = serializedObject.FindProperty("PivotMode");
        PropMeshObjects = serializedObject.FindProperty("MeshObjects");
        PropRootNode = serializedObject.FindProperty("RootNode");
    }

    public override void OnInspectorGUI()
    {
        int nButtonWidth = 200;

        serializedObject.Update();

        CombinedMesh combinedMesh = serializedObject.targetObject as CombinedMesh;

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        int nNumObjects = combinedMesh.MeshObjects != null ? combinedMesh.MeshObjects.Length : 0;

        EditorGUILayout.PropertyField(PropSaveMeshAsset,
            new GUIContent("Enable Prefab Usage",
                "If activated, will save the mesh to an asset file on disc. Use this if you want to use the generated mesh in prefabs, otherwise prefabs won't reference the mesh correctly."));
        EditorGUILayout.PropertyField(PropKeepPosition,
            new GUIContent("Keep Position",
                "If keep position is activated, the gameobject will keep its current position. Otherwise it will reposition itself to match the objects being combined."));
        EditorGUILayout.PropertyField(PropPivotMode, new GUIContent("Place Pivot Mode", "Where to place the pivot."));
        EditorGUILayout.PropertyField(PropMeshObjects,
            new GUIContent("Source Mesh Objects List (" + nNumObjects + " elements)",
                "The list of objects whose meshes to combine."), true);
        EditorGUILayout.PropertyField(PropRootNode,
            new GUIContent("Root node",
                "Specify an object to set it and its whole hierarchy to the list of objects to combine."));

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");

        serializedObject.ApplyModifiedProperties();

        if (GUILayout.Button(
            new GUIContent("Build List From Root Node",
                "Will build the Source Mesh Objects List using the given object and all the hierarchy below it."),
            GUILayout.Width(nButtonWidth)))
        {
            if (PropRootNode.objectReferenceValue)
            {
                List<MeshFilter> listMeshFilters = new List<MeshFilter>();
                BuildMeshFilterListRecursive(PropRootNode.objectReferenceValue as GameObject, listMeshFilters);
                combinedMesh.MeshObjects = listMeshFilters.ToArray();
            }
        }

        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");

        if (GUILayout.Button(new GUIContent("Combine", "Starts the combine process."), GUILayout.Width(nButtonWidth)))
        {
            try
            {
                combinedMesh.Combine(Progress);
            }
            catch (System.Exception e)
            {
                Debug.LogError("Exception Type: " + e.GetType().ToString() + ". Message: " + e.Message.ToString() +
                               ". Stack Trace: " + e.StackTrace.ToString());
            }

            EditorUtility.ClearProgressBar();
            GUIUtility.ExitGUI();
        }

        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();
    }

    void BuildMeshFilterListRecursive(GameObject node, List<MeshFilter> listMeshFilters)
    {
        MeshFilter meshFilter = node.GetComponent<MeshFilter>();

        if (meshFilter && node.GetComponent<Renderer>())
        {
            listMeshFilters.Add(meshFilter);
        }

        for (int nChild = 0; nChild < node.transform.childCount; nChild++)
        {
            BuildMeshFilterListRecursive(node.transform.GetChild(nChild).gameObject, listMeshFilters);
        }
    }
}

创建一个文件夹 命名为:Scripts (主要用于存放脚本的,命名个人习惯不同,我习惯用这个命名),在Scripts文件夹下创建一个脚本命名为:CombinedMesh
脚本内容如下:

using UnityEngine;
using System;
using System.Collections.Generic;

public class CombinedMesh : MonoBehaviour
{
    public bool SaveMeshAsset = false;
    public bool KeepPosition = true;
    public EPivotMode PivotMode = EPivotMode.Center;
    public MeshFilter[] MeshObjects = null;
    public GameObject RootNode = null;

    public delegate void CombineProgressDelegate(string strMessage, float fT);

#if UNITY_EDITOR
    [SerializeField] private string m_strMeshAssetFileName = "";
#endif

    private bool m_bCancelled = false;

    [Serializable]
    public class ObjectInfo
    {
        public ObjectInfo(Material[] aMaterials, Mesh mesh, Transform transform, Matrix4x4 mtxLocal)
        {
            this.aMaterials = new Material[aMaterials.Length];
            aMaterials.CopyTo(this.aMaterials, 0);
            this.mesh = Instantiate(mesh) as Mesh;

            v3LocalPosition = transform.localPosition;
            qLocalRotation = transform.localRotation;
            v3LocalScale = transform.localScale;

            this.mtxLocal = mtxLocal;
            this.mtxWorld = transform.localToWorldMatrix;

            if (mesh.normals != null)
            {
                av3NormalsWorld = mesh.normals;

                for (int nVertex = 0; nVertex < av3NormalsWorld.Length; nVertex++)
                {
                    av3NormalsWorld[nVertex] = mtxWorld.MultiplyVector(av3NormalsWorld[nVertex]);
                }
            }

            if (mesh.tangents != null)
            {
                av4TangentsWorld = mesh.tangents;

                for (int nVertex = 0; nVertex < av4TangentsWorld.Length; nVertex++)
                {
                    Vector3 v3Tangent = new Vector3(av4TangentsWorld[nVertex].x, av4TangentsWorld[nVertex].y,
                        av4TangentsWorld[nVertex].z);
                    v3Tangent = mtxWorld.MultiplyVector(v3Tangent);
                    av4TangentsWorld[nVertex] =
                        new Vector4(v3Tangent.x, v3Tangent.y, v3Tangent.z, av4TangentsWorld[nVertex].w);
                }
            }
        }

        public Material[] aMaterials;
        public Mesh mesh;
        public Vector3 v3LocalPosition;
        public Quaternion qLocalRotation;
        public Vector3 v3LocalScale;
        public Matrix4x4 mtxLocal;
        public Matrix4x4 mtxWorld;
        public Vector3[] av3NormalsWorld;
        public Vector4[] av4TangentsWorld;
    }

    class MaterialMeshInfo
    {
        public MaterialMeshInfo(Transform transform, Mesh mesh, int nSubMesh)
        {
            this.transform = transform;
            this.mesh = mesh;
            this.nSubMesh = nSubMesh;
        }

        public Transform transform;
        public Mesh mesh;
        public int nSubMesh;
    }

    public enum EPivotMode
    {
        Keep,
        Center,
        BottomCenter,
        TopCenter,
        Min,
        Max
    }

    [SerializeField] private List<ObjectInfo> m_listObjectInfo = new List<ObjectInfo>();

    private Dictionary<Material, List<MaterialMeshInfo>> m_dicMeshEntries =
        new Dictionary<Material, List<MaterialMeshInfo>>();

    public void CancelCombining()
    {
        m_bCancelled = true;
    }

    public bool CombiningCancelled()
    {
        return m_bCancelled;
    }

    public void TransformObjInfoMeshVectorsToLocal(Transform newTransform)
    {
        foreach (ObjectInfo objInfo in m_listObjectInfo)
        {
            if (objInfo.mesh.normals != null && objInfo.av3NormalsWorld != null)
            {
                Vector3[] av3Normals = new Vector3[objInfo.av3NormalsWorld.Length];
                objInfo.av3NormalsWorld.CopyTo(av3Normals, 0);

                for (int nVertex = 0; nVertex < av3Normals.Length; nVertex++)
                {
                    av3Normals[nVertex] = newTransform.InverseTransformDirection(av3Normals[nVertex]);
                }

                objInfo.mesh.normals = av3Normals;
            }

            if (objInfo.mesh.tangents != null)
            {
                Vector4[] av4Tangents = new Vector4[objInfo.av4TangentsWorld.Length];
                objInfo.av4TangentsWorld.CopyTo(av4Tangents, 0);

                for (int nVertex = 0; nVertex < av4Tangents.Length; nVertex++)
                {
                    Vector3 v3Tangent = new Vector3(av4Tangents[nVertex].x, av4Tangents[nVertex].y,
                        av4Tangents[nVertex].z);
                    v3Tangent = newTransform.InverseTransformDirection(v3Tangent);
                    av4Tangents[nVertex] = new Vector4(v3Tangent.x, v3Tangent.y, v3Tangent.z, av4Tangents[nVertex].w);
                }

                objInfo.mesh.tangents = av4Tangents;
            }
        }
    }

    public int GetObjectCount()
    {
        return m_listObjectInfo.Count;
    }

    public ObjectInfo GetObjectInfo(int nIndex)
    {
        return m_listObjectInfo[nIndex];
    }

    public void Combine(CombineProgressDelegate progress)
    {
        m_listObjectInfo.Clear();
        m_dicMeshEntries.Clear();
        m_bCancelled = false;

#if UNITY_EDITOR

        if (SaveMeshAsset)
        {
            if (m_strMeshAssetFileName.Length == 0 || System.IO.File.Exists(m_strMeshAssetFileName) == false)
            {
                m_strMeshAssetFileName = UnityEditor.EditorUtility.SaveFilePanelInProject("Save mesh asset",
                    "mesh_" + this.name + this.GetInstanceID().ToString() + ".asset", "asset",
                    "Please enter a file name to save the mesh asset to");
            }
        }

#endif


        bool bUseNormals = false;
        bool bUseTangents = false;
        bool bUseColors = false;
        bool bUseMapping1 = false;
        bool bUseMapping2 = false;

        Vector3 v3Min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        Vector3 v3Max = new Vector3(float.MinValue, float.MinValue, float.MinValue);

        int nMeshFilter = 0;

        foreach (MeshFilter meshFilter in MeshObjects)
        {
            if (progress != null)
            {
                progress("Preprocessing object " + meshFilter.name + "...",
                    (float)nMeshFilter / (float)MeshObjects.Length);
            }

            if (m_bCancelled)
            {
                return;
            }

            if (meshFilter == null)
            {
                continue;
            }

            if (meshFilter.GetComponent<Renderer>() == null)
            {
                Debug.LogWarning(meshFilter.name + " has no mesh renderer available");
                continue;
            }

            Mesh mesh = meshFilter.sharedMesh;

            Vector3[] aVertices = mesh.vertices;

            for (int v = 0; v < aVertices.Length; v++)
            {
                Vector3 v3Pos = meshFilter.transform.TransformPoint(aVertices[v]);

                if (v3Pos.x < v3Min.x) v3Min.x = v3Pos.x;
                if (v3Pos.y < v3Min.y) v3Min.y = v3Pos.y;
                if (v3Pos.z < v3Min.z) v3Min.z = v3Pos.z;
                if (v3Pos.x > v3Max.x) v3Max.x = v3Pos.x;
                if (v3Pos.y > v3Max.y) v3Max.y = v3Pos.y;
                if (v3Pos.z > v3Max.z) v3Max.z = v3Pos.z;
            }

            if (mesh.normals != null)
                if (mesh.normals.Length > 0)
                    bUseNormals = true;
            if (mesh.tangents != null)
                if (mesh.tangents.Length > 0)
                    bUseTangents = true;
            if (mesh.colors != null)
                if (mesh.colors.Length > 0)
                    bUseColors = true;
            if (mesh.colors32 != null)
                if (mesh.colors32.Length > 0)
                    bUseColors = true;
            if (mesh.uv != null)
                if (mesh.uv.Length > 0)
                    bUseMapping1 = true;
            if (mesh.uv2 != null)
                if (mesh.uv2.Length > 0)
                    bUseMapping2 = true;

            for (int nSubMesh = 0; nSubMesh < mesh.subMeshCount; nSubMesh++)
            {
                MaterialMeshInfo newMaterialMeshInfo = new MaterialMeshInfo(meshFilter.transform, mesh, nSubMesh);
                Material material = meshFilter.GetComponent<Renderer>().sharedMaterials[nSubMesh];

                if (m_dicMeshEntries.ContainsKey(material) == false)
                {
                    m_dicMeshEntries.Add(material, new List<MaterialMeshInfo>());
                }

                m_dicMeshEntries[material].Add(newMaterialMeshInfo);
            }

            m_listObjectInfo.Add(new ObjectInfo(meshFilter.GetComponent<Renderer>().sharedMaterials, mesh,
                meshFilter.transform, meshFilter.transform.localToWorldMatrix));
        }

        if (m_dicMeshEntries.Count > 0)
        {

            Vector3 v3Position = transform.position;

            switch (PivotMode)
            {
                case EPivotMode.Keep:
                    v3Position = transform.position;
                    break;
                case EPivotMode.Center:
                    v3Position = (v3Max + v3Min) * 0.5f;
                    break;
                case EPivotMode.BottomCenter:
                    v3Position = (v3Max + v3Min) * 0.5f;
                    v3Position.y = v3Min.y;
                    break;
                case EPivotMode.TopCenter:
                    v3Position = (v3Max + v3Min) * 0.5f;
                    v3Position.y = v3Max.y;
                    break;
                case EPivotMode.Min:
                    v3Position = v3Min;
                    break;
                case EPivotMode.Max:
                    v3Position = v3Max;
                    break;
            }

            Vector3 v3OriginalPosition = transform.position;
            Quaternion qOriginalRotation = transform.rotation;
            Vector3 v3OriginalScale = transform.localScale;

            transform.position = v3Position;
            transform.rotation = Quaternion.identity;
            transform.localScale = Vector3.one;

            Matrix4x4 mtxWorldToNewLocal = this.transform.worldToLocalMatrix;

            if (KeepPosition)
            {
                transform.position = v3OriginalPosition;
                transform.rotation = qOriginalRotation;
                transform.localScale = v3OriginalScale;
            }

            Material[] aMaterials = new Material[m_dicMeshEntries.Keys.Count];
            m_dicMeshEntries.Keys.CopyTo(aMaterials, 0);


            foreach (ObjectInfo objInfo in m_listObjectInfo)
            {
                objInfo.mtxLocal = mtxWorldToNewLocal * objInfo.mtxLocal;
            }


            List<int>[] aListIndices = new List<int>[m_dicMeshEntries.Count];

            List<Vector3> listVertices = new List<Vector3>();
            List<Vector3> listNormals = new List<Vector3>();
            List<Vector4> listTangents = new List<Vector4>();
            List<Color32> listColors = new List<Color32>();
            List<Vector2> listMapping1 = new List<Vector2>();
            List<Vector2> listMapping2 = new List<Vector2>();

            Dictionary<GameObject, int> dicObject2IndexStart = new Dictionary<GameObject, int>();

            int nMaterial = 0;

            foreach (List<MaterialMeshInfo> listMaterialMeshInfo in m_dicMeshEntries.Values)
            {

                aListIndices[nMaterial] = new List<int>();

                int nMaterialMeshInfoIndex = 0;

                foreach (MaterialMeshInfo materialMeshInfo in listMaterialMeshInfo)
                {
                    if (progress != null)
                    {
                        progress("Combining submesh for material " + aMaterials[nMaterial].name + "...",
                            (float)nMaterialMeshInfoIndex / (float)listMaterialMeshInfo.Count);
                    }

                    if (m_bCancelled)
                    {
                        return;
                    }

                    int nIndexStart = listVertices.Count;

                    if (dicObject2IndexStart.ContainsKey(materialMeshInfo.transform.gameObject))
                    {
                        nIndexStart = dicObject2IndexStart[materialMeshInfo.transform.gameObject];
                    }
                    else
                    {
                        Matrix4x4 mtxLocalToWorld = materialMeshInfo.transform.localToWorldMatrix;
                        Matrix4x4 mtxCombined = mtxWorldToNewLocal * mtxLocalToWorld;

                        dicObject2IndexStart.Add(materialMeshInfo.transform.gameObject, nIndexStart);

                        int nVertexCount = materialMeshInfo.mesh.vertexCount;

                        Vector3[] aVertices = materialMeshInfo.mesh.vertices;

                        for (int nVertex = 0; nVertex < aVertices.Length; nVertex++)
                        {
                            aVertices[nVertex] = mtxCombined.MultiplyPoint3x4(aVertices[nVertex]);
                        }

                        listVertices.AddRange(aVertices);

                        if (bUseNormals)
                        {
                            bool bCreateNormals = true;
                            if (materialMeshInfo.mesh.normals != null)
                                if (materialMeshInfo.mesh.normals.Length > 0)
                                    bCreateNormals = false;

                            if (bCreateNormals)
                            {
                                Debug.LogWarning(string.Format(
                                    "Object {0} has mesh with no vertex normals, and some other objects have them. Dummy normals have been added",
                                    materialMeshInfo.transform.name));
                            }

                            Vector3[] av3Normals =
                                bCreateNormals ? new Vector3[nVertexCount] : materialMeshInfo.mesh.normals;

                            for (int nVertex = 0; nVertex < av3Normals.Length; nVertex++)
                            {
                                av3Normals[nVertex] =
                                    materialMeshInfo.transform.TransformDirection(av3Normals[nVertex]);
                                av3Normals[nVertex] = this.transform.InverseTransformDirection(av3Normals[nVertex]);
                            }

                            listNormals.AddRange(av3Normals);
                        }

                        if (bUseTangents)
                        {
                            bool bCreateTangents = true;
                            if (materialMeshInfo.mesh.tangents != null)
                                if (materialMeshInfo.mesh.tangents.Length > 0)
                                    bCreateTangents = false;

                            if (bCreateTangents)
                            {
                                Debug.LogWarning(string.Format(
                                    "Object {0} has mesh with no vertex tangents, and some other objects have them. Dummy tangents have been added",
                                    materialMeshInfo.transform.name));
                            }

                            Vector4[] av4Tangents = bCreateTangents
                                ? new Vector4[nVertexCount]
                                : materialMeshInfo.mesh.tangents;

                            for (int nVertex = 0; nVertex < av4Tangents.Length; nVertex++)
                            {
                                Vector3 v3Tangent = new Vector3(av4Tangents[nVertex].x, av4Tangents[nVertex].y,
                                    av4Tangents[nVertex].z);
                                v3Tangent = materialMeshInfo.transform.TransformDirection(v3Tangent);
                                v3Tangent = transform.InverseTransformDirection(v3Tangent);
                                av4Tangents[nVertex] = new Vector4(v3Tangent.x, v3Tangent.y, v3Tangent.z,
                                    bCreateTangents ? 1.0f : av4Tangents[nVertex].w);
                            }

                            listTangents.AddRange(av4Tangents);
                        }

                        if (bUseColors)
                        {
                            bool bColors = false;
                            bool bColors32 = false;
                            bool bCreateColors = true;

                            if (materialMeshInfo.mesh.colors != null)
                                if (materialMeshInfo.mesh.colors.Length > 0)
                                {
                                    bColors = true;
                                    bCreateColors = false;
                                }

                            if (materialMeshInfo.mesh.colors32 != null)
                                if (materialMeshInfo.mesh.colors32.Length > 0)
                                {
                                    bColors32 = true;
                                    bCreateColors = false;
                                }

                            if (bCreateColors)
                            {
                                Debug.LogWarning(string.Format(
                                    "Object {0} has mesh with no vertex colors, and some other objects have them. Dummy colors have been added",
                                    materialMeshInfo.transform.name));
                            }

                            Color32[] aColors32 = null;

                            if (bCreateColors)
                            {
                                aColors32 = new Color32[nVertexCount];
                            }
                            else if (bColors)
                            {
                                aColors32 = new Color32[nVertexCount];
                                Color[] aColors = materialMeshInfo.mesh.colors;

                                for (int nColor = 0; nColor < nVertexCount; nColor++)
                                {
                                    aColors32[nColor] = aColors[nColor];
                                }
                            }
                            else if (bColors32)
                            {
                                aColors32 = materialMeshInfo.mesh.colors32;
                            }

                            listColors.AddRange(aColors32);
                        }

                        if (bUseMapping1)
                        {
                            bool bCreateMapping1 = true;
                            if (materialMeshInfo.mesh.uv != null)
                                if (materialMeshInfo.mesh.uv.Length > 0)
                                    bCreateMapping1 = false;

                            if (bCreateMapping1)
                            {
                                Debug.LogWarning(string.Format(
                                    "Object {0} has mesh with no vertex mapping (uv), and some other objects have them. Dummy mapping has been added",
                                    materialMeshInfo.transform.name));
                            }

                            Vector2[] av2Mapping1 =
                                bCreateMapping1 ? new Vector2[nVertexCount] : materialMeshInfo.mesh.uv;

                            listMapping1.AddRange(av2Mapping1);
                        }

                        if (bUseMapping2)
                        {
                            bool bCreateMapping2 = true;
                            if (materialMeshInfo.mesh.uv2 != null)
                                if (materialMeshInfo.mesh.uv2.Length > 0)
                                    bCreateMapping2 = false;

                            if (bCreateMapping2)
                            {
                                Debug.LogWarning(string.Format(
                                    "Object {0} has mesh with no vertex mapping (uv2), and some other objects have them. Dummy mapping has been added",
                                    materialMeshInfo.transform.name));
                            }

                            Vector2[] av2Mapping2 =
                                bCreateMapping2 ? new Vector2[nVertexCount] : materialMeshInfo.mesh.uv2;

                            listMapping2.AddRange(av2Mapping2);
                        }
                    }


                    int[] aSubMeshIndices = materialMeshInfo.mesh.GetTriangles(materialMeshInfo.nSubMesh);

                    for (int nIndex = 0; nIndex < aSubMeshIndices.Length; nIndex++)
                    {
                        aListIndices[nMaterial].Add(aSubMeshIndices[nIndex] + nIndexStart);
                    }

                    nMaterialMeshInfoIndex++;
                }

                nMaterial++;
            }


            if (m_bCancelled == false)
            {
                if (progress != null)
                {
                    progress("Building mesh...", 1.0f);
                }


                MeshFilter combinedMeshFilter = gameObject.GetComponent<MeshFilter>();

                if (combinedMeshFilter == null)
                {
                    combinedMeshFilter = gameObject.AddComponent<MeshFilter>();
                }


                if (GetComponent<Renderer>() == null)
                {
                    gameObject.AddComponent<MeshRenderer>();
                }

                GetComponent<Renderer>().sharedMaterials = aMaterials;


                int nVertexLimit = 65000;
                bool useInt32 = false;

                if (listVertices.Count > nVertexLimit)
                {
                    useInt32 = true;
                }

                Mesh newMesh = new Mesh();

                if (useInt32)
                {
                    newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                }

                newMesh.vertices = listVertices.ToArray();
                if (bUseNormals) newMesh.normals = listNormals.ToArray();
                if (bUseTangents) newMesh.tangents = listTangents.ToArray();
                if (bUseColors) newMesh.colors32 = listColors.ToArray();
                if (bUseMapping1) newMesh.uv = listMapping1.ToArray();
                if (bUseMapping2) newMesh.uv2 = listMapping2.ToArray();

                newMesh.subMeshCount = aListIndices.Length;

                for (int nSubMesh = 0; nSubMesh < aListIndices.Length; nSubMesh++)
                {
                    newMesh.SetTriangles(aListIndices[nSubMesh].ToArray(), nSubMesh);
                }

                combinedMeshFilter.sharedMesh = newMesh;

#if UNITY_EDITOR


                if (SaveMeshAsset && m_strMeshAssetFileName.Length > 0)
                {
                    UnityEditor.AssetDatabase.CreateAsset(newMesh, m_strMeshAssetFileName);
                    UnityEditor.AssetDatabase.Refresh();
                }
#endif
            }
        }
        else
        {
            Debug.LogWarning("No meshes were combined because none were found.");
        }
    }
}

此时在Hierarchy窗口下创建一个空节点,节点命名自己来定,在空节点上添加CombinedMesh脚本组件(也可点击Tool菜单栏下的Combined Mesh将在在面板中自动创建带有CombinedMesh组件的空节点)Unity 模型合并_第1张图片
Unity 模型合并_第2张图片
将需要合并Mesh的对象(模型、预制体)拖入Root node中,先点击Build List From Root Node,然后再点击Combine,模型就合并完成了。

是不是很简单!

你可能感兴趣的:(unity,游戏引擎)