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

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;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity3D中,uGUI是一个用户界面系统,它可以让开发者创建和管理游戏中的UI元素,如按钮、文本等。在uGUI中,世界坐标是指相对于场景原点的坐标系,而屏幕坐标是指相对于屏幕的坐标系。 在实际运用中,我们可以通过以下方式将世界坐标转换为屏幕坐标: ```csharp Vector3 worldPosition = new Vector3(10, 5, 0); Vector3 screenPosition = Camera.main.WorldToScreenPoint(worldPosition); ``` 这里我们使用了Camera.main.WorldToScreenPoint()方法,将世界坐标转换为屏幕坐标。这个方法需要指定一个摄像机,它将根据该摄像机的位置和朝向来计算屏幕坐标。在这个例子中,我们使用的是场景中的主摄像机,也就是Camera.main。 同样地,我们也可以将屏幕坐标转换为世界坐标: ```csharp Vector3 screenPosition = new Vector3(100, 100, 0); Vector3 worldPosition = Camera.main.ScreenToWorldPoint(screenPosition); ``` 这里我们使用了Camera.main.ScreenToWorldPoint()方法,将屏幕坐标转换为世界坐标。同样地,这个方法也需要指定一个摄像机。 在实际开发中,我们可以使用这些方法来处理鼠标点击、UI元素的位置调整等操作。例如,我们可以通过以下代码来将一个UI元素移动到鼠标点击的位置: ```csharp public void OnPointerClick(PointerEventData eventData) { Vector3 worldPosition = Camera.main.ScreenToWorldPoint(eventData.position); transform.position = worldPosition; } ``` 这里我们使用了Unity3D中的事件系统,当鼠标点击时,OnPointerClick()方法会被调用。在这个方法中,我们通过Camera.main.ScreenToWorldPoint()方法将屏幕坐标转换为世界坐标,然后将UI元素的位置设置为该世界坐标。这样,当我们点击鼠标时,UI元素就会移动到鼠标点击的位置。 总之,在uGUI中,世界坐标和屏幕坐标的转换是非常重要的,它们可以帮助我们处理一些常见的UI操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值