NGUI打图集工具Alpha通道分离

工具设计目的

NGUI中图集默认使用"Unlit/Transparent Colored" Shader来创建材质。这样的话需要一张RGBA32的带透明通道的贴图,一张515x512的图占用空间1M,加载到内存后变成2M。在手机内存还是比较宝贵的时代这个是不大能接受的。一般项目的做法是分隔成两张图一张color图包含RGB通道,一张alpha图包含A通道。通过shader来组合长RGBA的图,来实现接近于RGBA的效果。通常Android使用ETC格式,IOS使用PVRTC格式。一张ETC格式的512x512的图占用128kb,两张一起256kb。这样相对于RGBA32格式的只占用其四分之一的空间。

NGUI自带的图集工具并不支持打ETC通道分离。通常一般做法采用第三方工具TexturePacker来做这个,但是使用第三方工具来做这个也会比较麻烦。所以通过修改NGUI打图集工具来实现这个通道分离。

图集增加通道分离

通道分离的做法是生成一张完整RGBA32的图片,然后读取RGBA分别生成两张和RGBA32一样大小的RGB图片和Alpha图片。最后替换shader,生成两张图片合成的材质 。代码如下:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using com.geargames.common.utils;

public class UIAtlasChangeShaderTool : EditorWindow
{
    private bool _mSearched;
    private Object[] mObjects;
    private Vector2 mScroll = Vector2.zero;
    public static bool useAlpha = true;
    const string defShader = "Unlit/Transparent Colored";
    const string newShader = "这里填合成两张图的shader名字";

    private readonly string[] atlasPaths = { "这里填图集所在路径"};

    [MenuItem("Tool/Open UIAtlas Shader Change Window")]
    public static void OpenWindow()
    {
        UIAtlasChangeShaderTool window = (UIAtlasChangeShaderTool)EditorWindow.GetWindow(typeof(UIAtlasChangeShaderTool));
        window.Show();
    }
    void OnGUI()
    {
        if (mObjects != null && mObjects.Length != 0)
        {
            mScroll = GUILayout.BeginScrollView(mScroll);
            foreach (Object o in mObjects)
            {
                DrawUIAtalsObject(o);
            }
            GUILayout.EndScrollView();
        }
        GUILayout.Space(6f);
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        bool search = GUILayout.Button("Show All", "LargeButton", GUILayout.Width(120f));
        bool Normal = GUILayout.Button("Normal All", "LargeButton", GUILayout.Width(120f));
        bool Etc = GUILayout.Button("Etc All", "LargeButton", GUILayout.Width(120f));
        bool CreateTexture = GUILayout.Button("CreateTexture All", "LargeButton", GUILayout.Width(200f));
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
        if (search) Search(typeof(UIAtlas));

        if (mObjects == null || mObjects.Length == 0) return;
        if (Normal) foreach (Object o in mObjects) _ChangeShaderToNormal(o as UIAtlas);
        if (Etc) foreach (Object o in mObjects) _ChangeShaderToEtc(o as UIAtlas);
        if (CreateTexture)foreach (Object o in mObjects) SeperateRGBAandlphaChannel((o as UIAtlas).spriteMaterial.mainTexture as Texture2D);
    }

