【Godot4】queue_free()したNodeが削除されずget_children()で取得される原因と対処法

2026-06-10

Godotでノードを削除するときにqueue_free()を使ったのに、 直後のget_children()では削除したはずのノードがまだ取得されることがあります。

この記事では、queue_free()が即時削除ではないことと、 手札やカード一覧のように子ノードを並び替える処理での対処法を紹介します。

環境

  • Godot4.5
  • GDScript

問題例

次のように、カードゲームの手札を横並びに表示する場面を考えます。 ここでは、handsの子ノードとして各カードが追加されており、 それぞれのpositionを以下のコードで直接指定しています。

var i = 0
for card in hands.get_children():
	card.position = Vector2(100, 0) * i
	i += 1

ここで手札を1枚捨てたあと、残ったカードの表示位置を詰めようとすると、次のようなコードになります。

trash_card.queue_free()

var i = 0
for card in hands.get_children():
	card.position = Vector2(100, 0) * i
	i += 1

まず手札が3枚用意します。

カードが3枚横並びに並んでいる
カードが3枚横並びに並んでいる

この時、真ん中のカードを捨ててみます。

カードが2枚、間に1枚分開けて横並びに並んでいる
カードが2枚、間に1枚分開けて横並びに並んでいる

実行すると、削除したカードがまだget_children()に含まれているため、残ったカードの位置が期待通りに詰まりません。

こうなる原因はqueue_free()が即座に削除されるのでなく、現在のフレームの最後に削除されるため、並び替えるときのhands.get_children()queue_free()したカードが残っているからです。

修正案

対処法1:次のフレームまで待つ

削除が反映されたあとに並び替える方法です。

queue_free()した後に1フレームだけ待つことで削除されるまで待機します。

trash_card.queue_free()
await get_tree().process_frame

var i = 0
for card in hands.get_children():
	card.position = Vector2(100, 0) * i
	i += 1

対処法2:先に親から外す

remove_child()で先にhandsの子ノード一覧から外すことで、get_children()の対象からすぐに除外できます。

hands.remove_child(trash_card)
trash_card.queue_free()

var i = 0
for card in hands.get_children():
	card.position = Vector2(100, 0) * i
	i += 1

対処法3:削除対象をスキップする

一時的に削除対象だけを除外したい場合に使えます。

trash_card.queue_free()

var i = 0
for card in hands.get_children():
	if card == trash_card:
		continue

	card.position = Vector2(100, 0) * i
	i += 1

まとめ

queue_free()は即時削除ではなく、削除予約です。 そのため、直後にget_children()すると削除予定のノードが含まれることがあります。

手札や一覧UIのように、子ノードをすぐ並び替えたい場合は、

  • 次のフレームまで待つ
  • remove_child()で先に親から外す
  • 削除対象を処理から除外する

といった方法で対応できます。

ブログゲーム開発Godot