【Unity】スクリプトだけでドット絵を作る方法

導入

ドットゲームの中には、装備によってキャラクターの見た目が変わるものがあります。そのシステムを実装する際、全てのキャラクターの見た目を作っていたらキリがありません。

…もしスクリプトでドット絵を作ることができたら、その膨大とも思える作業量やメモリをコンパクトにできるでしょう。というわけで今回は、スクリプトのみでドット絵を表示させる方法を解説します。

こんな感じで、コードからドットの画像を作ることができます。(完全ランダムにも関わらず、既視感のある画像が何度も生成されているのは、Unityの疑似乱数の性質によるものです)

コード

以下のコードは、動画で使われているコードの内、無駄な部分を全て省いたバージョンです。なので、RawImageをアタッチして、変数riに「RawImageがアタッチされたゲームオブジェクト自身」をアタッチすれば、そのまま使えます。

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

public class BrogIntroduce : MonoBehaviour
{
    public RawImage ri;

    void Start()
    {
        ri = GetComponent<RawImage>();

        SetMarioTexture();
        
    }

    void Update()
    {
        
        
    }

    // ーーーーーーーーーーーーーーーーーーーー
    //    関数一覧
    // ーーーーーーーーーーーーーーーーーーーー

    public string[,] CreateMarioPicture()
    {
        string BBB_ = "#000f";
        string rrr_ = "#f00f";
        string _e_e = "#ff0f";
        string x___ = "#0000";

        string[,] picture_str = new string[,]
        {
            {x___,x___,x___,rrr_,rrr_,rrr_,rrr_,rrr_,x___,x___,x___,x___},
            {x___,x___,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,x___},
            {x___,x___,BBB_,BBB_,BBB_,_e_e,_e_e,BBB_,_e_e,x___,x___,x___},
            {x___,BBB_,_e_e,BBB_,_e_e,_e_e,_e_e,BBB_,_e_e,_e_e,_e_e,x___},
            {x___,BBB_,_e_e,BBB_,BBB_,_e_e,_e_e,_e_e,BBB_,_e_e,_e_e,_e_e},
            {x___,BBB_,BBB_,_e_e,_e_e,_e_e,_e_e,BBB_,BBB_,BBB_,BBB_,x___},
            {x___,x___,x___,_e_e,_e_e,_e_e,_e_e,_e_e,_e_e,_e_e,x___,x___},
            {x___,x___,BBB_,BBB_,rrr_,BBB_,BBB_,BBB_,x___,x___,x___,x___},
            {x___,BBB_,BBB_,BBB_,rrr_,BBB_,BBB_,rrr_,BBB_,BBB_,BBB_,x___},
            {BBB_,BBB_,BBB_,BBB_,rrr_,rrr_,rrr_,rrr_,BBB_,BBB_,BBB_,BBB_},
            {_e_e,_e_e,BBB_,rrr_,_e_e,rrr_,rrr_,_e_e,rrr_,BBB_,_e_e,_e_e},
            {_e_e,_e_e,_e_e,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,_e_e,_e_e,_e_e},
            {_e_e,_e_e,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,_e_e,_e_e},
            {x___,x___,rrr_,rrr_,rrr_,x___,x___,rrr_,rrr_,rrr_,_e_e,x___},
            {x___,BBB_,BBB_,BBB_,x___,x___,x___,x___,BBB_,BBB_,BBB_,x___},
            {BBB_,BBB_,BBB_,BBB_,x___,x___,x___,x___,BBB_,BBB_,BBB_,BBB_},
        };
        return picture_str;
    }

    public Texture2D ToTexture2D(string[,] picture_str)
    {
        Texture2D picture = new Texture2D(picture_str.GetLength(1),picture_str.GetLength(0));
        
        for (int x = 0; x < picture_str.GetLength(1); x++)
        {
            for (int y = 0; y < picture_str.GetLength(0); y++)
            {
                string colorCode = picture_str[y,x];
                Color color = default(Color);

                if(ColorUtility.TryParseHtmlString(colorCode, out color))
                {
                    picture.SetPixel(x, picture_str.GetLength(0)-1-y, color);
                }                
            }
        }

        return picture;
    }

    public void SetTexture(Texture2D texture) 
    {
        texture.filterMode = FilterMode.Point;
        texture.Apply();
        ri.texture = texture;
    }

    public void SetMarioTexture()
    {
        string[,] picture_str = CreateMarioPicture();
        Texture2D picture = ToTexture2D(picture_str);
        SetTexture(picture);
    }
}

