「3秒待ってから敵を出したい」「攻撃アニメーションが終わってからダメージ判定したい」「会話の次の行はボタン入力まで待ちたい」。こういう処理を全部 _process() のタイマー変数で書くと、すぐに見通しが悪くなります。
Godot 4の await は、こうした 一連の流れの途中で待つ処理 を読みやすくするための道具です。ゲーム全体を止めるのではなく、その関数だけを一時停止し、条件がそろったら続きから再開します。
この記事でわかること
awaitが「何を止めて、何を止めないのか」- タイマー、アニメーション、会話での実用例
connect()とawaitの使い分け- Godot 3の
yieldからGodot 4のawaitへの読み替え
awaitは「この関数 だけ待つ」仕組み
await は、待機対象が完了するまで 今の関数の続きを一時停止 します。その間も、Godotの描画、入力、物理処理、他ノードの _process() は動き続けます。

たとえば次のコードは、3秒待ってから敵を出します。
func spawn_enemy_after_countdown() -> void:
print("3秒後に敵が出ます")
# timeoutシグナルが出るまで、この関数の続きだけを待つ
await get_tree().create_timer(3.0).timeout
spawn_enemy()
await の行で関数は止まりますが、ゲーム自体は止まりません。プレイヤーの入力受付、画面描画、他の敵の移動は続きます。
このように、途中で止まって後から再開できる関数を コルーチン と呼びます。難しく聞こえますが、Godotでは「await を含む関数」と考えれば十分です。
ゲーム開発で使う場面
await が向いているのは、時間や演出の順番がある処理です。

- アクション: 攻撃ボ タン → 攻撃アニメーション終了待ち → 当たり判定 → クールダウン。
- RPG: セリフ表示 → 決定ボタン待ち → 次のセリフ表示。
- ステージ開始: カウントダウン → 3秒待機 → プレイヤー操作を有効化。
どれも「毎フレームずっと監視する」というより、流れを上から順に読みたい処理 です。こういう場面では、await を使うとコードがかなり自然になります。
タイマーを待つ
一定時間待つには、get_tree().create_timer() で一時的なタイマーを作り、その timeout シグナルを待ちます。

func start_stage() -> void:
# 1秒ごとに表示を変えるので、カウントダウンの流れが上から読める
$CountdownLabel.text = "3"
await get_tree().create_timer(1.0).timeout
$CountdownLabel.text = "2"
await get_tree().create_timer(1.0).timeout
$CountdownLabel.text = "1"
await get_tree().create_timer(1.0).timeout
$CountdownLabel.text = "START"
# 待機が終わってから操作を有効にする
player.can_control = true
Timer ノードをシーンに置かなくても、短い待機ならこの書き方で十分です。ステージ開始、リスポーン待ち、攻撃後のクールダウンなどに使いやすいです。
ただし 、何百体もの敵がそれぞれ create_timer() を大量に作るような設計は避けます。大量の個体管理では、共通のクールダウン管理やステートマシンの方が見通しが良くなります。
アニメーションやシグナルを待つ
await は、シグナルを待つ処理と相性が良いです。たとえば攻撃アニメーションが終わってから、次の処理へ進めます。
func perform_slash_attack() -> void:
# 攻撃中の二重入力を防ぐ
can_input = false
$AnimationPlayer.play("slash")
# 攻撃アニメーションが終わるまで次へ進まない
await $AnimationPlayer.animation_finished
apply_slash_damage()
# 演出後に入力を戻す
can_input = true
このコードは「入力を止める → slashを再生 → 終了を待つ → ダメージ判定 → 入力を戻す」と上から読めます。
会話でも同じ考え方が使えます。ボタンが押されたことをシグナルで通知し、そのシグナルを await します。
signal next_requested
func show_dialogue(lines: Array[String]) -> void:
for line in lines:
$DialogueBox.text = line
# プレイヤーが決定入力するまで次の行を表示しない
await next_requested
$DialogueBox.hide()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_accept"):
next_requested.emit()
ここで重要なのは、await は「一度待って、進む」ための書き方だということです。ボタンが押されるたびに毎回処理したい場合は、次の connect() の方が向いています。
実例:ジャンル別にawaitを組み込む
ここまでで、create_timer() で時間を待つ方法と、animation_finished や独自シグナルを待つ方法を見ました。ここからは、その基本を実際のゲーム機能へどう組み込むかを見ていきます。
await は単に「待てる」だけではありません。ゲームの中で「この順番で起きてほしい」という演出やルールを、実装手順としてそのままコードに落とし込めるのが強みです。
アクションRPGの攻撃シーケンス
たとえばアクションRPGで剣攻撃を作るなら、やりたいことは「ボタンを押した瞬間にすぐダメージ」ではありません。