    private void DrawUIAtalsObject(Object obj)
    {
        if (obj == null) return;
        Component comp = obj as Component;
        UIAtlas at = obj as UIAtlas;
        GUILayout.BeginHorizontal();
        {
            string path = AssetDatabase.GetAssetPath(obj);

            if (string.IsNullOrEmpty(path))
            {
                path = "[Embedded]";
                GUI.contentColor = new Color(0.7f, 0.7f, 0.7f);
            }
            else if (comp != null && EditorUtility.IsPersistent(comp.gameObject))
                GUI.contentColor = new Color(0.6f, 0.8f, 1f);

            GUILayout.Label(obj.name, "TextArea", GUILayout.Width(160f), GUILayout.Height(20f));
            GUILayout.Label(path.Replace("Assets/", ""), "TextArea", GUILayout.Width(300f), GUILayout.Height(20f));
            GUILayout.Label(at.spriteMaterial.shader.name, "TextArea", GUILayout.Width(200f), GUILayout.Height(20f));
            GUI.contentColor = Color.white;
            if (GUILayout.Button("Normal", "ButtonLeft", GUILayout.Width(60f), GUILayout.Height(16f)))
            {
                _ChangeShaderToNormal(at);
                AssetDatabase.Refresh();
            }

            if (GUILayout.Button("Etc", "ButtonLeft", GUILayout.Width(60f), GUILayout.Height(16f)))
            {
                _ChangeShaderToEtc(at);
                AssetDatabase.Refresh();
            }
            
            if (GUILayout.Button("CreateTexture", "ButtonLeft", GUILayout.Width(100f), GUILayout.Height(16f)))
            {
               string assetPath =  AssetDatabase.GetAssetPath(at).Replace(".prefab",".png")  ;
                Texture2D texture = AssetDatabase.LoadAssetAtPath(assetPath,typeof(Texture2D)) as Texture2D;
                SeperateRGBAandlphaChannel(at.texture as Texture2D);
                AssetDatabase.Refresh();
            }
        }
        GUILayout.EndHorizontal();

    }
    protected void Search(System.Type mType)
    {
        _mSearched = true;
        List pathList = new List();
        _GetAllAtlasPath(pathList);
        bool isComponent = mType.IsSubclassOf(typeof(Component));
        List list = new List();

        if (mObjects != null)
        {
            for (int i = 0; i < mObjects.Length; ++i)
                if (mObjects[i] != null)
                    list.Add(mObjects[i]);
        }

        string path = "";

        string[] paths = pathList.ToArray();
        for (int i = 0; i < paths.Length; ++i)
        {
            path = paths[i];

            EditorUtility.DisplayProgressBar("Loading", "Searching assets, please wait...", (float)i / paths.Length);
            Object obj = AssetDatabase.LoadMainAssetAtPath(path);
            if (obj == null || list.Contains(obj)) continue;

            if (!isComponent)
            {
                System.Type t = obj.GetType();
                if (t == mType || t.IsSubclassOf(mType) && !list.Contains(obj))
                    list.Add(obj);
            }
            else if (PrefabUtility.GetPrefabType(obj) == PrefabType.Prefab)
            {
                Object t = (obj as GameObject).GetComponent(mType);
                if (t != null && !list.Contains(t)) list.Add(t);
            }
        }
        list.Sort(delegate (Object a, Object b) { return a.name.CompareTo(b.name); });
        mObjects = list.ToArray();

        EditorUtility.ClearProgressBar();
    }
    private void _GetAllAtlasPath(List pathList)
    {
        for (int i = 0; i < atlasPaths.Length; i++)
        {
            FileUtils.GetAllFiles(Application.dataPath + atlasPaths[i], pathList);
        }

        for (int i = 0; i < pathList.Count; i++)
        {
            pathList[i] = pathList[i].Replace("\\", "/").Replace(Application.dataPath, "Assets");
        }
    }

