【Godot】マスクの実装方法

# 概要
この記事では Godot Engine でマスクを実装する方法について解説します。

# マスクとは
マスクとは、画像の特定部分を切り抜いて描画する方法です。
![shot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/6d66703d-047b-401b-5502-b090b64e9713.gif)
この GIF動画では、円形で画像をくり抜いて、その部分だけを描画するようにしています。

# 画像の準備
## マスク用の画像
マスク用に使う画像は、くり抜きたい部分を完全な白色 `(R,G,B)=(255,255,255)` にして、描画したくない部分は透過値を 0 に近づけます。
![Item-0_と_Item-0_と_Item-0_png___602_x_400px_72dpi_100_0__.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/bcd3f179-4f8d-6d34-0816-a8d8e2abb7e3.png)

透過値のある画像を作るには、GIMPなどのグラフィックツールが必要です。
ひとまず素材として以下の画像を用意しました。
![mask.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/794ca0bd-581f-ad2f-1850-5b3784caa91f.png)
↑真っ白でわかりにくいですが、これを "mask.png" として保存します。

## 背景用の画像
背景には以下の画像を使用します。ファイル名は "bg.jpg" として保存します。
![bg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/bf3c210b-7bc6-e504-0588-040379150f54.jpeg)

## くり抜く対象の画像
くり抜く対象の画像は以下のものを使用します。 "ch.png" として保存します。
![ch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/4a31df43-74a5-5765-01d2-d25d3541043f.png)

# プロジェクト作成と背景・キャラの配置
2Dシーン (ノード名は "Main" ) を作成して、"bg.jpg" と "ch.png" を配置します。
![Godot_Engine_-_TestMask_-_Main_tscn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/1d0c5ce3-a767-1f9c-b788-2b73ca2086ea.png)
キャラ画像が大きすぎた……ので、気になる場合は小さくしてもいいかもしれません。

## Light2Dの配置
マスクをするために `Light2D` を作成します。
![Godot_Engine_-_TestMask_-_Main_tscn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/9da98ace-2190-01b9-1835-b90d46e37ebe.png)
どこに追加しても良いですが、ひとまず ch ノードの下に追加しておきます。
![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/5abee056-0fac-acea-d811-9f418ea4c3b1.png)

"mask.png" をプロジェクトに追加して、`Light2D` のテクスチャに設定します。
![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/cdf84c95-1169-7da0-c999-50b6d63f00d3.png)

さらに `Light2D` の Modeを 「Mix」にします
![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/c07490ef-454a-d2c6-1513-128820b6fc30.png)

## マスクされる側のマテリアルを設定する
chノード(`Sprite`)を選択して、インスペクタから `CanvasItem > Material > Material > [空]` をクリックして、「新規 CanvasItemMaterial」を選択します。
![Godot_Engine_-_TestMask_-_Main_tscn_____と_イラスト2___512_x_512px_72dpi_100_0__.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/8346f167-4e1b-6a6c-337c-61601aba2ffd.png)

表示された　CanvasItemMaterialをクリックして、`LightMode > Light Only`を選びます![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/b2cf1ded-40e2-a5f8-08e5-b437e3e4b9e2.png)

するとこのようにマスクがかかります。
![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/4cd44b26-4d25-fcf3-b344-fd2af0a6a949.png)

# マスクをスクリプトで制御する
Main(2Dシーン)のスクリプトをアタッチして以下のように記述します。

```gd:Main.gd
extends Node2D

onready var light = $ch/Light2D

func _process(delta: float) -> void:
	# ライトの座標をマウスの座標に設定する
	light.position = get_viewport().get_mouse_position()
```

これで実行するとマウスの位置がくり抜かれる……はずなのですが、少しずれることがあります。"ch"ノードにぶら下げてしまったためで、"ch"ノードが原点に配置されていない場合はズレが発生します

なので、`Light2D` をMainノード直下に移動させます。
![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/184eface-db0b-3bc0-d2cf-1677c490a4b7.png)

そしてスクリプトを修正します。

```gd:Main.gd
extends Node2D

onready var light = $Light2D # Mainノード直下に移動した

func _process(delta: float) -> void:
	# ライトの座標をマウスの座標に設定する
	light.position = get_viewport().get_mouse_position()
```

実行するとマウスの中心を基準にくり抜かれるようになりました。
![shot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/8b5db7da-e5d4-a4b0-2e9b-e8f78a15c008.gif)

## フチをぼやけた画像に変えてみる
円のフチをぼやけた画像にすると、境界がぼやっとした感じになります。
以下の画像を "mask2.png" として保存します。
![mask2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/fe1edd61-f2c3-f0ca-a5d6-0f8d16ee6f6e.png)

そしてプロジェクトに追加し、`Light2D > Texture` に割り当てます。

![Godot_Engine_-_TestMask_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/5f3c45b5-2e65-0bed-db4c-b5c517e9e22e.png)

実行するとフチがぼやけた感じになりました！
![shot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/0b735e06-3247-0fff-f414-6c3c566c34bb.gif)

## 補足
複数のマスクを同時に使いたい場合は、`CanvasItem > Visibility > Light Mask` の設定をマスクされる画像と `Light2D` で合わせると、特定の画像に特定のマスクを割り当てる……ということもできます。
![Godot_Engine_-_TestMask_-_Main_tscn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/92097c6b-f91b-f45e-fc41-83f8677bc0c6.png)

※追記
Modeが `Mix` の場合は `Light Mask` の設定はうまく行かなさそうです。
複数のマスクを別々に適用したい場合には、以下の動画のように `Light2D`の Mode を `Mask` に設定して、`ViewportTexture`経由でマスクする必要がありそうです（未検証）

* [SPRITE MASK & TRAIL EFFECT - Godot Tutorial](https://www.youtube.com/watch?v=2ZD82i71-90)

# 参考

* [How do I mask a sprite? - Godot Engine - Q&A](https://godotengine.org/qa/3031/how-do-i-mask-a-sprite)