【Godot】@export系のアノテーションを使って、インスペクターの表示を整理しよう

# はじめに
Godotのインスペクターにノードやクラスを参照したりできる、`@export`などのアノテーションについてをまとめます。

一応、公式ドキュメントにもまとまっていますが、インスペクターの表示がされていないのでわかりづらいです

https://docs.godotengine.org/ja/4.x/tutorials/scripting/gdscript/gdscript_exports.html


### 使用環境
- Godot: 4.1.1 stable



# `@export`について
GDScriptで、他のノードやクラスを参照したいときや、エディタ側でintやstringなどの値を調整するために使います。
(Unityでいう[SerializeField]属性みたいな感じです)

実際の使用例です↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/28ef6c70-9904-2c87-57e3-b54d744009a2.png)

```Py
@export_category("Animations")
@export_group("Spawn")
@export var spawn_duration = 0.5

@export_subgroup("Easing")
@export var spawn_trans = Tween.TRANS_QUAD
@export var spawn_ease = Tween.EASE_IN_OUT
```

### 外部ノード・クラスの参照
他にもNodeクラスの`get_node()`や`find_children()`などで動的に参照する方法がありますが、`@export`に比べるとちょっと負荷がかかるので、できることなら`@export`で参照するのがベストかと思います

([公式ドキュメントの`find_child`系のメソッド](https://docs.godotengine.org/ja/4.x/classes/class_node.html#class-node-method-find-child)には、このような注意書きもされています)
> 注:このメソッドはノードのすべての子孫をたどるので、他のノードへの参照を取得するのに最も遅い方法です。可能な限り、ノード参照を変数にキャッシュすることを検討してください。





# 一覧
## 基本
### `@export`
**インスペクターの表示**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/18aa560a-f4c1-30f4-eb79-7a4aa24980cc.png)

```Py
@export var speed: float = 100.0
```





## 配列
### 型指定なしのArray
動的型付け言語なので、型を指定しないと配列にはバラバラの型を格納できちゃいます
```py
@export var array: Array
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/6a8bef36-a286-7d28-795f-e9a4514767c7.png)

初期値を設定した配列はこんな感じです
```py
@export var dynamic_array = [1, 2, 3]
```

int型のみ、などの制限はありません。(下図では後からVector3型の要素を追加しています)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/3fe3f416-81ba-dce1-bc8c-252a5faac123.png)



### 型指定付きのArray
こんな感じで型を指定すると、指定された型の配列ができます
```py
@export var int_array: Array[int]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/521a0af8-4de5-4ef7-15c2-b739a4e1e322.png)



### PackedArray
PackedArrayは、指定の型に特化した配列です。
静的なのでメモリなども最適化されています。
詳細は[公式ドキュメント](https://docs.godotengine.org/ja/4.x/tutorials/scripting/c_sharp/c_sharp_collections.html#packedarray)をご覧ください

```py
@export var packed_array = PackedInt32Array()
```

通常の配列と同様に表示されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/0ff54356-33eb-421f-c759-adc876754856.png)


PackedArrayの場合、初期値を設定しても、nullになってしまいます
```py
@export var packed_array = PackedInt32Array([1, 2, 3])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/00fd868f-943b-7b93-b169-9c663c353cab.png)






## 列挙体
### 定義した列挙体から選択する(enum型)
**定義した列挙体をインスペクター上で選択できる**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/db837867-6031-fda4-e251-3061fab5830f.png)


```py
enum EnumTest {
	ONE,
	TWO,
	THREE
}

@export var enum_test: EnumTest

```


### `@export_enum`
**enumを定義せずに、enumのような選択肢をインスペクター上に表示できる**


##### int
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/d407a2b3-2c80-0d8e-e1d6-f86fdb73b850.png)

```py
@export_enum("ONE:1", "TWO:2", "THREE:3") var enum_test_2: int

print(enum_test_2)    # result: 2
```

初期値を設定することもできる
```py
@export_enum("ONE:1", "TWO:2", "THREE:3") var enum_test_2: int = 2
```
(矢印ついてないのがデフォルト)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/d2885df0-ae5a-4604-89d3-3842a4b00f88.png)


##### string
見た目はintと同様
出力結果が、選択した項目の文字列
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/f041c457-5568-570d-c53b-474dbf6ca621.png)

```py
@export_enum("ONE", "TWO", "THREE") var enum_test_3: String

