NGUI 精灵:UISprite

一、UIAtlas图集和UISpriteData,负责数据的存储。
1.UIAtlas:图集是一个ScriptableObject对象。
  • 里面保存了图集的相关信息,主要是材质、Pixel Size、图集包含的UISpriteData列表。
GUIAtlas:INGUIAtlas 图集,维护图集内的UISpriteData。只是一个容器概念
        property:
        spriteMaterial/spriteList/texture:图集材质、精灵列表、贴图
        pixelSize:每个像素的大小
        replacement:替换图集,如果不为空,优先再此图集查找
        mSprites:图集内的UISpriteData集合
        mSpriteIndices:mSpriteIndices[spriteName] = index,这个index对应mSprites下标
        Padding:空白不显示区域
        
        function:
        GetSprite:按名字查找精灵
        MakePixelPerfect:像素修改,奇数修正成偶数值
        GetListOfSprites:获取图集内所有精灵的名字集合
        MarkAsChanged:刷新用到这个图集的所有对象 
unity ScriptableObject对象是什么:https://blog.csdn.net/candycat1992/article/details/52181814
如下图:打出来的大图坐标原点在左上角,即上面的Dimensions的x,y坐标是相对于图集大图的左上角的坐标,(x+width, y-height)是右下角的坐标,这边的坐标最终会转换成基于左下角的(0-1)的uv坐标,并赋值给Mesh四个顶点(mesh.uv = newUV;),当然这是相对于Sample类型的精灵,他只有四个顶点。
2.UISpriteData:记录图片在UIAtlas生成的那张大图的坐标,宽度和高度,以及图片拉伸的一些信息(Border,Padding)。
SpriteData就只是一个很简单的数据存储类。
public string name = "Sprite";
public int x = 0;
public int y = 0;
public int width = 0;
public int height = 0;
 
public int borderLeft = 0;
public int borderRight = 0;
public int borderTop = 0;
public int borderBottom = 0;
 
public int paddingLeft = 0;
public int paddingRight = 0;
public int paddingTop = 0;
public int paddingBottom = 0;
Border:九宫格拉伸需要的参数。九宫格拉伸可以参考文章https://www.cnblogs.com/wang-jin-fu/p/8277774.html
Padding的作用:四边留空大小,如下Padding.Left就是在左边留空的大小。
 
二、UIAtlasMaker:图集生成、修改工具。
UIAtlasMaker:NGUIAtlas图集生成、属性设置。核心类
        property:
        spriteList:当前图集内图片列表
        atlasTexture/spriteMaterial:渲染属性
        
        function:
        GetSelectedTextures:将当前选中的图片转成Texture并保存在一个list。
        PackTextures:大图打包并生成对应的SpriteEntry列表,Texture2D.PackTextures
        GetSpriteList:获取当前要显示在界面的精灵列表。0-已在图集且未选中,1-已在图集且选中(update),2未在图集且选中(add)
        CreateSprites:把List<Texture>转换成List<SpriteEntry>()
        ReplaceSprites:替换图集内的所有图片
        ExtractSprites:扩展图集,往图集添加精灵
        UpdateTexture:把List<SpriteEntry>打进图集
        UpdateAtlas:更新图集,核心方法
临时精灵数据类:SpriteEntry,方便UISpriteData跟Texture关联。
  1. OnSelectAtlas 选中一个图集,调用Repaint刷新界面(调用到OnGUI方法)。
void OnSelectAtlas (Object obj)
{
   // Legacy atlas support
   if (obj != null && obj is GameObject) obj = (obj as GameObject).GetComponent<UIAtlas>();
 
   if (NGUISettings.atlas != obj as INGUIAtlas)
   {
      NGUISettings.atlas = obj as INGUIAtlas;
      Repaint();
   }
}
  1. GetSelectedTextures: 收集当前选中的Texture对象,用于后续添加/更新到图集。
  1. GetSpriteList:把上一步的选中列表和图集原有的精灵列表集合在一起,设置上面界面的按钮状态。
  • 状态:0 = No change ,1 = Update,2 = Add
  • textures:上一步收集的当前选中的图片。
Dictionary<string, int> GetSpriteList (List<Texture> textures)
{
   var spriteList = new Dictionary<string, int>();
 
   // If we have textures to work with, include them as well
   if (textures.Count > 0)
   {
      List<string> texNames = new List<string>();
      foreach (Texture tex in textures) texNames.Add(tex.name);
      texNames.Sort();
      foreach (string tex in texNames) spriteList.Add(tex, 2);
   }
 
   var atlas = NGUISettings.atlas;
 
   if (atlas != null)
   {
      var spriteNames = atlas.GetListOfSprites();
 
      if (spriteNames != null)
      {
         foreach (string sp in spriteNames)
         {
            if (spriteList.ContainsKey(sp)) spriteList[sp] = 1;
            else spriteList.Add(sp, 0);
         }
      }
   }
   return spriteList;
}
  1. 打包图集PackTextures:实际调用的是unity 的Texture2D.PackTextures,这边对最终显示的坐标做了处理,把原点为左下角的(0-1)的uv坐标,转换成了原点在左上角的坐标,就是上面UIAtlas在Editor下显示的坐标。
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, NGUISettings.allow4096 ? 4096 : 2048);
#endif
      if (NGUISettings.unityPacking)
      {
         for (int i = 0; i < sprites.Count; ++i) textures[i] = sprites[i].tex;
         rects = tex.PackTextures(textures, NGUISettings.atlasPadding, maxSize);
      }
 
      for (int i = 0; i < sprites.Count; ++i)
      {
         Rect rect = NGUIMath.ConvertToPixels(rects[i], tex.width, tex.height, true);
 
         // Apparently Unity can take the liberty of destroying temporary textures without any warning
         if (textures[i] == null) return false;
 
         // 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;
   }
  1. uv点坐标和UISpriteData内的x,y坐标的转换。
