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