awaitって何や_基本編

Godot3ではyieldでしたが、Godot4よりawaitというしくみに変わりました。

この記事では、awaitの基本的な使い方と動作について解説します。

awaitについて知識がない初学者向けです。


await を使うことで、実行中に「他のものの完了を待つ」ことができます。


1秒待つコード await get_tree().create_timer(1.0).timeoutをくわしく

サンプルコードとして頻出する、この1行コードです。

たしかにこのコードを実行すると「1秒待つ」のですが、

これを「1秒待つ」とだけ認識するのではなく、詳しくみていきましょう。

extends Node

func _ready():
	print("_ready開始")

	await get_tree().create_timer(1).timeout

	print("1秒待った")

↑このコードを実行すると、1秒たってから1秒待ったが出力されますね。

この1行を分解します。

extends Node

func _ready():
	print("_ready開始")

#	「このスクリプトをアタッチしたノード」があるシーンツリーを参照する
	var scene_tree:SceneTree = get_tree()

#	そのシーンツリーから、シーンツリータイマーを作成する。時間は1秒を指定。
#	シーンツリータイマーは、作成された瞬間にタイマースタートする。
	var scene_tree_timer:SceneTreeTimer = scene_tree.create_timer(1)

#	そのシーンツリータイマーの timeout シグナルを参照する
	var timeout_signal:Signal = scene_tree_timer.timeout

#	そのシグナルが emit するのを await で待つ
	await timeout_signal

	print("1秒待った")

↑このようになります。

1行で書かれることが多いコードですが、やっていることを詳しくみるとこのような処理になっています。


つまり、

「1秒待つ」

↓詳しく

「1秒のシーンツリータイマーを作成し、そのシーンツリータイマーのtimeoutシグナルのemitを待つ」


emit はシグナルが発火することを示します。

Godot組み込みノードはシグナルがemitされますし、

自作シグナルならコードからzisaku_signal.emit()を呼ぶことでemitされます。


このコードから言えることは、

await「対象のシグナルがemitされるまで待つ」 ことができます。


awaitで待てるもの

では、awaitは「シグナルがemitされるまで待つ」だけなのか?と思ってしまいますが、そうではありません。

awaitシグナルのemit以外にも待てるものがあります。

それはコルーチンです。

コルーチンは一般的には実行中に処理を中断し、再開できることを指します。


Godotにおいて、awaitで待つことができる「GDScriptのコルーチンとは」を分かりやすく言うと、

実行途中でawaitで止まった関数 です。

つまり、await を使用すると「実行途中でawaitで止まった関数 が完了する」のを待つことができます。

extends Node

func _ready():
	print("_ready開始")

	await totyuu_de_await_no_func()
	print("1 完了")

	var result = await totyuu_de_await_no_func_2()
	print(str(result))
	print("2 完了")

func totyuu_de_await_no_func():
	print("途中でawaitの関数 returnなし")
	await get_tree().create_timer(1).timeout
	print("もっと待つ")
	await get_tree().create_timer(1).timeout

func totyuu_de_await_no_func_2():
	print("途中でawaitの関数 returnあり")
	await get_tree().create_timer(1).timeout
	return 2
_ready開始
途中でawaitの関数 returnなし
もっと待つ
1 完了
途中でawaitの関数 returnあり
2
2 完了

また、コルーチンが実行途中でawaitで止まった関数であることから分かるとおり、

そのコルーチン内でまたawaitでコルーチンを待つ~のように、await待ちの連鎖をすることができます。

よく使われます。


まとめると、

「シグナルを待つ」「コルーチンを待つ」

awaitはこの2つのことができます。

extends Node

func _ready():
	print("_ready開始")

	await signal_no_func()
	print("これは関数でreturnされるシグナルのemitをawaitで待つ")

	await totyuu_de_await_no_func()
	print("これは「シグナルのemitをawaitで待つ」コルーチンの完了をawaitで待つ")

func signal_no_func():
	print("関数 returnシグナル")
	return get_tree().create_timer(1).timeout

func totyuu_de_await_no_func():
	print("途中でawaitの関数 returnなし")
	await get_tree().create_timer(1).timeout
_ready開始
関数 returnシグナル
これは関数でreturnされるシグナルのemitをawaitで待つ
途中でawaitの関数 returnなし
これは「シグナルのemitをawaitで待つ」コルーチンの完了をawaitで待つ

次回

awaitを使ってなにか作ります。