print(enum_test_3)    # result: TWO
```






## グループ系
### `@export_category`
**見出し付きのグループ分けができる**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/e81b2749-424e-3f4b-e8c3-f97bacc48801.png)

```Py
@export_category("Parameters")
@export var hp: int = 100
@export var mp: int = 100

@export_category("Movement Parameters")
@export var speed: float = 100.0
```



### `@export_group`
**開閉できるトグル付きのグループ分けができる**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/0ec122c0-4881-f577-9b81-5998792cdd82.png)

```Py
@export_category("Parameters")
@export_group("Base")
@export var hp: int = 100
@export var mp: int = 100

@export_group("Movement")
@export var speed: float = 100.0
```



### `@exort_subgroup`
**グループの中でさらにグループ分けができる**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/551dbcaa-9197-8019-80b2-786c1512a542.png)


```Py
@export_group("Movement")
@export_subgroup("Base")
@export var speed: float = 100.0

@export_subgroup("Jump")
@export var jump_height: float = 100.0
@export var jump_count: int = 2
```




## パス系
### `@export_file`
**ファイルを選択して、ファイルパスを参照できる**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/83fd10fe-7d97-e92a-b500-8b1d84d91563.png)

ファイルを選択すると、選択したファイルのパスが取得できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/ebc189d5-89bf-c588-0921-5f111ceb6687.png)

```py
@export_file var file
```

また、拡張子を指定して、指定された拡張子のみを選択対象にすることもできる

例えば、このように*.gdと指定すると、gdscriptファイルのみを選択可能になる
```py
@export_file("*.gd") var script_file
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/83ffb172-5da1-915a-e992-43981a4d6eda.png)





### `@export_dir`
**ファイル同様、ディレクトリパスを取得**

フォルダ以外のファイルも表示されていますが、選択可能なのはフォルダのみです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/a34fc3c2-3c7b-39ed-6e4a-9efecbf5dbcf.png)



### `@export_global_file` / `@export_global_dir`
**プロジェクト外のファイルパスを参照できる**
一応参照できますが、ゲーム内で使うことは少ないと思います
基本的にツールモード(エディタ拡張)で使います


> Using paths in the global filesystem is also possible, but only in scripts in tool mode.
(グローバル ファイル システム内のパスを使用することも可能ですが、ツール モードのスクリプトでのみ使用できます。)
([公式ドキュメント](https://docs.godotengine.org/ja/4.x/tutorials/scripting/gdscript/gdscript_exports.html#strings-as-paths)より抜粋)







## ノード、クラス、Resourceなど
### ノードの参照
**ノードを参照できる**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/3da2f6f7-d6ae-6cdd-7526-050edca1698b.png)


```py
@export var node: Node
```

また、4.0以前でのノードの参照方法として、NodePathを参照して`get_node()`する方法がありますが、手間なので普通にNode参照した方がいいです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/ad8c30fc-c4b1-0826-4c8d-737e826e23be.png)


```py
@export var node_path: NodePath
@onready var node_path_node = get_node(node_path)
```



### クラスの参照
**自身が定義したクラスを参照することもできる**

```referenced_class.py
@icon("res://icon.svg")
class_name ReferencedClass
extends Node
```

```export_test.py
@export var referenced_class: ReferencedClass
```

(デフォルトのGodot君をアイコンに指定したら、プロパティの高さが広くなった)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/0fecba8e-e372-44a8-d1ab-f6f507323793.png)


### アセット(Resource)の参照
**テクスチャ、オーディオなどのアセット(Resource)を参照できる**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/c1a0e830-26fd-bcf7-0eab-1b875a29996e.png)

```py
@export_category("Resources")
@export var resource: Resource
@export var gdscript: GDScript
@export var icon: Texture
```





## その他の`@export`
### `@export_range`
**intやfloatなどの範囲を制限した値を取得できる**
変数を静的型付けで型を指定することで、見た目と操作が変わる

