大家在开发端游的时候,我们经常需要通过工具程序手动合并dds图片,意思是为了提高效率,需要我们把一些小的图片合并成一张1024或者2048的大图片。它的图片合成跟NGUI的图集原理是一样的,在这里我们不用NGUI的合并图集,我们手动合并图集。

   大家在游戏开发中我们需要讲一些零碎的图片合并成一张大的,比如3D场景中一些物件的材质,Avatar换装的材质等等。

   接下来我们用代码实现,我们的实现原理是通过二叉树原理实现,首先我们需要定义AslatsNode节点,这个节点满足我们插入节点,建立二叉树,首先我们需要定义图集的大小,鉴于移动端硬件的限制,我们初步定义为1024,插入节点核心代码如下:

 public AtlasNode Insert(Texture2D p_w_picpath, int index) {

   if (p_w_picpath == null) 
    return null;

    if (child != null) 

    AtlasNode newNode = child[0].Insert(p_w_picpath, index);

    if (newNode != null)

     return newNode;

    return child[1].Insert(p_w_picpath, index);

   }

   else {

    if (hasImage)

     return null;

    if (!ImageFits(p_w_picpath, rc))

     return null;

    if (PerfectFit(p_w_picpath, rc)) {

     hasImage = true;

     p_w_picpathRef = p_w_picpath;

     name = p_w_picpathRef.name;

     sortIndex = index;

     return this;

    }

    child = new AtlasNode[2];

    child[0] = new AtlasNode();

    child[1] = new AtlasNode();

    float deltaW = rc.width - p_w_picpath.width;

    float deltaH = rc.height - p_w_picpath.height;

    

    if (deltaW > deltaH) {

     child[0].rc = new Rect(rc.xMin, rc.yMin, p_w_picpath.width, rc.height);

     child[1].rc = new Rect(rc.xMin + p_w_picpath.width + TEXTURE_PADDING, rc.yMin, rc.width - (p_w_picpath.width + TEXTURE_PADDING), rc.height);

    }

    else {

     child[0].rc = new Rect(rc.xMin, rc.yMin, rc.width, p_w_picpath.height);

     child[1].rc = new Rect(rc.xMin, rc.yMin + p_w_picpath.height + TEXTURE_PADDING, rc.width, rc.height - (p_w_picpath.height + TEXTURE_PADDING));

    }
    return child[0].Insert(p_w_picpath, index);

   }

  }

以上代码实现了图片在二叉树进行图片的插入,接下来我们需要建立二叉树代码如下:

public void Build(Texture2D target) {

   if (child != null) {

    if (child[0] != null) {

     child[0].Build(target); 

    }

    if (child[1] != null) {

     child[1].Build(target);

    }

   }

   if (p_w_picpathRef != null) {

    Color[] data = p_w_picpathRef.GetPixels(0);

    for (int x = 0; x < p_w_picpathRef.width; ++x) {

     for (int y = 0; y < p_w_picpathRef.height; ++y) {

      target.SetPixel(x + (int)rc.x, y + (int)rc.y, data[x + y * p_w_picpathRef.width]); 

     }

    }

    if (TEXTURE_PADDING > 0 && BLEED) {

     for (int y = 0; y < p_w_picpathRef.height; ++y) {

      int x = p_w_picpathRef.width - 1;

      target.SetPixel(x + (int)rc.x + TEXTURE_PADDING, y + (int)rc.y, data[x + y * p_w_picpathRef.width]); 

     }

     for (int x = 0; x < p_w_picpathRef.width; ++x) {

      int y = p_w_picpathRef.height - 1;

      target.SetPixel(x + (int)rc.x, y + (int)rc.y + TEXTURE_PADDING, data[x + y * p_w_picpathRef.width]); 

     }

    }

   }

  }

 }

第三步是创建图集代码如下:

public static Atlas[] CreateAtlas(string name, Texture2D[] textures, Atlas startWith = null) {
  List<Texture2D> toProcess = new List<Texture2D>();
  toProcess.AddRange(textures);
  int index = toProcess.Count - 1;
  toProcess.Reverse(); // Because we index backwards
  
  List<Atlas> result = new List<Atlas>();
  
  int insertIndex = 0;
  if (startWith != null) {
   insertIndex = startWith.root.sortIndex;
  }
  while(index >= 0) {
   Atlas _atlas = startWith;
   if (_atlas == null) {
    _atlas = new Atlas();
    _atlas.texture = new Texture2D(AtlasSize, AtlasSize, TextureFormat.RGBA32, false);
    _atlas.root = new AtlasNode();
    _atlas.root.rc = new Rect(0, 0, AtlasSize, AtlasSize);
   }
   startWith = null;
   while (index >= 0 && (_atlas.root.Contains(toProcess[index].name) || _atlas.root.Insert(toProcess[index], insertIndex++) != null)) {
    index -= 1; 
   }
   result.Add(_atlas);
   _atlas.root.sortIndex = insertIndex;
   insertIndex = 0;
   _atlas = null;
  }
  
  foreach(Atlas atlas in result) { 
   atlas.root.Build(atlas.texture);
   List<AtlasNode> nodes = new List<AtlasNode>();
   atlas.root.GetBounds(ref nodes);
   nodes.Sort(delegate (AtlasNode x, AtlasNode y) {
    if (x.sortIndex == y.sortIndex) return 0;
    if (y.sortIndex > x.sortIndex) return -1;
    return 1;
   });
   
   List<Rect> rects = new List<Rect>();
   foreach(AtlasNode node in nodes) {
    Rect normalized = new Rect(node.rc.xMin / atlas.root.rc.width, node.rc.yMin / atlas.root.rc.height, node.rc.width / atlas.root.rc.width, node.rc.height / atlas.root.rc.height);
    normalized.x += 0.5f / atlas.root.rc.width;
    normalized.width -= 1.0f / atlas.root.rc.width;
    normalized.y += 0.5f / atlas.root.rc.height;
    normalized.height -= 1.0f / atlas.root.rc.height;
    rects.Add(normalized);
   }
   
   atlas.uvRects = new AtlasDescriptor[rects.Count];
   for (int i = 0; i < rects.Count; i++) {
    atlas.uvRects[i] = new AtlasDescriptor();
    atlas.uvRects[i].width = (int)nodes[i].rc.width;
    atlas.uvRects[i].height = (int)nodes[i].rc.height;
    atlas.uvRects[i].name = nodes[i].name;
    atlas.uvRects[i].uvRect = rects[i];
   }
   
   atlas.root.Clear();
   atlas.texture.Apply(false, false);
   SaveAtlas(atlas, name);  
   atlas.texture.Apply(false, false);
  
  }
  
  return result.ToArray();
 }

 

最后一步实现就是把我们生成的图集保存一下:

 public static void SaveAtlas(Atlas atlas, string name) {

  if (atlas == null || atlas.texture == null)

   return;
  var bytes = atlas.texture.EncodeToPNG();
  if (!System.IO.Directory.Exists(Application.dataPath + "/Debug/"))

   System.IO.Directory.CreateDirectory(Application.dataPath + "/Debug/");
   string file = Application.dataPath + "/Debug/" + name + ".png";
  System.IO.File.WriteAllBytes(file, bytes);

 }

图片示例如下:

wKiom1S0w5jj1YI3AAEGf31II8Q552.jpg

以上是零散的图片,接下来我们将其合成图集:

wKiom1S0w8ugIfRNAAJWpmlpkC4938.jpg