この記事の内容はタイトルそのままです。
複数画像を1つのテクスチャにパッキングして、そこから元の画像の枚数分Spriteを生成した時、SetPassCallは1で済むということです。
SetPassCallをどれだけ減らせるかが大事なモバイル環境では特に有用なのではないかと思います。
今回はTexturePackingもSprite生成も動的に行うことによって柔軟にこれを活用できる方法を書いていきます。
(※わかりづらいですが、何もない場合のSetPassCallは2なので増えたのは1だけです。)
なお、Unity5.6.0f3で検証しています。
#はじめに
まさかと思うけど大量のTextureをPackingして1枚画像にして、そこからSprite.Createした場合のSetPassCallって1なんですか
— Negipoyoc (@CST_negi) 2017年4月15日
@CST_negi そういうことですよ。
— 伊藤周 (@warapuri) 2017年4月15日
#検証する
Scriptを1つ書きます。
内容は
- WWWを使って、画像を10枚ダウンロードする。
- すべての画像がダウンロードできたらそれらを用いて1枚の画像にパッキングする。
- パッキングされたテクスチャからSprite.Createを用いて元の画像をSprite化する。
- 10個のSpriteRendererに対して3.で作ったSpriteを割り当てる。
TexPacker.cs
using System.Collections;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using System.Linq;
public class TexPacker : MonoBehaviour
{
void Start()
{
//10個SpriteRendererを取得する
var spriteRenderers = GetComponentsInChildren<SpriteRenderer>();
//1.WWWを使って、画像を10枚ダウンロードする
var streams = Enumerable.Range(0,spriteRenderers.Count()).Select(i => Observable.FromCoroutine<Texture2D>(obs => GetTextures(i.ToString(), obs)));
//2. すべての画像がダウンロードできたら
Observable.WhenAll(streams)
.Subscribe(textures =>
{
//2.それらを用いて1枚の画像にパッキングする。
var packedTexture = new Texture2D(4096, 4096);
//このuvsにはPackした画像のどこに元画像があるかを格納するものです。
//(x:0.39, y:0.88, width:0.25, height:0.06)のような値がパックした枚数分入ってます。
var uvs = packedTexture.PackTextures(textures, 0, 4096, false);
//元画像を切り出すためにPackした画像のWとHをとっておく
var packedTextureWidth = packedTexture.width;
var packedTextureHeight = packedTexture.height;
for (var i = 0; i < spriteRenderers.Length; i++)
{
//3.元の画像をSprite化する。Packした画像のwとh、そしてuvsを使うことで元の画像を切り抜ける。
//またメッシュは矩形でいいので、SpriteMeshType.FullRectを指定してSprite化するとき余計な負荷がかからないようにする。
var s = Sprite.Create(packedTexture, new Rect(packedTextureWidth * uvs[i].x, packedTextureHeight * uvs[i].y, packedTextureWidth * uvs[i].width, packedTextureHeight * uvs[i].height), Vector2.one * 0.5f, 100, 10, SpriteMeshType.FullRect);
//4.10個のSpriteRendererに対して3.で作ったSpriteを割り当てる。
spriteRenderers[i].sprite = s;
}
});
}
//WWWを使ってダウンロードするやつ。今回はStreamingAssetsからロードしている。
IEnumerator GetTextures(string i, IObserver<Texture2D> observer)
{
var filePath = "file:///" + Application.streamingAssetsPath + "/image" + i + ".png";
var www = new WWW(filePath);
{
while (!www.isDone) { yield return null; }
var tex = www.texture;
observer.OnNext(tex);
}
observer.OnCompleted();
}
}
#終わり
結果は記事の最初に示した通りです。
以上で複数のSpriteがあってもSetPassCallを1に抑えることができました。
動的にできるのでネット上に画像があってもWWW使ってやればいいと思います。
こういうときにUniRxのWhenAllのありがたみを感じますね。