NGUI讨论群:333417608

NGUI版本号:3.6.5



1、參见SZUIAtlasMakerRuntimeTest设置对应的值以上值须要提前设置好

2、没有检查是否atlas可以正确创建,自己可以改,增加返回值

3、代码都是在NGUI里面拷贝出来的,仅仅是进行修改,没有新代码

4、适用与那种从网上下图片,之后还不想用UITexture的人,可是还是建议用UITexture假设drawcall不是问题的话

5、自己以后更新按我的方式改改就能够

6、动态创建速度较慢,建议在游戏启动的时候执行

7、游戏时能够将创建的atlas保存到可写文件夹,避免每次都新创建

SZUIAtlasMakerRuntimeTest.cs

using UnityEngine;
using System.Collections; public class SZUIAtlasMakerRuntimeTest : MonoBehaviour { public Texture2D[] texs;
public UISprite sprite;
private UIAtlas atlas; void Start () { SZUIAtlasMakerRuntime.atlasTrimming = true;
SZUIAtlasMakerRuntime.atlasPMA = atlas != null ? atlas.premultipliedAlpha : false;
SZUIAtlasMakerRuntime.unityPacking = false;
SZUIAtlasMakerRuntime.atlasPadding = 1;
SZUIAtlasMakerRuntime.allow4096 = true;
SZUIAtlasMakerRuntime.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;
} }

