游戏UI动态加载图片优化

说到UI优化,很多人对其并不以为意,UI的制作无非使用UGUI或者NGUI。UI优化主要是针对图集,还有一些依赖项的优化,针对的是内存优化,上面这些都是关于静态UI的优化,这个是作为程序员都要经历的阶段。其实很多开发者对使用UGUI或者NGUI已经墨守成规了,我们如果真想去优化,其实完全可以自己封装一套UI的底层代码,效率很高的,摒弃了UGUI和NGUI影响效率的代码,自己封装的简单实用,在我的视频课程——飞车竞速
在这个游戏中使用的UI都是自己封装的,游戏 UI底层代码封装如下所示:
这里写图片描述
这个UI底层封装的是针对我们项目的需求开发的,在这里是给读者拓展一下思路,UI优化并不仅限于图集和图片大小的优化,代码层面也是可以做的。
以上给读者介绍的都是关于静态UI的制作与优化,本篇博客主要是给读者介绍一下关于UI的动态图片优化,首先明白什么是动态加载图片,比如我们创建的角色ICON,还有游戏生成的怪物ICON等等,就是说这些ICON不是事先就有的,而是根据游戏动态加载到场景中的,我们的优化就是针对这些动态加载的ICON去做优化,在程序运行的过程中打图集,下面我们先实现一个2D动作播放的动态图集生成的Demo。
在Unity中如下图所示:
这里写图片描述
上图就是我们常见的序列帧动画,它的制作方式很多,我们可以事先将其打成图集,按照顺序播放,也可以直接播放,我们接下来实现的是动态的加载,动态的打图集算法。我们实现的思路是首先获取到需要打图集的图片,然后将其按照顺序依次在已创建的空白图片上把序列帧图片粘贴上,同时我们还需要使用一个Json文件记录我们对应图片的信息,这个信息非常重要,它主要目的是帮助我们找到对应的图片,这个信息包含:图片所在位置,图片的宽,高,图片的名字。
这样我们就完成了图集的动态生成,接下来就是逻辑代码的编写了。
第一步:获取动态加载的文件,在这里我们是将其拷贝到一个指定的目录下面,方便我们打图集,这个目录我们自己指定,就是模拟程序运行时动态获取到图片,然后去加载,代码如下:

        CopyPasteFoldersAndPNG(Application.dataPath + "/SpriteGenerator/Demos/Sprites", Application.persistentDataPath);

        string[] files = Directory.GetFiles(Application.persistentDataPath + "/Textures", "*.png");

第二步:定义图集大小,将获取到的图片在图集上进行排列, 核心算法如下:

protected IEnumerator createPack(string savePath = "") {

            if (savePath != "") {

                if (deletePreviousCacheVersion && Directory.Exists(Application.persistentDataPath + "/AssetPacker/" + cacheName + "/"))
                    foreach (string dirPath in Directory.GetDirectories(Application.persistentDataPath + "/AssetPacker/" + cacheName + "/", "*", SearchOption.AllDirectories))
                        Directory.Delete(dirPath, true);

                Directory.CreateDirectory(savePath);
            }

            List<Texture2D> textures = new List<Texture2D>();
            List<string> images = new List<string>();

            foreach (TextureToPack itemToRaster in itemsToRaster) {

                WWW loader = new WWW("file:///" + itemToRaster.file);

                yield return loader;

                textures.Add(loader.texture);
                images.Add(itemToRaster.id);
            }

            int textureSize = allow4096Textures ? 4096 : 2048;

            List<Rect> rectangles = new List<Rect>();
            for (int i = 0; i < textures.Count; i++)
                if (textures[i].width > textureSize || textures[i].height > textureSize)
                    throw new Exception("A texture size is bigger than the sprite sheet size!");
                else
                    rectangles.Add(new Rect(0, 0, textures[i].width, textures[i].height));

            const int padding = 1;

            int numSpriteSheet = 0;
            while (rectangles.Count > 0) {

                Texture2D texture = new Texture2D(textureSize, textureSize, TextureFormat.ARGB32, false);
                Color32[] fillColor = texture.GetPixels32();
                for (int i = 0; i < fillColor.Length; ++i)
                    fillColor[i] = Color.clear;

                RectanglePacker packer = new RectanglePacker(texture.width, texture.height, padding);

                for (int i = 0; i < rectangles.Count; i++)
                    packer.insertRectangle((int) rectangles[i].width, (int) rectangles[i].height, i);

                packer.packRectangles();

                if (packer.rectangleCount > 0) {

                    texture.SetPixels32(fillColor);
                    IntegerRectangle rect = new IntegerRectangle();
                    List<TextureAsset> textureAssets = new List<TextureAsset>();

                    List<Rect> garbageRect = new List<Rect>();
                    List<Texture2D> garabeTextures = new List<Texture2D>();
                    List<string> garbageImages = new List<string>();

                    for (int j = 0; j < packer.rectangleCount; j++) {

                        rect = packer.getRectangle(j, rect);

                        int index = packer.getRectangleId(j);

                        texture.SetPixels32(rect.x, rect.y, rect.width, rect.height, textures[index].GetPixels32());

                        TextureAsset textureAsset = new TextureAsset();
                        textureAsset.x = rect.x;
                        textureAsset.y = rect.y;
                        textureAsset.width = rect.width;
                        textureAsset.height = rect.height;
                        textureAsset.name = images[index];

                        textureAssets.Add(textureAsset);

                        garbageRect.Add(rectangles[index]);
                        garabeTextures.Add(textures[index]);
                        garbageImages.Add(images[index]);
                    }

                    foreach (Rect garbage in garbageRect)
                        rectangles.Remove(garbage);

                    foreach (Texture2D garbage in garabeTextures)
                        textures.Remove(garbage);

                    foreach (string garbage in garbageImages)
                        images.Remove(garbage);

                    texture.Apply();

                    if (savePath != "") {

                        File.WriteAllBytes(savePath + "/data" + numSpriteSheet + ".png", texture.EncodeToPNG());
                        File.WriteAllText(savePath + "/data" + numSpriteSheet + ".json", JsonUtility.ToJson(new TextureAssets(textureAssets.ToArray())));
                        ++numSpriteSheet;
                    }

                    foreach (TextureAsset textureAsset in textureAssets)
                        mSprites.Add(textureAsset.name, Sprite.Create(texture, new Rect(textureAsset.x, textureAsset.y, textureAsset.width, textureAsset.height), Vector2.zero, pixelsPerUnit, 0, SpriteMeshType.FullRect));
                }

            }

            OnProcessCompleted.Invoke();
        }

代码虽然比较长,但是理解起来不难,没有用任何算法,它只是按照顺序去排列,然后保存成图集和json文件,最终的图集如下所示:
这里写图片描述
Json文件如下所示:

{"assets":[{"x":0,"y":0,"width":286,"height":387,"name":"walking0004"},{"x":0,"y":388,"width":286,"height":387,"name":"walking0005"},{"x":0,"y":776,"width":286,"height":387,"name":"walking0003"},{"x":0,"y":1164,"width":286,"height":387,"name":"walking0001"},{"x":0,"y":1552,"width":286,"height":387,"name":"walking0002"},{"x":287,"y":0,"width":286,"height":387,"name":"walking0006"},{"x":287,"y":388,"width":286,"height":387,"name":"walking0010"},{"x":287,"y":776,"width":286,"height":387,"name":"walking0011"},{"x":287,"y":1164,"width":286,"height":387,"name":"walking0009"},{"x":287,"y":1552,"width":286,"height":387,"name":"walking0007"},{"x":574,"y":0,"width":286,"height":387,"name":"walking0008"}]}

接下来就是播放动画了,也就是最后一步。
第三步:播放动画逻辑代码实现如下:

    IEnumerator LoadAnimation() {

        Sprite[] sprites = assetPacker.GetSprites("walking");

        int j = 0;
        while (j < sprites.Length) {

            anim.sprite = sprites[j++];

            yield return new WaitForSeconds(0.1f);

            if (j == sprites.Length)
                j = 0;
        }
    }

实现效果如下所示:
这里写图片描述
这样就完成了动态图集的生成,当然我们还可以生成大的图集,这个一般在端游中使用的,效果如下图所示:
这里写图片描述

以上是关于动态图集的生成,我们测试了一下实现500个矩形,类似500个小ICON,花费时间是8ms,非常理想。该技术可以应用到项目开发中。。。。。。
项目下载地址:链接:https://pan.baidu.com/s/1bV6hepy2H6t1Xnzg88JOdg 密码:tybj

阅读更多

扫码向博主提问

海洋_

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 3D引擎架构
  • 服务器架构
  • GPU渲染
  • 客户端架构
  • 引擎优化
去开通我的Chat快问
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/81542381
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