查看: 4469|回复: 4
打印 上一主题 下一主题

[GUI] Unity3d运行时程序创建NGUI图集 - 转载

[复制链接]

2722

主题

42

听众

3万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
38268
精华
111

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2014-11-24 01:41:22 |只看该作者 |倒序浏览

项目中UI经常要用到动态加载图片,比较简单的方法是使用UITexture控件,这样只用3W类下载好图片后直接给UITexture控件赋值就好了,缺点是DrawCall太高了,一个UITexture一个DrawCall,通常只有是背景图片这种才会使用UITexture控件,如果是下拉列表项,我们通常都是使用的UISprite控件,这时就需要我们在程序运行时,由程序创建图集,给UISprite控件使用

请先看一下帖子:http://www.unitymanual.com/thread-16004-1-1.html
帖子的第一种方法其实已经说明了如何在程序中创建图集,只不过他这里是使用一张图片做成图集,再给UISprite控件使用。
=====================
=============================================
public class ImageLoader : MonoBehaviour {

//需要加载动态图片的对象

public UISprite m_img;

//自用的Atlas

private UIAtlas m_uiAtlas;

///

/// 加载的贴图

///

/// Tex.

public void ImageLoad(Texture2D tex)

{

if(tex == null)

{

return;

}

if(tex.name == m_img.spriteName)

{

return;

}

//准备对象和材质球

if(m_uiAtlas == null)

{

Material mat;

Shader shader = Shader.Find(“Unlit/Transparent Colored”);

mat = new Material(shader);

m_uiAtlas = this.gameObject.AddComponent();

m_uiAtlas.spriteMaterial = mat;

}

//设定贴图

m_uiAtlas.spriteMaterial.mainTexture = tex;

m_uiAtlas.coordinates = UIAtlas.Coordinates.Pixels;

//为对应UISprite接口,给Atlas加对象

UIAtlas.Sprite sprite = new UIAtlas.Sprite();

sprite.name = tex.name;

sprite.outer = sprite.inner = new Rect(0f, 0f, tex.width, tex.height);

m_uiAtlas.spriteList.Clear();

m_uiAtlas.spriteList.Add(sprite);

//设置完成

m_img.atlas = m_uiAtlas;

m_img.spriteName = tex.name;

}

}
============================================================
       我们工作中常常遇到的是需要将多张图片制作成一个图集,再给多个UISprite控件使用,其实看懂了上面的代码运行时如何生成图集,再结合平常在编辑器中制作图集,答案就已经出来了。请大家好好看看NGUI的编辑器脚本UIAtlasMaker,也就是我们平常在编辑器中制作图集时用的脚本。我们缺少的仅仅是将多张图片PackTextures(打包)成一张大图(也就是我们的图集啦),其实UIAtlasMaker脚本那么长,核心的操作也就是为了PackTextures,也就是unity3d的API里Texture2D.PackTextures函数,这里大家可以参考我以前转的别人的一篇关于该函数用法的文章:http://blog.sina.com.cn/s/blog_930ffa0b0102uyl8.html
和一篇别人分析UIAtlasMaker脚本的文章:http://dsqiu.iteye.com/blog/1967088
好了,最后就是我将UIAtlasMaker脚本里打包操作的函数拔出来后的工具类了,只有CreatAtlasFromTex这一个对外使用的函数。使用时只需要传人个UIAtlas组件和图片LIST就可以了,图集生成好后,UISprite控件就可以使用该动态图集了,这样好多个UISprite控件组成的下拉列表项只占一个DrawCall。
=============================CreatAtlas.cs=============================
using UnityEngine;
using System.Collections.Generic;

///
/// 运行时创建NGUI图集,NGUI2.6.3
///
public static class CreatAtlas {

    class SpriteEntry {
        public Texture2D tex;    // Sprite texture -- original texture or a temporary texture
        public Rect rect;        // Sprite's outer rectangle within the generated texture atlas
        public int minX = 0;    // Padding, if any (set if the sprite is trimmed)
        public int maxX = 0;
        public int minY = 0;
        public int maxY = 0;
        public bool temporaryTexture = false;    // Whether the texture is temporary and should be deleted
    }