SZUIAtlasMakerRuntime.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class SZUIAtlasMakerRuntime { 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; 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;
} /// <summary>
/// Used to sort the sprites by pixels used
/// </summary> static int Compare (SpriteEntry a, SpriteEntry b)
{
// A is null b is not b is greater so put it at the front of the list
if (a == null && b != null) return 1; // A is not null b is null a is greater so put it at the front of the list
if (a != null && b == null) return -1; // Get the total pixels used for each sprite
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;
} /// <summary>
/// Pack all of the specified sprites into a single texture, updating the outer and inner rects of the sprites as needed.
/// </summary> 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); // Make sure that we don't shrink the textures
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);
}
} /// <summary>
/// Update the sprite atlas, keeping only the sprites that are on the specified list.
/// </summary> static public void UpdateAtlas (UIAtlas atlas, List<SpriteEntry> sprites)
{
if (sprites.Count > 0)
{
// Combine all sprites into a single texture and save it
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();
} /// <summary>
/// Replace the sprites within the atlas.
/// </summary> 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 class UITexturePacker
{
// sz modify
public static bool forceSquareAtlas = true;
public int binWidth = 0;
public int binHeight = 0;
public bool allowRotations; public List<Rect> usedRectangles = new List<Rect>();
public List<Rect> freeRectangles = new List<Rect>(); public enum FreeRectChoiceHeuristic
{
RectBestShortSideFit, //< -BSSF: Positions the rectangle against the short side of a free rectangle into which it fits the best.
RectBestLongSideFit, //< -BLSF: Positions the rectangle against the long side of a free rectangle into which it fits the best.
RectBestAreaFit, //< -BAF: Positions the rectangle into the smallest free rect into which it fits.
RectBottomLeftRule, //< -BL: Does the Tetris placement.
RectContactPointRule //< -CP: Choosest the placement where the rectangle touches other rects as much as possible.
}; 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;
} public Rect Insert (int width, int height, FreeRectChoiceHeuristic method)
{
Rect newNode = new Rect();
int score1 = 0; // Unused in this function. We don't need to know the score after finding the position.
int score2 = 0;
switch (method)
{
case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;
case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;
case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break;
case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;
case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;
} if (newNode.height == 0)
return newNode; int numRectanglesToProcess = freeRectangles.Count;
for (int i = 0; i < numRectanglesToProcess; ++i)
{
if (SplitFreeNode(freeRectangles[i], ref newNode))
{
freeRectangles.RemoveAt(i);
--i;
--numRectanglesToProcess;
}
} PruneFreeList(); usedRectangles.Add(newNode);
return newNode;
} public void Insert (List<Rect> rects, List<Rect> dst, FreeRectChoiceHeuristic method)
{
dst.Clear(); while (rects.Count > 0)
{
int bestScore1 = int.MaxValue;
int bestScore2 = int.MaxValue;
int bestRectIndex = -1;
Rect bestNode = new Rect(); for (int i = 0; i < rects.Count; ++i)
{
int score1 = 0;
int score2 = 0;
Rect newNode = ScoreRect((int)rects[i].width, (int)rects[i].height, method, ref score1, ref score2); if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))
{
bestScore1 = score1;
bestScore2 = score2;
bestNode = newNode;
bestRectIndex = i;
}
} if (bestRectIndex == -1)
return; PlaceRect(bestNode);
rects.RemoveAt(bestRectIndex);
}
} void PlaceRect (Rect node)
{
int numRectanglesToProcess = freeRectangles.Count;
for (int i = 0; i < numRectanglesToProcess; ++i)
{
if (SplitFreeNode(freeRectangles[i], ref node))
{
freeRectangles.RemoveAt(i);
--i;
--numRectanglesToProcess;
}
} PruneFreeList(); usedRectangles.Add(node);
} Rect ScoreRect (int width, int height, FreeRectChoiceHeuristic method, ref int score1, ref int score2)
{
Rect newNode = new Rect();
score1 = int.MaxValue;
score2 = int.MaxValue;
switch (method)
{
case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;
case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;
case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1);
score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better.
break;
case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;
case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;
} // Cannot fit the current rectangle.
if (newNode.height == 0)
{
score1 = int.MaxValue;
score2 = int.MaxValue;
} return newNode;
} /// Computes the ratio of used surface area.
public float Occupancy ()
{
ulong usedSurfaceArea = 0;
for (int i = 0; i < usedRectangles.Count; ++i)
usedSurfaceArea += (uint)usedRectangles[i].width * (uint)usedRectangles[i].height; return (float)usedSurfaceArea / (binWidth * binHeight);
} Rect FindPositionForNewNodeBottomLeft (int width, int height, ref int bestY, ref int bestX)
{
Rect bestNode = new Rect();
//memset(bestNode, 0, sizeof(Rect)); bestY = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i)
{
// Try to place the rectangle in upright (non-flipped) orientation.
if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
{
int topSideY = (int)freeRectangles[i].y + height;
if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = width;
bestNode.height = height;
bestY = topSideY;
bestX = (int)freeRectangles[i].x;
}
}
if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
{
int topSideY = (int)freeRectangles[i].y + width;
if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestY = topSideY;
bestX = (int)freeRectangles[i].x;
}
}
}
return bestNode;
} Rect FindPositionForNewNodeBestShortSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)
{
Rect bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rect)); bestShortSideFit = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i)
{
// Try to place the rectangle in upright (non-flipped) orientation.
if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
{
int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert); if (shortSideFit < bestShortSideFit || (shortSideFit == bestShortSideFit && longSideFit < bestLongSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit = shortSideFit;
bestLongSideFit = longSideFit;
}
} if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
{
int flippedLeftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
int flippedLeftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
int flippedShortSideFit = Mathf.Min(flippedLeftoverHoriz, flippedLeftoverVert);
int flippedLongSideFit = Mathf.Max(flippedLeftoverHoriz, flippedLeftoverVert); if (flippedShortSideFit < bestShortSideFit || (flippedShortSideFit == bestShortSideFit && flippedLongSideFit < bestLongSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit = flippedShortSideFit;
bestLongSideFit = flippedLongSideFit;
}
}
}
return bestNode;
} Rect FindPositionForNewNodeBestLongSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)
{
Rect bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rect)); bestLongSideFit = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i)
{
// Try to place the rectangle in upright (non-flipped) orientation.
if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
{
int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert); if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit = shortSideFit;
bestLongSideFit = longSideFit;
}
} if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
{
int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert); if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit = shortSideFit;
bestLongSideFit = longSideFit;
}
}
}
return bestNode;
} Rect FindPositionForNewNodeBestAreaFit (int width, int height, ref int bestAreaFit, ref int bestShortSideFit)
{
Rect bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rect)); bestAreaFit = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i)
{
int areaFit = (int)freeRectangles[i].width * (int)freeRectangles[i].height - width * height; // Try to place the rectangle in upright (non-flipped) orientation.
if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
{
int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = width;
bestNode.height = height;
bestShortSideFit = shortSideFit;
bestAreaFit = areaFit;
}
} if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
{
int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestShortSideFit = shortSideFit;
bestAreaFit = areaFit;
}
}
}
return bestNode;
} /// Returns 0 if the two intervals i1 and i2 are disjoint, or the length of their overlap otherwise.
int CommonIntervalLength (int i1start, int i1end, int i2start, int i2end)
{
if (i1end < i2start || i2end < i1start)
return 0;
return Mathf.Min(i1end, i2end) - Mathf.Max(i1start, i2start);
} int ContactPointScoreNode (int x, int y, int width, int height)
{
int score = 0; if (x == 0 || x + width == binWidth)
score += height;
if (y == 0 || y + height == binHeight)
score += width; for (int i = 0; i < usedRectangles.Count; ++i)
{
if (usedRectangles[i].x == x + width || usedRectangles[i].x + usedRectangles[i].width == x)
score += CommonIntervalLength((int)usedRectangles[i].y, (int)usedRectangles[i].y + (int)usedRectangles[i].height, y, y + height);
if (usedRectangles[i].y == y + height || usedRectangles[i].y + usedRectangles[i].height == y)
score += CommonIntervalLength((int)usedRectangles[i].x, (int)usedRectangles[i].x + (int)usedRectangles[i].width, x, x + width);
}
return score;
} Rect FindPositionForNewNodeContactPoint (int width, int height, ref int bestContactScore)
{
Rect bestNode = new Rect();
//memset(&bestNode, 0, sizeof(Rect)); bestContactScore = -1; for (int i = 0; i < freeRectangles.Count; ++i)
{
// Try to place the rectangle in upright (non-flipped) orientation.
if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
{
int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, width, height);
if (score > bestContactScore)
{
bestNode.x = (int)freeRectangles[i].x;
bestNode.y = (int)freeRectangles[i].y;
bestNode.width = width;
bestNode.height = height;
bestContactScore = score;
}
}
if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
{
int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, height, width);
if (score > bestContactScore)
{
bestNode.x = (int)freeRectangles[i].x;
bestNode.y = (int)freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestContactScore = score;
}
}
}
return bestNode;
} bool SplitFreeNode (Rect freeNode, ref Rect usedNode)
{
// Test with SAT if the rectangles even intersect.
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
return false; if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x)
{
// New node at the top side of the used node.
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height)
{
Rect newNode = freeNode;
newNode.height = usedNode.y - newNode.y;
freeRectangles.Add(newNode);
} // New node at the bottom side of the used node.
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height)
{
Rect newNode = freeNode;
newNode.y = usedNode.y + usedNode.height;
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
freeRectangles.Add(newNode);
}
} if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y)
{
// New node at the left side of the used node.
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width)
{
Rect newNode = freeNode;
newNode.width = usedNode.x - newNode.x;
freeRectangles.Add(newNode);
} // New node at the right side of the used node.
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width)
{
Rect newNode = freeNode;
newNode.x = usedNode.x + usedNode.width;
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
freeRectangles.Add(newNode);
}
} return true;
} void PruneFreeList ()
{
for (int i = 0; i < freeRectangles.Count; ++i)
for (int j = i + 1; j < freeRectangles.Count; ++j)
{
if (IsContainedIn(freeRectangles[i], freeRectangles[j]))
{
freeRectangles.RemoveAt(i);
--i;
break;
}
if (IsContainedIn(freeRectangles[j], freeRectangles[i]))
{
freeRectangles.RemoveAt(j);
--j;
}
}
} bool IsContainedIn (Rect a, Rect b)
{
return a.x >= b.x && a.y >= b.y
&& a.x + a.width <= b.x + b.width
&& a.y + a.height <= b.y + b.height;
}
} }



