[Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题

2017年07月05日 15:57:44

阅读数:1494

http://www.cppblog.com/shly/archive/2015/12/09/212443.html

[Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题

UGUI Image已Sprite为主,而简单的合并图集可以使用自带的SpritePacker。
而当在使用AssetBundle的时候情况有些不同,会造成每个AB都会引入完整的Sprite图集,显然就增加了AB的大小,重复资源。
为了解决这个问题我们可以手动合并图集,那么方案也有多种,这里我们可以编辑器自带的API来实现一个小的图集合并工具。

 private static bool CombineSpritesHelper(string path, string dpath, string name, int padding)
    {
        string[] paths = AssetDatabase.FindAssets("t:sprite", new string[] { path });
        List<Sprite> spriteList = new List<Sprite>();
        List<Texture2D> texList = new List<Texture2D>();

        foreach (var o in paths)
        {
            Sprite s = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(o), typeof(Sprite)) as Sprite;
            if (null != s)
            {
                spriteList.Add(s);
                texList.Add(s.texture);
            }
        }

        if (texList.Count > 0)
        {
            Texture2D tex = new Texture2D(1024, 1024, TextureFormat.ARGB32, true);
            Rect[] uvs = UITexturePacker.PackTextures(tex, texList.ToArray(), 4, 4, padding, 1024);
            if (null == uvs)
            {
                EditorUtility.DisplayDialog(path, "图集超过1024,需要分组成多张图集", "点击退出");
                Object.DestroyImmediate(tex);
                tex = null;
                return false;
            }
            else
            {
                List<SpriteMetaData> metaList = new List<SpriteMetaData>();
                for (int i = 0; i < uvs.Length; ++i)
                {
                    SpriteMetaData data = new SpriteMetaData();
                    data.alignment = (int)SpriteAlignment.Center;
                    data.border = spriteList[i].border;
                    data.name = spriteList[i].name;
                    data.pivot = spriteList[i].pivot;
                    data.rect = new Rect(uvs[i].x * tex.width, uvs[i].y * tex.height, uvs[i].width * tex.width, uvs[i].height * tex.height);
                    metaList.Add(data);
                }

                //string dpath = path.Substring(0, path.Length - obj.name.Length) + "SpriteSet";
                if (!System.IO.Directory.Exists(dpath))
                {
                    System.IO.Directory.CreateDirectory(dpath);
                }

                string file = dpath + "/" + name + ".png";
                if (System.IO.File.Exists(file))
                {
                    System.IO.File.Delete(file);
                }
                System.IO.File.WriteAllBytes(file, tex.EncodeToPNG());

                AssetDatabase.ImportAsset(file, ImportAssetOptions.ForceUpdate);
                TextureImporter importer = AssetImporter.GetAtPath(file) as TextureImporter;
                importer.spritesheet = metaList.ToArray();
                importer.spriteImportMode = SpriteImportMode.Multiple;
                importer.textureType = TextureImporterType.Sprite;
                importer.textureFormat = TextureImporterFormat.ARGB32;
                importer.mipmapEnabled = true;
                importer.mipmapFilter = TextureImporterMipFilter.BoxFilter;
                importer.assetBundleName = "ui_image/" + name.ToLower();
                AssetDatabase.ImportAsset(file);
                AssetDatabase.Refresh();
            }
        }
        return true;
    }

    [MenuItem("Tool/Combine Sprites")]
    [MenuItem("Assets/Tool/Combine Sprites")]
    public static void CombineSprites()
    {
        EditorUtility.DisplayProgressBar("Combine Sprites", "Initializing ", 0);
        try
        {
            Object obj = Selection.activeObject;
            string path = AssetDatabase.GetAssetPath(obj.GetInstanceID());
            string dpath = path.Substring(0, path.Length - obj.name.Length) + "SpriteSet";

            if (System.IO.Directory.Exists(path))
            {
                string[] directories = System.IO.Directory.GetDirectories(path);
                int count = 0;
                if (directories.Length > 0)
                {
                    foreach (var directory in directories)
                    {
                        count++;
                        EditorUtility.DisplayProgressBar("Combine Sprites", string.Format("combing {0}", count), (float)(count) / (directories.Length));
                        if (!CombineSpritesHelper(directory, dpath, string.Concat(obj.name, "_", count.ToString()), 1))
                        {
                            break;
                        }
                    }
                }
                else
                {
                    EditorUtility.DisplayProgressBar("Combine Sprites", "combing 0", 1);
                    CombineSpritesHelper(path, dpath, obj.name, 1);
                }
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError(e);
        }
        EditorUtility.ClearProgressBar();
    }

使用方法很简单,可以修改源码中的路径,可以将多个Single Sprite合成一个Multi Sprite。然后在供,UGUI使用,注意最好保存好合并前的源文件,不然可能造成新增图片打图集后对应不上从而造成引用丢失。

补充UITextPacker.cs :引用自NGUI

/*
    Based on the Public Domain MaxRectsBinPack.cpp source by Jukka Jylänki
    https://github.com/juj/RectangleBinPack/

Ported to C# by Sven Magnus
    This version is also public domain - do whatever you want with it.
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class UITexturePacker
{
    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
        /*
        if (NGUISettings.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] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题的更多相关文章

  1. font-size:0的妙用,用于解决inline或者inline-block造成的间隙

    1.图片间的缝隙(图片间的间隙一般是由换行.缩进造成的) <div> <img src="1.jpg"> <img src="2.jpg&q ...

  2. c#3.0提供的扩展方法

    在c#3.0之前,想要为内置的类型添加一个方法显然是不可能的.但是,c#3.0提供的扩展方法可以解决这个问题.具体代码如下: public static class ExtendedClass {pu ...

  3. Unity3D 5.0版本+注册工具分享

    Unity3D引擎5.0正式版本发布也有一段时间了.笔者今天下载了新版本顺便分享一下资源. 主要有两个资源,一个是5.0f4的官方客户端,另外一个是vs的调试插件.有需要的盆友就拿去.都在下面的连接地 ...

  4. jQuery源码分析-03扩展工具函数jQuery.extend

    // 扩展工具函数 jQuery.extend({ // http://www.w3school.com.cn/jquery/core_noconflict.asp // 释放$的 jQuery 控制 ...

  5. Unity3d项目合作 场景的合并和还原

    Unity3d项目合作  场景的合并和还原 特别声明:转载自Unity3D研究院 如何侵犯版权,请通知我删除! 摘要: 导出Unity场景的所有游戏对象信息,一种是XML一种是JSON.本篇文章我们把 ...

  6. HBase2.0中的Benchmark工具 — PerformanceEvaluation

    简介 在项目开发过程中,我们经常需要一些benchmark工具来对系统进行压测,以获得系统的性能参数,极限吞吐等等指标. 而在HBase中,就自带了一个benchmark工具—PerformanceE ...

  7. (转载)详解7.0带来的新工具类:DiffUtil

    [Android]详解7.0带来的新工具类:DiffUtil 标签: diffutil 2017-04-17 18:21 226人阅读 评论(0) 收藏 举报  分类: Android学习笔记(94) ...

  8. pycharm中添加扩展工具pylint

    今天调试了好几个小时,想吧pylint集成到pycharm中去,从网上找了个宝贝帖 子,但是不好用,原因是作者写的脚本是检查工程和模块的,而我的是单独检查一个文件,当然前者肯定会在项目后期用的.所以就 ...

  9. centos7安装redis3.0和phpredis扩展详细教程(图文)

    整理一下centos7安装redis3.0和phpredis扩展的过程,有需要的朋友可以拿去使用. 一.安装redis3.0 1.安装必要的包 yum install gcc 2.centos7安装r ...

随机推荐

  1. C#中委托实现的异步编程

    所谓同步:如果在代码中调用了一个方法,则必须等待该方法所有的代码执行完毕之后,才能回到原来的地方执行下一行代码. 异步:如果不等待调用的方法执行完,就执行下一行代码. 1.0 同步例子: class ...

  2. Scrum Master 面试题 – 你必须知道的22个Scrum基础知识

    以下的22个问题基本上涵盖了Scrum所涉及的内容,如果你能够正确回答出所有问题,那么你已经具备了作为一名Scrum Master的基本素质:当然,作为一名合格的Scrum Master,更重要的是你 ...

  3. linux下svn的命令

    原文地址:http://www.cnblogs.com/wanqieddy/archive/2011/06/09/2076783.html

  4. C#与js的各种交互

    今天遇到一个问题,查到不错的资料,放上来记录一下,以防忘记地址,算是我的笔记吧! 很多人都向在服务器端调用客户端的函数来操作,也就是在asp中调用javascript脚本中已经定义好的脚本函数.经过研 ...

  5. IDEA 15 社区版 Maven项目 启动Tomcat调试

    1.在pom下添加Tomcat插件: <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifac ...

  6. 勤快的love枫[ZJOI2007]

    题目描述 小绝恋love 枫是一个出纳,经常需要做一些统计报表的工作.今天是绝恋love 枫的生日,小绝恋love 枫希望可以帮爸爸分担一些工作,作为他的生日礼物之一.经过仔细观察,小绝恋love 枫 ...

  7. LINUX环境并发服务器的三种实现模型

    服务器设计技术有很多,按使用的协议来分有TCP服务器和UDP服务器.按处理方式来分有循环服务器和并发服务器. 1  循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器,为了 ...

  8. C++ Primer Plus (Stephen Prata 著)

    第1章 预备知识 (已看) 第2章 开始学习C++ (已看) 第3章 处理数据 (已看) 第4章 复合类型 (已看) 第5章 循环和关系表达式 (已看) 第6章 分支语句和逻辑运算符 (已看) 第7章 ...

  9. java基础(三) -基本数据类型

    变量就是申请内存来存储值.也就是说,当创建变量的时候,需要在内存中申请空间. 内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据. 因此,通过定义不同类型的变量,可以在内存 ...

  10. 用jquery实现小火箭到页面顶部的效果

    恩,不知道之前在哪看过一个页面效果就是如果页面被滑动了就出现一个小火箭,点击这个小火箭就可以慢慢回到页面顶部,闲的没事,自己搞了一下 需要引入jquery 代码和布局都很简单 <!DOCTYPE ...