Godot 4のAnimationTree
ステートマシン完全ガイド

ステートマシン、トランジション、ブレンドツリー、OneShot、travel()と条件分岐 — 実践的なGDScriptコード例付きの完全ガイド。

1. はじめに

AnimationTreeはGodot 4における複雑なアニメーションブレンディングと状態遷移のためのシステムです。AnimationPlayer.play()の呼び出しをコード中にばらまいて複数のアニメーションを管理しようとした経験があるなら、それがいかに早く手に負えなくなるかご存知でしょう。AnimationTreeはビジュアルな状態とトランジションのグラフでこの問題を解決します。

AnimationTreeは非常に強力であるにもかかわらず、公式ドキュメントは不十分で、開発者が手探りになることも少なくありません。このガイドでは、基本セットアップから高度なブレンドツリーまで、プロジェクトにそのままコピーできる実践的なGDScriptコードとともに全てを解説します。

このガイドで学べること

ステートマシン、トランジション、ブレンドスペース(1D・2D)、攻撃用OneShot、travel()と条件の使い分け、完全なキャラクターコントローラーパターン。

2. 前提条件

AnimationTreeをセットアップする前に、以下が必要です:

  • Idle、Walk、Run、Jumpなどのアニメーションが作成済みのAnimationPlayerノード
  • AnimationPlayerはAnimationTreeを追加するノードの兄弟または子である必要があります(通常、両方ともキャラクターのルートノードの子です)
  • Godot 4.x(このガイドはGodot 4のAPIを使用しています — AnimationTreeのAPIはGodot 3から大きく変更されました)
Godot 3 と Godot 4 の違い

Godot 4ではAnimationTree.animation_playerAnimationTree.anim_playerに変更されました。playbackのパラメータパスも変わっています。Godot 3から移行する場合は公式移行ガイドを確認してください。

3. 基本セットアップ

AnimationTreeのセットアップは4ステップです:

  1. AnimationTreeノードを追加 — AnimationPlayerと並べて、キャラクター(例:CharacterBody2DまたはCharacterBody3D)の子として追加します。
  2. anim_playerを設定 — インスペクターで「Anim Player」プロパティをAnimationPlayerノードに向けます。
  3. tree_rootを設定 — インスペクターの「Tree Root」プロパティをクリックして、新しいAnimationNodeStateMachineを作成します。
  4. active = trueに設定 — インスペクターで「Active」チェックボックスをオンにするか、コードで設定します。

シーンツリーは以下のようになります:

CharacterBody2D (or CharacterBody3D)
  +-- Sprite2D (or Sprite3D)
  +-- CollisionShape2D
  +-- AnimationPlayer      <-- has Idle, Walk, Run, Jump animations
  +-- AnimationTree         <-- points to AnimationPlayer above
GDScript
# Minimal code setup (usually done via the editor instead):
@onready var anim_tree: AnimationTree = $AnimationTree

func _ready() -> void:
    # If you set these in the Inspector, you don't need this code
    anim_tree.anim_player = ^"../AnimationPlayer"
    anim_tree.tree_root = AnimationNodeStateMachine.new()
    anim_tree.active = true
エディタ vs コード

実際の開発では、ほぼ常にエディタでAnimationTreeを設定します。上記のコードは網羅性のために示していますが、通常はスクリプトでanim_tree.active = trueだけで十分です(これもエディタで設定可能です)。

4. ステートマシンの基本

AnimationTreeのステートマシンはシンプルなコンセプトで動作します:状態(state)はアニメーションを表し、トランジションは状態間の切り替え条件を定義します。

状態の追加

AnimationTreeのtree_rootがAnimationNodeStateMachineに設定されたら、インスペクターでダブルクリックしてステートマシンエディタを開きます:

  1. グラフエリアを右クリックし、Add Animationを選択
  2. AnimationPlayerからアニメーションを選択(Idle、Walk、Run、Jumpなど)
  3. 必要な各アニメーション状態に対して繰り返します
特殊な状態

Startはエントリーポイントです。最初のトランジションは必ずここから始まります。Endはオプションで、ステートマシンの終了を示します(ネストされたステートマシンで有用)。

トランジションの追加

