スプライトを
分割して読み込む

このページはVersion 2018.4.1f1 Personalを対象としています。

更新履歴

参考URL

概要

このページではResourcesからスプライトを分割して読み込むやり方をまとめています。 例として、下の画像のようにアイコンが並べられた画像ファイルから、1つのアイコンを選んで表示します。

方法としては、Sprite EditorのSlice機能を使う方法と、Sprite.Createメソッドを使う方法を挙げています。

Sprite EditorのSlice機能を使う

Sprite EditorのSlice機能を使う方法です。 読み込んだ画像のImport SettingsでSprite ModeをMultipleに設定し、 Sprite EditorボタンでSprite Editorを開きます。

Sprite Editorウィンドウで、上部メニューのSliceを選択し、 TypeをGrid By Cell Sizeに、Pixel SizeをX=16, Y=16にそれぞれ設定し、 Sliceボタンを押します。 その後Applyボタンで編集を適用します。

するとProjectビューに、下の画像のように、アイコンが連番で生成されます。

この画像を読み込んで表示するコードは次のようになります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpriteController: MonoBehaviour {

    SpriteRenderer sr;
    Sprite[] icons;

    // Use this for initialization
    void Start () {
        sr = GetComponent<SpriteRenderer>();
        icons = Resources.LoadAll<Sprite>("icon");
        sr.sprite = icons[0];//コインのアイコンは[1]ではなく[0]
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

ただし、Grid By Cell Sizeでは、完全に透明なアイコンは読み飛ばされるので、 連番とアイコン番号がずれてしまい、上手くありません。 これを回避する一番簡単な方法は、背景を何らかの色で塗りつぶし、完全に透明なアイコンをなくすことです(参考URL)。

Sprite.Createメソッドを使う

Sprite.Createメソッドを使う方法です。

この方法ではSprite Editorのスライス機能は使わないので、 画像ファイルのImport SettingsのSprite ModeはSingleのままです。

スプライトを分割して読み込むGetDivSpriteメソッドを作りました。 イメージとしてはDXライブラリのLoadDivGraph関数に近いです。 コードは以下のようになります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpriteController: MonoBehaviour {

    SpriteRenderer sr;
    Sprite[] icons;

    //スプライトを「Assets/Resorces/path」から読み込み、
    //幅width・高さheightで分割し、、
    //右方向・下方向の順で配列として返す。
    Sprite[] GetDivSprite(string path, int width, int height)
    {
        Sprite sprite = Resources.Load<Sprite>(path);
        int xMax = ((int)sprite.bounds.size.x / width);
        int yMax = ((int)sprite.bounds.size.y / height);
        Sprite[] sprites = new Sprite[xMax * yMax];
        for (int y = 0; y < yMax; y++)
        {
            for (int x = 0; x < xMax; x++)
            {
                Rect rect = new Rect(x * width, (yMax - y - 1) * height, width, height);
                sprites[y * xMax + x] = 
                    Sprite.Create(sprite.texture, rect, new Vector2(0.5f, 0.5f), 1.0f);
            }
        }
        return sprites;
    }

    // Use this for initialization
    void Start () {
        sr = GetComponent<SpriteRenderer>();
        icons = GetDivSprite("icon", 16, 16);
        sr.sprite = icons[1];
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

ポイントはSprite.Createメソッドで、 sprite.textureの一部分から新たなスプライトを作成しているところです。 引数pivotで中心座標を指定するようです。 new Vector2(0.5f, 0.5f)で中央が中心になります。 キャラクターなどには良いですが、マップのタイルなどに使うと使いづらいかもしれません。 ちなみにUIのImageオブジェクトで使うと、pivotは無視され左上が基点となるようです。

ただしSprite.Createメソッドを使う方法では、 同じスプライトであっても、読み込むたびに、 新たなスプライトとして読み込まれます。 また、Sprite.Createメソッドで読み込んだスプライトは、 Destroyメソッドで明示的に開放しないと、 メモリリークの原因となります。 さらに、きちんとDestroyメソッドで解放したとしても、 小さなスプライトの確保・解放を頻繁に繰り返すと、 「failed to create buffer」「attempt to lock null buffer」などのエラーが出て、 画面が真っ白になってしまうことがあります。

スプライトのサイズがあまり大きくなければ、 ゲーム開始時にすべて読み込んで、 Dictionaryにバッファして、 それを使いまわすのが良いかと思います。

Sprite Atlasから取得したスプライトをさらに分割する

Sprite Atlasから取得したスプライトをさらに分割する場合、 Sprite Atlasの設定や、分割する際の座標の計算に注意が必要です。

まず、Sprite Atlasのインスペクタで以下の画像のように、 Packingの「Allow Rotation」「Tight Packing」のチェックを外します。 またドット絵の場合、「Filter Mode」を「Point」に、「Compression」を「None」に変更しておくとよいでしょう。

次に、SpriteAtlas.GetSprite()メソッドで取得したスプライトは、 以下のコードのGetDivSprite()メソッドで分割できます。

//読み込み済みのスプライトを、
//幅width・高さheightで分割し、、
//右方向・下方向の順で配列として返す。
public static Sprite[] GetDivSprite(Sprite sprite, int width, int height)
{
    int xMax = (sprite.packed ? (int)sprite.textureRect.width : (int)sprite.bounds.size.x)
        / width;
    int yMax = (sprite.packed ? (int)sprite.textureRect.height : (int)sprite.bounds.size.y)
        / height;
    Sprite[] divSprites = new Sprite[xMax * yMax];
    for (int y = 0; y < yMax; y++)
    {
        for (int x = 0; x < xMax; x++)
        {
            Rect rect = new Rect(
                (sprite.packed ? sprite.textureRect.x : 0) + x * width,
                (sprite.packed ? sprite.textureRect.y : 0) + (yMax - y - 1) * height,
                width,
                height);
            divSprites[y * xMax + x] = 
                Sprite.Create(sprite.texture, rect, new Vector2(0.5f, 0.5f), 1.0f);
        }
    }
    return divSprites;
}

戻る

inserted by FC2 system