スイッチを踏むと開き、離れると閉まる扉のように、決まった動きを再生したいときは、AnimationPlayer でキーを打ちます。開始と終わりの2つのキーを打てば、そのあいだの動きは Godot が埋めてくれます。
ただ、キーを打ったつもりでも、再生すると動かないことがよくあります。タイムラインの1行がインスペクタの1項目と対応している、という仕組みが見えていないと、どこを直せばいいのか分かりません。
この記事では、タイムラインとインスペクタの対応をつかみ、スイッチを踏むと開く扉を作る方法を解説します。
この記事でわかること
- タイムラインの1行とインスペクタの1項目の対応、鍵アイコンが何を書き込むのか
- 針・値・鍵を押す順番と、その順番を崩したときに起きること
- キーを打ったのに再生しても動かないときに、確かめる4つの点
- 逆再生がどこから始まるか、再生が終わったアニメの名前がどこに残るか
動作確認環境: Godot 4.7
タイムラインとインスペクタの対応
AnimationPlayer を選ぶと、画面下に Animation パネルが開きます。ここで新しいアニメーションを作ると、左半分にトラック(動かす項目ごとの行)の一覧、右半分に時間の目盛りが並んだ画面になります。目盛りの上には、いまの時刻を指す縦の線(再生位置の針)があります。
この状態でノードを選ぶと、インスペクタの各項目の右端に鍵のアイコンが出ます。これを押すと「この時刻の、この項目は、いまこの値」という1点が、その項目の行に記録されます。この1点がキーです。

トラックの一覧に並ぶ1行は、どのノードの、どの項目か を指しています。Door:position という行は、Door というノードの position を担当します。Sprite2D:modulate なら、Sprite2D の色です。
扉を閉じた位置から開いた位置まで動かすには、同じ行に時刻と値の違うキーが2つ要ります。0.00秒に (320, 300)、0.50秒に (320, 236) と入っていれば、再生したときにその間を埋めた値が流れます。この「間を埋める」計算を補間と呼びます。
絵を切り替えるためだけの道具ではなく、鍵が出る項目なら、位置でも色でも角度でも同じやり方で動かせます。絵の切り替わりに判定や音をそろえる話は AnimationPlayerで攻撃を1本にまとめる記事 にあります。
他のエンジンでいうと: Godot は AnimationPlayer でプロパティの変化を再生します。扉のように時間に沿って物を動かす用途は、Unity の Animation ウィンドウで作る Animation Clip や、Unreal Engine の Timeline を使った実装に近いものです。
鍵を押す順番
キーが打てない原因のほとんどは、順番です。

- 針を
0.00秒へ動かす - 閉じている状態の値をインスペクタで確認する
- 鍵を押す
- 針を
0.50秒へ動かす - 開いた状態の値を入れて、もう一度鍵を押す
針を動かすのは、値を変える前だけです。値を変えたあとに針を動かすと、移動先の時刻の値で上書きされます。すでにトラックがあると、針を動かすたびにその時刻の値が流し込まれるためです。
「扉を動かしてから時刻を決めよう」という順番が、いちばんよくある詰まり方です。変えた値が消えるので、キーを打ったつもりのまま先へ進んでしまいます。
キーを打つときの4つの確認点
鍵を押せているのに再生しても動かないときは、次の4つを確かめます。
別のアニメーションへ入っている。 パネルの選択欄が RESET のままだと、鍵は RESET という別のアニメーションへ入ります。RESET は、保存するときに各項目を戻しておく初期の姿勢を入れる、特別なアニメーションです。open を再生してもトラックが無いので、何も起きません。鍵を押す前に、選択欄が open になっているかを見ます。

