【Godot】Godotの3Dスケルタルアニメーション: AnimationPlayerとAnimationTree

作成: 2026-02-08最終更新: 2026-07-08

Godot 4のSkeleton3Dとボーン、AnimationPlayerによる3Dアニメーション再生、BlendSpaceによる方向ブレンド、SkeletonIK3Dによる接地調整を実例で解説します。

3Dキャラクターに歩く・走る・攻撃するといった動きを付けたい。でも「アニメの切り替えがカクつく」「坂道や段差で足が地面にめり込む」「状態管理が複雑になる」——3Dアニメには2Dにはない悩みがつきものです。

この記事では、3Dならではの部分——Skeleton3D(骨格)とボーン、AnimationPlayer での再生、BlendSpace による方向ブレンド、SkeletonIK3D による接地調整——を中心に解説します。AnimationTree とステートマシンの基本的な考え方は2Dと共通なので、AnimationTreeとステートマシン と合わせて読むと理解が深まります。

半透明の3D人型キャラの内部に発光するボーンが透けて見える、3Dスケルタルアニメーションのイメージ図

この記事でわかること

  • Skeleton3D のボーン階層と、スクリプトからのボーン取得
  • GLBインポートで自動生成される AnimationPlayer での再生・ブレンド
  • BlendSpace1D/2D で速度・方向に応じてアニメを混ぜる方法
  • SkeletonIK3D で足を地面に接地させる動的調整(と非推奨化の注意)

Sponsored

Skeleton3Dとボーン階層

アニメーションを扱う前に、まずは骨格の仕組みを押さえておきましょう。すべてのスケルタルアニメーションは、このボーン階層の上に成り立っています。

Skeleton3D は3Dキャラクターの骨格構造を表すノードです。人間の骨格のように階層的に配置されたボーンを、時間経過で動かすことでアニメーションが生まれます。

Skeleton3Dのボーン階層の図。人型の骨格と、Hipsを親にSpineやLegが枝分かれするボーンのツリー構造

腰(Hips)のような根元のボーンを親に、背骨・頭・腕・脚が子としてぶら下がります。親を動かすと子もついてくる——この親子関係が、体全体をまとめて動かす土台になります。

インポートしたモデルにどんなボーンがあるかは、スクリプトから一覧できます。

var skeleton := $MeshInstance3D.find_child("Skeleton3D", true, false) as Skeleton3D
print("ボーン数: ", skeleton.get_bone_count())
for i in skeleton.get_bone_count():
    print("  ", skeleton.get_bone_name(i))

# 特定のボーンのインデックスと現在のポーズを取得
var head_idx := skeleton.find_bone("Head")
if head_idx != -1:
    var pose := skeleton.get_bone_pose(head_idx)  # Transform3D
    print("頭のローカル位置: ", pose.origin)
Sponsored

AnimationPlayerで3Dアニメを再生する

AnimationPlayer は最も基本的なアニメーション再生ノードです。3Dモデル(GLB)をインポートすると、MeshInstance3DSkeleton3D と一緒に自動生成され、すぐ使える状態になっています。

GLBファイルをインポートすると、MeshInstance3D・Skeleton3D・AnimationPlayer(idle/walk/runクリップ付き)が自動生成される流れの図

基本再生

@onready var anim_player: AnimationPlayer = $AnimationPlayer

func _ready() -> void:
    for anim_name in anim_player.get_animation_list():
        print("  ", anim_name)
    if anim_player.has_animation("idle"):
        anim_player.play("idle")

ブレンド時間で切り替えを滑らかに

切り替えた瞬間にカクッと見えるのは、ブレンドなしで一瞬で切り替わっているためです。set_blend_time() で滑らかな遷移になります。

# アニメーション間のブレンド時間(秒)を設定しておく
anim_player.set_blend_time("idle", "walk", 0.2)
anim_player.set_blend_time("walk", "run", 0.3)

func change_to_walk() -> void:
    anim_player.play("walk")  # idleから0.2秒かけてwalkへ

再生速度とシグナル

anim_player.play("walk", -1, 2.0)   # 2倍速
anim_player.play_backwards("walk")   # 逆再生

