UnitySpriteの画像をシルエット化する方法

<h5>　Sprite画像をシルエットにした状態で登場させ、その後に正体(色)を出すということをしてみました。<br>
　(2番煎じ感も否めませんがあくまでもメモということで)</h5>
<br>

<h5>　まず、適当に画像を用意します。今回は、Unityの2D ObjectのSpriteをHierarchyに置き、以下のようにします。<br>
なお、オブジェクト名は、'EnemySprite'です。(下画面が)</h5>

![Picture01.png](https://qiita-image-store.s3.amazonaws.com/0/96154/94cba455-061f-a1fe-049b-cb11fdb31718.png)
<br>
<h5>　次に、シルエットの作り方です。シルエットは、このSpriteのInspectorのSpriteRendererコンポーネントのColorをいじることで再現できます。(下画面の青表示の部分が該当箇所です。)<br>
このColorはRGBA(赤,緑,青,不透明度)を0~255の値で取っているので、真っ黒なシルエットは、Inspector上においては、R:255 G:255 B:255 A:0に設定することでシルエットに出来ます。</h5>
![Picture02.png](https://qiita-image-store.s3.amazonaws.com/0/96154/20fd4110-c31d-a94e-4496-ee8312d8b918.png) ![Picture03.png](https://qiita-image-store.s3.amazonaws.com/0/96154/35e3e649-697a-f2ea-29f7-1b0107bb0879.png)
<br>
<br>
<h5>　それでは、プログラムの方を見ていきます。プログラム上ではSpriteRendererクラスのcolorが上記で説明したInspectorのColorに当たります。それぞれRGBAの値は、floatの0~1で表されます。まず、実際にそうなのか下のプログラムを作成して確認をしてみましょう。<br></h5>


```csharp:ColorValuesReader.cs

using UnityEngine;
using System.Collections;

public class ColorValuesReader : MonoBehaviour {

    //SpriteRenderクラス
    SpriteRenderer sr;

    //それぞれカラーの値(floatで返す)

    [SerializeField]
    private float red;      //赤
    [SerializeField]
    private float green;    //緑
    [SerializeField]
    private float blue;     //青
    [SerializeField]    
    private float alpha;    //不透明度

    // Use this for initialization
	void Start () {

        //SpriteRenderのコンポーネント取得
        sr = gameObject.GetComponent<SpriteRenderer>( );

	}
	
	// Update is called once per frame
	void Update () {

        this.red    = sr.color.r;   //赤の値を格納
        this.green  = sr.color.g;   //緑の値を格納
        this.blue   = sr.color.b;   //青の値を格納
        this.alpha  = sr.color.a;   //不透明度の値を格納
	
	}
}


```
<br>
<h5>では、上記プログラムをEnemySpriteにアタッチして動かしてみたください。動かすときに、EnemySpriteのInspectorのColorをダブルクリックして、カラーパレットをてきとうに移動させてみましょう。</h5><br>

![Picture04.png](https://qiita-image-store.s3.amazonaws.com/0/96154/1fda66bf-90b5-848f-d912-d7cfc80b48c3.png)

おそらく、Inspector上のColorValueReaderのred、green、blue、alphaがそれぞれ0~1を取っているのがわかると思います。</h5><br>

<h5>　それでは、敵が下からせり上がってきて移動完了後、敵のシルエットが解けるプログラムを書いていきましょう。<br>
ここでは、"色の変化速度"というColor変数を用いてシルエットが解けるプログラムを書いていきます。<br></h5>

```csharp:RiseEnemy.cs

using UnityEngine;
using System.Collections;

public class RiseEnemy : MonoBehaviour {

    //SpriteRenderクラス
    SpriteRenderer sr;

    //到達判定とする距離
    private float range = 0.05f;
    //色の変化速度
    private Color colorSpeed = new Color(1.0f, 1.0f, 1.0f, 0.0f);
    //色の割合(1.0fを超えられないのであえてこの値にしている)
    private float maxColor = 0.99f;

    //スタート位置
    [SerializeField]
    private Vector3 startPoint  = new Vector3(0.0f,-10.0f,0.0f);
    //ゴール位置(この地点の付近まで到達したかどうかで処理を考える)
    [SerializeField]
    private Vector3 risePoint   = Vector3.zero;
    //移動速度
    [SerializeField]
    private Vector3 speed       = new Vector3(0.0f, 2.0f, 0.0f);

    //それぞれカラーの値(Inspectorに表示)

    [SerializeField]
    private float red;      //赤
    [SerializeField]
    private float green;    //緑
    [SerializeField]
    private float blue;     //青
    [SerializeField]
    private float alpha;    //不透明度

    //オブジェクトを動かす関数
    //引数　：移動速度,到達判定の距離,ゴール地点
    //返り値：ゴール地点まで近づいたかどうか真偽で返す
    private bool moveSprite (Vector3 spd,float r,Vector3 gop )
    {
        //現在位置を格納
        Vector3 pos = this.transform.position;
        //現在位置からゴール地点までの距離を計算
        float dist = Vector3.Magnitude(gop - pos);
        //距離が縮まったかどうか
        if (dist < r)
        {
            //縮まったならtrueを返す
            return true;
        }
        //縮まってなければSpriteを移動させる
        this.transform.position += spd * Time.deltaTime;
        //falseを返す
        return false;
    }

    //オブジェクトがシルエットから正体を表す関数
    //引数：色の変化の速度,色の値
    private void riseEnemy (Color cs,float v)
    {
        //Spriteの色を更新する
        sr.color  +=cs * Time.deltaTime;
        //色の割合が限界値を超えたかどうか
        if (sr.color.r>v)
            //this.timer > ft)//
        {
            //無理やり色を白にする(キャラの正体が完全にわかる)
            sr.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            //ENDを流す
            print("END");
            //強制的にreturn
            return;
        }
        //まだ超えていなければ,現在の色の値を表示
        print("C:" + sr.color);
    }

    //Inspectorに表示してある色の値を更新する
    //引数：色
    private void readColor (Color co )
    {
       this.red     = co.r; //赤
       this.green   = co.g; //緑
       this.blue    = co.b; //青
       this.alpha   = co.a; //不透明度

    }
	// Use this for initialization
	void Start () {
        //SpriteRenderのコンポーネント取得
        sr = gameObject.GetComponent<SpriteRenderer>( );
        //Spriteの位置をスタート位置に置く
        this.transform.position = this.startPoint;
        //Spriteの色を真っ黒(シルエット)にする
        sr.color = new Color(0.0f,0.0f,0.0f,1.0f);
	}
	
	// Update is called once per frame
	void Update () {
        //移動が終了したかどうか
        if (moveSprite(this.speed, this.range, this.risePoint))
        {
            //終了すれば,シルエットから正体を表していく
            riseEnemy(this.colorSpeed,this.maxColor);
            //色の値をInspectorに表示
            readColor(sr.color);

        }
	}

}


```
<h5>では、先ほど、作った"ColorValueReader.cs"を外し、代わりに上記プログラムをEnemySpriteにアタッチして動かしてみたください。<br>
すると、下の画面のように動きます。<br></h5>

![PictureAnimations01.gif](https://qiita-image-store.s3.amazonaws.com/0/96154/3a005eb2-a4ff-a89d-9017-32f40411c2ed.gif)

<h5>コンソール画面やInspectorで値が少しずつ変化しています。(見難いと思いますが)</h5><br>

<h5>また、"色の変化速度"を用いずに、経過時間を使い、"経過時間 / 変化し切るまでの時間"の割合で色の値を表現する方法もあります。これは、下記URLにあったSpriteをフェードアウトさせる方法が書かれたサイトのプログラムを参考に作りました。<br></h5>

参考URL(Spriteをフェードアウトさせる方法)：http://ftvoid.com/blog/post/686
<br>

```csharp:RiseEnemy_Timever.cs

using UnityEngine;
using System.Collections;

public class RiseEnemy_Timever : MonoBehaviour {

    //SpriteRenderクラス
    SpriteRenderer sr;

    //到達判定とする距離
    private float range = 0.05f;

    //スタート位置
    [SerializeField]
    private Vector3 startPoint  = new Vector3(0.0f,-10.0f,0.0f);
    //ゴール位置(この地点の付近まで到達したかどうかで処理を考える)
    [SerializeField]
    private Vector3 risePoint   = Vector3.zero;
    //移動速度
    [SerializeField]
    private Vector3 speed       = new Vector3(0.0f, 2.0f, 0.0f);

    //それぞれカラーの値(Inspectorに表示する)

    [SerializeField]
    private float red;      //赤
    [SerializeField]
    private float green;    //緑
    [SerializeField]
    private float blue;     //青
    [SerializeField]
    private float alpha;    //不透明度

    //シルエットから正体を表すまでの総時間
    private float riseTime = 1.5f;
    //シルエットを解除し始めてからの経過時間
    private float timer     = 0.0f;

    //オブジェクトを動かす関数
    //引数　：移動速度,到達判定の距離,ゴール地点
    //返り値：ゴール地点まで近づいたかどうか真偽で返す
    private bool moveSprite (Vector3 spd,float r,Vector3 gop )
    {
        //現在位置を格納
        Vector3 pos = this.transform.position;
        //現在位置からゴール地点までの距離を計算
        float dist = Vector3.Magnitude(gop - pos);
        //距離が縮まったかどうか
        if (dist < r)
        {
            //縮まったならtrueを返す
            return true;
        }
        //縮まってなければSpriteを移動させる
        this.transform.position += spd * Time.deltaTime;
        //falseを返す
        return false;
    }

    //オブジェクトがシルエットから正体を表す関数
    //引数：シルエットから正体を表すまでの総時間
    private void riseEnemy (float ft)
    {
        
        //シルエットを開場し始めてからの経過時間を増やす
        this.timer += Time.deltaTime;
        //色(RGB)の値を更新する
        float cv = this.timer / ft;
        
        //Spriteの色を更新する
        sr.color  = new Color(cv, cv, cv, 1.0f);//+=cs * Time.deltaTime;//
        //色の割合が限界値を超えたかどうか
        if (this.timer > ft)
            //this.timer > ft)//
        {
            //無理やり色を白にする(キャラの正体が完全にわかる)
            sr.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            //ENDを流す
            print("END");
            //強制的にreturn
            return;
        }
        //まだ超えていなければ,現在の色の値をコンソール表示
        print("C:" + sr.color);
    }

    //Inspectorに表示してある色の値を更新する
    //引数：色
    private void readData (Color co )
    {
       this.red     = co.r; //赤
       this.green   = co.g; //緑
       this.blue    = co.b; //青
       this.alpha   = co.a; //不透明度

    }

	// Use this for initialization
	void Start () {
        //SpriteRenderのコンポーネント取得
        sr = gameObject.GetComponent<SpriteRenderer>( );
        //Spriteの位置をスタート位置に置く
        this.transform.position = this.startPoint;
        //Spriteの色を真っ黒(シルエット)にする
        sr.color = new Color(0.0f,0.0f,0.0f,1.0f);
	}
	
	// Update is called once per frame
	void Update () {
        //移動が終了したかどうか
        if (moveSprite(this.speed, this.range, this.risePoint))
        {
            //終了すれば,シルエットから正体を表していく
            riseEnemy(this.riseTime);
            //色の値をInspectorに表示
            readData(sr.color);
        }
	}

}

```

<h5>この２つのプログラム以外にもっと効率よいプログラムも見つかるかもしれませんが、詰んだ時はぜひ利用してみてください。</h5><br>