【Godot4】Tween は self.create_tween() を使うべき? queue_free 時にハマった罠

Godot4ではキャラクターの位置や大きさを滑らかに変化させるために Tween を使用します。
Tween は create_tween()get_tree().create_tween() のどちらでも作れます。
しかしこの2つは同じではなく、Node に紐づくかどうか が違います。
これを理解せずに get_tree().create_tween() を使用したところ、Node を削除したあとも Tween が残り、意図しない処理が続く原因になりました。

この記事では、Tweenの初期化方法の違いと安全な書き方について解説します。

環境

  • Godot4.5
  • GDScript

Tweenの説明

数値を滑らかに変化する AnimationPlayer と似たようなことができますが、Tween ではスクリプト上から動的に数値を変化させるのに適しています。

例えば、キャラクターの大きさが現在の2倍になるように徐々に大きくなるようにコードを書いてみます。

var tween = self.create_tween()
tween.set_process_mode(Tween.TWEEN_PROCESS_PHYSICS)
tween.tween_property(self, "scale", scale*2, 3.0).from(scale)

上記コードの
1行目はこのスクリプトがアタッチされているノードにバインドされた Tween を作成しています。

2行目はそのTweenが物理フレームで処理をすることを設定しています。デフォルトでは _process() と同じタイミングで処理されるが、今回の場合は他のキャラクターを _physics_process() で動かしているのでこのような設定にします。

3行目は self の scale を scale*2 の値に3.0秒間かけて、現在の scale の値から変化させる命令になっています。つまり、2倍の大きさに3秒間で変化するようになっています。

Tween は作成後、次の process フレームまたは physics フレームで自動的に開始されます。
実際に実行してみた結果が以下の動画です。

ほかにも set_loops() で繰り返しを設定したり、tween_callback() で任意の関数を呼び出したりできるので、詳しくは公式ドキュメントやほかの人のページを参照してください。

陥った罠

公式ドキュメントを読まずにネットに転がっているコードをコピペして Tween を使用したコードを書いた後、スマホでデバッグしていたら異様に発熱しました。
動作自体が止まったわけではないがまともに遊べなくなりました。

Tween の使い方を詳しく調べたところ、初期化の時の書き方が複数ありました。

それを以下に示します。

var tween = self.create_tween()

self.create_tween()では self にバインドされた Tween を作成するため、self を削除すると一緒に削除されます。

var tween = create_tween()

create_tween()の前にある self が省略されている形なので実際はself.create_tween()となります。

var tween = get_tree().create_tween()

get_tree().create_tween()では現在のSceneTreeにTweenが追加されるため、selfが削除されてもTweenは削除されません。

私が使用していたコードではget_tree().create_tween()で Tween を作っており、Node の寿命と Tween の寿命が切り離されていました。これが負荷増加の一因だった可能性があります。self に Tween をバインドすることで解決したのでおそらくこれが原因だと思われます。

queue_free() で弾や敵を削除する場面では、Node の寿命と Tween の寿命が連動していないと、不要な Tween が残りやすくなります。

解決方法

  • self.create_tween()にする
  • get_tree().create_tween().bind_node(self)にする

この2つはどちらとも self にバインドされた Tween を作成する命令になるので、
どちらかをすることで self が削除されると Tween も同時に削除されるようになり、残された Tween が負荷になることがなくなります。

公式ドキュメントでも、Node の寿命に依存する Tween では bind_node() を使うことが推奨されています。

まとめ

Tween は使用する Node にバインドすればよい。
self.create_tween()とするか、get_tree().create_tween().bind_node(self)とすればよい。💯

蛇足

似た名前の関数として get_tree().create_timer() がありますが、こちらには self.create_timer() に相当するものはありません。
一方で Tween には create_tween() があり、Node にバインドされた形で簡単に作れます。

保守性不在のSNS

タイトルとURLをコピーしました