[Unity] StainSystemを使用してオブジェクトにインク効果を適用する

# この記事について

[Unity アセット真夏のアドベントカレンダー 2018 Summer!](http://assetstore.info/eventandcontest/adventcalendar/asset-adventcalendar2018summer/) の7日目の記事です。

昨日(8/6)は [enpel](https://twitter.com/enpel) さんの [Editor拡張を楽にできるEasyEditor紹介](https://note.mu/enpel/n/n8621c094bd04) でした。
アトリビュートだけで機能追加出来るの楽そうですし、[公式サイト](http://easy-editor.info/demonstration.php) のデモとコードの対比も非常に見やすくて良いですね...!!

# 💬StainSystem

さて、今回は [StainSystem](https://assetstore.unity.com/packages/tools/particles-effects/stain-system-67146) というアセットについて紹介しようと思います。

<img width="1138" alt="Stain System - Asset Store 2018-08-04 14-07-01.png" src="https://qiita-image-store.s3.amazonaws.com/0/102664/e5f7ca08-fc11-1dac-bf1f-a011ac64b101.png">

StainSystemはインクや血液等の飛沫表現を手軽に実装するためのアセットです。
(以下、サンプルシーンよりいくつか抜粋)

![stain1.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/4451779e-e88d-d23f-7fcf-95b33a84e42f.gif)

![stain2.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/fc52a2c3-2e8d-2958-fac1-369d2a93a1e6.gif)


## マニュアル
http://dustyroom.com/splatter-system-manual/

# 2つの描画方法
StainSystemでは使用用途やプラットフォームに応じて2つの描画方法が用意されています。
下記のような使い分けをすると良さそうです。

* より多くのペイントを2D平面に表示したい場合は `Bitmap splatters`
* 個々のペイントに対して柔軟な表現をさせたかったり3D平面に表示したい場合は `Mesh splatters`

# 🖼Bitmap splatters
## 概要
テクスチャに書き込んでSpriteRendererを用いて描画する方法です。

![Unity 2018.2.2f1 Personal (64bit) - Demo1 - Click (Bitmap particles).unity - StainSystem - PC, Mac & Linux Standalone (Personal… 2018-08-05 15-19-28.png](https://qiita-image-store.s3.amazonaws.com/0/102664/d258ad46-8717-38fd-e030-5358d3bc4682.png)

以下のような特徴があります。

* 2Dでのみ動作
* 無限数のペイントを描画できる
* 動的オブジェクト/静的オブジェクトで使用可
* テクスチャメモリを使用して描画領域を生成します。
* 一度描画したペイントは個々に取り外すことが出来ない

## 実装方法
クリックした場所にペイントを描画するサンプルを作ってみます。

![stain3.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/6cdb714d-1d39-7cc1-51f0-88af73cb5cda.gif)

### 作業手順
#### ⚒下準備/描画対象の形状が四角の場合
* 背景/ステージを用意する (上記サンプルでは `SpriteRenderer` で黒い長方形を配置)
* 描画対象のオブジェクトに対して Collider をアタッチする
* `StainSystem/Prefabs/SplatterSystemBitmap` を Hierarchy にドラッグして配置
* `StainSystem/Prefabs/BitmapSplatterArea` を Hierarchy にドラッグして配置
    * 描画対象が複数存在する時は複数個配置する
* `BitmapSplatterArea` の位置/大きさを描画対象の大きさに合わせる
* Projectビューにて `Create` から `SplatterSettings` を生成
    * `Splatter mode` は `Bitmap` を選択する
    * 生成後 `StainSystem/Prefabs/SplatterSystemBitmap` に設定を指定する

![stain4.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/4bf570f8-1efc-db2f-7227-1068c4d23cdc.gif)

#### ⚒下準備/描画対象の形状が形状が四角でない場合
* `StainSystem/Prefabs/BitmapSplatterArea` の代わりに `BitmapSplatterAreaSelective` を配置する
* 描画対象のオブジェクトのMaterial を `StainSystem/Materials/SpriteSplatterSurface` に変更

#### 🎨描画処理の呼び出し
描画をするタイミングで `BitmapSplatterManager.Spawn()` を呼び出す必要があります。
今回は `Physics2DRaycaster` を使用するアプローチでを対応してみました。
(勿論 ` Input.GetMouseButtonDown` を使用してクリック判定取るアプローチでも可)

* MainCamera に　`Physics2DRaycaster` をアタッチ
* EventSystem の配置
* 以下のScriptを持つ `Board` Componentを作成し、描画対象のオブジェクト (Collider付き) にアタッチ
* `Board` に対して `BitmapSplatterManager` を指定

```csharp:Board.cs
using SplatterSystem;
using UnityEngine;
using UnityEngine.EventSystems;

public class Board : MonoBehaviour, IPointerDownHandler
{
    [SerializeField] private BitmapSplatterManager BitmapSplatterManager;

    public void OnPointerDown(PointerEventData eventData)
    {
        Vector2 worldPosition = Camera.main.ScreenToWorldPoint(eventData.position);
        this.BitmapSplatterManager.Spawn(worldPosition);
    }
}
```

![stain5.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/e53909f8-2531-89c8-a017-d87bc5302a7a.gif)

肝は **BitmapSplatterManager.Spawn() に任意の座標を指定して呼び出すこと** なので、サンプルにある通りプラットフォーマーなゲーム等にも適用することが可能です。

# 🖌Mesh splatters
## 概要
ParticleSystemを利用して単一のメッシュに描画する方法です。

![Unity 2018.2.2f1 Personal (64bit) - Demo1 - Click (Bitmap particles).unity - StainSystem - PC, Mac & Linux Standalone (Personal… 2018-08-05 15-19-28.png](https://qiita-image-store.s3.amazonaws.com/0/102664/e7074288-a75f-3e14-1d97-d3804a71c09c.png)

以下のような特徴があります。

* 2D/3Dで動作する
* ただし平面であるという制限がある
    * 角に対して物理的な動作をシミュレートできない
* ペイント数はGPUによって制限される
* 個々のペイントを時間経過によってフェードアウトすることが可能
* アンチエイリアスが可能
* 動的オブジェクトを使用してペイントを移動することは不可

### 作業手順

* 背景/ステージを用意する
* 描画対象のオブジェクトに対して Collider をアタッチする
* `StainSystem/Prefabs/SplatterSystemMesh` を Hierarchy にドラッグして配置
* 描画対象のオブジェクトのMaterial を `StainSystem/Materials/StandardSplatterSurface` (3Dオブジェクト) もしくは `SpriteSplatterSurface` (Sprite) に変更
* Projectビューにて `Create` から `SplatterSettings` を生成
    * `Splatter mode` は `Mesh` を選択する
    * 生成後 `StainSystem/Prefabs/SplatterSystemBitmap` に指定する
* 描画をするタイミングで `MeshSplatterManager.Spawn()` を呼び出す

![stain6.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/c65ee086-39ed-fb8e-1133-9bbee239d757.gif)

▲ 予め含まれている `Demo5 - Isometric` で同様の処理をしているので参考にすると良さそうです、

# 描画するペイントの設定を調整する
生成した `SplatterSettings` の各要素を調整することで色や大きさなどを調整することが出来ます。

![stain7.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/0c81d256-a9f5-d4f7-b0d5-173333826417.gif)

各項目が何を指しているかは一応最下部の `Utility/Show explanmations`　をチェックすることで確認できます。

![stain8.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/0abbfded-96ab-eb42-e479-3b721516c366.gif)

とはいえ、1から良さそうな演出を作るのも大変なので用意されているプリセットを加工することをオススメします。
プリセットは `StainSystem/Demos/Demo1- Click/Splatter Settings - Presets` 以下にあります。
この `Demo1` のSceneを開き実行すると、プリセットを切り替えるボタンが表示され、そこで内容を確認できます。

![stain9.gif](https://qiita-image-store.s3.amazonaws.com/0/102664/813a945e-f677-5a1f-74f1-af11db3ad3e6.gif)

# 💨最後に 
シンプルなゲームでも尖った表現を組み込むことで差別化を図れると思うので、ぜひ試してみてください!!
明日(8/8)のアドカレは [きっポジ@KITPOSITION](https://twitter.com/kitposition) さんの　[UI 3D-Systemを試す！](http://kitposition.hatenablog.com/entry/2018/08/08/190014) です！