攻撃らしく見せるには、次のような順番が必要です。
- 攻撃中の連打や移動を止める。
- 振りかぶりのアニメーションを少し見せる。
- 当たり判定を短い時間だけ有効にする。
- 攻撃後の硬直を入れる。
- 入力を戻す。
await を使うと、この手順を上から順に書けます。
func perform_sword_attack() -> void:
if not can_input:
return
# 攻撃中は移動や再攻撃を受け付けない
can_input = false
$AnimationPlayer.play("sword_attack")
# 振りかぶりを少し待ってから、当たり判定を出す
await get_tree().create_timer(0.12).timeout
$SwordHitbox.monitoring = true
# 判定は短い時間だけ有効にする
await get_tree().create_timer(0.08).timeout
$SwordHitbox.monitoring = false
# 攻撃アニメーション全体が終わってから、入力を戻す
await $AnimationPlayer.animation_finished
can_input = true
この順番にする理由は、プレイヤーの操作感とゲームルールを両方守るためです。入力を止めないと、攻撃中にもう一度攻撃が始まり、アニメーションや判定が重なります。判定を出しっぱなしにすると、剣を振っていない時間にも敵へ当たります。後隙を待たずに入力を戻すと、攻撃を連打できすぎて、アニメーションの重みがなくなります。
変えるなら、まず待ち時間を調整します。軽い短剣なら予備動作と後隙を短く、重い斧なら長くします。コンボを作る場合は、最後にすぐ can_input = true にせず、「次の攻撃入力を受け付ける短い窓」を別のフラグで用意すると管理しやすくなります。
タワーディフェンスのウェーブ開始
タワーディフェンスでは、「いきなり敵が出る」よりも、警告、カウントダウン、間隔を空けた出現がある方がプレイヤーに準備の余地が生まれます。

signal wave_finished
func run_wave(wave_data: Array[PackedScene]) -> void:
# まずプレイヤーに次のウェーブ開始を知らせる
$WaveLabel.text = "WAVE INCOMING"
$WaveLabel.show()
# 準備時間を作ってから敵を出し始め る
await get_tree().create_timer(2.0).timeout
$WaveLabel.hide()
# 敵を一度に出さず、間隔を空けて順番に出す
for enemy_scene in wave_data:
spawn_enemy(enemy_scene)
await get_tree().create_timer(0.4).timeout
# ウェーブが終わったことを他のUIや進行管理へ知らせる
wave_finished.emit()
ここで await が効くのは、「警告を出す」「2秒待つ」「敵を0.4秒ごとに出す」「終わったら通知する」という進行を、1本のイベントとして読めるところです。_process() にタイマー変数を何個も置くより、ウェーブの設計意図がコードに残ります。
ただし、ウェーブ中に早送り、ポーズ、スキップを入れたいなら、単純な await だけでは足りません。is_wave_running やステートマシンで中断条件を持ち、待機後に「まだウェーブ中か」を確認する設計にします。
RPGの会話シーン
RPGの会話は、await の考え方とかなり相性が良いです。1行表示し、プレイヤーの決定入力を待ち、次の行へ進むからです。

signal next_line_requested
func show_event_dialogue(lines: Array[String]) -> void:
# 会話中にプレイヤーが動かないようにする
player.can_control = false
$DialogueBox.show()
for line in lines:
$DialogueBox.text = line
# 決定入力のシグナルが来るまで、次のセリフ へ進まない
await next_line_requested
# すべて表示したらUIを閉じ、操作を戻す
$DialogueBox.hide()
player.can_control = true
この形にしておくと、「宝箱を開ける → セリフを読む → アイテムを受け取る → 操作を戻す」のようなイベントも、順番を保ったまま書けます。
connectとawaitの使い分け
connect() と await はどちらもシグナルに関係しますが、用途が違います。

