NGUI打圖集工具Alpha通道分離

工具設(shè)計(jì)目的

NGUI中圖集默認(rèn)使用"Unlit/Transparent Colored" Shader來創(chuàng)建材質(zhì)。這樣的話需要一張RGBA32的帶透明通道的貼圖,一張515x512的圖占用空間1M,加載到內(nèi)存后變成2M。在手機(jī)內(nèi)存還是比較寶貴的時(shí)代這個(gè)是不大能接受的。一般項(xiàng)目的做法是分隔成兩張圖一張color圖包含RGB通道,一張alpha圖包含A通道。通過shader來組合長RGBA的圖,來實(shí)現(xiàn)接近于RGBA的效果。通常Android使用ETC格式,IOS使用PVRTC格式。一張ETC格式的512x512的圖占用128kb,兩張一起256kb。這樣相對(duì)于RGBA32格式的只占用其四分之一的空間。

NGUI自帶的圖集工具并不支持打ETC通道分離。通常一般做法采用第三方工具TexturePacker來做這個(gè),但是使用第三方工具來做這個(gè)也會(huì)比較麻煩。所以通過修改NGUI打圖集工具來實(shí)現(xiàn)這個(gè)通道分離。

圖集增加通道分離

通道分離的做法是生成一張完整RGBA32的圖片,然后讀取RGBA分別生成兩張和RGBA32一樣大小的RGB圖片和Alpha圖片。最后替換shader,生成兩張圖片合成的材質(zhì) 。代碼如下:

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<string> pathList = new List<string>();
        _GetAllAtlasPath(pathList);
        bool isComponent = mType.IsSubclassOf(typeof(Component));
        List<Object> list = new List<Object>();

        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<string> 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 <sw; i++)
        {
            for (int j = 0; j < sh; j++)
            {
                int ind = j * sw + i;
                Color color = sc[ind];
                color.r = color.a;
                color.g = color.a;
                color.b = color.a;
                ac[ind] = color;
            }
        }
        rgbTex.SetPixels(sc);
        rgbTex.Apply();
        alphaTex.SetPixels(ac);
        alphaTex.Apply();
        byte[] bytes = rgbTex.EncodeToPNG();
        File.WriteAllBytes(cpath, bytes);
        bytes = alphaTex.EncodeToPNG();
        File.WriteAllBytes(apath, bytes);

        AssetDatabase.ImportAsset(cpath);
        AssetDatabase.ImportAsset(apath);
        TextureImporter tic = AssetImporter.GetAtPath(cpath) as TextureImporter;
        TextureImporter tia = AssetImporter.GetAtPath(apath) as TextureImporter;
        SetTextureFormat(tic, tia);
        MakeTextureAnAtlas(ti, path, false, true);
        MakeTextureAnAtlas(tic, cpath, false, false);
        MakeTextureAnAtlas(tia, apath, false, true);
    }
    static public bool MakeTextureReadable(TextureImporter ti, string path, bool force)
    {
        TextureImporterSettings settings = new TextureImporterSettings();
        ti.ReadTextureSettings(settings);

        if (force || !settings.readable || settings.npotScale != TextureImporterNPOTScale.None || settings.alphaIsTransparency)
        {
            settings.readable = true;
            if (NGUISettings.trueColorAtlas) settings.textureFormat = TextureImporterFormat.AutomaticTruecolor;
            settings.npotScale = TextureImporterNPOTScale.None;
            settings.alphaIsTransparency = false;
            ti.SetTextureSettings(settings);
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
        }
        return true;
    }
    static bool MakeTextureAnAtlas(TextureImporter ti, string path, bool force, bool alphaTransparency)
    {
        TextureImporterSettings settings = new TextureImporterSettings();
        ti.ReadTextureSettings(settings);

        if (force ||
            settings.readable ||
            settings.maxTextureSize < 4096 ||
            settings.wrapMode != TextureWrapMode.Clamp ||
            settings.npotScale != TextureImporterNPOTScale.ToNearest)
        {
            settings.readable = false;
            settings.maxTextureSize = 4096;
            settings.wrapMode = TextureWrapMode.Clamp;
            settings.npotScale = TextureImporterNPOTScale.ToNearest;

            if (NGUISettings.trueColorAtlas)
            {
                settings.textureFormat = TextureImporterFormat.ARGB32;
                settings.filterMode = FilterMode.Trilinear;
            }

            settings.aniso = 4;
            settings.alphaIsTransparency = alphaTransparency;
            ti.SetTextureSettings(settings);
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
        }
        return true;
    }
    static public void SetTextureFormat(TextureImporter tic, TextureImporter tia)
    {
        tic.alphaIsTransparency = false;
        tic.isReadable = tia.isReadable = false;
        tic.mipmapEnabled = tia.mipmapEnabled = false;
        // TextureImporterPlatformSettings textureImporterPlatformSettings = tic.
        // UnityEditor.TextureImporter.SetPlatformTextureSettings();
        
        TextureImporterPlatformSettings tips = new TextureImporterPlatformSettings();
        tips.name = "Android";
        tips.overridden = true;
        tips.maxTextureSize = 2048;
        tips.format = TextureImporterFormat.ETC_RGB4;
        tips.textureCompression = (int)UnityEditor.TextureCompressionQuality.Fast;

        tic.SetPlatformTextureSettings(tips);
        tia.SetPlatformTextureSettings(tips);
        tips.overridden = true;
        tips.name = "iPhone";
        tips.format = TextureImporterFormat.PVRTC_RGB4;
        tic.SetPlatformTextureSettings(tips);
        tia.SetPlatformTextureSettings(tips);
#if UNITY_ANDROID || UNITY_STANDALONE
        tips.name = "Standalone";
        tips.format = TextureImporterFormat.ETC_RGB4;
        tips.overridden = true;
        tic.SetPlatformTextureSettings(tips);
        tia.SetPlatformTextureSettings(tips);
        // tic.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.ETC_RGB4, (int)TextureCompressionQuality.Fast);
        // tia.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.ETC_RGB4, (int)TextureCompressionQuality.Fast);
        // tic.textureFormat = TextureImporterFormat.ETC_RGB4;
        // tia.textureFormat = TextureImporterFormat.ETC_RGB4;
#else
        tips.name = "Standalone";
        tips.format = TextureImporterFormat.PVRTC_RGB4;
        tips.overridden = true;
        tic.SetPlatformTextureSettings(tips);
        tia.SetPlatformTextureSettings(tips);
        // tic.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.PVRTC_RGB4, (int)TextureCompressionQuality.Fast);
        // tia.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.PVRTC_RGB4, (int)TextureCompressionQuality.Fast);
        // tic.textureFormat = TextureImporterFormat.PVRTC_RGB4;
        // tia.textureFormat = TextureImporterFormat.PVRTC_RGB4;
#endif
    }

    public static bool flg = false;
    static public void OnInspectorGUI(UIAtlas mLastAtlas)
    {
        GUILayout.BeginHorizontal();
        UIAtlasChangeShaderTool.flg = EditorGUILayout.Toggle("spaceAlpha", UIAtlasChangeShaderTool.flg, GUILayout.Width(100f));
        GUILayout.Label("分隔color,Alpha兩通道");
        GUILayout.EndHorizontal();
    }
    static public void updateAtlas(UIAtlas mLastAtlas)
    {
        updateAtlas(mLastAtlas, UIAtlasChangeShaderTool.flg);
    }
    static public void updateAtlas(UIAtlas mLastAtlas, bool flg)
    {
        if (flg)
        {
            SeperateRGBAandlphaChannel(mLastAtlas.texture as Texture2D);
            _ChangeShaderToEtc(mLastAtlas);
        }
        else
        {
            _ChangeShaderToNormal(mLastAtlas);
        }
        AssetDatabase.Refresh();
    }
}

注意,我這用的是Unity2018.3,比較早期的版本在圖集格式設(shè)置那塊需要修改 。就是SetPlatformTextureSettings這個(gè)方法。
NGUI中打圖集的工具寫在UIAtlasMaker.cs里面。我們需要在系統(tǒng)打圖集的選項(xiàng)里面增加一個(gè)選擇打分離通道的。如下:


image.png

UIAtlasMaker.cs需要增加代碼位置為:

    void OnEnable () { instance = this;
                //默認(rèn)勾選上
        UIAtlasChangeShaderTool.flg = true;
    }
void OnGUI ()
    {
//省略之前代碼。在布局的最后添加這個(gè)。
        UIAtlasChangeShaderTool.OnInspectorGUI(NGUISettings.atlas);
        NGUIEditorTools.EndContents();

                if (delete)
                {
                    List<SpriteEntry> sprites = new List<SpriteEntry>();
                    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){
                     //更新的位置還原默認(rèn)圖集,用來生成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();
                  //這里生成兩個(gè)圖集
                    UIAtlasChangeShaderTool.updateAtlas(NGUISettings.atlas);
                }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容