2つの状態間にトランジションを作成するには:

  1. ソースの状態ノードをクリック
  2. デスティネーションの状態にドラッグしてトランジション矢印を作成
  3. トランジション矢印をクリックしてインスペクターで設定

トランジションのプロパティ

プロパティ 説明
advance_mode Auto — 条件がtrueのとき発火。Enabled — travel()で常に利用可能。Disabled — ブロック。
advance_condition ブールパラメータの名前(例:is_moving)。trueのときトランジションが自動発火。
xfade_time クロスフェードの秒数。アニメーション間のスムーズなブレンド。典型的:0.1〜0.3秒。
switch_mode Immediate — 即座に切替。Sync — 再生位置を合わせる。AtEnd — 現在のアニメーション終了まで待機。

プラットフォーマーキャラクターの典型的なセットアップ:

Start --> Idle
Idle  --> Walk   (condition: is_moving)
Walk  --> Idle   (condition: is_idle)
Idle  --> Jump   (condition: is_jumping)
Walk  --> Jump   (condition: is_jumping)
Jump  --> Fall   (condition: is_falling)
Fall  --> Idle   (condition: is_on_floor, switch_mode: Immediate)

5. コードからステートマシンを制御する

ステートマシンを駆動する方法は主に2つあります:条件パラメータの設定(自動トランジション)とtravel()の呼び出し(手動トランジション)です。両方のアプローチを組み合わせることもできます。

GDScript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree
@onready var state_machine: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/playback")

func _physics_process(delta: float) -> void:
    # ... movement logic here ...
    move_and_slide()
    _update_animation_parameters()

func _update_animation_parameters() -> void:
    # Approach 1: Set condition parameters — transitions fire automatically
    anim_tree.set("parameters/conditions/is_moving", velocity.length() > 10.0)
    anim_tree.set("parameters/conditions/is_idle", velocity.length() <= 10.0)
    anim_tree.set("parameters/conditions/is_on_floor", is_on_floor())
    anim_tree.set("parameters/conditions/is_jumping", velocity.y < 0 and not is_on_floor())
    anim_tree.set("parameters/conditions/is_falling", velocity.y > 0 and not is_on_floor())

    # Approach 2: Use travel() for direct control
    # if velocity.length() > 10.0:
    #     state_machine.travel("Walk")
    # else:
    #     state_machine.travel("Idle")
パラメータパス

パラメータのパターンはparameters/conditions/<condition_name>です(トランジションに設定した条件の場合)。条件名はエディタでトランジションに設定したadvance_conditionと一致する必要があります。

6. travel() と条件の使い分け

条件ベース(移動系に推奨)

毎フレームブールパラメータを設定し、トランジションを自動発火させます。より宣言的でコードがクリーンになります。ステートマシンがトランジションロジック、クロスフェード、エッジケースを処理します。

# Declarative: just describe the current state of the world
anim_tree.set("parameters/conditions/is_moving", velocity.length() > 10.0)
anim_tree.set("parameters/conditions/is_on_floor", is_on_floor())

travel()(単発アクションに推奨)

travel()は状態遷移をリクエストします。トランジションルールを尊重するため、現在の状態からターゲットへの有効なパスがなければ呼び出しは無視されます。繰り返し呼んでも安全です。攻撃、エモート、カットシーンアニメーションなどの単発トリガーに使用します。

# travel() — requests a transition (respects transition rules)
state_machine.travel("Jump")

# Get current state name
var current: StringName = state_machine.get_current_node()
print(current)  # "Idle", "Walk", etc.

# Check if travel is possible
var is_playing: bool = state_machine.is_playing()
print(is_playing)
どちらを使うべきか?

条件は連続的な状態(待機、歩行、走行、落下)に。travel()はイベントトリガーの状態(攻撃、回避、インタラクト)に。多くのプロジェクトでは両方を使います:移動は条件、戦闘アクションはtravel()。

7. ブレンドツリー

ブレンドツリーは、離散的な状態間のハードスイッチではなく、連続値に基づいて複数のアニメーション間をスムーズに補間できます。歩行/走行速度のブレンドや方向移動に最適です。

BlendSpace1D

単一軸に沿った2つ以上のアニメーション間の1Dブレンド。一般的な使用例:移動速度に基づくWalkとRunのブレンド。

