Unity教程之-Unity3d中手动合并图集

 

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

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

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


public AtlasNode Insert(Texture2D image, int index) {

if (image == null)
return null;

if (child != null)

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

if (newNode != null)

return newNode;

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

}

else {

if (hasImage)

return null;

if (!ImageFits(image, rc))

return null;

if (PerfectFit(image, rc)) {

hasImage = true;

imageRef = image;

name = imageRef.name;

sortIndex = index;

return this;

}

child = new AtlasNode[2];

child[0] = new AtlasNode();

child[1] = new AtlasNode();

float deltaW = rc.width - image.width;

float deltaH = rc.height - image.height;

if (deltaW > deltaH) {

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

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

}

else {

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

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

}
return child[0].Insert(image, 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 (imageRef != null) {

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

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

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

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

}

}

if (TEXTURE_PADDING > 0 && BLEED) {

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

int x = imageRef.width - 1;

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

}

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

int y = imageRef.height - 1;

target.SetPixel(x + (int)rc.x, y + (int)rc.y + TEXTURE_PADDING, data[x + y * imageRef.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