Disable HUD inside the Level when Player's HUD is disabled

Godot Version

4.7.1

Question

I wonder if there is some easy way how to manage HUD’s.
In level and character itself is HUD to not get overlapping boxes of text.
There is plugin which manage this from localization.
Currently I’m adapting a more aspects from https://forum.godotengine.org/t/rick-o-shea/141433/3 .

Ok what scene is looks like ?

Area3D got collision shape which is entered and text is pulled on screen on enter, and on exit hide.

@onready var shooting_gallery: MapLevel3D = $".."
func _ready() -> void:
	body_entered.connect(_on_body_entered)
	body_exited.connect(_on_body_exited)


func _on_body_entered(body: Node3D) -> void:
	if body is Player:
		canvas_layer.show()
		shooting_gallery.canvas_active = true


func _on_body_exited(body: Node3D) -> void:
	if body is Player:
		canvas_layer.hide()
		shooting_gallery.canvas_active = false

I thought to make something in those lines to not get overlap of text:

@onready var shooting_gallery: MapLevel3D = $".."
func _process(delta: float) -> void:
	if cat_rescued == true:
		if shooting_gallery.canvas_active == false:
			canvas_layer.show()
		if shooting_gallery.canvas_active == true:
			canvas_layer.hide()
	else:
		pass

But then realized there is another issue, when I pause game the player’s hud use simple script

extends CanvasLayer


func _notification(what: int) -> void:
	match what:
		NOTIFICATION_PAUSED:
			hide()
		NOTIFICATION_UNPAUSED:
			show()

So logical solution would be use it on all CanvasLayers inside Area3D and Teleport, but this open another problem, it ignoring process function.

So before I dedicate to make extension of the CanvasLayer script and move condition there is there some simple solution how to manage those HUDs:
So they won’t overlap, and when I’m in Area3D collision it shows CanvasLayer there, when pause game it disable, and when return it be shown again, but when I’m out of area3d it check whatever I finished task before it’s displayed.

BTW current condition to check whatever open portal is this:

func _on_cat_rescued() -> void:
	number_of_cats -= 1
	
	if number_of_cats <= 0:
		print("CATS COMPLETE")
		cat_rescued = true
		collision_shape_3d.disabled = false
		gpu_particles_3d.emitting = true
		get_node("Portal").show()

Any advice be appreciated.


Update After digging and start making array to update it from singleton, I figure out there is more to it.

I’ll need to make signal, or state, to update HUD overall to stop overlaps.

func set_canvas_visible(visibility_status: bool) -> void:
	canvas_layer.visible = visibility_status

Then in level I made control for Array, but this relay on for loop, which I would rather avoid same as process_function.

But ultimately didn’t solve it, by looking what is in control of Start is Game State Machine which probably would make sense to integrate level control of elements there to stop overlaps.

There is another possible issue which I see is overlapping 3DAreas with Text, so question where to handles in to keep it clean and make use of addons?

Ok here is update, it does working but not quite ideal I guess:

in each level

@onready var canvas_active: bool =  false
var _ui_stack: Array[Node] = []

func request_ui(source: Node) -> void:
	_ui_stack.erase(source) 
	_ui_stack.push_back(source)
	_update_ui()

func release_ui(source: Node) -> void:
	_ui_stack.erase(source)
	if source.has_method("set_canvas_visible"):
		source.set_canvas_visible(false)
	_update_ui()

func _update_ui() -> void:
	for ui in _ui_stack.size():
		var source: Node = _ui_stack[ui]
		if source.has_method("set_canvas_visible"):
			source.set_canvas_visible(ui == _ui_stack.size() - 1)

then have this for Area3D to control CanvasLayer inside of it


extends Area3D

@onready var level_currently: MapLevel3D = $".."
@onready var canvas_layer: CanvasLayer = $CanvasLayer

var player_inside: bool = false

func _ready() -> void:
	body_entered.connect(_on_body_entered)
	body_exited.connect(_on_body_exited)

func _notification(what: int) -> void:
	match what:
		NOTIFICATION_PAUSED:
			canvas_layer.hide()
		NOTIFICATION_UNPAUSED:
			if player_inside:
				level_currently.request_ui(self)

func _on_body_entered(body: Node3D) -> void:
	if body is Player:
		player_inside = true
		level_currently.request_ui(self)

func _on_body_exited(body: Node3D) -> void:
	if body is Player:
		player_inside = false
		level_currently.release_ui(self)

func set_canvas_visible(v: bool) -> void:
	canvas_layer.visible = v

This does a trick, but it’s a bit overkill maybe.
It checking for array last item, or top. the ( v ) parameter.

Edit: in release_ui as it didn’t explicitly updated when Teleport was emitted along the Area3D.