同じ時刻に押し直している。 同じ行の同じ時刻で鍵を押すと、キーは増えずに値が上書きされます。トラックは増えたように見えても、中身は1点のままです。2点目にするには、先に針を動かします。
長さの外に残っている。 新しく作ったアニメーションの長さは1秒です。1秒の位置にキーを打ってから長さを 0.5 に縮めると、そのキーは消えずに残ったまま、範囲の外なので再生されません。長さは先に決めます。
絵だけを動かしている。 鍵は、いま選んでいるノードの項目に付きます。扉の絵である Sprite2D を選んだまま押すと、Door/Sprite2D:position の行ができます。再生すると絵だけが上へ動き、当たり判定はその場に残ります。扉ごと動かしたいなら、選ぶのは絵ではなく、絵と当たり判定をまとめている親のほうです。
補間と更新モード
トラックの右側には、似て見える2つの設定が並びます。
- 補間: キーとキーのあいだをどう埋めるか。Linear は直線で結び、Nearest は次のキーに届くまで前のキーの値を保ち、Cubic は滑らかな曲線を描く。扉の位置は Linear で足りる
- 更新モード: その値をいつ書き込むか。Continuous は毎フレーム書き込み、Discrete はキーを通過したときだけ書き込む。位置や色は Continuous、チェックボックスのようにオンとオフしかない項目は Discrete

Nearest は「近いほうのキーへ寄る」動きではありません。0.00秒と0.50秒にキーがあれば、0.49秒でもまだ0.00秒の値のままです。
長さの欄とループのボタンは、タイムラインの右上に並んでいます。扉のように「開いて終わり」のアニメーションは、ループをオフのままにします。オンにすると、開き切った扉が閉じた姿勢へ飛んで、また開きはじめます。
逆再生と、再生が終わったあとの状態
再生は play("open") の1行です。逆方向は play_backwards("open") を使います。
play_backwards() は、同じアニメーションを再生している途中なら、その位置から戻ります。0.2秒まで開いたところで呼べば、0.2秒から閉じていきます。終端へ移ってから戻るのは、別のアニメーションから切り替えたときと、位置が0秒のときです。

再生が終わったあとの状態にも、つまずきや すいところがあります。 再生が止まると current_animation は空の文字列になります。 アニメーション名が残るのは assigned_animation のほうです。位置と長さは終端の値のままなので、「もう開き切っているか」は、再生中でないこと・assigned_animation が "open" であること・位置が長さと等しいことの3つで見ます(実践の _is_fully_open())。
これを見ずに play("open") を呼ぶと、再生は0秒からやり直しになります。開き切った扉が、いったん閉じてから開き直します。
実践:踏むと開く扉
ダンジョンの仕掛け扉、レースゲームのスタートゲート、脱出ゲームの隠し戸——近づくと開いて、離れると閉まる形はどれも同じです。扉が上へ64ピクセル退避して通路が空き、通り過ぎると元へ戻るところまで作ります。
① 置くもの
通路を横切る扉を、新しいシーンに1つ作ります。
- ルートに Node2D を置いて名前を Level にし、
level.gdを付ける - Level の子に AnimatableBody2D を置いて Door。位置
(320, 300)。Sync To Physics をオンにする - Door の子に Sprite2D(Texture に
icon.svg・Scale を(0.5, 0.5))と CollisionShape2D(RectangleShape2D・サイズ(64, 64))を置く - Level の子に AnimationPlayer を置く。インスペクタの Callback Mode の中の Process を Physics にする
- Level の子に StaticBody2D を2つ置いて WallTop(位置
(320, 236))と WallBottom(位置(320, 364)) - 壁2つの子に CollisionShape2D(RectangleShape2D・サイズ
(640, 64))をそれぞれ置く - Level の子に Area2D(重なりを知らせるだけで、体は止めない範囲)を置いて Switch。位置
(320, 300)。子に CollisionShape2D(RectangleShape2D・サイズ(256, 128)) - Level の子に CharacterBody2D を置いて Player。位置
(140, 300)。Groups ドックでグループplayer(ノードに付ける名札)に入れて、player.gdを作って付ける - Player の子に Sprite2D(Texture に
icon.svg・Scale を(0.3, 0.3))と CollisionShape2D(CircleShape2D・半径20)を置く