エディタで、ステートマシン内(またはスタンドアロンのツリールートとして)BlendSpace1Dノードを作成します。アニメーションポイントを追加:

# BlendSpace1D setup (in editor):
# Point 0.0 = Walk animation
# Point 1.0 = Run animation

# Control from code:
var speed_factor: float = clamp(velocity.length() / max_speed, 0.0, 1.0)
anim_tree.set("parameters/WalkRun/blend_position", speed_factor)

BlendSpace2D

2軸を使った2Dブレンド。8方向移動やキャラクターが任意の方向に移動できるトップダウンゲームに最適です。

# BlendSpace2D setup (in editor):
# Place animations at positions:
#   Idle at (0, 0)
#   WalkRight at (1, 0), WalkLeft at (-1, 0)
#   WalkUp at (0, -1), WalkDown at (0, 1)
#   Diagonals at corners

# Control from code:
var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
anim_tree.set("parameters/Movement/blend_position", input_dir)
ブレンドモード

BlendSpace2Dは複数のブレンドモードをサポートします。デフォルトの三角分割はほとんどの場合うまく機能します。ピクセルアート風の補間なしアニメーションが必要な場合は、離散モード(最寄りのポイントにスナップ)も選択できます。

8. 主要なノードタイプ

AnimationTreeは複数のノードタイプをサポートし、組み合わせることで複雑なアニメーション動作を作成できます:

ノードタイプ 用途
AnimationNodeStateMachine トランジション付きステートマシン。最も一般的なルートノード。状態はアニメーションまたはネストされたステートマシン。
AnimationNodeBlendSpace1D 1軸に沿った1Dブレンド。歩行/走行速度、エイム角度など。
AnimationNodeBlendSpace2D 2軸を使った2Dブレンド。方向移動、横歩きブレンド。
AnimationNodeBlendTree ブレンド操作のグラフ。複数のブレンドノードをカスタムロジックで組み合わせ。
AnimationNodeAdd2 加算ブレンド。アニメーションの上に別のアニメーションを重ねる(例:歩行の上にエイムオフセット)。
AnimationNodeTimeScale 速度制御。実行時にアニメーションの再生速度を変更。
AnimationNodeOneShot ワンショットアニメーションオーバーレイ。攻撃、エモート、被弾リアクションに最適。
AnimationNodeTransition クロスフェード付きで複数の入力間を切り替え。シンプルなセットアップではステートマシンの代替。

9. OneShotパターン(攻撃・エモート)

OneShotノードはAnimationTreeで最も有用なパターンの一つです。ベースアニメーションの上にワンタイムアニメーションを再生し(歩行中に攻撃スイングを再生するなど)、終了後に自動的にベースアニメーションに戻ります。

BlendTree内のセットアップ

OneShotを使うには、AnimationTreeのルート(またはその中の状態)がBlendTreeである必要があります:

# BlendTree graph setup:
#
#   [StateMachine] ---> [OneShot "AttackOneShot"] ---> [Output]
#   (base locomotion)      ^
#                          |
#                   [Animation "Attack"]
#                   (shot input)
#
# The StateMachine provides the base (idle/walk/run).
# The Attack animation is connected to the OneShot's "shot" input.

コードからのトリガー

GDScript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("attack"):
        _play_attack()

func _play_attack() -> void:
    # Fire the one-shot animation
    anim_tree.set(
        "parameters/AttackOneShot/request",
        AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
    )

func _process(delta: float) -> void:
    # Check if the one-shot is currently active
    var is_attacking: bool = anim_tree.get("parameters/AttackOneShot/active")
    if is_attacking:
        # Optionally disable movement during attack
        pass

OneShotリクエスト定数

定数 効果
ONE_SHOT_REQUEST_FIRE ワンショットアニメーションの再生開始
ONE_SHOT_REQUEST_ABORT ワンショットをキャンセルし即座にベースに戻る
ONE_SHOT_REQUEST_FADE_OUT ワンショットをフェードアウト(fadeout_timeプロパティを使用)
複数攻撃の実装

コンボシステムには、複数のOneShotノードを連続使用するか、OneShotのshot入力内にAttack1 → Attack2 → Attack3状態を持つネストされたステートマシンを使用します。

10. 実践例:完全なキャラクターコントローラー

