当前位置:   article > 正文

Unity Edior下合并场景 合并网格 合并贴图_unity场景资源合并

unity场景资源合并

声明:这篇绝不是网上那些转来转去千篇一律的 合并方法 搜老半天看的都是同一篇博客 这里写图片描述
一点帮助都没有 还是自己写个吧

1.工程里面 合并场景主要是为了降低draw call
2、不同shader的尽量不要合并 不同shader 参数的也尽量不要合并
3、本篇博客主要是合并场景里的 不会动的 不考虑人物 只考虑MeshRenderer

步骤:
一、首先要找出场景里用到最多的shader 不一定是一个 可能是两个三个个 至少这些shader在90%的物体上用到了
二、打开这个场景 EditorSceneManger.OpenScene(path) 在Editor下打开这个场景 拿到一个Scene对象
三、用Scene.GetRootGameObjects() 获取到根目录下的游戏物体
四、遍历这个根目录 拿到所有符合条件的MeshRenderer组件(材质球只有一个、shader是满足条件的shader、贴图大小满足条件、组件是激活的 等等)
五、遍历这些MeshRenderer 将这些按照 合并策略分类(我这个项目合并策略是 场景里一个15x15的格子里 shader一样、shader参数一样的才进行合并) (坐标|shader|参数 为字典的Key List为字典的Value)
六、遍历这个字典的每一项 开始进行合并
1、先合并这些对应的贴图 我用的是texturePacker进行合并贴图 这样省去很多功夫
①写好一个.bat文件 用于直接进行命令行处理TexturePacker合并文件
需要设置环境变量
具体参数可以去TexturePacker官网上查

set "Max_Size=%1" 
set "Poweroftow=%2"
set "BorderPadding=%3"
set "ShapePadding=%4"
set "TargetPath=%5"
set "SourcePaths=%~6"

TexturePacker --sheet %TargetPath%_tp{n}.tga ^
--data %TargetPath%_tp{n}_cfg.txt ^
--texture-format tga ^
--disable-rotation ^
--format unity ^
--multipack  ^
--max-size %Max_Size% ^
--trim-mode None ^
--extrude 0 ^
--opt RGBA8888 ^
--png-opt-level 2 ^
--border-padding %BorderPadding% ^
--shape-padding %ShapePadding% ^
--disable-auto-alias ^
--size-constraints %Poweroftow%  %SourcePaths%
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

②先将要合并的资源拷呗到一个文件夹下
③然后执行这个.bat文件 具体怎么执行 用Process 类 再具体就百度吧
④执行完之后 合并出来的可能不只一张图片 如果超过一张图片 就要重新合并 此时要讲原来的MeshRenderer二分,用递归再次合并两个一半的MeshRenderer数组 直到合并出来只有一个图集 同时会生成一个cfg配置文件 用来显示原来的图片在生成的图片在那一部分
2、贴图合并成功之后就该进行网格合并了
①拿到上一步贴图合并成功的MeshRenderer数组 再拿到MeshRenderer对象上的MeshFilter身上的sharedMesh
②用Unity Api Mesh.Combine(CombineInstance[]) 进行合并网格
3、 网格合并成功之后 要修改这个网格的UV坐标
一个网格有多少个顶点 就有多少个Uv坐标 这个坐标用来控制这个顶点显示图片上哪一部分的内容
所以这一步很重要 要重新刷一遍网格的所有UV坐标
根据之前合并贴图成功时生成的配置文件,这个文件是个Json文件,
将原来的MeshUV 根据映射转换成 大的图集上的UV坐标
4、创建个空物体 带有MeshRenderer MeshFilter 的组件 用于接收生成的网格 和 材质球
创建材质球 设置shader 和参数 和贴图
给MeshRenderer设置这个材质球 MeshFilter设置这个Mesh
5、最后将生成的Material 和Mesh 分别生成一个资源 AssetDatabase.CreateAssert(Obejc,path)
因为之前生成的Material和Mesh只是在内存里 一切换场景就没有了 所有要生成.asset文件

代码:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using UnityEditor.SceneManagement;
using System.Linq;
using System.Collections.Generic;
using System.IO;

