Sprite2DまたはTextureRectが表示されない問題

【Godotのバージョン】Godot4.6
【OSとそのバージョン】win11
【言語】GDScript
【内容】Weapon XPのUIバーが、値やロジックは正しいのに見た目だけ更新されない
Godot 4で、武器のXPに応じてUIバー(15段階)を更新するシステムを作っています。
プレイヤーがXPを拾う→XPに応じて弾の種類が変わる→XPが加算されるごとにUIのバーが増えます。
GameHUD.tscnはAutoload(GameHUD)

PlayerスクリプトでXPを加算し、それをGameHUDに渡しています。

var xp_count: int = 0

func collect_xp(amount: int):
    xp_count += amount
    GameHUD.update_weapon_ui(xp_count)

GameHUD側、15個のTextureRect(以前はSprite2Dだった)

@onready var weapon_bars := [
    $weapon_level_bar/Panel/TextureRect,
    $weapon_level_bar/Panel2/TextureRect,
    ...
]

そして、UIを更新します

func update_weapon_ui(weapon_xp: int):

    var local_xp = weapon_xp % 15
    if local_xp == 0 and weapon_xp != 0:
        local_xp = 15

    for i in range(weapon_bars.size()):
        weapon_bars[i].visible = i < local_xp

実際に値がプレイヤーからGameHUDに渡されているか確認するために、weapon xpおよびtexture rectのvisibilityをprint
結果は、すべて正しく行われていた。

CALLED FROM:/root/GameHUD
weapon_xp = 4
local_xp = 4
0 true
1 true
2 true
3 true
4 false
...

試しに、GameHUDのシーン内でready時に、update_weapon_ui(5)と入力すると、問題なくUIは更新された。
プレイヤー側から値は渡せているのに、なぜUIが更新しないのかがわかりません。

from Discord by gera310

※追記
武器レベルの1,2,3は更新されていました。
ただ、やはり青い部分のバーが更新されません。

image

from Discord by gera310

Godot 4で、武器のXPに応じてUIバー(15段階)を更新するシステムを作っています。
プレイヤーがXPを拾う→XPに応じて弾の種類が変わる→XPが加算されるごとにUIのバーが増えます。
こんにちは。上記のようなシステムを実現したいのであればProgressBarノードを使った方が良いかと思います。
ProgressBar — Godot Engine (4.3)の日本語のドキュメント

連続的な1ゲージを15段階で見せたいだけであれば、ProgressBar 1個(max_value=15, step=1)で済み、表示バグか?を切り分けるコストも一段下がると思います。
(段階ごとに異なるテクスチャを使ったり、セグメント間に固有の演出を入れる、といった意図があるのであれば現状の構成(TextureRect を15個並べる)に意味があります)


from Discord by mgnk33

回答ありがとうございます。
じつはPanel - texturerectでも試してみたのですが、同様にうまくいきませんでした。。。
ProgressBarを使えば問題は解決するのでしょうが、どうしてもこの問題を解決したく質問させていただきました。


from Discord by gera310

えー大変失礼いたしました。
問題がわかりました。
Canvas Layerを一番下にinstantiateしていたのが原因だったようです。一番上に移動させたら表示されました:man_facepalming:t2::man_facepalming:t2:
ちなみにですが、なぜ一番下にあると表示されなかったのかどなたかご教示いただけますでしょうか。

from Discord by gera310

image

from Discord by gera310

ProgressBarを使えば問題は解決するのでしょうが、どうしてもこの問題を解決したく質問させていただきました。
ProgressBarの方法ですと以下の設定で実現できると思います。
・max_value=15
・min_value=0
・step=1
・GDScriptにて、XPを加算するタイミングでProgressBarのvalueを更新する。
Canvas Layerを一番下にinstantiateしていたのが原因だったようです。一番上に移動させたら表示されました:man_facepalming:t2::man_facepalming:t2:
ちなみにですが、なぜ一番下にあると表示されなかったのかどなたかご教示いただけますでしょうか。

解決したみたいでよかったです!
なお、今回の現象は Godot の基本ルール通りに起きていたので、整理してご説明します。