ステートマシンによる移動とOneShot攻撃を組み合わせた、完全な2Dプラットフォーマーキャラクタースクリプトです。プロジェクトに合わせて応用できる実用的なパターンです。

GDScript — player.gd
extends CharacterBody2D

const SPEED := 200.0
const JUMP_VELOCITY := -350.0
const SPRINT_MULTIPLIER := 1.6

@onready var anim_tree: AnimationTree = $AnimationTree
@onready var playback: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/playback")
@onready var sprite: Sprite2D = $Sprite2D

var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")

func _ready() -> void:
    anim_tree.active = true

func _physics_process(delta: float) -> void:
    _apply_gravity(delta)
    _handle_jump()
    _handle_movement()
    move_and_slide()
    _update_animation()

func _apply_gravity(delta: float) -> void:
    if not is_on_floor():
        velocity.y += gravity * delta

func _handle_jump() -> void:
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

func _handle_movement() -> void:
    var direction := Input.get_axis("move_left", "move_right")
    var is_sprinting := Input.is_action_pressed("sprint")
    var current_speed := SPEED * (SPRINT_MULTIPLIER if is_sprinting else 1.0)

    if direction != 0.0:
        velocity.x = direction * current_speed
        sprite.flip_h = direction < 0.0
    else:
        velocity.x = move_toward(velocity.x, 0.0, SPEED)

func _update_animation() -> void:
    # Skip animation updates during attack
    var is_attacking: bool = anim_tree.get("parameters/AttackOneShot/active")
    if is_attacking:
        return

    if not is_on_floor():
        if velocity.y < 0:
            playback.travel("Jump")
        else:
            playback.travel("Fall")
    elif abs(velocity.x) > 10.0:
        if Input.is_action_pressed("sprint"):
            playback.travel("Run")
        else:
            playback.travel("Walk")
    else:
        playback.travel("Idle")

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("attack") and is_on_floor():
        anim_tree.set(
            "parameters/AttackOneShot/request",
            AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
        )
この例のAnimationTree構造

Root = BlendTree。内部:StateMachineノード(Idle/Walk/Run/Jump/Fall状態)がOneShotノード("AttackOneShot")に接続され、Outputに接続。攻撃アニメーションはOneShotの"shot"入力に接続。

11. トラブルシューティング

「アニメーションが再生されない」

AnimationTreeのactive = trueを確認し、anim_playerプロパティが有効なAnimationPlayerを指していることを確認してください。参照しているアニメーション名がAnimationPlayerに実際に存在することも確認しましょう。

「travel()が効かない」

現在の状態とターゲット状態の間に有効なトランジションパスが存在することを確認してください。パスが存在しない場合、travel()は静かに失敗します。state_machine.get_current_node()で実際にどの状態にいるかデバッグしましょう。

「ブレンドが機能しない」

blend_positionの値がブレンドスペースのポイント範囲内にあることを確認してください。ポイントが0.0と1.0の場合、5.0の値は期待通りに機能しません。clamp()を使用してください。

「Warning: AnimationTree is not active」

エディタ(インスペクターのチェックボックス)か_ready()関数でactive = trueを設定してください。AnimationTreeはアクティブ化するまで何も行いません。

「条件ベースのトランジションが発火しない」

以下を再確認してください:(1) トランジションのadvance_modeAutoに設定されている、(2) advance_conditionの名前がコードで設定した名前と完全に一致する(大文字小文字を区別)、(3) _physics_process()で毎フレームパラメータを設定している。

「アニメーションは再生されるがキャラクターが動かない」

AnimationTreeはアニメーション再生のみを処理します。移動ロジック(velocitymove_and_slide())は別であり、スクリプトの_physics_process()で実装する必要があります。

AIにAnimationTreeを構築させたい?

Godot MCP Proはステートマシンの作成、状態とトランジションの追加、ブレンドツリーの設定、パラメータの設定を — 全て1つのプロンプトから実行できます。AIアシスタントに望むアニメーション動作を伝えるだけで、AnimationTree全体を構築します。

  • create_animation_tree
  • add_state_machine_state
  • add_state_machine_transition
  • set_blend_tree_node
  • set_tree_parameter
  • get_animation_tree_structure
Godot MCP Proを入手 — $15