注:一些代码是在网上找的,太多就没有写。
前几天做项目,遇到所要用的的大量图片都必须从网上下载,当时用的NGUI做的UI界面,当时上网找了好多,都不行,最后找一篇还差不多,但是也不好用,我重新捋了一遍;
首先,我想到的就是用NGUI的UITexture组件的UITexture.mainTexture来直接将WWW获取的图片给赋值上去,这样很简单,但是有新的问题,图片量比较大,如果图片少的话可以使用这种方法。
其次,换UI界面了,用UGUI做,UGUI是自动打图集,所以说方便,也可以用一两个RawTexture,但是UGUI要显示图片,必须将图片转换成Sprite,这也是一个难点,但是能够解决,可以将WWW 获取的图片用代码转换成Sprite格式,(转换这篇博客就不说了,下一篇我在说一下,有不会的可以看一看)。
最后,就是本博客的主要内容;NGUI动态打图集:
研究了好长时间,这种方法的只能是将图集打出来,但是不能弄成预设,图集打在一个空的GameObject上面,直接上代码:
using UnityEngine; using System.Collections; using System.Collections.Generic; public class NGUIAtlasMaker : MonoBehaviour { public static bool atlasTrimming = true; public static bool atlasPMA = false; public static bool unityPacking = false; public static int atlasPadding = 1; public static bool allow4096 = true; //主要就是继承UISpriteData,我们要修改它的类型 public class SpriteEntry : UISpriteData { // Sprite texture -- original texture or a temporary texture public Texture2D tex; // Whether the texture is temporary and should be deleted public bool temporaryTexture = false; } static int Compare(SpriteEntry a, SpriteEntry b) { if (a == null && b != null) return 1; if (a != null && b == null) return -1; int aPixels = a.width * a.height; int bPixels = b.width * b.height; if (aPixels > bPixels) return -1; else if (aPixels < bPixels) return 1; return 0; } //对于Android和IOS是不一样的 static bool PackTextures(Texture2D tex, List<SpriteEntry> sprites) { Texture2D[] textures = new Texture2D[sprites.Count]; Rect[] rects; #if UNITY_3_5 || UNITY_4_0 int maxSize = 4096; #else int maxSize = SystemInfo.maxTextureSize; #endif #if UNITY_ANDROID || UNITY_IPHONE maxSize = Mathf.Min(maxSize, allow4096 ? 4096 : 2048); #endif if (unityPacking) { for (int i = 0; i < sprites.Count; ++i) textures[i] = sprites[i].tex; rects = tex.PackTextures(textures, atlasPadding, maxSize); } else { sprites.Sort(Compare); for (int i = 0; i < sprites.Count; ++i) textures[i] = sprites[i].tex; rects = UITexturePacker.PackTextures(tex, textures, 4, 4, atlasPadding, maxSize); } for (int i = 0; i < sprites.Count; ++i) { Rect rect = NGUIMath.ConvertToPixels(rects[i], tex.width, tex.height, true); if (Mathf.RoundToInt(rect.width) != textures[i].width) return false; SpriteEntry se = sprites[i]; se.x = Mathf.RoundToInt(rect.x); se.y = Mathf.RoundToInt(rect.y); se.width = Mathf.RoundToInt(rect.width); se.height = Mathf.RoundToInt(rect.height); } return true; } static public void AddOrUpdate(UIAtlas atlas, Texture2D tex) { if (atlas != null && tex != null) { List<Texture> textures = new List<Texture>(); textures.Add(tex); List<SpriteEntry> sprites = CreateSprites(textures); ExtractSprites(atlas, sprites); UpdateAtlas(atlas, sprites); } } static public void UpdateAtlas(UIAtlas atlas, List<SpriteEntry> sprites) { if (sprites.Count > 0) { if (UpdateTexture(atlas, sprites)) { // Replace the sprites within the atlas ReplaceSprites(atlas, sprites); } // Release the temporary textures ReleaseSprites(sprites); return; } else { atlas.spriteList.Clear(); NGUITools.Destroy(atlas.spriteMaterial.mainTexture); atlas.spriteMaterial.mainTexture = null; } atlas.MarkAsChanged(); } /// <summary> /// Add a new sprite to the atlas, given the texture it's coming from and the packed rect within the atlas. /// </summary> static public UISpriteData AddSprite(List<UISpriteData> sprites, SpriteEntry se) { // See if this sprite already exists foreach (UISpriteData sp in sprites) { if (sp.name == se.name) { sp.CopyFrom(se); return sp; } } UISpriteData sprite = new UISpriteData(); sprite.CopyFrom(se); sprites.Add(sprite); return sprite; } /// <summary> /// Create a list of sprites using the specified list of textures. /// </summary> /// static public List<SpriteEntry> CreateSprites(List<Texture> textures) { List<SpriteEntry> list = new List<SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = tex as Texture2D; // If we aren't doing trimming, just use the texture as-is if (!atlasTrimming && !atlasPMA) { SpriteEntry sprite = new SpriteEntry(); sprite.SetRect(0, 0, oldTex.width, oldTex.height); sprite.tex = oldTex; sprite.name = oldTex.name; sprite.temporaryTexture = false; list.Add(sprite); 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 if (atlasTrimming) { 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; } } } } else { xmin = 0; xmax = oldWidth - 1; ymin = 0; ymax = oldHeight - 1; } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.x = 0; sprite.y = 0; sprite.width = oldTex.width; sprite.height = oldTex.height; // If the dimensions match, then nothing was actually trimmed if (!atlasPMA && (newWidth == oldWidth && newHeight == oldHeight)) { sprite.tex = oldTex; sprite.name = oldTex.name; 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); if (atlasPMA) newPixels[newIndex] = NGUITools.ApplyPMA(pixels[oldIndex]); else newPixels[newIndex] = pixels[oldIndex]; } } // Create a new texture sprite.temporaryTexture = true; sprite.name = oldTex.name; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Remember the padding offset sprite.SetPadding(xmin, ymin, oldWidth - newWidth - xmin, oldHeight - newHeight - ymin); } list.Add(sprite); } } return list; } /// <summary> /// Release all temporary textures created for the sprites. /// </summary> static public void ReleaseSprites(List<SpriteEntry> sprites) { foreach (SpriteEntry se in sprites) { if (se.temporaryTexture) { NGUITools.Destroy(se.tex); se.tex = null; } } Resources.UnloadUnusedAssets(); } static public void ReplaceSprites(UIAtlas atlas, List<SpriteEntry> sprites) { // Get the list of sprites we'll be updating List<UISpriteData> spriteList = atlas.spriteList; List<UISpriteData> kept = new List<UISpriteData>(); // 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[i]; UISpriteData sprite = AddSprite(spriteList, se); kept.Add(sprite); } // Remove unused sprites for (int i = spriteList.Count; i > 0; ) { UISpriteData sp = spriteList[--i]; if (!kept.Contains(sp)) spriteList.RemoveAt(i); } // Sort the sprites so that they are alphabetical within the atlas atlas.SortAlphabetically(); atlas.MarkAsChanged(); } /// <summary> /// Extract the specified sprite from the atlas. /// </summary> /// static public SpriteEntry ExtractSprite(UIAtlas atlas, string spriteName) { if (atlas.texture == null) return null; UISpriteData sd = atlas.GetSprite(spriteName); if (sd == null) return null; Texture2D tex = atlas.texture as Texture2D; SpriteEntry se = ExtractSprite(sd, tex); return se; } /// <summary> /// Extract the specified sprite from the atlas texture. /// </summary> static SpriteEntry ExtractSprite(UISpriteData es, Texture2D tex) { return (tex != null) ? ExtractSprite(es, tex.GetPixels32(), tex.width, tex.height) : null; } /// <summary> /// Extract the specified sprite from the atlas texture. /// </summary> static SpriteEntry ExtractSprite(UISpriteData es, Color32[] oldPixels, int oldWidth, int oldHeight) { int xmin = Mathf.Clamp(es.x, 0, oldWidth); int ymin = Mathf.Clamp(es.y, 0, oldHeight); int xmax = Mathf.Min(xmin + es.width, oldWidth - 1); int ymax = Mathf.Min(ymin + es.height, oldHeight - 1); int newWidth = Mathf.Clamp(es.width, 0, oldWidth); int newHeight = Mathf.Clamp(es.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) return null; Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { int cy = ymin + y; if (cy > ymax) cy = ymax; for (int x = 0; x < newWidth; ++x) { int cx = xmin + x; if (cx > xmax) cx = xmax; int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - cy) * oldWidth + cx; newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(es); sprite.SetRect(0, 0, newWidth, newHeight); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); return sprite; } /// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static public void ExtractSprites(UIAtlas atlas, List<SpriteEntry> finalSprites) { Texture2D tex = atlas.texture as Texture2D; if (tex != null) { Color32[] pixels = null; int width = tex.width; int height = tex.height; List<UISpriteData> sprites = atlas.spriteList; float count = sprites.Count; int index = 0; foreach (UISpriteData es in sprites) { bool found = false; foreach (SpriteEntry fs in finalSprites) { if (es.name == fs.name) { fs.CopyBorderFrom(es); found = true; break; } } if (!found) { if (pixels == null) pixels = tex.GetPixels32(); SpriteEntry sprite = ExtractSprite(es, pixels, width, height); if (sprite != null) finalSprites.Add(sprite); } } } } static public bool UpdateTexture(UIAtlas atlas, List<SpriteEntry> sprites) { // Get the texture for the atlas Texture2D tex = atlas.texture as Texture2D; bool newTexture = tex == null; if (newTexture) { // Create a new texture for the atlas tex = new Texture2D(1, 1, TextureFormat.ARGB32, false); } // Pack the sprites into this texture if (PackTextures(tex, sprites) && tex != null) { // Update the atlas texture if (newTexture) { if (atlas.spriteMaterial == null) { Shader shader = Shader.Find(atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored"); atlas.spriteMaterial = new Material(shader); } atlas.spriteMaterial.mainTexture = tex; ReleaseSprites(sprites); } return true; } else { return false; } } // save as ngui's public UITexturePacker(int width, int height, bool rotations) { Init(width, height, rotations); } public void Init(int width, int height, bool rotations) { binWidth = width; binHeight = height; allowRotations = rotations; Rect n = new Rect(); n.x = 0; n.y = 0; n.width = width; n.height = height; usedRectangles.Clear(); freeRectangles.Clear(); freeRectangles.Add(n); } private struct Storage { public Rect rect; public bool paddingX; public bool paddingY; } public static Rect[] PackTextures(Texture2D texture, Texture2D[] textures, int width, int height, int padding, int maxSize) { if (width > maxSize && height > maxSize) return null; if (width > maxSize || height > maxSize) { int temp = width; width = height; height = temp; } // Force square by sizing up // sz modify //if (NGUISettings.forceSquareAtlas) if (forceSquareAtlas) { if (width > height) height = width; else if (height > width) width = height; } UITexturePacker bp = new UITexturePacker(width, height, false); Storage[] storage = new Storage[textures.Length]; for (int i = 0; i < textures.Length; i++) { Texture2D tex = textures[i]; if (!tex) continue; Rect rect = new Rect(); int xPadding = 1; int yPadding = 1; for (xPadding = 1; xPadding >= 0; --xPadding) { for (yPadding = 1; yPadding >= 0; --yPadding) { rect = bp.Insert(tex.width + (xPadding * padding), tex.height + (yPadding * padding), UITexturePacker.FreeRectChoiceHeuristic.RectBestAreaFit); if (rect.width != 0 && rect.height != 0) break; // After having no padding if it still doesn't fit -- increase texture size. else if (xPadding == 0 && yPadding == 0) { return PackTextures(texture, textures, width * (width <= height ? 2 : 1), height * (height < width ? 2 : 1), padding, maxSize); } } if (rect.width != 0 && rect.height != 0) break; } storage[i] = new Storage(); storage[i].rect = rect; storage[i].paddingX = (xPadding != 0); storage[i].paddingY = (yPadding != 0); } texture.Resize(width, height); texture.SetPixels(new Color[width * height]); // The returned rects Rect[] rects = new Rect[textures.Length]; for (int i = 0; i < textures.Length; i++) { Texture2D tex = textures[i]; if (!tex) continue; Rect rect = storage[i].rect; int xPadding = (storage[i].paddingX ? padding : 0); int yPadding = (storage[i].paddingY ? padding : 0); Color[] colors = tex.GetPixels(); // Would be used to rotate the texture if need be. if (rect.width != tex.width + xPadding) { Color[] newColors = tex.GetPixels(); for (int x = 0; x < rect.width; x++) { for (int y = 0; y < rect.height; y++) { int prevIndex = ((int)rect.height - (y + 1)) + x * (int)tex.width; newColors[x + y * (int)rect.width] = colors[prevIndex]; } } colors = newColors; } texture.SetPixels((int)rect.x, (int)rect.y, (int)rect.width - xPadding, (int)rect.height - yPadding, colors); rect.x /= width; rect.y /= height; rect.width = (rect.width - xPadding) / width; rect.height = (rect.height - yPadding) / height; rects[i] = rect; } texture.Apply(); return rects; } 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 代码量挺大,我就不全写出来了,如果要深入研究,可以下载附件,这个脚本只是一个要被调用的脚本。 } 下面就是我们要调用的参数: using UnityEngine; using System.Collections; public class NGUIAtlasMakerTest : MonoBehaviour { public UITexture tt; private Texture[] img; private Texture2D[] texs; public UISprite sprite; private UIAtlas atlas; void Start() { StartCoroutine(GetTupian()); Invoke("TokeImage", 3.0f); } //此方法就是调用打图集的方法 public void TokeImage() { //将下载的图片存储到texs中,进行动态打图集 texs = new Texture2D[img.Length]; for (int i = 0; i < img.Length; i++) { texs[i] = img[i] as Texture2D; } NGUIAtlasMaker.atlasTrimming = true; NGUIAtlasMaker.atlasPMA = atlas != null ? atlas.premultipliedAlpha : false; NGUIAtlasMaker.unityPacking = false; NGUIAtlasMaker.atlasPadding = 1; NGUIAtlasMaker.allow4096 = true; NGUIAtlasMaker.UITexturePacker.forceSquareAtlas = true; if (atlas == null) { atlas = this.gameObject.AddComponent<UIAtlas>(); } string lastName = string.Empty; foreach (var tex in texs) { SZUIAtlasMakerRuntime.AddOrUpdate(atlas, tex); lastName = tex.name; } sprite.atlas = atlas; sprite.spriteName = lastName; } //图片下载过程(注意:我在这里只下载了一张图片,根据需要,你可以下载多张,只要存储到img数组或者texs数组中就行) 这些都是很简单了,但是下载的图片是没有名字的,我想了个办法给命名了, IEnumerator GetTupian() { Debug.Log(1212); WWW www = new WWW("http://pic.baike.soso.com/p/20090711/20090711101754-314944703.jpg"); yield return www; Texture2D tu = //命名的过程 tu.name = "ttt"; Debug.Log(www.texture .name); img = new Texture[1]; for (int i = 0; i < img.Length ; i++) { img[i] = tu; } tt.mainTexture = img[0]; } 这种动态打图集,只能将图集打到一个空的GameObject上,,也就是说GameObject就是一个图集,但却不是预设,如果大家有什么好的建议或者方法,都可以分享出来,或者联系我