    static Shader s_shader = Shader.Find("Unlit/Transparent Colored");
    static Material s_mat = new Material(s_shader);
    static int s_maximumAtlasSize = 2048;
    static TextureFormat s_TextureFormat = TextureFormat.RGBA32;
  
    static void Init ( UIAtlas uIAtlas ) {
        if ( uIAtlas.spriteMaterial == null ) {
            // 准备材质球   
            uIAtlas.spriteMaterial = s_mat;
        }     
    }

    ///
    /// 运行时创建NGUI图集,NGUI2.6.3
    ///
    /// 使用该UIAtlas在运行时创建NGUI图集
    /// 用雨创建图集的多张小图
    public static void CreatAtlasFromTex ( UIAtlas uIAtlas, List textures ) {   
        if ( null == textures || textures.Count <= 0 ) {
            Debug.LogWarning("textures is null or count <= 0 !!");
            return;
        }
        if ( null == uIAtlas ) {
            Debug.LogWarning("uIAtlas is null");
            return;
        } else {
            Init(uIAtlas);

            // 设定贴图,将小图映射为SpriteEntry
            List sprites = CreateSprites(textures);
            // 将多个小图PackTexture为一张大图,给图集用
            uIAtlas.spriteMaterial.mainTexture = UpdateTexture(uIAtlas, sprites);
            ReplaceSprites(uIAtlas, sprites);

        }     
    }

    #region copy UIAtlasMaker编辑器脚本

