Skeyllのブログ

インディーデベロッパー

【Unity】スクリプトで様々な形のSpriteを作成する

 UnityではTexture2DからSpriteを作成することができる。Texture2D自体はインスタンスを作るだけで作成できるため、スクリプトから自在にSpriteを作成することができる。
 ちなみにSpriteというものはUnityでUIのイメージ画像や2Dモデルとして使用されているものだ。これをImageやSpriteRenderに設定することで画像(外部から取り込んだものも含む)を画面に表示している。

f:id:Skeyll:20200726130226p:plain

環境:Windows10、Unity 2019.2f
コード: BlogSample/Unity/Sprite at master · skeyll/BlogSample · GitHub

Spriteを作成

Sprite.Createを使うことでSpriteを作成することができる。引数のタイプには種類があり、すくなくとも(Texture2D texture, Rect rect, Vector2 pivot)の三つは指定する必要がある。
 Texture2Dの作成自体は簡単であり、高さと幅を指定してコンストラクタで新しくインスタンスを生成するだけでいい。これで作成したものをそのままCreateの引数に指定してもいいが、別途色を変更することもできる。

using UnityEngine;

public class SpriteCreater : MonoBehaviour{
    //UnityEditer上で2D→Spriteを作成し、それをアタッチする
    [SerializeField] SpriteRenderer testSprite;
    void Start() {
        CreateSquare(9);
    }

    void CreateSquare(){
        //(Width, height)
        Texture2D texture2D = new Texture2D (128, 128);

        //最初から色を付けたい場合はテクスチャの各ピクセルに色を入れていく
        //単色の場合はあとからカラーを指定して変更することもできる。その場合は以下Sprite.Createまで不要
        int pixelAmount = 128 * 128; //テクスチャの高さや幅はピクセル数を表すので総ピクセルは両辺の積
        Color[] changedPixels = new Color[pixelAmount];
        for (int i = 0; i < pixelAmount; i++) {
            changedPixels.SetValue(Color.white, i);
        }

        texture2D.SetPixels (changedPixels);
        texture2D.Apply();

        Sprite sprite = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
        
       testSprite.sprite = sprite;
    }
}

四角形以外のSpriteを作成

 Texture2Dには高さと幅、そして色と画像の表示方法に関する設定以外を指定することができない。そのため、実際は正四角形以外のSpriteを作成することはできないが、表示したくない部分の色に透明を指定することで疑似的に他の形のSpriteを作成する。
 なおPixelは左下を原点として左から右へ、下から上へと添え字が与えられる。底辺の長さと剰余計算を使えばだいたいの場合で理想の形を作ることができる。

一部が欠けた長方形

using UnityEngine;

public class SpriteCreater : MonoBehaviour{
    [SerializeField] SpriteRenderer testSprite;
    void Start() {
        CreateNewTexture(9);
    }
    void CreateNewTexture(int commandAmount){
        //Pure rectangle
        int width = 65 * commandAmount;
        int row = 1 + commandAmount / 7;
        int height = 65 * row;
        //Create rextangle texture
        Texture2D texture2D = new Texture2D (width, height);
        //Rectangle pixels
        int pixelAmount = width * height;
        //Amount of colored pixels in a low 
        int clearBaundary = 6 - (commandAmount % 6);
        //Create array for color
        Color[] changedPixels = new Color[pixelAmount];
        //input color
        for (int i = 0; i < pixelAmount; i++) {
            if(i >= pixelAmount / row){
                changedPixels.SetValue(Color.white, i);
            }else if (width / clearBaundary <= i % width){
                changedPixels.SetValue(Color.clear, i);
            }else{
                changedPixels.SetValue(Color.white, i);
            }
        }
        //Create texture with pixels
        Texture2D newTexture = new Texture2D (texture2D.width, texture2D.height, TextureFormat.RGBA32, false);
        newTexture.filterMode = FilterMode.Point;
        newTexture.SetPixels (changedPixels);
        newTexture.Apply();
        //Create sprite of texture
        Sprite sprite = Sprite.Create(newTexture, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
        testSprite.sprite = sprite;
    }
}

三角形

using UnityEngine;

public class SpriteCreater : MonoBehaviour{
    [SerializeField] SpriteRenderer testSprite;
    void Start() {
        CreateTriangle();
    }    
    void CreateTriangle(){
        //Pure rectangle
        int width = 65;
        int height = 65;
        //Create rextangle texture
        Texture2D texture2D = new Texture2D (width, height);
        //Rectangle pixels
        int pixelAmount = width * height;
        //The part of clear
        int heightCount = 0;
        //Create array for color
        Color[] changedPixels = new Color[pixelAmount];
        bool putColor = false;
        //input color
        for (int i = 0; i < pixelAmount; i++) {
            if((height * heightCount) == i){
                heightCount += 1;
                putColor = false;
            }else if((height * heightCount) - heightCount == i){
                putColor = true; 
            }
            if(putColor){
                changedPixels.SetValue(Color.white, i);
            }else{
                changedPixels.SetValue(Color.clear, i);
            }
        }
        //Create texture with pixels
        Texture2D newTexture = new Texture2D (texture2D.width, texture2D.height, TextureFormat.RGBA32, false);
        newTexture.filterMode = FilterMode.Point;
        newTexture.SetPixels (changedPixels);
        newTexture.Apply();
        //Create sprite of texture
        Sprite sprite = Sprite.Create(newTexture, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
        testSprite.sprite = sprite;
    }
}

まとめ

 スクリプトから動的にSpriteを作成するのはブロックの形が定まっていないテトリスやおえかきアプリなどで使えるかもしれない。UIのImageと2DモデルとしてのSpriteは同じ画像描写だが描画方法が異なっており、画像を動かす場合はSpriteRenderを利用した方がパフォーマンスがよくなる。しかし、SpriteはせいぜいScaleが変更できるぐらいで、動的に描画したものを変更することは難しく、例えばLayoutGroupで描画しているものを変更することはできない。
 そのため、操作可能な所持アイテムの入ったサイズが変化するインベントリ(例えば最大装填数が変わる銃のマガジンのようなもの)をゲーム中に作成するためにこのコードを書いたが、インベントリ内に綺麗にアイテムを並べる手間や、サイズの調整が難しいSpriteでアイテムをインベントリに合うサイズで生成する手間などのために使うことはなかった。インベントリをGUIで、動かすアイテムをSpriteで二重に作成した方が明らかに楽だからだ。
 しかし、ネットを検索してもSprite作成に関して出てくるのは正四角形か、元画像を上書きする形での操作ばかりであったためこの記事を作成した。役に立つかはわからないが、使い道があれば幸いだ。

参考
Unity - Scripting API: Texture2D
Unity - Scripting API: Sprite
[https://kan-kikuchi.hatenablog.com/entry/Texture2DToSprite:title=Texture2DからSpriteを生成する【Unity】 - (:3[kanのメモ帳]]
Textureをスクリプトから編集してみる - のしメモ アプリ開発ブログ
Unityで学ぶ画像処理【色の変換編】 - のしメモ アプリ開発ブログ

プライバシーポリシー  ©Skeyll 2019- All Rights Reserved.