connect(): ボタン、HP変更、敵撃破など、イベントが起きるたびに何度も反応したい。await: 会話の次行、攻撃終了、3秒待機など、一連の処理の途中で一度待ちたい。
たとえばメニューの「開始」ボタンは、押されるたびに反応するイベントです。
func _ready() -> void:
# ボタンは押されるたびに反応したいのでconnectで登録する
$StartButton.pressed.connect(_on_start_button_pressed)
func _on_start_button_pressed() -> void:
get_tree().change_scene_to_file("res://stages/stage_01.tscn")
一方、ステージ開始のカウントダウンは「3、2、1、START」という一回の流れです。
func run_start_sequence() -> void:
# カウントダウン中は操作不可
player.can_control = false
# show_countdown()の完了を一度だけ待つ
await show_countdown()
player.can_control = true
判断に迷ったら、 「繰り返し反応するイベントか、一度だけ待つ流れか?」 と考えてください。
Godot 3のyieldからの移行
Godot 3では yield() を使っていた処理を、Godot 4では await に置き換えます。
| Godot 3 | Godot 4 |
|---|---|
yield(get_tree().create_timer(1.0), "timeout") | await get_tree().create_timer(1.0).timeout |
yield($AnimationPlayer, "animation_finished") | await $AnimationPlayer.animation_finished |
yield(button, "pressed") | await button.pressed |
Godot 4では、シグナル名を文字列で書くより、object.signal_name の形で待てるため、読みやすくなります。
よくあるつまずき
_process() の中で毎フレームawaitを開始する
これはかなり危険です。毎フレーム新しい待機処理を作ってしまい、1秒後に大量の処理が一斉に再開することがあります。
# 悪い例: 毎フレーム新しい待機が始まる
func _process(delta: float) -> void:
# _process()は毎フレーム呼ばれるため、待機処理が大量に増える
await get_tree().create_timer(1.0).timeout
spawn_enemy()
繰り返し処理なら Timer ノードや状態変数で管理しましょう。
func _ready() -> void:
# 繰り返し発生するスポーンはTimerに任せる
$SpawnTimer.timeout.connect(_on_spawn_timer_timeout)
$SpawnTimer.start()
func _on_spawn_timer_timeout() -> void:
spawn_enemy()
待っている間にノードが消える
await 中に対象ノードが queue_free() されると、続きの処理が想定通りに動かないことがあります。演出中にシーン遷移する、敵が倒される、UIが閉じられる、といった場面です。
func play_hit_flash(target: Node2D) -> void:
# 待機前に、対象ノードがまだ存在するか確認する
if not is_instance_valid(target):
return
target.modulate = Color.WHITE
await get_tree().create_timer(0.1).timeout
# 待機中に敵やUIが消えることがあるので、再開後にも確認する
if not is_instance_valid(target):
return
target.modulate = Color(1, 1, 1, 1)
待機後に対象を触るなら、再開時にも is_instance_valid() で確認すると安全です。
何でもawaitで順番に書きすぎる
await は読みやすい反面、長い演出を1本の関数に詰め込みすぎると、途中キャンセルやスキップが難しくなります。会話スキップ、演出キャンセル、死亡時の中断が必要なら、状態変数やステートマシンと組み合わせましょう。
おまけ:先に知っておくと良いこと
await の基本を押さえたら、次はこのあたりを知っておくと安心です。今の段階では、細かい最適化テクニックとして覚えるより、 使いどころを間違えないための注意 として読めば十分です。
awaitは重い処理を軽くする機能ではない:awaitは「タイマーやシグナルを待って、あとで続きを実行する」ための機能です。大量生成や重い計算で画面が止まる場合は、awaitで解決しようとする前に、ロードタイミング、オブジェクトプール、表示範囲だけ作る設計などを見直します。- 1フレーム待つ書き方もある: Godotには
await get_tree().process_frameのように、次のフレームまで待つ書き方もあります。リザルト画面で報酬アイコンを少しずつ出す、UIの反映を1フレーム待つ、といった場面で使うことがあります。ただし、初心者〜中級者の通常制作で頻繁に使うものではありません。必要になったときに思い出せば十分です。 - 長い流れはキャンセル方法も考える:
awaitで会話、演出、攻撃シーケンスを上から順に書くと読みやすくなります。一方で、スキップ、死亡、シーン遷移で途中終了したい場合は、フラグやステートマシンと組み合わせます。
発展として大事なのは、Godotの細かい待機方法を全部暗記することではありません。 「これは待つ処理なのか、設計を見直すべき処理なのか」 を分けて考えられることです。
次に状態管理を整理したい場合は、 ステートマシンで管理するAIとプレイヤーの状態 がつながります。
まとめ
await は、Godotで「少し待ってから続きを実行する」処理を読みやすくする機能です。
- タイマー待ちには
await get_tree().create_timer(seconds).timeout。 - アニメーションやボタンなど、シグナル待ちにも使える。
- 繰り返し反応するイベントは
connect()が向いている。 _process()で毎フレームawaitを始めない。- 重い処理そのものを軽くする機能ではない。
判断基準は、 「これは一度だけ待つ流れか、何度も反応するイベントか?」 です。一度だけ待つ流れなら await、何度も反応するイベントなら connect()。この区別だけでも、非同期処理の見通しはかなり良くなります。