【なぜ「下にあると見えなかった」のか?】
Godot の描画順は、**「シーンツリーで下にあるノードほど前面に描画される」**というのが基本ルールです。
うまくいかなかったシーンツリーでは Player_hud (水色部分)が先に描画され、その後で TileMap (青色部分)が描画されるため、「覆いかぶさる形になり背後に隠れて見えなかった」というのが原因です。

【より堅牢な解決策】
ただ、ツリーの順番で描画順を制御する方法は、シーン構造を変えたときに壊れやすいです。HUDのように「絶対に前面に出したいUI」などは、CanvasLayer の layer プロパティで明示的に指定するのが定石です。
HUD なら 10 や 100 など余裕を持たせておくと、後で背景レイヤーやエフェクト用 CanvasLayer を追加してもツリーの並び順を気にせず済みます。

【さらに細かい制御が必要なら・・・】
同じ CanvasLayer の中で、もし、特定の要素だけを前面に出したい場合は、 z_index プロパティが使えます。

ProgressBarを使えば問題は解決するのでしょうが、どうしてもこの問題を解決したく質問させていただきました。
ProgressBarの方法の場合は、以下の設定で実現できると思います。
・ProgressBarノードを追加。
・max_value=15
・min_value=0
・step=1
・GDScriptにて、XPを更新するタイミングでProgressBarのvalueを更新する。


from Discord by mgnk33

解答文を修正しました。
(すみません。今の情報だけだと根本的な原因まではわからなかったので推測の形で記載を直しました)


from Discord by mgnk33

Canvas Layerを一番下にinstantiateしていたのが原因だったようです。一番上に移動させたら表示されました:man_facepalming:t2::man_facepalming:t2:
ちなみにですが、なぜ一番下にあると表示されなかったのかどなたかご教示いただけますでしょうか。
水色の部分だけ表示されてませんでした。

回答を一旦取り下げました。
確認させてください。

最初の投稿で「GameHUD.tscn は Autoload」と書かれていましたが、それとは別に Main Scene 内にも player_hud をインスタンス化(ノードとして配置)していませんでしたか?

もしそうだとすると今回の現象がすべて腑に落ちるかもしれません。

  • 画面に映っていたのは Main Scene 内に配置した方のインスタンス:Autoload の方は SceneTree root 直下にあるが、画面には Main Scene 内のインスタンスが表示されている。
  • GameHUD.update_weapon_ui() で更新されるのは Autoload 側:シングルトンとして呼んでいるのは root 直下の方。
  • print の出力は正しいのに画面が変わらなかった:Autoload 側の weapon_bars の visibility は正しく true になっていたが、画面に映っている Main Scene 内インスタンスは更新されていなかった。
  • 「Canvas Layer を一番下→一番上」で表示された:Main Scene 内インスタンスの兄弟順序を変えたことで、TileMapやPlayer、Enemy との前後関係が変わり、SceneTree rootのplayer_hud が視覚的に見えるようになった。

from Discord by mgnk33

お返事が遅くなりました。
ご丁寧にご教示いただきありがとうございます :man_bowing:t2: :man_bowing:t2: :man_bowing:t2:
それとは別にMain Scene内にもインスタンス化していました!
Autoloadにシーンを追加したら、Main Scene内に自動でインスタンス化されるということでしょうか?


from Discord by gera310

それとは別にMain Scene内にもインスタンス化していました!
Autoloadにシーンを追加したら、Main Scene内に自動でインスタンス化されるということでしょうか?
こんばんは。
はい。画像のNotifyManagerなどのように、.tscnをAutoloadとして追加するとSceneTree root直下に追加されます。

「Canvas Layer を一番下→一番上」で表示された:Main Scene 内インスタンスの兄弟順序を変えたことで、TileMapやPlayer、Enemy との前後関係が変わり、SceneTree root直下のplayer_hud が視覚的に見えるようになった。
そうとはいえ、Main Scene内にもインスタンス化していたplayer_hudの順番を変えたら、
SceneTree root直下(Autoload)のplayer_hudが見えるようになったって現象は説明がつかないですね。
#ありうるケースとしては、他のシーンやGDScriptの中で、player_hud含め他のCanvasLayerのlayerプロパティや、
#z_index値を変更していて、その処理がノードの順番を変更したことで挙動が変わり見えるようになった等・・・


from Discord by mgnk33