同一のTextureから複数のSpriteを生成した時のSetPassCallは1という話+動的にそれを行う方法

この記事は最終更新日から1年以上が経過しています。

この記事の内容はタイトルそのままです。
複数画像を1つのテクスチャにパッキングして、そこから元の画像の枚数分Spriteを生成した時、SetPassCallは1で済むということです。
SetPassCallをどれだけ減らせるかが大事なモバイル環境では特に有用なのではないかと思います。

今回はTexturePackingもSprite生成も動的に行うことによって柔軟にこれを活用できる方法を書いていきます。

以下にわかりやすい結果の画像を貼っておきます。
test1.png

(※わかりづらいですが、何もない場合のSetPassCallは2なので増えたのは1だけです。)

なお、Unity5.6.0f3で検証しています。

はじめに

検証する

Scriptを1つ書きます。
内容は
1. WWWを使って、画像を10枚ダウンロードする。
2. すべての画像がダウンロードできたらそれらを用いて1枚の画像にパッキングする。
3. パッキングされたテクスチャからSprite.Createを用いて元の画像をSprite化する。
4. 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();
    }
}

この時のHierarchyはこうなっています。
CropperCapture[324].png

終わり

結果は記事の最初に示した通りです。

以上で複数のSpriteがあってもSetPassCallを1に抑えることができました。
動的にできるのでネット上に画像があってもWWW使ってやればいいと思います。
こういうときにUniRxのWhenAllのありがたみを感じますね。

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした