您的位置:首页 > 产品设计 > UI/UE

关于NGUI动态加载图片,并且实现动态打图集的过程

2016-01-13 23:13 801 查看
注:一些代码是在网上找的,太多就没有写。
前几天做项目,遇到所要用的的大量图片都必须从网上下载,当时用的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就是一个图集,但却不是预设,如果大家有什么好的建议或者方法,都可以分享出来,或者联系我


本文出自 “Unity_3D技术探讨” 博客,请务必保留此出处http://myselfdream.blog.51cto.com/9944020/1734813
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: