問題発生

Brackeys Game Jam 2024.1に投稿した「次元ドア」の開発中にシーン絡みでいくつかの問題に悩まされました。

こららについて調べてみました。TileMapは別に改めてまとめます。

調べたいこと

簡単なシーンを作成して、以下のことを調べてみました。

調査方法

Godot 4.2.1の互換性レンダラーで以下のようなプロジェクトを作成しました。

以下、簡単な作成手順です。

  1. Godotで互換性の新規プロジェクトを作成
  2. Nodeを作成して、名前をMainにしてmain.tscnでシーンとして保存
  3. mainをメインシーンに設定
  4. 子ノードを追加からCharacterBody2Dで作成。名前をChrにする
  5. Chrに以下の子ノードを追加
    1. CollisionShape2Dを作成して、Shapeから新規RectangleShapeを作成
    2. ColorRectを作成
  6. chr.gdというスクリプトを作成して、ChrとCollisionShape2Dにアタッチ
  7. chr.gdのスクリプトは以下の通り
extends Node

@export var _node_nanika: Node

func _ready():
	print_debug("%s: %s" % [_node_nanika.get_parent().name, _node_nanika.modulate.color])
  1. ChrとCollisionShape2DのインスペクターのNode NanikaにColor Rectを設定
  2. CHr, CollisionShapw2D, ColorRectのModulateを変更しておきます

これらの変更はすべてmain.tscnに保存されます。

この状態で、Chrをシーンとして保存してみます。

  1. シーンパネルでChrを右クリックして、ブランチをシーンとして保存を選択して、chr.tscnシーンとして保存する

これでmainシーンのChrはChrシーンのインスタンスになりました。先に設定していたNode NanikaとModulateがどうなったかを確認してみます。

ChrのNode Nanikaは設定が消えていて、戻すアイコンが表示されている状態です。

設定が消えて、戻すボタン

それに対してModulateは先ほどの設定が保存されていて、戻すアイコンがありません。

Modulateは設定されている

この動作の不一致は混乱の元になりそうです。さらにChrを編集可能な子にして中身を確認します。

chr.gdのNode Nankaはやはり消えていて、Modulateは保存されています。

Gitでmainシーンの差分を見ると以下の通りです。

main.tscn

消えた行のうち、12行目と18行目がChrとCollisionShape2Dに設定したNode Nanikaの設定です。ここでColorRectをアタッチした情報があります。この時点で、mainシーンからはNode Nanikaへの設定が消えています。

これがchrシーンに移動しているかを確認します。

chr.tscn

10行目と16行目に_node_nanikaへの代入がありますが、接続が消えてしまっています。これが設定が消えた原因です。一方で、moduleteの値はmainから消えたものがchrに追加されています。

疑問

先の確認で、インスタンスを割り当てたNode Nanikaはシーンに変換した際に引き継がれないことが分かりました。そして新たな疑問が浮かびます。

このあたりを確認します。まずは最初の方です。ScriptにColor要素を追加して、同様のテストをしてみます。

extends Node

@export var _node_nanika: Node
@export var _my_color: Color

func _ready():
	print_debug("%s: %s" % [_node_nanika.get_parent().name, _node_nanika.modulate.color])

ChrとCollisionShape2DそれぞれのMy Colorを変更して保存したら、Chrをシーンにします。

確認すると色は引き継がれることが分かりました。

色は保存される

シリアライズ化できるものは保存されて、インスタンスの参照のような設定は引き継がれないようです。

子シーンのノードパスは引き継げるか

次は、子シーンにノードパスを設定した場合に親に引き継がれるかを確認してみます。

保存すると、Chrシーンに設定が

Chrシーン内でのノードパスは設定できている

mainシーンに戻ると今度はノードパスが引き継がれていることが確認できます。

mainシーンのchrのノードが設定されている

ここで戻るアイコンが表示されていることが引っかかりました。試しに押してみてもColorRectが設定されたままです。mainシーンを保存してGitで確認しましたが、mainシーンは何も保存されていませんでした。

この挙動はバグのような感じがします。もとのmainシーンにはNode Nankaの設定はありません。子シーンを読み込んだだけなのでここに変化はないはずなのですが、恐らくChrをシーンにする前の何らかの設定がエディター内に残っていて、それが不一致だったので戻るアイコンが表示されたのではないかと推察します。この挙動は分かりにくいので報告した方がいいかも知れませんね。

継承シーンの挙動

Chrの継承クラスChrInheritedを作成して、パラメーターの引き継がれ状況を観察します。

Gitで差分を見ると、継承シーンは非常にシンプルな構造であることが分かります。

継承したシーン

細かい設定がないことから問題なく移行できていることが推測できます。確認すると、実際に問題なく参照できています。

継承できている

ただここでも戻るアイコンが表示されることが引っかかります。押してみると、mainシーンのときと同様、ファイルを保存しても何も変更されませんでした。

継承シーンをmainシーンに配置したところ、NodePathの参照は正しく引き継がれていました。ただし、戻すアイコンは表示されます。押しても何も変更はありませんが、気持ちが良いものではありません。

まとめ

以上から、開発中に悩まされたことの原因が掴めたような気がします。

問題の元凶はブランチをシーンとして保存するときにNodePathが引き継がれないことでした。これにより、親のシーンで編集している分には問題がなかった設定が、保存したシーンに引き継がれておらず、

さらに混乱を招いたのが、戻すアイコンが表示されることでした。値が変わっていないのにこのアイコンが表示されてしまうことで、何か操作を間違えたのかとぎょっとすることが多発しました。

追加

去年の10月にすでにIssuesに報告されてた。

https://github.com/godotengine/godot/issues/84016

10/30に以下へ移行

https://github.com/godotengine/godot/issues/84146

SceneTreeでノードやPackedSceneをシーンを複製やコピペするさいに、ノードパスをただコピーをするだけで(あてはめ)しない。

https://github.com/godotengine/godot/issues/78060

mainシーンのChrから

継承シーンでの状況を確認する

この段階で観察すると、ChrにアタッチしたスクリプトのColorRectとCollisionShape2DのShapeに戻すアイコンが表示されます。保存しても消えません。これはデフォルトの状態ではない、ということになります。これらが保存されているのはmainシーンです。

Chrを選択して、ブランチをシーンとして保存します。mainシーンから

検証

ルートシーンに配置した子のシーンに変更を加えるとどこに保存されるのか?

chrの座標を変更して保存してみます。

GitHubで確認すると、main.tscnに保存されていることが分かりました。

chrシーンを開くと座標はもとのままなので、mainシーンで編集した場合はmainシーンに保存されることが確認できました。また、変更した項目には戻すボタンが追加されます。

Godotはシーンの情報をすべてテキストで保存するので、Gitを使えば保存データがどのファイルに記録されたかを簡単に確認することができます。

足元にアタッチした子のオブジェクトのリンクが外れる問題

かなり繰り返し悩まされた現象です。ステージ作成をしているうちに、仲間や敵が変な場所で反転する症状が出ました。

ここまでの検証から、子に行った変更は親には影響を与えないはずです。しかし、何らかの

やったこととしては、キャラの複製と継承して別のキャラを作ったことが挙げられる。

推測

親のシーンでアタッチしたのではないかと推定。