func _ready() -> void:
    # 攻撃モーションが終わったら待機へ戻す、といった制御に使う
    anim_player.animation_finished.connect(_on_animation_finished)

func _on_animation_finished(anim_name: String) -> void:
    if anim_name == "attack":
        anim_player.play("idle")

AnimationPlayer単体の作り込み(メソッド呼び出しトラック、RESETなど)は AnimationPlayerの高度な活用 を参照してください。

Sponsored

AnimationTreeで状態を管理する

歩き・走り・ジャンプ・攻撃と状態が増えたら、AnimationTree のステートマシンでまとめます。 考え方は2Dとまったく同じ で、AnimationPlayer(アニメの倉庫)から AnimationTree(司令塔)がクリップを取り出し、コードは travel() で「今の状態」を伝えるだけです。

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

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

func _physics_process(delta: float) -> void:
    var speed := velocity.length()
    var target := "idle"
    if speed > 0.1:
        target = "run" if Input.is_action_pressed("sprint") else "walk"
    if state_machine.get_current_node() != target:  # 変わったときだけ
        state_machine.travel(target)

ステートマシンの設計(ノード追加・遷移・Xfade Time)や、コード側との分担の詳しい考え方は AnimationTreeとステートマシン で解説しています。ここからは、3Dで特に役立つ BlendSpaceIK に進みます。

BlendSpaceで速度・方向をブレンドする

ステートマシンが「歩き」「走り」のような離散的な状態を切り替えるのに対し、速度や方向のように 連続的に変化する値 には BlendSpace が向いています。

BlendSpace1D は1つの値(例:移動速度)でブレンドします。速度0で待機、3で歩き、10で走り、を滑らかに繋げます。

# エディタでBlendSpace1Dを作り、0.0=idle / 3.0=walk / 10.0=run を配置
var speed := velocity.length()
anim_tree.set("parameters/movement/blend_position", speed)

BlendSpace2D は2次元のベクトル(例:移動方向)でブレンドします。アクションRPGのように全方向へ動くキャラの、8方向歩きアニメに最適です。

BlendSpace2Dの図。正方形の8方向に各向きの歩きアニメを配置し、ブレンドカーソルの位置で近い方向のアニメが混ざる
# 四隅と上下左右に walk_forward / walk_right などを配置しておく
var dir := Vector2(
    Input.get_axis("move_left", "move_right"),
    Input.get_axis("move_back", "move_forward")
)
anim_tree.set("parameters/locomotion/blend_position", dir)
Sponsored

SkeletonIK3Dで足を地面に合わせる

アニメーションデータだけでは、地形の凹凸に足を合わせたり、動く物体に手を伸ばしたりといった その場の調整 ができません。ここで使うのが IK(Inverse Kinematics/逆運動学)です。

IKなしとIKありの比較図。IKなしでは段差に足がめり込むが、IKありでは足首が曲がって段差に接地する

SkeletonIK3D は、足首から先を目標位置へ合わせるようにボーンを逆算し、足の接地や手の到達を実現します。平地用の歩きアニメのままでも、段差や坂で足が地面にぴたっと着くようになります。

tips: SkeletonIK3D はGodot 4.xで非推奨の方向にあり、SkeletonModifier3D 系への移行が進んでいます。新規プロジェクトでは SkeletonModifier3D の利用を検討してください。既存プロジェクトでは SkeletonIK3D も引き続き動作しますが、将来のバージョンで削除される可能性があります。

# Skeleton3D の下に SkeletonIK3D(Root Bone: Hips / Tip Bone: FootL)を置き、
# Target に地面の位置を表す Node3D を割り当てておく
@onready var foot_ik: SkeletonIK3D = $Skeleton3D/FootIK_L
@onready var foot_target: Node3D = $FootTarget_L

func _ready() -> void:
    foot_ik.start()  # IKを開始

func _process(delta: float) -> void:
    # 足の下へレイを飛ばし、地面の高さへターゲットを合わせる
    var space := get_world_3d().direct_space_state
    var from := foot_target.global_position + Vector3.UP
    var to := foot_target.global_position + Vector3.DOWN * 2.0
    var hit := space.intersect_ray(PhysicsRayQueryParameters3D.create(from, to))
    if hit:
        foot_target.global_position = hit.position

