ゲーム開発において、処理の「待機」や「一時停止」は頻繁に発生します。例えば、アニメーションの終了を待つ、ネットワーク通信の応答を待つ、あるいは一定時間後に処理を再開するなどです。これらの処理をメインループ(フレーム処理)を止めずに効率的に行うために不可欠なのが、非同期処理 と コルーチン の概念です。
Godot EngineのGDScriptでは、この非同期処理をシンプルかつ強力に扱うためのキーワードとしてawaitが導入されました。本記事では、awaitの基本的な使い方から、その背後にあるコルーチンの仕組み、パフォーマンスの注意点、そしてGodot 3系で使われていたyieldからの移行方法までを解説します。
なぜ非同期処理とawaitが重要なのか
ゲームは常にスムーズに動作し続ける必要があります。もし、何らかの処理(例えば、巨大なファイルの読み込みや複雑な計算)がメインスレッドを長時間占有してしまうと、ゲームはフリーズし、ユーザー体験は著しく損なわれます。これを ブロッキング と呼びます。
awaitを使った非同期処理は、このブロッキングを回避するための強力な武器です。awaitは、関数の実行をその場で一時停止し、待機が完了するまでGodotエンジンに制御を戻します。その間、エンジンは他の処理(描画、入力受付、物理演算など)を続行できるため、ゲームはフリーズしません。そして、待機していたイベント(シグナルの発行など)が発生すると、エンジンは停止した箇所から自動的に関数の実行を再開します。
この「一時停止と再開」の仕組みを持つ関数がコルーチンです。GDScriptでは、関数内でawaitを使った瞬間に、その関数は自動的にコルーチンになります。
awaitの基本的な使い方
awaitは、シグナルまたは他のコルーチンの完了を待機するために使用します。
1. シグナルの待機
最も一般的な使い方は、ノードから発行されるシグナルを待つことです。
# 例: 攻撃アニメーションが終了したらダメージ判定を行う
func _on_attack_button_pressed():
$AnimationPlayer.play("attack")
# AnimationPlayerが持つ`animation_finished`シグナルを待機する
await $AnimationPlayer.animation_finished
# アニメーションが終わったので、ここでダメージ計算などの処理を実行
print("攻撃アニメーション終了!ダメージ判定!")
2. 一定時間を待機する (タイマー)
指定した時間だけ処理を中断したい場合は、SceneTreeが提供するタイマー生成機能と組み合わせます。
# 例: 3秒後に敵を出現させる
func spawn_enemies_after_delay():
print("ゲーム開始!3秒後に敵が出現します。")
# SceneTreeTimerを作成し、その`timeout`シグナルを待機する
await get_tree().create_timer(3.0).timeout
print("時間です!敵を生成します。")
# ここで敵インスタンスを生成する処理
3. 他のコルーチンの完了を待機する
awaitは、それ 自体がコルーチンである他の関数の完了を待つこともできます。
# 非同期でリソースをロードし、完了を待つ関数
func load_level_async() -> void:
print("レベルデータのロードを開始します...")
await get_tree().create_timer(2.0).timeout # ダミーのロード時間
print("レベルデータのロードが完了しました。")
# ゲーム開始シーケンス
func start_game():
# まずUIをフェードインさせる(コルーチン)
await fade_in_ui()
# 次にレベルデータを非同期でロードする(コルーチン)
await load_level_async()
# すべての準備が整ったので、プレイヤーを操作可能にする
print("ゲームスタート!")
func fade_in_ui():
var tween = create_tween()
tween.tween_property($UI/CanvasLayer, "modulate:a", 1.0, 1.0)
await tween.finished
Tweenと
awaitの注意点: Tweenのdurationが0、または開始時点で既に目標値に到達している場合など、Tweenが即座に完了するケースではfinishedシグナルが発行されない可能性があります。このような場合は、awaitが永遠に待機し続けるため、事前に条件チェックを行うか、タイムアウト処理を併用することを検討してください。
awaitとコルーチンの関係
awaitが使われている関数は、自動的に