Switch と壁は形だけなので、実行しても見えません。エディタ上部の Debug から Visible Collision Shapes をオンにしてください。
上下の壁が作る通路は y が 268 から 332 の帯で、閉じた扉がちょうどそこを塞いでいます。扉が64ピクセル上がると 204 から 268 へ移り、通路から完全に外れます。
Switch は扉より広く取ります。扉の手前だけを覆う形にすると、通路へ入った瞬間にスイッチから外れて、通り抜ける前に扉が下りてきます。広く取ったぶん、Switch には扉や壁も重なるので、体が範囲に入ったときに届く body_entered はそれらにも来ます。コードがグループでプレイヤーだけを見分けているのは、このためです。
扉を StaticBody2D ではなく AnimatableBody2D にするのは、動く扉として物を押せるようにするためです。StaticBody2D を動かしても、その動きは物を押す移動として扱われません。Sync To Physics と、AnimationPlayer の Process を Physics にそろえると、扉の移動が物理の更新と同じタイミングで行われ、押された側の動きががたつきません。
② キーを2つ打つ
AnimationPlayer を選び、下のパネルで新しいアニメーションを open という名前で作ります。最初に長さを 0.5 にして、ループはオフのままにします。
そのあと、シーンツリーで Door を選んでから次の順に進めます。Sprite2D ではなく Door を選ぶのが大事なところです。
- 針を
0.00秒へ動かす - インスペクタの Transform > Position が
(320, 300)になっているのを確認して、右端の鍵を押す - トラックを作るかを聞かれるので、Use Bezier Curves はオフ(オンにすると別の種類のトラックになる)、Create RESET Track(s) はオンのまま作成する
- 針を
0.50秒へ動かす - Position の
yを236に変える(64ピクセル上) - もう一度、Position の鍵を押す
Door:position という行が1本でき、0.00秒と0.50秒にキーが並んでいれば成功です。Door/Sprite2D:position になっていたら、選び直してやり直します。
Switch を選び、Signals ドックから body_entered と body_exited を Level へ接続します。
# level.gd
extends Node2D
@onready var anim: AnimationPlayer = $AnimationPlayer
func _on_switch_body_entered(body: Node2D) -> void:
if not body.is_in_group("player"):
return
if _is_fully_open():
return # 開き切っているのに play すると0秒へ戻る
anim.play("open")
func _on_switch_body_exited(body: Node2D) -> void:
if not body.is_in_group("player"):
return
anim.play_backwards("open") # 途中なら、その位置から戻る
func _is_fully_open() -> bool:
if anim.is_playing():
return false # 動いている最中は開き切りではない
if anim.assigned_animation != "open":
return false # 終わると current_animation は空になる
return is_equal_approx(anim.current_animation_position, anim.current_animation_length) # 小数の誤差を許して比べる
# player.gd
extends CharacterBody2D
const SPEED := 300.0
func _physics_process(_delta: float) -> void:
velocity = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * SPEED
move_and_slide()
③ 動かして確かめる