【Unity3D】【NGUI】Atlas的动态创建的更多相关文章

  1. unity3d通过代码动态创建销毁游戏对象

    只能动态创建内部提供的游戏对象,代码如下: //按下C后创建 if (Input.GetKeyDown (KeyCode.C)) { GameObject s1 = GameObject.Create ...

  2. 【Unity3D】Unity3D之 Resources.Load 动态加载资源

    [Unity3D]Unity3D之 Resources.Load 动态加载资源 1.Resources.Load:使用这种方式加载资源,首先需要下Asset目录下创建一个名为Resources的文件夹 ...

  3. Unity3d ngui基础教程

    Unity3d ngui基础教程 NGUI教程:步骤1-Scene 1.创建一个新的场景(New Scene).2.选择并删除场景里的MainCamera.3.在NGUI菜单下选择Create a N ...

  4. NGUI Atlas, Atlas Type Reference

    在NGUI中,通过创建图集及引用图集,实际使用时指定引用图集而非具体图集本身,可以创建多语言,或高配低配版本资源包. NGUI Atlashttp://www.cnblogs.com/answerwi ...

  5. 3D语音天气球(源码分享)——通过天气服务动态创建3D球

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3 ...

  6. Unity3D NGUI学习(一)血条

    这次来讲讲Unity3D NGUI这个插件的学习,这个插件是收费的,不过去网上可以下载得很多可用版本.用来做用户的交互UI,学习起来比较简单 第一步,导入NGUI包 http://pan.baidu. ...

  7. Unity3d Web3d资源的动态加载

    Unity3d Web3d资源的动态加载 @灰太龙 参考了宣雨松的博客,原文出处http://www.xuanyusong.com/archives/2405,如果涉及到侵权,请通知我! Unity3 ...

  8. NGUI的UISprite动态染色的一种方法

    本文主要参考iwinterice 的 NGUI的UISprite动态染色的一种解决方案 文章. 参考参考,就是既参详又拷贝,-,-||| 相关理论依据,还请去移步 NGUI的UISprite动态染色的 ...

  9. 3D语音天气球(源代码分享)——通过天气服务动态创建3D球

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3 ...

