[Unity]タップして波紋エフェクトを出す手順

# <i class="fa fa-cubes" style="font-size:1em;"></i> 概要
Unityのシェーダーを使ってタップ（またはクリック）した時に波紋のアニメーションを生成する方法

![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/183676/30964fd4-6bfc-b739-4aba-755856fb34ef.gif)


こんな感じで水面に広がる波紋のようにアニメーションさせるスクリプトを書いてみました。
<i class="fa fa-cloud-download" style="font-size:1em;"></i> https://github.com/katsuma99/TapRippleShader

以下にシェーダーのソースを載せていますが、今回は細かい解説はなしに波紋エフェクトが使えるように手順を説明していきます。また、波紋エフェクトはタップした時に発生させることを想定しているため、タップ時の当たり判定についても少し解説します。

```csharp:Ripple.shader
Shader "Custom/Ripple" {
	Properties
	{
		[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
		[HideInInspector]_StartTime("StartTime", Float) = 0
		_AnimationTime("AnimationTime", Range(0.1, 10.0)) = 1.5
		_Width("Width", Range(0.1, 3.0)) = 0.3
		_StartWidth("StartWidth", Range(0, 1.0)) = 0.3
		[Toggle] _isAlpha("isAlpha",Float) = 1
		[Toggle] _isColorShift("isColorShift",Float) = 1
		[MaterialToggle] PixelSnap("Pixel snap", Float) = 1
	}

	SubShader
	{
		Tags
		{
			"Queue" = "Transparent"
			"IgnoreProjector" = "True"
			"PreviewType" = "Plane"
		}

		Lighting Off
		ZWrite Off
		Fog{ Mode Off }
		Blend SrcAlpha OneMinusSrcAlpha

		Pass
		{
			CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile DUMMY PIXELSNAP_ON
#include "UnityCG.cginc"

			struct v2f
			{
				float4 vertex   : SV_POSITION;
				half2 texcoord  : TEXCOORD0;
			};

			v2f vert(appdata_base IN)
			{
				v2f OUT;
				OUT.vertex = UnityObjectToClipPos(IN.vertex);
				OUT.texcoord = IN.texcoord;
				return OUT;
			}

			fixed3 shift_col(fixed3 RGB, half3 shift)
			{
				fixed3 RESULT = fixed3(RGB);
				float VSU = shift.z*shift.y*cos(shift.x*3.14159265 / 180);
				float VSW = shift.z*shift.y*sin(shift.x*3.14159265 / 180);

				RESULT.x = (.299*shift.z + .701*VSU + .168*VSW)*RGB.x
					+ (.587*shift.z - .587*VSU + .330*VSW)*RGB.y
					+ (.114*shift.z - .114*VSU - .497*VSW)*RGB.z;

				RESULT.y = (.299*shift.z - .299*VSU - .328*VSW)*RGB.x
					+ (.587*shift.z + .413*VSU + .035*VSW)*RGB.y
					+ (.114*shift.z - .114*VSU + .292*VSW)*RGB.z;

				RESULT.z = (.299*shift.z - .3*VSU + 1.25*VSW)*RGB.x
					+ (.587*shift.z - .588*VSU - 1.05*VSW)*RGB.y
					+ (.114*shift.z + .886*VSU - .203*VSW)*RGB.z;

				return RESULT;
			}

			sampler2D _MainTex;
			float _StartTime,_Width,_StartWidth, _AnimationTime, _isAlpha,_isColorShift;
			fixed4 frag(v2f IN) : SV_Target
			{
				fixed4 color = tex2D(_MainTex, IN.texcoord);
				float2 pos = (IN.texcoord - float2(0.5,0.5)) * 2; //-1~1 幅2
				float dis = (_Time.y - _StartTime) / _AnimationTime + _StartWidth - length(pos);

				if (dis < 0 || dis > _Width)
					return fixed4(0,0,0,0);

				float alpha = 1;
				if (_isAlpha == 1)
				{
					alpha = clamp((_Width - dis) * 3, 0.1, 1.5);
				}

				fixed3 shiftColor = color;
				if (_isColorShift == 1)
				{
					half3 shift = half3(_Time.w * 10, 1, 1);
					shiftColor = shift_col(color, shift);
				}

				return fixed4(shiftColor, color.a * alpha);
			}
			ENDCG
		}
	}
	Fallback "Sprites/Default"
}
```


# <i class="fa fa-list" style="font-size:1em;"></i> 使用するための手順
　1. マテリアルの作成
　2. ゲームオブジェクトの作成
　3. タッチパネルの作成

## <i class="fa fa-cube" style="font-size:1em;"></i> Step1．マテリアルの作成
まずは、波紋シェーダーをアタッチするために、Projectタブでマテリアル`TapRippleMat`を作成します。

　　*マテリアルの作成：Project > Create > Material*