public class CombineTool : Editor
{
    private const int GRIDSIDE = 15;
    private const string PATH = "Assets/Resources/scene/";
    private static string ASSET_PATH = EditorConfig.assetPath;

    [MenuItem("Test/Test")]
    public static void Test()
    {
    }

    [MenuItem("Debug/合并场景不烘焙")]
    public static void CombineSceneNotBake()
    {
        CombineScene(false);
    }

    /// <summary>
    /// 合并所有场景
    /// </summary>
    /// <param name="bake"></param>
    private static void CombineScene(bool bake)
    {
       var files=AssetDatebase.GetAllAssetPath().where(s=>s.EndWith(".unity"));
       foreach(var item in files){
        CombineOneScene(item, bake);
       }  
        EditorUtility.ClearProgressBar();
    }
    /// <summary>
    /// 合并一个场景
    /// </summary>
    /// <param name="path"></param>
    private static void CombineOneScene(string path, bool bake)
    {
        var scene = EditorSceneManager.OpenScene(path);
        PBTitle = "正在合并场景:" + scene.name;
        SceneData sd = GetSceneData(scene);
        if (sd == null) return;
        var combineGO = CreatCombineObj(scene);
        var dic = ClassifyByPositionAndShaderAndProprity(scene);
        int count = 0;
        foreach (var item in dic)
        {
            PBContent = count + "/" + dic.Count;
            PBProcess = (float)count / dic.Count;
            string[] ss = item.Key.Split('|');   //0是坐标 1是shader 2是颜色 3是cutoff值
            Combine(item.Value.Where(s => s != null).ToArray(), scene.name, ref count, ss, combineGO.transform);
        }
        sd.Export(bake);
        Debug.Log("合并场景:" + scene.name);
    }

    /// <summary>
    /// 指定位置创建一个Combine物体 用于存放合并后的物体
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns>
    private static GameObject CreatCombineObj(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                break;
            }
        }
        return combineGO;
    }
    /// <summary>
    /// 获取场景里的所有只有一个材质球的MeshRenderer shader为Scene/Diffuse  或者 是 Scene/Cutout/Diffuse  且 贴图长和宽不能大于1024
    /// </summary>
    /// <param name="s"></param>
    private static MeshRenderer[] GetAllMeshsInScene(UnityEngine.SceneManagement.Scene scene)
    {
        PBState = "查找符合标准的MeshRenderer";
        const int texture_MaxSize = 1024;
        var roots = scene.GetRootGameObjects();
        var meshrenders = new List<MeshRenderer>();
        foreach (var item in roots)
        {
            meshrenders.AddRange(item.GetComponentsInChildren<MeshRenderer>());
        }
        return meshrenders.Where(s => s
        && s.enabled == true
        && s.sharedMaterials.Length == 1
        && s.sharedMaterial
        && (s.sharedMaterial.shader.name == "scenes/Diffuse"
        || s.sharedMaterial.shader.name == "scenes/Cutout/Diffuse")
        && s.sharedMaterial.GetTexture("_MainTex")
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.x <= texture_MaxSize
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.y <= texture_MaxSize)
        .ToArray();
    }
    /// <summary>
    /// 保存资源
    /// </summary>
    /// <param name="go">对象</param>
    /// <param name="path">相对于Resource/scene/的路经</param>
    /// <param name="name">名字</param>
    private static void CreateAsset(UnityEngine.Object go, string path, string name)
    {
        PBState = "保存资源";
        if (!Directory.Exists(PATH + path)) Directory.CreateDirectory(PATH + path);
        AssetDatabase.CreateAsset(go, PATH + path + "/" + name);
    }
    /// <summary>
    /// 根据坐标,shader,参数分类
    /// </summary>
    /// <param name="meshrenderers"></param>
    private static Dictionary<string, List<MeshRenderer>> ClassifyByPositionAndShaderAndProprity(UnityEngine.SceneManagement.Scene scene)
    {
        PBState = "分类MeshRenderer";
        Dictionary<string, List<MeshRenderer>> dic = new Dictionary<string, List<MeshRenderer>>();
        foreach (var item in GetAllMeshsInScene(scene))
        {
            Material mt = item.sharedMaterial;
            string tag;
            if (mt.shader.name == "scenes/Cutout/Diffuse")
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Color") + "|" + mt.GetFloat("_Cutoff");
            else
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Colors");
            if (!dic.ContainsKey(tag))
            {
                dic.Add(tag, new List<MeshRenderer>() { item });
            }
            else
            {
                dic[tag].Add(item);
            }
        }
        //清除只有1个的List
        List<string> checkOneRenderList = new List<string>();
        foreach (var item in dic)
        {
            if (item.Value.Count == 1) checkOneRenderList.Add(item.Key);
        }
        foreach (var item in checkOneRenderList)
        {
            dic.Remove(item);
        }
        return dic;
    }
    /// <summary>
    /// 合并网格
    /// </summary>
    /// <param name="renderers"></param>
    ///  <param name="filePath">_tp0_cfg.txt的路径</param>
    /// <returns></returns>
    private static Mesh CombineMesh(MeshRenderer[] renderers, string filePath)
    {
        PBState = "合并网格";
        var dicOfPackFrame = GetPackedFram(filePath);//根据图片名称 拿到在合并后的图片里的信息
        List<Vector2> uvs = new List<Vector2>();   //存放原来的Mesh的UV
        CombineInstance[] cis = new CombineInstance[renderers.Length];
        int uvCount = 0;
        for (int i = 0; i < cis.Length; i++)
        {
            cis[i].mesh = renderers[i].GetComponent<MeshFilter>().sharedMesh;
            cis[i].transform = renderers[i].transform.localToWorldMatrix;
            Texture t = renderers[i].sharedMaterial.GetTexture("_MainTex");
            string textureName = Path.GetFileName(AssetDatabase.GetAssetPath(t));
            uvs.AddRange(cis[i].mesh.uv.Select(s => TransfromUV(s, dicOfPackFrame[textureName], t.wrapMode)).ToArray());
            uvCount += cis[i].mesh.uv.Length;
        }
        Mesh m = new Mesh();
        m.CombineMeshes(cis, true, true);
        m.uv = uvs.ToArray();
        return m;
    }
    /// <summary>
    /// 合并   合并一个List MeshRnder 的  整个逻辑
    /// </summary>
    /// <param name="renderers"></param>
    /// <param name="sceneName">场景名称 用于标记</param>
    /// <param name="index">计数器</param>
    /// <param name="parameter">material的各个参数   //0是坐标 1是shader 2是颜色 3是cutoff值</param>
    /// <param name="combineRoot">合并的根物体 所有合并产生的物体都要放到这个物体下面</param>
    /// <returns></returns>
    private static void Combine(MeshRenderer[] renderers, string sceneName, ref int index, string[] parameter, Transform combineRoot)
    {
        if (renderers.Length < 2) return;
        PBState = "合并";
        var textures = renderers.Select(s => {
            if (s == null)
            {
                Debug.Log(s);
                Debug.Log("有贴图为空");
            }
            return s.sharedMaterial ? s.sharedMaterial.GetTexture("_MainTex") : null;
        });
        var texturePaths = textures.Select(s => ASSET_PATH + AssetDatabase.GetAssetPath(s)).ToArray();//获取贴图的绝对路径 
        string path = CopyFileToDirectiory(texturePaths, PATH + sceneName + "/" + index);//将其拷呗到指定文件夹下
        if (CombineTexture(path))//若果合并成功
        {
            Mesh m = CombineMesh(renderers, path + "/_tp0_cfg.txt");
            Texture t = AssetDatabase.LoadAssetAtPath<Texture>(path + "/_tp0.tga");
            Material mat = new Material(Shader.Find(parameter[1]));
            if (parameter[1].Contains("Cutoff"))
            {
                mat.SetColor("_Color", ColorParse(parameter[2]));
                mat.SetFloat("_Cutoff", float.Parse(parameter[3]));
            }
            else
            {
                mat.SetColor("_Colors", ColorParse(parameter[2]));
            }
            mat.SetTexture("_MainTex", t);
            GameObject go = new GameObject(index + "", typeof(MeshRenderer), typeof(MeshFilter));
            go.transform.SetParent(combineRoot.transform);
            go.GetComponent<MeshFilter>().sharedMesh = m;
            go.GetComponent<MeshRenderer>().sharedMaterial = mat;
            CreateAsset(m, sceneName + "/" + index, index + "_mesh.asset");
            CreateAsset(mat, sceneName + "/" + index, index + "_mat.asset");
            index++;
            foreach (var item in renderers)
            {
                if (item) DestroyImmediate(item.gameObject, false);
            }
        }
        else//合并贴图失败
        {
            if (renderers.Length > 2)
            {
                MeshRenderer[] r1 = new MeshRenderer[renderers.Length / 2];
                MeshRenderer[] r2 = new MeshRenderer[renderers.Length - r1.Length];
                for (int i = 0; i < r1.Length; i++)
                {
                    r1[i] = renderers[i];
                }
                for (int i = 0; i < r2.Length; i++)
                {
                    r2[i] = renderers[r1.Length + i];
                }
                if (r1.Length > 1) Combine(r1, sceneName, ref index, parameter, combineRoot);
                Combine(r2, sceneName, ref index, parameter, combineRoot);
            }
        }
    }
    /// <summary>
    /// 找到一个场景里的SceneData
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns> 
    private static SceneData GetSceneData(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        foreach (var item in roots)
        {
            SceneData m = item.GetComponentInChildren<SceneData>();
            if (m) return m;
        }
        return null;
    }

    /// <summary>
    /// 输入一个原本的uv 转换成合并后贴图的uv
    /// </summary>
    /// <param name="uv"></param>
    /// <param name="packedFrame"></param>
    /// <param name="twm">5.3.7版本 只有两种 以后可能会有多种</param>
    /// <returns></returns>
    /// 
    static int tempppp = 0;
    private static Vector2 TransfromUV(Vector2 uv, TexturePacker.PackedFrame packedFrame, TextureWrapMode twm)
    {
        Vector2 temp = uv;
        Vector2 temp2 = uv;
        Vector2 temp3 = uv;
        Vector2 temp4 = uv;
        if (twm == TextureWrapMode.Clamp)
        {
            uv.x = Mathf.Clamp(uv.x, 0, 1);
            uv.y = Mathf.Clamp(uv.y, 0, 1);
        }
        else if (twm == TextureWrapMode.Repeat)//*****************以后可能会有多种
        {
            uv.x = Mathf.Repeat(uv.x, 1);
            uv.y = Mathf.Repeat(uv.y, 1);
        }
        Vector2 altaSize = packedFrame.atlasSize;
        Rect frame = packedFrame.frame;
        Vector2 smallZuoxiaJiao = new Vector2(frame.x, altaSize.y - frame.y - frame.height);//原来图片的左下角相对于合并后的图片的位置
        Vector2 point = new Vector2(smallZuoxiaJiao.x + Mathf.CeilToInt(uv.x * frame.width), smallZuoxiaJiao.y + Mathf.CeilToInt(uv.y * frame.height));
        Vector2 result = new Vector2(point.x / altaSize.x, point.y / altaSize.y);
        return result;
    }

    /// <summary>
    /// 合并一个路径下的图片 还是放在这个路径下
    /// </summary>
    /// <param name="path"> 绝对路径 最后不要加/</param>
    private static bool CombineTexture(string path)
    {
        PBState = "合并贴图";
        string[] files = Directory.GetFiles(path);
        var maxSize = 2048;
        var poweroftow = "POT";
        var borderPadding = 0;
        var shapePadding = 0;
        var command = GlobalEditorHelper.GetAssetsPath(ASSET_PATH + PATH + "combinetexture.bat");
        Util.ExecuteCmd(EditorConfig.processPath, command, string.Format(" {0} {1} {2} {3} {4} {5}", maxSize, poweroftow, borderPadding, shapePadding, path + "/", path));
        foreach (var item in files)
        {
            File.Delete(item);
        }
        if (Directory.GetFiles(path).Length > 2)  //如果大于2 说明合并了1个以上的贴图
        {
            foreach (var item in Directory.GetFiles(path))
            {
                File.Delete(item);
            }
            return false;
        }
        else
        {
            AssetDatabase.Refresh();
            return true;
        }
    }

    /// <summary>
    /// 将一个txt文件解析成List PacekdFram
    /// </summary>
    /// <param name="path"></param>
    private static Dictionary<string, TexturePacker.PackedFrame> GetPackedFram(string path)
    {
        TextAsset txt = AssetDatabase.LoadAssetAtPath<TextAsset>(path.Substring(path.IndexOf("Assets")));
        if (txt == null) Debug.Log(path);
        TexturePacker.MetaData meta = TexturePacker.GetMetaData(txt.text);
        Hashtable table = txt.text.hashtableFromJson();
        List<TexturePacker.PackedFrame> frames = new List<TexturePacker.PackedFrame>();
        Hashtable frameTable = (Hashtable)table["frames"];
        foreach (DictionaryEntry entry in frameTable)
        {
            frames.Add(new TexturePacker.PackedFrame((string)entry.Key, meta.size, (Hashtable)entry.Value));
        }
        return frames.ToDictionary(s => s.name, s => s);
    }

    /// <summary>
    /// 拷呗一些文件 到另一个文件夹下面
    /// </summary>
    /// <param name="files"></param>
    /// <param name="path">绝对路径 最后不要加斜线</param>
    private static string CopyFileToDirectiory(string[] files, string path)
    {

        PBState = "拷呗贴图";
        if (!Directory.Exists(path)) Directory.CreateDirectory(path);
        foreach (var item in files)
        {
            if (File.Exists(item)) File.Copy(item, path + "/" + item.Substring(item.LastIndexOf('/')), true);
        }
        return path;
    }

    /// <summary>
    /// 将一个特定字符串解析成Color 
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    private static Color ColorParse(string s)
    {
        s = s.Substring(5, s.Length - 6);
        string[] ss = s.Split(',');
        return new Color(float.Parse(ss[0]), float.Parse(ss[1]), float.Parse(ss[2]), float.Parse(ss[3]));
    }
    /// <summary>
    /// 判读两个物体是不是在同一个格子里
    /// </summary>
    /// <param name="t1"></param>
    /// <param name="t2"></param>
    /// <returns></returns>
    private static bool InSameGrid(Transform t1, Transform t2)
    {
        return (GetGridIndex(t1) == GetGridIndex(t2));
    }
    /// <summary>
    /// 计算一个物体的格子坐标
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    private static Vector2 GetGridIndex(Transform t)
    {
        return new Vector2((int)(t.position.x / GRIDSIDE), (int)(t.position.z / GRIDSIDE));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402

BUT!!!
There is a big Problem.
如果原来贴图的格式是Clamp格式的 那么以上代码完全可行
如果是Repeat格式的 就非常有可能不可行
因为转换UV坐标时 要将原来的UV坐标给Math.Clamp 或者 Math.Repeat 处理到0~1
假如两个相邻的顶点uv坐标是(0.9,0.5)(1.1,0.5) 那么这两个点之间本应该draw 0.2个单位
经过Math.Repeat处理一下之后就变成了(0.9,0.5)(0.1,0.5) 那么这两个点之间就会draw 0.8个单位 而且还是反着draw的
所以 这个问题基本上无法避免
而且 很无奈的是 要自己处理这个问题会非常费时费力 遇到这种情况就要用插件了
推荐一款MeshBaker插件 AssetStore 有免费版的
但是这个插件是一步一步操作的 所以要写工具一键全部合并还是得靠自己写代码
下载完插件之后 以下代码就能运行了
代码:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using UnityEditor.SceneManagement;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using DigitalOpus.MB.Core;

public class CombineTool : Editor
{
    private const int GRIDSIDE = 15;
    private const string PATH = "Assets/Resources/scene/";
    private static string ASSET_PATH = EditorConfig.assetPath;
    private const string LAYER_NAME = "block";
    #region ProgressBar
    private static string title = "";
    private static string PBTitle
    {
        set
        {
            title = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
        get { return title; }
    }
    private static string content = "";
    private static string PBContent
    {
        get { return content; }
        set
        {
            content = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    private static string state = "";
    private static string PBState
    {
        get { return state; }
        set
        {
            state = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    private static float process = 0;
    private static float PBProcess
    {
        get { return process; }
        set
        {
            process = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    #endregion

    [MenuItem("Debug/合并场景不烘焙")]
    public static void CombineSceneNotBake()
    {
        CombineScene(false);
    }

    /// <summary>
    /// 合并所有场景
    /// </summary>
    /// <param name="bake"></param>
    private static void CombineScene(bool bake)
    {
          var scenes = AssetDatabase.GetAllAssetPaths().Where(s => s.EndsWith(".unity"));
          foreach (var item in scenes)
          {
              CombineOneScene(item, bake);
          }
      //  CombineOneScene("Assets/Scenes/Scenes01_PLZZ.unity",bake);
        EditorUtility.ClearProgressBar();
    }
    /// <summary>
    /// 合并一个场景
    /// </summary>
    /// <param name="path"></param>
    private static void CombineOneScene(string path, bool bake)
    {
        var scene = EditorSceneManager.OpenScene(path);
        PBTitle = "正在合并场景:" + scene.name;
        SceneData sd = GetSceneData(scene);
        if (sd == null) return;
        var combineGO = CreatCombineObj(scene);
        var dic = ClassifyByPositionAndShaderAndProprity(scene);
        if (dic.Count == 0)
        {
            sd.Export(bake);
            return;
        }
        string directory = PATH + scene.name + "/";
        if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
        int count = 0;
        foreach (var item in dic)
        {
            PBContent = count + "/" + dic.Count;
            PBProcess = (float)count / dic.Count;
            Combine(item.Value.Where(s => s != null).Select(s => s.gameObject).ToArray(), scene, ref count, combineGO.transform, directory);
        }
        sd.Export(bake);
        Debug.Log("合并场景:" + scene.name);
    }

    /// <summary>
    /// 指定位置创建一个Combine物体 用于存放合并后的物体
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns>
    private static GameObject CreatCombineObj(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                break;
            }
        }
        return combineGO;
    }
    /// <summary>
    /// 获取场景里的所有只有一个材质球的MeshRenderer shader为Scene/Diffuse  或者 是 Scene/Cutout/Diffuse  且 贴图长和宽不能大于1024
    /// </summary>
    /// <param name="s"></param>
    private static MeshRenderer[] GetAllMeshsInScene(UnityEngine.SceneManagement.Scene scene)
    {
        const int texture_MaxSize = 1024;
        var roots = scene.GetRootGameObjects();
        var meshrenders = new List<MeshRenderer>();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                meshrenders.AddRange(item.GetComponentsInChildren<MeshRenderer>());
                break;
            }
        }
        foreach (var item in meshrenders.Where(s => s.gameObject.layer == LayerMask.NameToLayer(LAYER_NAME)))
        {
            item.transform.SetParent(combineGO.transform);
        } 
        return meshrenders.Where(s => s
        && s.gameObject.layer!=LayerMask.NameToLayer(LAYER_NAME)
        && s.enabled == true
        && s.sharedMaterials.Length == 1
        && s.sharedMaterial
        && (s.sharedMaterial.shader.name == "scenes/Diffuse"
        || s.sharedMaterial.shader.name == "scenes/Cutout/Diffuse")
        && s.sharedMaterial.GetTexture("_MainTex")
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.x <= texture_MaxSize
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.y <= texture_MaxSize)
        .ToArray();
    }

    /// <summary>
    /// 根据坐标,shader,参数分类
    /// </summary>
    /// <param name="meshrenderers"></param>
    private static Dictionary<string, List<MeshRenderer>> ClassifyByPositionAndShaderAndProprity(UnityEngine.SceneManagement.Scene

scene)
    {
        Dictionary<string, List<MeshRenderer>> dic = new Dictionary<string, List<MeshRenderer>>();
        foreach (var item in GetAllMeshsInScene(scene))
        {
            Material mt = item.sharedMaterial;
            string tag;
            if (mt.shader.name == "scenes/Cutout/Diffuse")
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Color") + "|" + mt.GetFloat

("_Cutoff");
            else
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Colors");
            if (!dic.ContainsKey(tag))
            {
                dic.Add(tag, new List<MeshRenderer>() { item });
            }
            else
            {
                dic[tag].Add(item);
            }
        }
        //清除只有1个的List
        List<string> checkOneRenderList = new List<string>();
        foreach (var item in dic)
        {
            if (item.Value.Count == 1) checkOneRenderList.Add(item.Key);
        }
        foreach (var item in checkOneRenderList)
        {
            dic.Remove(item);
        }
        return dic;
    }
    /// <summary>
    /// 合并   合并一个List MeshRnder 的  整个逻辑
    /// </summary>
    /// <param name="renderers"></param>
    /// <param name="sceneName">场景名称 用于标记</param>
    /// <param name="index">计数器</param>
    /// <param name="parameter">material的各个参数   //0是坐标 1是shader 2是颜色 3是cutoff值</param>
    /// <param name="combineRoot">合并的根物体 所有合并产生的物体都要放到这个物体下面</param>
    /// <returns></returns>
    private static void Combine(GameObject[] gos, UnityEngine.SceneManagement.Scene scene, ref int index, Transform

combineRoot, string directory)
    {
        if (gos.Length < 2) return;
        GameObject tempgo = new GameObject(scene.name + "_combine_" + index);
        tempgo.transform.SetParent(combineRoot);

        GameObject meshbaker = MB3_MeshBakerEditor.CreateNewMeshBaker();
        meshbaker.transform.SetParent(tempgo.transform);
        MB3_TextureBaker mbtb = meshbaker.GetComponent<MB3_TextureBaker>();
        MB3_MeshBaker mbmb = meshbaker.GetComponentInChildren<MB3_MeshBaker>();
        mbtb.fixOutOfBoundsUVs = true;
        mbtb.maxAtlasSize = 2048;
        mbtb.maxTilingBakeSize = 1024;
        mbtb.atlasPadding = 0;
        mbtb.considerNonTextureProperties = true;
        mbtb.meshBakerTexturePackerForcePowerOfTwo = true;
        mbtb.objsToMesh = gos.ToList();
        //mbtb.textureBakeResults  这个赋值会包含在下面这个方法里
        MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(mbtb, directory + index + ".asset");
        mbtb.CreateAtlases(null, true, new MB3_EditorMethods());
        if (mbtb.textureBakeResults != null) EditorUtility.SetDirty(mbtb.textureBakeResults);

        mbmb.textureBakeResults = mbtb.textureBakeResults;
        mbmb.CombinedMeshContains(tempgo);
        mbmb.useObjsToMeshFromTexBaker = true;
        mbmb.meshCombiner.doNorm = true;
        mbmb.meshCombiner.doTan = true;
        mbmb.meshCombiner.doUV = true;
        mbmb.resultPrefab = tempgo;
        mbmb.meshCombiner.outputOption = MB2_OutputOptions.bakeIntoSceneObject;
        mbmb.meshCombiner.resultSceneObject = tempgo;
       // mbmb.meshCombiner.targetRenderer = tempgo.AddComponent<MeshRenderer>();
        mbmb.meshCombiner.Apply();
        MB3_MeshBakerEditorInternal.bake(mbmb);

        Mesh tempMesh = tempgo.GetComponentInChildren<MeshFilter>().sharedMesh;
        AssetDatabase.CreateAsset(tempMesh, directory + "/" + index + "-mesh.asset");
        index++;
        foreach (var item in gos)
        {
            DestroyImmediate(item, false);
        }
        DestroyImmediate(meshbaker);
    }
    /// <summary>
    /// 找到一个场景里的SceneData
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns> 
    private static SceneData GetSceneData(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        foreach (var item in roots)
        {
            SceneData m = item.GetComponentInChildren<SceneData>();
            if (m) return m;
        }
        return null;
    }


    /// <summary>
    /// 判读两个物体是不是在同一个格子里
    /// </summary>
    /// <param name="t1"></param>
    /// <param name="t2"></param>
    /// <returns></returns>
    private static bool InSameGrid(Transform t1, Transform t2)
    {
        return (GetGridIndex(t1) == GetGridIndex(t2));
    }
    /// <summary>
    /// 计算一个物体的格子坐标
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    private static Vector2 GetGridIndex(Transform t)
    {
        return new Vector2((int)(t.position.x / GRIDSIDE), (int)(t.position.z / GRIDSIDE));
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299

代码简单了很多。。。
而且不会有上述那种棘手的问题。
这个插件处理上述问题是通过合并贴图的时候压缩一下,分析UV坐标,可能合并很多张一样的,可能会进行拉伸或者缩放
这些反正都不用自己考虑了

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/98094
推荐阅读
相关标签
  

闽ICP备14008679号