随机推荐

  1. 【传输文件】文件传输协议FTP、SFTP和SCP

    网络通信协议分层 应用层: HTTP(Hypertext Transfer Protocol 超文本传输协议,显示网页) DNS(Domain Name System) FTP(File Transf ...

  2. Jquery操作层级选择器

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. Python Challenge 第十二关

    这一关依旧只有一张图,右键源代码也没有任何注释,也用PIL处理过那张图但没任何头绪,没办法只有上网搜答案. 别人的博客里说,源代码里面图片的名字是 evil1.jpg,那肯定会有 evil2.jpg. ...

  4. 当前标识(IIS APPPOOL\ASP.NET v4.0)没有对“C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files”的写访问权限 解决方案

    今天搭环境的时候遇到这个问题,折腾了一阵子,最后在微软官方论坛上找到了解决方案. https://forums.asp.net/t/1566987.aspx?Current+Identity+does ...

  5. Spoj MKTHNUM - K-th Number

    题目描述 English Vietnamese You are working for Macrohard company in data structures department. After f ...

  6. MySQL错误:Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL easonjim

    错误: Error Code: . You are using safe update mode and you tried to update a table without a WHERE tha ...

  7. “应用程序无法正常启动(oxc000007b)”解决方案

  8. 【Android】attr、style和theme

    一.Attr 属性,风格样式的最小单元: Attr 的定义 在自定义 View 的时候,在 res/attrs.xml 文件中声明属性,而Android 系统的属性也是以同样的方式定义的.比如 lay ...

  9. Android 获取虚拟按键的高度

    //获取虚拟按键的高度 public static int getNavigationBarHeight(Context context) { int result = 0; if (hasNavBa ...

  10. [置顶] zabbix告警信息-lykchat信息发送系统

    lykchat信息发送系统 lykchat信息发送系统是Python3开发的,通过模拟微信网页端,基于个人微信号,为系统管理人员提供信息发送工具. 实现的功能有用户登录管理.微信登陆管理和微信信息发送 ...