    static string c_flg = "_C";
    static string a_flg = "_A";
    static string exp_flg = ".png";
    static private void _ChangeShaderToNormal(UIAtlas obj)
    {
        if (obj.spriteMaterial.shader.name != defShader)
        {
            obj.spriteMaterial.shader = Shader.Find(defShader);
            string path = AssetDatabase.GetAssetPath(obj.spriteMaterial.mainTexture);
            string p = path.Substring(0, path.IndexOf(c_flg + "."))+ ".png";
            obj.spriteMaterial.mainTexture = AssetDatabase.LoadAssetAtPath(p , typeof(Texture2D)) as Texture2D;
            AssetDatabase.SaveAssets();
        }
    }
    static private void _ChangeShaderToEtc(UIAtlas obj)
    {
        if (obj.spriteMaterial.shader.name != newShader)
        {
            obj.spriteMaterial.shader = Shader.Find(newShader);
            string path = AssetDatabase.GetAssetPath(obj.spriteMaterial.mainTexture);
            string p = path.Substring(0, path.IndexOf("."));
            string cpath = p + c_flg + exp_flg;
            string apath = p + a_flg + exp_flg;
            obj.spriteMaterial.mainTexture = AssetDatabase.LoadAssetAtPath(cpath, typeof(Texture2D)) as Texture2D;
            obj.spriteMaterial.SetTexture("_MaskTex", AssetDatabase.LoadAssetAtPath(apath, typeof(Texture2D)) as Texture2D);
            Debug.Log("path = " + cpath + "  " + apath);
            AssetDatabase.SaveAssets();
        }
    }
    static void SeperateRGBAandlphaChannel(Texture2D sourcetex)
    {
        string path = AssetDatabase.GetAssetPath(sourcetex);
        AssetDatabase.ImportAsset(path);
        string p = path.Substring(0, path.IndexOf("."));
        string cpath = p + c_flg + exp_flg;
        string apath = p + a_flg + exp_flg;
        AssetDatabase.DeleteAsset(cpath);
        AssetDatabase.DeleteAsset(apath);
        TextureImporter ti = AssetImporter.GetAtPath(path) as TextureImporter;
        MakeTextureReadable(ti, path, false);
        Color[] sc = sourcetex.GetPixels();
        int sw = sourcetex.width;
        int sh = sourcetex.height;
        Texture2D rgbTex = new Texture2D(sw, sh, TextureFormat.RGB24, false);
        Texture2D alphaTex = new Texture2D(sw, sh, TextureFormat.RGB24, false);
        Color[] ac = new Color[sw * sh];
        for (int i = 0; i  
 

注意,我这用的是Unity2018.3,比较早期的版本在图集格式设置那块需要修改 。就是SetPlatformTextureSettings这个方法。
NGUI中打图集的工具写在UIAtlasMaker.cs里面。我们需要在系统打图集的选项里面增加一个选择打分离通道的。如下:


NGUI打图集工具Alpha通道分离_第1张图片
image.png

UIAtlasMaker.cs需要增加代码位置为:

    void OnEnable () { instance = this;
                //默认勾选上
        UIAtlasChangeShaderTool.flg = true;
    }
void OnGUI ()
    {
//省略之前代码。在布局的最后添加这个。
        UIAtlasChangeShaderTool.OnInspectorGUI(NGUISettings.atlas);
        NGUIEditorTools.EndContents();

                if (delete)
                {
                    List sprites = new List();
                    ExtractSprites(NGUISettings.atlas, sprites);

                    for (int i = sprites.Count; i > 0; )
                    {
                        SpriteEntry ent = sprites[--i];
                        if (mDelNames.Contains(ent.name))
                            sprites.RemoveAt(i);
                    }
                    UpdateAtlas(NGUISettings.atlas, sprites);
                    mDelNames.Clear();
                    NGUIEditorTools.RepaintSprites();
                }
                else if (update){
                     //更新的位置还原默认图集,用来生成RGBA32
                    UIAtlasChangeShaderTool.updateAtlas(NGUISettings.atlas, false);
                    UpdateAtlas(textures, true);
                } 
                else if (replace) UpdateAtlas(textures, false);

                if (NGUISettings.atlas != null && !string.IsNullOrEmpty(selection))
                {
                    NGUIEditorTools.SelectSprite(selection);
                }
                else if (NGUISettings.autoUpgradeSprites && (update || replace))
                {
                    NGUIEditorTools.UpgradeTexturesToSprites(NGUISettings.atlas);
                    NGUIEditorTools.RepaintSprites();
                  //这里生成两个图集
                    UIAtlasChangeShaderTool.updateAtlas(NGUISettings.atlas);
                }
}

你可能感兴趣的:(NGUI打图集工具Alpha通道分离)