【Godot】awaitとコルーチンの基礎

作成: 2025-12-08最終更新: 2026-07-07

Godot 4のawaitを使って、タイマー、アニメーション、会話、演出などの待機処理を読みやすく書く方法を、ゲーム実装例で解説します。

「3秒待ってから敵を出したい」「攻撃アニメーションが終わってからダメージ判定したい」「会話の次の行はボタン入力まで待ちたい」。こういう処理を全部 _process() のタイマー変数で書くと、すぐに見通しが悪くなります。

Godot 4の await は、こうした 一連の流れの途中で待つ処理 を読みやすくするための道具です。ゲーム全体を止めるのではなく、その関数だけを一時停止し、条件がそろったら続きから再開します。

awaitで処理を一時停止し、ゲームは動いたまま再開を待つイメージ

この記事でわかること

  • await が「何を止めて、何を止めないのか」
  • タイマー、アニメーション、会話での実用例
  • connect()await の使い分け
  • Godot 3の yield からGodot 4の await への読み替え
Sponsored


awaitは「この関数だけ待つ」仕組み

await は、待機対象が完了するまで 今の関数の続きを一時停止 します。その間も、Godotの描画、入力、物理処理、他ノードの _process() は動き続けます。

await timer.timeoutで関数だけが待機し、入力や描画など他の処理は続くタイムライン

たとえば次のコードは、3秒待ってから敵を出します。

func spawn_enemy_after_countdown() -> void:
    print("3秒後に敵が出ます")

    # timeoutシグナルが出るまで、この関数の続きだけを待つ
    await get_tree().create_timer(3.0).timeout

    spawn_enemy()

await の行で関数は止まりますが、ゲーム自体は止まりません。プレイヤーの入力受付、画面描画、他の敵の移動は続きます。

このように、途中で止まって後から再開できる関数を コルーチン と呼びます。難しく聞こえますが、Godotでは「await を含む関数」と考えれば十分です。


ゲーム開発で使う場面

await が向いているのは、時間や演出の順番がある処理です。

攻撃、会話、開始前待機でawaitを使う場面
  • アクション: 攻撃ボタン → 攻撃アニメーション終了待ち → 当たり判定 → クールダウン。
  • RPG: セリフ表示 → 決定ボタン待ち → 次のセリフ表示。
  • ステージ開始: カウントダウン → 3秒待機 → プレイヤー操作を有効化。

どれも「毎フレームずっと監視する」というより、流れを上から順に読みたい処理 です。こういう場面では、await を使うとコードがかなり自然になります。


タイマーを待つ

一定時間待つには、get_tree().create_timer() で一時的なタイマーを作り、その timeout シグナルを待ちます。

create_timerで3→2→1→STARTとカウントダウンし、各待機をawaitで挟んでから操作を有効にする流れの図
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で剣攻撃を作るなら、やりたいことは「ボタンを押した瞬間にすぐダメージ」ではありません。

アクションRPGの攻撃で入力停止、予備動作、判定ON/OFF、後隙、入力復帰を順に進める図

攻撃らしく見せるには、次のような順番が必要です。

  1. 攻撃中の連打や移動を止める。
  2. 振りかぶりのアニメーションを少し見せる。
  3. 当たり判定を短い時間だけ有効にする。
  4. 攻撃後の硬直を入れる。
  5. 入力を戻す。

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 にせず、「次の攻撃入力を受け付ける短い窓」を別のフラグで用意すると管理しやすくなります。

タワーディフェンスのウェーブ開始

タワーディフェンスでは、「いきなり敵が出る」よりも、警告、カウントダウン、間隔を空けた出現がある方がプレイヤーに準備の余地が生まれます。

タワーディフェンスで警告表示、2秒待機、0.4秒ごとの敵出現、ウェーブ終了通知を順に進める図
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行表示し、プレイヤーの決定入力を待ち、次の行へ進むからです。

RPGの会話イベントで操作停止、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は毎回反応、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 3Godot 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()。この区別だけでも、非同期処理の見通しはかなり良くなります。