/// <summary>
/// Convert from top-left based pixel coordinates to bottom-left based UV coordinates.
/// </summary>
 
static public Rect ConvertToTexCoords (Rect rect, int width, int height)
{
   Rect final = rect;
 
   if (width != 0f && height != 0f)
   {
      final.xMin = rect.xMin / width;
      final.xMax = rect.xMax / width;
      final.yMin = 1f - rect.yMax / height;
      final.yMax = 1f - rect.yMin / height;
   }
   return final;
}
 
/// <summary>
/// Convert from bottom-left based UV coordinates to top-left based pixel coordinates.
/// </summary>
 
static public Rect ConvertToPixels (Rect rect, int width, int height, bool round)
{
   Rect final = rect;
 
   if (round)
   {
      final.xMin = Mathf.RoundToInt(rect.xMin * width);
      final.xMax = Mathf.RoundToInt(rect.xMax * width);
      final.yMin = Mathf.RoundToInt((1f - rect.yMax) * height);
      final.yMax = Mathf.RoundToInt((1f - rect.yMin) * height);
   }
   else
   {
      final.xMin = rect.xMin * width;
      final.xMax = rect.xMax * width;
      final.yMin = (1f - rect.yMax) * height;
      final.yMax = (1f - rect.yMin) * height;
   }
   return final;
}
  1. UpdateTexture:更新图集,最终还是会调用到PackTextures。
三、UISprite:精灵,核心方法就是OnFill。
UISprite:UIBasicSprite
        property:
        mainTexture/material:获取到图集对应的材质spriteMaterial和纹理
        atlas:当前使用图集
        spriteName:当前使用的的精灵名字。用于在atlas中查找UISpriteData
        applyGradient:渐变标记
        padding:Tiled模式下,重复的间隔
        border:Sliced模式下的九宫格数据
        
        function:
        GetSprite:查找并返回当前atlas中spriteName对应的sprite信息
        drawingDimensions:渲染范围计算,Sprite矩形坐标,需要注意的是drawingDimensions是只当前Sprite的局部坐标
        GetAtlasSprite:获取当前精灵使用的UISpriteData
        SetAtlasSprite:设置当前精灵使用的UISpriteData
        OnFill:核心填充方法
OnFill:和UITexture一样,这个方法调用Fill方法,并把geometry.verts, geometry.uvs, geometry.cols传给Fill,在UIBaseSprite.Fill完成对geometry的顶点、uv、颜色等进行设置。
这边的outer、inter是根据精灵在图集中设置的Border计算来的。
public override void OnFill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols)
   {
      var tex = mainTexture;
      if (tex == null) return;
 
      if ((!mSpriteSet || mSprite == null) && GetAtlasSprite() == null) return;
 
      var outer = new Rect(mSprite.x, mSprite.y, mSprite.width, mSprite.height);
      var inner = new Rect(mSprite.x + mSprite.borderLeft, mSprite.y + mSprite.borderTop,
         mSprite.width - mSprite.borderLeft - mSprite.borderRight,
         mSprite.height - mSprite.borderBottom - mSprite.borderTop);
 
      //uv转换,mSprite的坐标轴原点在左上角,转换成左下角,并转换成(0,1)的坐标
      //outer.xMin = mSprite.x/tex.width, outer.xMax = (mSprite.x + mSprite.width)/tex.width
      //outer.yMin = (tex.height - mSprite.y - mSprite.width)/tex.height, outer.yMax = (tex.height - mSprite.y)/tex.height
      outer = NGUIMath.ConvertToTexCoords(outer, tex.width, tex.height);
      inner = NGUIMath.ConvertToTexCoords(inner, tex.width, tex.height);
 
      //verts, uvs, cols geometry内的属性,这时候还是空的
      var offset = verts.Count;
      Fill(verts, uvs, cols, outer, inner);
 
      if (onPostFill != null)
#if OPTIMISE_NGUI_GC_ALLOC
         onPostFill(this, offset, geometry.verts, geometry.uvs, geometry.cols);
#else
         onPostFill(this, offset, verts, uvs, cols);
#endif
   }

 

 
posted @ 2020-08-15 15:56  柯腾_wjf  阅读(455)  评论(0编辑  收藏  举报