##### int
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/bdc2135a-2e06-a960-c1b3-0fc8b9a2fbd2.png)


```py
@export_range(0, 100) var range_int: int
```


##### float
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/151a4e3e-92b1-f88f-8e27-b50f4094c714.png)


```py
@export_range(0, 100) var range_float: float
```


##### スナップ付き
**`@export_range()`の第三引数に値を指定すると、スナップ(その値ごとに値を増減させること)ができる**

以下のようにすると、float型でもint型のような操作方法になる
```py
@export_range(0, 100, 1) var range_float: float
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/40773144-ed46-7c75-6085-d601b6931233.png)

スナップ値を0.1くらいにすると、細かすぎる値にならなくていい感じになる
```py
@export_range(0, 100, .1) var range_float: float
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/375ec6b2-009e-ce11-b225-db8a645da4cb.png)

##### スライダー操作時のみに値の制限をする
**第四引数以降に、"or_greater"や"or_less"などを追加すると、スライダーのみの制限をかけられる**
値を数値入力によって変更した場合には、制限しない

```py
@export_range(0, 100, 1, "or_greater", "or_less") var range_int: int
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/68685276-a89a-2a74-f1c9-a81898c7a33b.png)







### `@export_multiline`
**複数行テキストを編集できる**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/437ff2bc-6449-e1eb-4377-cb7e87c6c2c4.png)


```py
@export_multiline var text
```



### `@export_exp_easing`
**ease()メソッドの結果を確認できる…？**
ease()については、下記リンクを参照してください

https://docs.godotengine.org/ja/4.x/classes/class_@globalscope.html#class-globalscope-method-ease

こんな感じでカーブを取得できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/0008eb3b-906e-b026-c269-e8ddf72601d7.png)

```py
@export_exp_easing var easing
```


### 色の指定
**静的型付けでColor型を指定すると、色を選択できるようになる**


```py
@export var color: Color
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/e5aaeeee-da31-5e4e-b53c-cc4838181bf5.png)


##### アルファ値を無視
**`@export_color_no_alpha`で、アルファ値が1の色を取得する**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/d65e9a1f-4180-af6b-ae79-ee15cd6c05ae.png)


```py
@export_color_no_alpha var color_no_alpha:
```




### ビットフラグ
ビットフラグとは、ビット演算でフラグ管理をすることです
int型でフラグの配列を持たせることができる感じです

##### `@export_flags`
チェックボックスでフラグ管理できるビットフラグです
```py
@export_flags("A", "B", "C") var flags: int
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/4bec1311-102d-616a-6fe3-81f5e80a3eb2.png)


##### その他のビットフラグ
Node2DのCanvasItemノードのレイヤーとかもビットフラグで管理されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/53038232-035f-9981-a19f-ca3df7168628.png)


これを自前で用意したい場合は、`@export_flags_2d_render`などのアノテーションを使います
```py
@export_flags_2d_render var layers_2d_render
```

他にもこんな感じのがあります
```py
@export_flags_2d_physics var layers_2d_physics
@export_flags_2d_render var layers_2d_render
@export_flags_2d_navigation var layers_2d_navigation
@export_flags_3d_physics var layers_3d_physics
@export_flags_3d_render var layers_3d_render
@export_flags_3d_navigation var layers_3d_navigation
```



# その他
### ツールチップ(プロパティの説明)を付与
プロパティにマウスオーバーすると表示される説明ですが、`@export`変数に対してドキュメントコメントを付与することで追加できます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/f87f7dec-394c-b99e-bc3d-b4b1a9494f67.png)

```Py
## This is a test variable
@export var test: int = 0
```

ただ、バグなのか不明ですが、`@export_category`などの直下にある変数に対してドキュメントコメントを付与しても、ツールチップが表示されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/f7ccc4bb-47ca-fcaa-2e79-6e3cc57cd619.png)



```Py
@export_category("Parameters")
## This is a test variable
@export var test: int = 0
```



# さいごに
基本的に`@export`だけ使ってれば問題なさそうですね
グループ分けを使うと、もっと分かりやすくなりそうです。

Unityの属性のように、自作でカスタマイズしたものも作れるのかも調べたいです！