↓ ちなみにコードの結果はこうなります(右はさっき居た奴です)

コードの説明

大筋

①マリオの絵をカラーコードで描く→②UnityEngine.Texture2Dに置き換える→③ゲームオブジェクトのRawImageのテクスチャを置き換える

①マリオの絵をカラーコードで描く

CreateMarioPictureの部分で担当。

BBB_とか_e_eとか変な名前が付いていますが、「遠くから見たらそれっぽく見える」以外に深い理由はありません。

②UnityEngine.Texture2Dに置き換える

ToTexture2Dの部分で担当。

GetLength(int i)は配列のi次元の要素数を取得する関数。

ColorUtility.TryParseHtmlString(string colorCode, Color outcolor)は、「colorCodeがUnityEngineColorに変換できるか」の判定をbool(真偽)を返し、trueの場合、outcolorに変換後の色を代入する関数。

SetPixel(int x, int y, Color color)は、Texture2Dの座標(x, y)のピクセルの色をcolorに変更する関数。Unityの座標はx座標が→方向、y座標が↑方向になっている一方、コード内のpicture_strはx座標が→方向、y座標が↓方向になっているので、y座標をpicture_str.GetLength(0)-1-yで置換しています。

③ゲームオブジェクトのRawImageのテクスチャを置き換える

SetTextureの部分で担当。

filterModeをPointに変更しているのは、ドットがぼやけないようにする為。

Apply()最も重要な関数。この関数は、Texture2Dの変更内容を適用する関数。これが無ければ、変更が適用されないので、灰色の画像が現れます。

補足

Textureの画像はピクセル数が増えても、大きさが変わらないので、大きさを不変にするか、大きさを変えるコードを作っておいた方が良いです。

また、作ったピクセルを動かしたい場合は、UIのRawImageではなく、ゲームオブジェクトにRawImageをアタッチして下さい。

Q&A

Q1. SpriteRendererのSprite.textureを使用せず、RawImage.textureを使用しているのは何故?

A1. SpriteRendererのSprite.textureは読み取り専用だからです。

Q2. ピクセルが表示されない。

A2. テクスチャを表示するにはキャンバスが必要なので、キャンバスの階層の下に移動させて下さい。

【Unity】「ドット絵がぼやける」対処法を紹介

導入

spriteのぼやけを直すときは、素材となる画像の設定をいじれば良かったが、コードで作った(素材となる画像が存在しない)textureのぼやけを直すときは…あれ?どこをいじれば良いんだろう…。

――というわけで「texture ぼやける」と検索。

しかし、解決策が出てこない…。仕方なくUnityの公式ドキュメントで検索したところ、使えそうなコードを発見!

というわけで、使えそうなコードを見つけたので、Unityドット勢には是非知っておいてほしいコードを共有したいと思います。

コード

やっていることは、コードを使ってtextureのfilterModeをPointに変えているだけ。

テクスチャのぼやけを消したい場合

// どのtextureに対して行うか選ぶ
// (例1)
Texture texture = GetComponent<RawImage>().texture;
// (例2)
Texture texture = GetComponent<Renderer>().material.mainTexture;

// 今回紹介するコード(フィルターモードをpointに変更する)
texture.filterMode = FilterMode.Point;

スプライトのぼやけを消したい場合

スプライトのtextureに対して、さっきの処理をするだけです。

// どのspriteに対して行うか選ぶ
// (例1)
Texture texture = GetComponent<Renderer>().sprite.texture;

// さっきと同じ
texture.filterMode = FilterMode.Point;

いつ使うの?

冒頭でも触れましたが、「いつ使うか?」と聞かれたら、コード内で作ったtextureのfilterModeをPointにしたいときです。それか「ゲームの途中でfilterModeを変えるとき」とか(『Evoland』っぽいゲームを作りたいなら使うかも)。

あと、なぜコード内でtextureを作っているかといえば、「2DObjectの4方向から見たimageを作る手間を省くため」です。

↓ 実際に使う場面はこのような場面です。

https://nazenavi.com/2021/02/24/%e3%80%90unity%e3%80%91%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%a0%e3%81%ae%e3%81%bf%e3%81%a7%e3%83%89%e3%83%83%e3%83%88%e7%b5%b5%e3%82%92%e8%a1%a8%e7%a4%ba%e3%81%95%e3%81%9b%e3%82%8b%e6%96%b9/

参考文献

今回の参考文献は冒頭でも触れた通り、「Unityの公式ドキュメント(2021.1)」>「Filter Mode」

Unity公式ドキュメント – Filter Mode