IKの効き具合は interpolation(0.0=無効〜1.0=完全適用)で調整でき、stop() で一時的に切ることもできます。

実践:移動ブレンド+接地IKを組む

3Dアクションやオープンワールド、TPSなどで「移動速度に応じて歩き↔走りが混ざり、坂道や段差では足がちゃんと地面に着く」キャラを作ってみます。コツは、 アニメ(大まかな動き)とIK(接地の微調整)を別の役割として重ねる ことです。

  • アニメ側BlendSpaceAnimationTree が、速度から歩き/走りのポーズを作る
  • IK側 :足の下へレイキャストして地面を探し、SkeletonIK3D が足首をそこへ合わせる
実践の図。速度からblend_positionでAnimationTreeが大きな動きを作り、地面レイキャストからIKターゲットでSkeletonIK3Dが接地を微調整する
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var foot_ik: SkeletonIK3D = $Skeleton3D/FootIK_L
@onready var foot_target: Node3D = $FootTarget_L

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

func _physics_process(delta: float) -> void:
    # アニメ: 速度をブレンド位置に渡す(歩き↔走り)
    anim_tree.set("parameters/locomotion/blend_position", velocity.length())

    # IK: 足の下の地面の高さへターゲットを合わせる
    var space := get_world_3d().direct_space_state
    var from := foot_target.global_position + Vector3.UP
    var to := foot_target.global_position + Vector3.DOWN * 2.0
    var hit := space.intersect_ray(PhysicsRayQueryParameters3D.create(from, to))
    if hit:
        foot_target.global_position = hit.position

ポイントは2つです。

  • アニメとIKは役割が違う :アニメは「歩いている」という大きな動きを、IKは「その足を、今いる地面の高さに合わせる」という微調整を担当します。分けておくと、同じ歩きアニメのまま平地でも坂でも自然に見えます。
  • 重いのはIK :IKは毎フレーム計算します。次のように、遠くのキャラや画面外のキャラではIKを止めて負荷を下げます。

パフォーマンス最適化

リッチなアニメーションは魅力的ですが、大量のキャラがいるシーンでは処理負荷が問題になります。カメラからの距離に応じて、処理を段階的に減らしましょう。

距離に応じたLODの図。近距離は毎フレーム更新、中距離はフレームスキップ、遠距離は更新を停止

画面の端に小さく映るキャラを、主人公と同じ精度で更新する必要はありません。

func _process(delta: float) -> void:
    var distance := global_position.distance_to(camera.global_position)

    if distance > 50.0:
        anim_tree.active = false                     # 遠距離: 処理を停止
    else:
        anim_tree.active = true
        if distance > 20.0 and Engine.get_process_frames() % 2 != 0:
            return                                   # 中距離: 1フレームおきにスキップ
        # 近距離: 通常更新

tips: anim_tree.active = false にするとアニメーション処理が完全に止まります。遠距離では active を false にし、中距離では active を true のまま更新をスキップするのが安全です。あわせて、プレイヤー以外のIK(foot_ik.stop())を切る、インポート時にアニメーション圧縮(Compression: Lossy / Optimize)を有効にする、といった手も効きます。

まとめ

  • Skeleton3D :ボーンの親子階層。親を動かすと子もついてくるのが土台
  • AnimationPlayer :GLBインポートで自動生成。play()set_blend_time() で再生・ブレンド
  • AnimationTree :状態管理は2Dと同じ考え方(AnimationTreeとステートマシン)。3Dでは BlendSpace の方向ブレンドが強力
  • SkeletonIK3D :足の接地など動的なポーズ調整(非推奨化が進行中、SkeletonModifier3D への移行を推奨)
  • 最適化 :距離に応じたLOD、不要なIKの停止、アニメーション圧縮

まずはGLBを読み込んで AnimationPlayer で歩き・走りを再生し、慣れてきたら BlendSpace で方向ブレンド、SkeletonIK3D で接地、と一段ずつ重ねていきましょう。被弾フラッシュのような単発演出は Tween で足すのが手軽です。