次に、シェーダー`Ripple`を作成し、冒頭で紹介したコードをそのままコピペして上書きします。

　　_シェーダーの作成：Project > Create > Shader > ImageEffectShader_

最後に、作成した`TapRippleMat`に`Ripple`をアタッチし、InspectorタブのShaderプルダウンでCustom/Rippleを指定します。これで、波紋シェーダーのマテリアルが完成しました。シェーダーのパラメータをいじることで様々な波紋エフェクトを作成できます。（後で説明）

![シェーダーinspector.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/8721a9eb-0d77-4dc9-047e-206f0097f83c.png)


## <i class="fa fa-cube" style="font-size:1em;"></i> Step2．ゲームオブジェクトの作成
タップした場所に波紋を生成するゲームオブジェクト`TapEffect`を作成します。正確には波紋を描画するための平面をアタッチしたゲームオブジェクトを生成するため、Hierarchy上でSpriteを生成します。

　　_スプライトの作成：Hierarchy > 2DObject > Sprite_

次に、以下のテクスチャをProject上にドラック＆ドロップでインポートします。そして、`TapEffect`のSpriteRenderer > Spriteに割り当てます。

![TapCircle.png](https://qiita-image-store.s3.amazonaws.com/0/183676/a71460dc-49fb-ca99-4e02-1a4cfdcd945b.png)

次に、C#スクリプト`TapCircle`を作成し、以下のコードをコピペで上書きして、`TapEffect`にアタッチします。

　　_C#スクリプトの作成：Project > Create > C#Script_

```csharp:TapCircle
using UnityEngine;

[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(Collider2D))]
public class TapCircle : MonoBehaviour
{
    SpriteRenderer mSpriteRenderer;
    Collider2D mCircleCollider;

    void Awake()
    {
        mSpriteRenderer = transform.GetComponent<SpriteRenderer>();
        mCircleCollider = transform.GetComponent<Collider2D>();
    }

    void Start()
    {
        Invoke("unenabledTrigger", 0.05f);

        mSpriteRenderer.material.SetFloat("_StartTime", Time.time);

        float animationTime = mSpriteRenderer.material.GetFloat("_AnimationTime");
        float destroyTime = animationTime;
        destroyTime -= mSpriteRenderer.material.GetFloat("_StartWidth") * animationTime;
        destroyTime += mSpriteRenderer.material.GetFloat("_Width") * animationTime;
        Destroy(transform.gameObject, destroyTime);
    }

    public void unenabledTrigger()
    {
        mCircleCollider.enabled = false;
    }

    public void OnTriggerEnter2D(Collider2D collider)
    {
        //タップしたオブジェクト : collider

        //-----------------処理-----------------//

    }
}
```

すると自動的に`TapEffect`にCircleCollider2Dがアタッチされるので、CircleCollider2D > Radiusに0.5を指定してください。

ちなみに、タップの当たり判定については`TapCircle`のOnTriggerEnter2Dの関数内でタップしたオブジェクト`collider`に対して処理を行うことができます。
なお、タップするオブジェクトにはColliderと一緒にRigidbodyもアタッチしておいてください。当たり判定はColliderをアタッチしたどちらかのゲームオブジェクトにRigidbodyがないとできない仕様になっています。

　　*当たり判定の注意：Colliderの他にRigidbodyが必要*

以上で、ゲームオブジェクトの設定ができたので、`TapEffect`をHierarchyからProjectへ移しておいてください。

![inspector.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/5f47805f-f5b1-4e4b-eb0f-53bc95c84a71.png)


## <i class="fa fa-cube" style="font-size:1em;"></i> Step3．タッチパネルの作成
まずは、C#スクリプト`PlayTouchPanel`を作成し、以下のコードをコピペして上書きしてください。

```csharp:PlayTouchPanel
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[RequireComponent(typeof(Image))]
public class PlayTouchPanel : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
	public GameObject mTapEffect;

    public void OnPointerDown(PointerEventData eventData)
    {
        //マウスクリック時
        NewTapEffect(eventData.position);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
    }

    public void OnDrag(PointerEventData eventData)
    {
    }


    void Update()
    {
        //タップ時
        CheckTap();
    }

    void CheckTap()
    {
        foreach (Touch t in Input.touches)
        {
            if (t.phase == TouchPhase.Began)
            {
                NewTapEffect(t.position);
            }
        }
    }

    //タップエフェクトを出す
    void NewTapEffect(Vector2 pos)
    {
        Vector2 worldPos = Camera.main.ScreenToWorldPoint(pos);
        Object.Instantiate(mTapEffect, worldPos, Quaternion.identity, transform);
    }
}
```



以上でProjectの編集は完了し、以下のように6ファイルを作成できていると思います。

![project.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/7534fd00-4166-8d17-1b7c-609c4e0252fa.png)

次に、Hierarchyにキャンバス`TouchCanvas`を作成し、その下に空のゲームオブジェクト`TouchPanel`を作ります。`EventSystem`はキャンバスを作成すると自動的に作られるタッチイベントを可能にするゲームオブジェクトです。

　　_キャンバスの作成：Hierarchy > UI > Canvas_
　　_空のオブジェクトの作成：Hierarchy > CreateEmpty_

![Hierarchy.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/84606572-bb14-a9b7-66c3-b443ab3d8864.png)


`TouchPanel`に`PlayTouchPanel`をアタッチし、Scene上で`TouchPanel`の枠を操作して、RectTransformの領域をカメラ画面に合わせておきます。
また`PlayTouchPanel`をアタッチすると、自動的にCanvasRendererとImageがアタッチされていることがわかります。この2種類のコンポーネントはタッチイベントを発生させるために必要です。タッチイベントは、キャンバス上のイメージにタッチすることがトリガーとなっていることに注意してください。

　　*タッチイベントの注意：CanvasRendererとImageが必要*

![touchPanelInspector.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/96809fa6-149c-2842-5f46-5190e238de70.png)

以上でタッチパネルの作成が完了しました。
アプリを実行して画面にタッチすることで波紋が出ることを確認してい下さい。


# <i class="fa fa-list" style="font-size:1em;"></i> パラメーターをいじる

| 項目| 意味|
|:---------------|:------------------|
| AnimationTime           |波紋エフェクトが発生して消えるまでの時間 |
| Width          |広がる波紋の幅（全長 : 2） |
| StartWidth         |エフェクト発生時の波紋の広がり度合（中心からの距離） |
| isAlpha   　  |波紋にアルファをかけて柔らかくする |      
| isColorShift    |波紋の色を変えてカラフルにする |  

![ripple1.gif](https://qiita-image-store.s3.amazonaws.com/0/183676/c46a59d9-d467-be9c-17f1-1a4c32a6fa5b.gif)![ripple1.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/30a2ec4f-9a26-3d7c-4cda-5f2ae1055310.png)

![ripple2.gif](https://qiita-image-store.s3.amazonaws.com/0/183676/38ccaad9-8b41-889c-107c-16d07c53ee23.gif)![ripple2.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/6cad5140-271c-85b2-eb5e-fcd49a8fcdce.png)

![ripple3.gif](https://qiita-image-store.s3.amazonaws.com/0/183676/ea69966b-b117-559f-0c3b-cad319c7f63d.gif)![ripple3.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/7bef584b-dc9f-34ff-140b-f1cc978ba16a.png)   


お気に入りの波紋アニメーションを作成してみよう。

# <i class="fa fa-thumb-tack" style="font-size:1em;"></i> 使用例
落ちてくる物をタップして、得点を稼ぐゲームです。
特にタップゲーはタップした時になにか反応がないと、タッチしたかわからず、操作性が微妙ってことになってしまうと思います。
タップした部分に波紋を広げることで、爽快感とタップした触感を視覚的に与えることができるのではないでしょうか。

[![使用例.gif](https://drive.google.com/uc?export=view&id=0B_v5rHiNSeseSDNfS3BCQ0tUWUU "使用例.gif")](https://youtu.be/uQPB9tvN_hk)

<a href= https://play.google.com/store/apps/details?id=com.secretcommandlab.taptapdrop><img src=https://qiita-image-store.s3.amazonaws.com/0/50917/e5ad66a0-6cce-c312-9ae5-3f6473de9783.png width=150px align="left">
</a>

<a href="https://itunes.apple.com/us/app/%E8%90%BD%E3%81%A1%E3%81%A6%E3%81%8F%E3%82%8B%E7%89%A9%E3%81%99%E3%81%B9%E3%81%A6%E3%82%BF%E3%83%83%E3%83%97/id1228691845?mt=8"><img src=https://qiita-image-store.s3.amazonaws.com/0/50917/37d2e0ca-bf3d-86f3-6d5b-173029f5ad20.png align="left" width=150px>
</a>


と、ただのゲームの宣伝なのですが。。。よければ応援お願いしますm・・m

# <i class="fa fa-book" style="font-size:1em;"></i> 参考資料
 - [【Unity】タップした位置にエフェクトを表示する - テラシュールブログ](http://tsubakit1.hateblo.jp/entry/2016/04/28/023104)
 - [UGUIでNGUIのEventTriger的なものを作るレポ - Qiita](http://qiita.com/iris3577/items/a6726919397cd14fcee1)
 - [Unityの衝突判定はRigidBodyが必要 | ヤマヤタケシのブログ](http://yamayatakeshi.jp/unityの衝突判定はrigidbodyが必要/)
 - [UnityのCollider2DやColliderに対してクリックやドラッグのイベントを使う - テラシュールブログ](http://tsubakit1.hateblo.jp/entry/2015/01/31/015304)