图集资源打包与加载

图集的打包与加载

开始

图集由来

图集是指将多张较小的图片,拼成一张大图,打包进assetBundle中。好处如下:
1.减少draw call: 多张图片需要多次draw call,合成了一张大图则只需要一次draw call。
2.减少内存占用:OpenGL ES中每张贴图都需要设置成2的n次方才能使用。比如你有一张宽高为100x100和一张宽高为10x10的图片,如果不合成大贴图,那么需要使用128x128和16x16的两张图片(分别是2的7次方和2的4次方),但如果使用一张大图的话,可以把100x100和10x10的图片放到128x128的大图中,这样就用一张图片。

生成图集

使用texturePacker插件生成图集。
原理是,使用texturePacker工具,可将多张小图生成一张整图,并且会生成txt信息,记录了每个小图的信息。 用于之后在加载精灵。
核心代码如下:

        //核心代码:  传入四个参数, 依次是commond命令载体, 打成整图后的图片路径, 生成的txt文件位置,   需要被打成图集的图片们(依次用空格间隔开)
      string commond = @" --sheet {0} --data {1} --format unity-texture2d --trim-mode None --pack-mode Best --algorithm Polygon --max-size " + AtlasSize + @" --size-constraints POT --disable-rotation --scale 1 {2}";
        string arg = string.Format(commond, pngPath, dataPath, atlasPaths.ToString());
        EditorUtility.DisplayProgressBar("message","texturePacker",0.1f);
        System.Diagnostics.Process process = new System.Diagnostics.Process();
        process.StartInfo.Arguments = arg;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.FileName = @"TexturePacker.exe";
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.Start();
        process.WaitForExit();
        process.Dispose();
        EditorUtility.ClearProgressBar();
        AssetDatabase.Refresh();

开启进程调用texturePacker插件,装载多个小图,进行生成。
如下图所示,小图们变成了单张图和一个文本文件
生成前的小图们
生成后变为一张大图
大图详情

图集精灵的文本文件数据信息

解析图集

生成图集后,还需要对生成的txt文件进行处理,解析成unity中方便处理的数据,这是加载精灵图时必要的信息。如下需要大图以及精灵信息。

Sprite sprite = Sprite.Create(tx2D,data.rect,data.pivot, 100f,0,SpriteMeshType.Tight,Vector4.zero);

精灵数据

精灵数据结构如下:
精灵数据结构
所以首先建立如下类,与其对应:

public class TUiSpriteData
{

    public string spriteName;
    public string url;
    public Rect rect;
    public Vector2 pivot;
    public Vector4 border;
}

存储数据的实体

图集这些信息需要缓存下来,所以再增加对应的scriptObject类型文件,此文件可缓存用于缓存数据信息,方便对资源的数据进行处理。
类如下:

public class TUIAtlasSheet : ScriptableObject {
    public  List<TUiSpriteData> uispriteList = new List<TUiSpriteData>();
}

生成后的实体数据资源文件如下:
存储精灵数据的资源文件

prefab进行引用

为了方便管理,再新建prefab去引用到asset文件和png。
新建prefab

核心代码

    void AnalySheet(string _atlasName)
    {
        string atlasPath = $"{atlasResPath}/{_atlasName}";
        string outPngPath = $"{atlasResPath}/{_atlasName}/{_atlasName}.png";
        string outTxtPath = $"{atlasResPath}/{_atlasName}/{_atlasName}.txt";
        string outTextAssetPath = $"{atlasResPath}/{_atlasName}/{_atlasName}.asset";
        string outPrefabPath = $"{atlasResPath}/{_atlasName}/{_atlasName}.prefab";

        if (Directory.Exists(outPrefabPath))
        {
            Directory.Delete(outPrefabPath,true);
        }
        AssetDatabase.Refresh();

        GameObject prefab = new GameObject();
        Object o = PrefabUtility.CreateEmptyPrefab(BuildTool.RemovePathPrefix(outPrefabPath));
        TUIAtlas tAtlas = prefab.AddComponent<TUIAtlas>();
        TUIAtlasSheet tSheetList = ScriptableObject.CreateInstance<TUIAtlasSheet>();
        AssetDatabase.CreateAsset(tSheetList, BuildTool.RemovePathPrefix( outTextAssetPath));


        TextAsset txtAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(BuildTool.RemovePathPrefix( outTxtPath));
        Debug.Log("Txt:" + txtAsset);
        string txtString = txtAsset.ToString() ;
        string[] split = txtString.Split('\n');
        foreach (var _curSplit in split)
        {
            if (_curSplit.Contains(";"))
            {
                string [] _dataSplit = _curSplit.Split(';');
                
                TUiSpriteData data = new TUiSpriteData();
                data.border = Vector4.zero;
                data.spriteName = _dataSplit[0];
                float x = 0;
                float.TryParse(_dataSplit[1],out x);
                float y = 0;
                float.TryParse(_dataSplit[2],out y);
                float width = 0;
                float.TryParse(_dataSplit[3],out width);
                float height = 0;
                float.TryParse(_dataSplit[4],out height);
                
                data.rect = new Rect(x,y,width,height);
                data.pivot = new Vector2(0.5f,0.5f);
                tSheetList.uispriteList.Add(data);
            }
        }
        
        SpriteMetaData[] metaData = new SpriteMetaData[tSheetList.uispriteList.Count];
        for (int i = 0; i < metaData.Length; i++)
        {
            metaData[i].pivot = tSheetList.uispriteList[i].pivot;
            metaData[i].border = tSheetList.uispriteList[i].border;
            metaData[i].rect = tSheetList.uispriteList[i].rect;
            metaData[i].name = tSheetList.uispriteList[i].spriteName;
        }
        
        TextureImporter textureImporter = TextureImporter.GetAtPath(BuildTool.RemovePathPrefix(outPngPath)) as TextureImporter;
        textureImporter.spritesheet = metaData;
        textureImporter.textureType = TextureImporterType.Sprite;
        textureImporter.spriteImportMode = SpriteImportMode.Multiple;
        textureImporter.mipmapEnabled = false;
        textureImporter.SaveAndReimport();

        tAtlas.sheet = tSheetList;
   
        Texture2D tx2d = AssetDatabase.LoadAssetAtPath<Texture2D>(BuildTool. RemovePathPrefix(outPngPath));
        tAtlas.Texture = tx2d;
        PrefabUtility.ReplacePrefab(prefab, o);
        
    }