    static List CreateSprites (List textures) {
        List list = new List( );

        foreach (Texture tex in textures) {
            Texture2D oldTex = tex as Texture2D;
            if (oldTex == null) continue;
            // If we want to trim transparent pixels, there is more work to be done
            Color32[ ] pixels = oldTex.GetPixels32( );

            int xmin = oldTex.width;
            int xmax = 0;
            int ymin = oldTex.height;
            int ymax = 0;
            int oldWidth = oldTex.width;
            int oldHeight = oldTex.height;

            // Find solid pixels
            for (int y = 0, yw = oldHeight; y < yw; ++y) {
                for (int x = 0, xw = oldWidth; x < xw; ++x) {
                    Color32 c = pixels[y * xw + x];

                    if (c.a != 0) {
                        if (y < ymin) ymin = y;
                        if (y > ymax) ymax = y;
                        if (x < xmin) xmin = x;
                        if (x > xmax) xmax = x;
                    }
                }
            }

            int newWidth = (xmax - xmin) + 1;
            int newHeight = (ymax - ymin) + 1;

            // If the sprite is empty, don't do anything with it
            if (newWidth > 0 && newHeight > 0) {
                SpriteEntry sprite = new SpriteEntry( );
                sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height);

                // If the dimensions match, then nothing was actually trimmed
                if (newWidth == oldWidth && newHeight == oldHeight) {
                    sprite.tex = oldTex;
                    sprite.temporaryTexture = false;
                } else {
                    // Copy the non-trimmed texture data into a temporary buffer
                    Color32[ ] newPixels = new Color32[newWidth * newHeight];

                    for (int y = 0; y < newHeight; ++y) {
                        for (int x = 0; x < newWidth; ++x) {
                            int newIndex = y * newWidth + x;
                            int oldIndex = (ymin + y) * oldWidth + (xmin + x);
                            newPixels[newIndex] = pixels[oldIndex];
                        }
                    }

                    // Create a new texture
                    sprite.temporaryTexture = true;
                    sprite.tex = new Texture2D(newWidth, newHeight);
                    sprite.tex.name = oldTex.name;
                    sprite.tex.SetPixels32(newPixels);
                    sprite.tex.Apply( );

                    // Remember the padding offset
                    sprite.minX = xmin;
                    sprite.maxX = oldWidth - newWidth - xmin;
                    sprite.minY = ymin;
                    sprite.maxY = oldHeight - newHeight - ymin;
                }
                list.Add(sprite);
            }
        }
        return list;
    }

    static Texture2D UpdateTexture (UIAtlas atlas, List sprites) {

        Texture2D tex = new Texture2D(1, 1, s_TextureFormat, false);
        PackTextures(tex, sprites);
        atlas.spriteMaterial.mainTexture = tex;
        return tex;
    }

    static void PackTextures (Texture2D tex, List sprites) {
        Texture2D[ ] textures = new Texture2D[sprites.Count];
        for (int i = 0; i < sprites.Count; ++i) textures = sprites.tex;

        Rect[ ] rects = tex.PackTextures(textures, 1, s_maximumAtlasSize);

        for (int i = 0; i < sprites.Count; ++i) {
            sprites.rect = NGUIMath.ConvertToPixels(rects, tex.width, tex.height, true);
        }
    }

    static void ReplaceSprites (UIAtlas atlas, List sprites) {
        // Get the list of sprites we'll be updating
        List spriteList = atlas.spriteList;
        List kept = new List( );

        // The atlas must be in pixels
        atlas.coordinates = UIAtlas.Coordinates.Pixels;

        // Run through all the textures we added and add them as sprites to the atlas
        for (int i = 0; i < sprites.Count; ++i) {
            SpriteEntry se = sprites;
            UIAtlas.Sprite sprite = AddSprite(spriteList, se);
            kept.Add(sprite);
        }

        // Remove unused sprites
        for (int i = spriteList.Count; i > 0; ) {
            UIAtlas.Sprite sp = spriteList[--i];
            if (!kept.Contains(sp)) spriteList.RemoveAt(i);
        }
        atlas.MarkAsDirty( );
    }

    static UIAtlas.Sprite AddSprite (List sprites, SpriteEntry se) {
        UIAtlas.Sprite sprite = null;

        // See if this sprite already exists
        foreach (UIAtlas.Sprite sp in sprites) {
            if (sp.name == se.tex.name) {
                sprite = sp;
                break;
            }
        }

        if (sprite != null) {
            float x0 = sprite.inner.xMin - sprite.outer.xMin;
            float y0 = sprite.inner.yMin - sprite.outer.yMin;
            float x1 = sprite.outer.xMax - sprite.inner.xMax;
            float y1 = sprite.outer.yMax - sprite.inner.yMax;

            sprite.outer = se.rect;
            sprite.inner = se.rect;

            sprite.inner.xMin = Mathf.Max(sprite.inner.xMin + x0, sprite.outer.xMin);
            sprite.inner.yMin = Mathf.Max(sprite.inner.yMin + y0, sprite.outer.yMin);
            sprite.inner.xMax = Mathf.Min(sprite.inner.xMax - x1, sprite.outer.xMax);
            sprite.inner.yMax = Mathf.Min(sprite.inner.yMax - y1, sprite.outer.yMax);
        } else {
            sprite = new UIAtlas.Sprite( );
            sprite.name = se.tex.name;
            sprite.outer = se.rect;
            sprite.inner = se.rect;
            sprites.Add(sprite);
        }

        float width = Mathf.Max(1f, sprite.outer.width);
        float height = Mathf.Max(1f, sprite.outer.height);

        // Sprite's padding values are relative to width and height
        sprite.paddingLeft = se.minX / width;
        sprite.paddingRight = se.maxX / width;
        sprite.paddingTop = se.maxY / height;
        sprite.paddingBottom = se.minY / height;
        return sprite;
    }

    #endregion copy UIAtlasMaker编辑器脚本

}
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

heise    

4

主题

3

听众

4249

积分

中级设计师

Rank: 5Rank: 5

纳金币
105
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

沙发
发表于 2014-11-24 01:50:57 |只看该作者
感谢分享1
回复

使用道具 举报

hyui    

1

主题

2

听众

6671

积分

高级设计师

Rank: 6Rank: 6

纳金币
2715
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

板凳
发表于 2014-11-24 02:27:44 |只看该作者
cool code !!
回复

使用道具 举报

3

主题

1

听众

6189

积分

高级设计师

Rank: 6Rank: 6

纳金币
370
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

地板
发表于 2014-11-24 11:55:06 |只看该作者
但是有个问题,虽然DC是低了,但是内存高了,如果大量的碎图都不同,而里面的一些图片不用同时加载的话,还是宁愿牺牲DC换内存来的好
回复

使用道具 举报

0

主题

1

听众

38

积分

设计初学者

Rank: 1

纳金币
5
精华
0
5#
发表于 2014-12-26 14:21:49 |只看该作者
图集制作的时候,prefab如何关联那些sprite
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-12-2 20:49 , Processed in 0.105485 second(s), 36 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部