実行する前に、タイムラインの針を手で動かします。Door を選んだまま針を 0.25 秒に置くと、インスペクタの Position は (320, 268) です。0.00秒の 300 と0.50秒の 236 のちょうど中間で、自分では一度も打っていない数字です。あいだを補間が埋めています。300 のままなら、0.50秒のキーが無いか、補間が Nearest か更新モードが Discrete になっています。
確認できたら、針を 0.00 秒へ戻してから保存します。AnimationPlayer の Reset On Save(既定でオン)が、保存のときに RESET の値へ戻します。別の時刻を映したまま保存しても、保存されるのは閉じた位置です。
実行して、右矢印キーで右へ歩きます。スイッチの範囲に入ると扉が上がりはじめます。扉が上がり切る前にプレイヤーが届くので、少しのあいだ扉の下の角に引っかかり、スイッチに入ってから約0.4秒で下をくぐれるようになります。扉の絵だけが上がって通れないなら、キーを打ったのが Sprite2D の行です。扉の下を 通り抜けてもまだスイッチの中なので、扉は開いたままです。右端まで歩いてスイッチから出ると、扉が下りてきて通路を塞ぎます。
仕上げに、スイッチの左端に少しだけ入って立ち止まり、扉が上がり切る前に左へ出ます。扉は上がったその高さから閉まりはじめ、いったん上まで飛んでから閉じることはありません。打ったのは0.00秒と0.50秒の2つのキーだけで、途中の高さはすべて補間が埋めています。
ポイントは2つです。
- 鍵は「いま選んでいるノードの、その項目」に付く: Sprite2D を選んだまま押すと絵だけが動き、当たり判定はその場に残ります。行の名前が、動かしたいノードと一致しているかを毎回見てください
- 止まっているアニメの名前は
assigned_animationにある:current_animationが返すのは再生中の名前だけです。止まると空になるので、そこと名前を比べても一致しません
閉じ切ったときや開き切ったときに音や演出を足したくなったら、AnimationPlayerで判定と音を1本にまとめる記事 へ進んでください。
うまくいかないときは
| 症状 | 原因 | 直し方 |
|---|---|---|
| 起動した時点で扉が開いている | RESET のトラックが無いまま、開いた時刻を映して保存した。または RESET に開いた姿勢が入っている | 選択欄を RESET にして、Door:position のキーを (320, 300) に打ち直す。RESET が無いなら、針を0.00秒へ戻してから保存する |
| 扉を別の場所へ動かしたら、再生した瞬間に元の位置へ飛ぶ | キーは、親の Level から見た (320, 300) という位置そのものを記録している | 扉を置き直したらキーも打ち直す。扉を1つのシーンにまとめ、そのシーンの中の子の position にキーを打てば、置き場所に左右されない(ルートの position に打つと、置いた場所に関係なくその座標へ飛ぶ) |
| 扉を複製したら、片方しか動かない | トラックは Door:position のように名前で相手を指す。複製した Door2 は対象に入っていない | 扉ごとに AnimationPlayer を持たせる。扉を1つのシーンにして、インスタンスとして並べる |
おまけ:先に知っておくと良いこと
Node2D の rotation は、そのノードの原点を中心に回ります。蝶番のように端を軸にして回したいときは、軸の位置に親のノードを置き、その子に絵をずらして置いて、親の rotation を動かします。扉の絵そのものに角度を付けると、真ん中で回ってしまいます。
扉が開いているあいだだけ当たり判定を消す作り方もあります。CollisionShape2D の Disabled にも鍵は押せます。この場合は更新モードを Discrete にして、切り替えたい時刻にだけキーを置きます。実践のように扉ごと動かせるなら、判定を触らないほうが状態が1つで済みます。
行き先が実行時まで決まらない動き、たとえば拾ったコインが飛んでいく先のようなものは、キーを打つより Tween のほうが向きます。キーは行き先が最初から決まっているときの道具です。
絵の切り替えとの役割分担は AnimatedSprite2Dとの比較、扉が開くのを見せるあいだ操作を止める組み方は カットシーン入門 にあります。
まとめ
- 1行はインスペクタの1項目: 鍵は「この時刻の、この項目は、いまこの値」を書き込む
- 針、値、鍵の順: 値を変えてから針を動かすと、移動先の値で上書きされる
- 長さは先に決める: あとから縮めると、外に出たキーは残ったまま再生されない
- 鍵は選んでいるノードに付く: 扉ごと動かすなら、絵ではなく親を選ぶ
play_backwards()は途中から戻る: 同じアニメーションの途中ならその位置から。止まったアニメーションの名前はassigned_animationに残る
鍵を押す前に選ぶノードで、動くものが決まります。いま動かしたいのは絵でしょうか。それとも、絵と当たり判定をまとめた入れ物のほうでしょうか。