打包图集

打包的思路,
1、 先通过md5算法计算资源的hash值存在本地, 之后每次打包前判断,如果该文件的md5值与之前也存在本地的相同,说明资源没有变化,所以不用打包,否则再进行打包。 由于打包大图集比较耗时,所以这种方法可以跳过不必要的打包,节约时间。
2、进行打包环节后,对prefab和texture进行打包,由于prefab引用了asset数据文件,所以它也会被自动进行打入包中。

md5版本对比

                string prefabPath = $"{dirPath}/{atlasName}.prefab";
                TCommon.GetNewAssetVersion( prefabPath );
                TCommon.GetOldAssetVersion(TCommon._atlas);
                if (TCommon.IsNeedPak(prefabPath))
                {
                    Build(dirPath,atlasName);
                }
                TCommon.SaveAssetVersion(TCommon._atlas);

图集的本地hash文件

打包

对prefab与Texture打包

        BuildTool.DeleteOutPutDir(TCommon._atlas);
        BuildTool.ClearAB();
        string prefabPath =  $"{inAtlasPath}/{atlasName}/{atlasName}.prefab";
        prefabPath = BuildTool.RemovePathPrefix(prefabPath);
        string pngPath = $"{inAtlasPath}/{atlasName}/{atlasName}.png";
        pngPath = BuildTool.RemovePathPrefix(pngPath);
        Debug.Log(BuildTool.RemovePathPrefix(prefabPath));
        AssetImporter importerPrefab = AssetImporter.GetAtPath( prefabPath );
        importerPrefab.assetBundleName =  atlasName;
        importerPrefab.assetBundleVariant =  BuildTool.ABSUFFIX;
        AssetImporter importerPng = AssetImporter.GetAtPath(pngPath);
        importerPng.assetBundleName =  atlasName;
        importerPng.assetBundleVariant = BuildTool.ABSUFFIX;
        BuildTool.BuildBundle(outAtlasPath);
        BuildTool.ClearAB();
        BuildTool.DeleteManifest();
        AssetDatabase.Refresh();

包查看

加载图集

加载的思路就对应打包的思路,加载出texture与prefab,再获取到prefab得到精灵的数据信息,最后调用Sprite.Create,得到和传入参数。

    public Sprite LoadAtlas(string atlasName, string spriteName )
    {
        Debug.Log("LoadAtlas");
        string abPath = $"{Application.streamingAssetsPath}/atlas/{atlasName}{TCommon.ABSUFFIX_AFTER}";
        AssetBundle ab = AssetBundle.LoadFromFile(abPath);
        GameObject atlasPrefab = ab.LoadAsset<GameObject>(atlasName);
        atlasPrefab.SetParent(GameObject.Find("Canvas").transform);
        TUIAtlas tuiAtlas = atlasPrefab.GetComponent<TUIAtlas>();
        TUIAtlasSheet sheet = tuiAtlas.sheet;
        TUiSpriteData data = new TUiSpriteData();
        for (int i = 0; i < sheet.uispriteList.Count; i++)
        {
            if (sheet.uispriteList[i].spriteName == spriteName)
            {
                data = sheet.uispriteList[i];
                break;
            }
        }
        Texture2D tx2D = ab.LoadAsset(atlasName) as Texture2D;
        //使用图集和精灵信息,加载出精灵
        Sprite sprite = Sprite.Create(tx2D,data.rect,data.pivot, 100f,0,SpriteMeshType.Tight,Vector4.zero);
        return sprite;
    }

结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值