GDScriptの関数「あっ!これ便利!」

## まえがき
GodotEngineを触り初めて3年くらいが経ちましたが、未だに「あんた今日までこんな事も知らなかったの!?」の連続。毎日がエブリディ。驚きの日々です。これに関しては「ちゃんとGodotDocs読め」に尽きると思います。 ちゃんとGodotDocs読め。しかしながら「他人に厳しく自分に甘く」これが長生きのコツだそうですので、小生今後も反省せず、右から左、ごまかしごまかし生きていこうと思います。テヘペロ。

という事で今回、私が個人的に最近知って、且つみんなは既に知ってそうなGDScriptの関数などをまとめてみました。

## CanvasItem.get_global_mouse_position() -> Vector2
CanvasItemを継承したノードならこれでマウスカーソルの座標がわかるんですって。知らなかったです。
```gd
func _process(_delta):
    # $PlayerはSprite2D
    $Player.position = get_global_mouse_position()
```
![ac2024_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954756/b1e59a63-7b09-1df1-7140-dd8f81a52062.gif)

これでもう何でも作れるんじゃねぇかと思いました。
## Rect2.intersects(b: Rect2) -> bool
Rect2同士の重なりはこれでわかるんですって。知らなかった。対象が点なら`has_point(point: Vector2)`だそうです。他にも「そう言われればそりゃあるよなぁ」という超便利メソッドがRect2には沢山ありました。
でも回転には弱いぽい。
```gd
func _process(_delta):
    # $PlayerはTextureRect, $WallはColorRect
    $Player.position = get_global_mouse_position() - $Player.size
    if $Player.get_rect().intersects($Wall.get_rect()): #PlayerとWallは重なってる?
        $Player.modulate = Color.RED # trueなら赤でmodulate
    else:
        $Player.modulate = Color.WHITE # falseなら白で。
```
![ac2024_02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954756/3cdbc422-3639-c64c-b515-756d3cfddd48.gif)

Area2D要らず。とまでは言わないけど、Area2Dをつけるほどでもないちょっとした用事。みたいなのはたまにある気がしなくもない。個人的にはマウスドラッグでの範囲選択とか作るときに便利でした。

### 補足のSprite2D.get_rect() -> Rect2
上の項は実はちょっとインチキをしていて、GobotちゃんがTextureRectノードなんすね。
GobotちゃんにはSprite2D使いたいじゃん!って思うのですが`Sprite.get_rect()`ではおそらく希望とは違う結果(下のgifの黄色い矩形)になりますので困りました。正解は`transform * get_rect()`みたいです。(下のgifの赤い矩形)。それでも間違っていますけど。
つか、Sprite2Dなら普通にArea2Dを使うべよ。
でもこれはこれでちょっと面白い。インチキ当り判定。
```gd
# $PlayerはSprite2D
func _process(delta: float) -> void:
	var viewport_rect = get_viewport_rect()
	var ticks = Time.get_ticks_msec()
	$Player.position.x = pingpong(ticks / 20.0, viewport_rect.size.x)
	$Player.position.y = pingpong(ticks / 30.0, viewport_rect.size.y)
	$Player.rotation += 0.5 * delta
	$Player.scale.x = pingpong(ticks / 2000.0, 1.0)
	$Player.scale.y = pingpong(ticks / 3000.0, 1.0)
	queue_redraw()

func _draw() -> void:
    # 黄色い矩形が$Player.get_rect() のRect2
    # 赤い矩形は$Player.transform * $Player.get_rect() のRect2
    
	draw_rect($Player.get_rect(), Color(Color.YELLOW, 0.5))
	draw_rect($Player.transform * $Player.get_rect(), Color(Color.RED, 0.5))
 ```
![ac2024_06.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954756/a917c0e9-66a4-c384-aab3-00faa1d2a0c9.gif)



## Dictionary.keys() -> Array
これは知ってたけど、なんとenumでも使えるんですって。 知らなかった。
つか、
```gd
enum State {NONE, LEFT, RIGHT}
```
は、
```gd
const State {"NONE": 0, "LEFT": 1, "RIGHT": 2}
```
と同じ意味なので、Dictionaryのconstなメソッドなら何でも使えるんですと。

```gd
enum State {NONE, LEFT, RIGHT}
var state := State.IDLE

func _process(_delta):
    # いろいろあって
    $Player/DebugLabel.text = State.keys[state] 
```
![ac2024_03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954756/d57fbde4-7381-86bd-4974-9e42bfbb5c1a.gif)

stateのデバッグ、いままで`match(state)`いっぱい書いてました。もう超ラク。さすがにちょっと反省しました。

## Array.assign() -> void
子のノードのうち、ColorRectノードだけを配列で欲しい。とかいう時にfilter便利なので結構使うんです。
```gd
# 子ノードのうち、is ColorRectなものをArrayで得る
var color_rects: Array = get_children().filter(func(node): return node is ColorRect)
```
でも折角だから`Array[ColorRect]`とか使いたいー。と思って、
```gd
var color_rects: Array[ColorRect] = []
color_rects = get_children().filter(func(node): return node is ColorRect)
```
ってするんだけど、これはエラーになっちゃう。`filter()`の戻り値はArrayなので惜しいけどダメ。
こういう時は`assign()`を使うんですって。知りませんでした。
```gd
var color_rects: Array[ColorRect] = []
var _ary = get_children().filter(func(node): return node is ColorRect)
color_rects.assign(_ary)
```
型を変える以外にもなんか使い方があるっぽいですがよくわかりませんでした。
## Object.connectの時のbind()
知らなかった訳では無いけど、いまいちピンとこない感じだったんですが、最近ようやく慣れてきました。

```gd
func _ready() -> void:
	for child in get_children():
		if child is ColorRect:
			child.mouse_entered.connect(_on_color_rect_mouse_entered.bind(child))
			child.mouse_exited.connect(_on_color_rect_mouse_exited)

func _on_color_rect_mouse_entered(color_rect):
	$Label.text = "Name: %s\nColor%s" % [color_rect.name, color_rect.color]

func _on_color_rect_mouse_exited():
	$Label.text = ""
```
![ac2024_04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954756/ad6730da-8c7c-3d2a-17ce-0d6bf1b49ca1.gif)

なんか、とりあえず自身への参照渡しておけば、後はなんとかなる気がする。

## 今年も大変お世話になった関数
* clamp(min, max, value)
* lerp(from, to, weight)
* inverse_lerp(from, to, weight)
* Array.sort_custom(func: Callable)
* Array.shuffle()
* Array.map(func: Callable)
* Array.reduce(method: Callable, accum: Variant = null)

説明は省略
## おまけ
この記事を書くにあたり
* 1ゲームにつき1つのgdファイル
* コメント除いて80行以内
* Area2D禁止
* 自作シグナル禁止

という縛りでGDScript再入門を兼ねたゲームを色々作ってみたので、よかったら遊んでみてください。

HottestGames8in1 @ GodotPlayer
https://godotplayer.com/games/gac_2024_8in1

ソースコード
https://github.com/sanitygames/GodotEngineAdventCalendar2024