Textures embedded in a scene that are changed programmatically are not exported with the game.

Godot Version

4.4.dev1

Question

I have created two scenes that I use for my HUD. One for abilities and one for keyboard keys. Both have exported variables that contain information about textures that I load in the editor into child nodes. When I play the game in the editor, everything loads fine. When I export the game to Windows or the Web, they do not.

I partially fixed the problem by enabling Editable Children. (See the in-game screenshot below where the 1, and the first ability on the bottom and right appear correctly.) However this has three drawbacks. 1.) I have to enable editable children. 2.) If I further embed them in other scenes, editable children no longer solves the problem. 3.) The _input events for keypresses no longer work in the export. (When you press the 1 key it’s supposed to show you that it was pressed.)

I suspected it might have something to do with being a tool script, but I commented that line out, and no change.

I also tried the reparenting suggested in this thread: Programmatically changed scene instance wont save
I tried it with the following code:

func _ready() -> void:
	self.tree_entered.connect(_on_tree_entered)

func _on_tree_entered():
	var root_node = get_tree().current_scene
	self.owner = root_node
	mask.owner = root_node
	ability_icon.owner = root_node

Here is the HUD Ability Code:

@tool
extends Control

class_name HUDAbility

@export var ability_icon_64_x_64_px: Texture:
	set(value):
		if not is_node_ready():
			await ready
		ability_icon_64_x_64_px = value
		ability_icon.texture = value
@export var is_round: bool = false:
	set(value):
		if not is_node_ready():
			await ready
		is_round = value
		if is_round:
			mask.set_clip_children_mode(Control.ClipChildrenMode.CLIP_CHILDREN_ONLY)
		else:
			mask.set_clip_children_mode(Control.ClipChildrenMode.CLIP_CHILDREN_DISABLED)


@onready var mask: TextureRect = $Mask
@onready var ability_icon: TextureRect = $Mask/AbilityIcon
@onready var level_label = $LevelLabel


var current_level: int = 0


func update_level(level: int):
	current_level = level
	if current_level > 0:
		level_label.text = str(current_level)
		self.show()
		level_label.show()
	else:
		self.hide()
		level_label.hide()

Here is the Keyboard Key Code:

@tool
extends Control


@export var key_enum: Key:
	set(value):
		if not is_node_ready():
			await ready
		key_enum = value
		key_name = OS.get_keycode_string(key_enum)
@export var key_name: String:
	set(value):
		if not is_node_ready():
			await ready
		key_name = value
		var filename = path + "keyboard_" + value
		var extension = ".png"
		var unpressed_filename = filename + "_outline" + extension
		var pressed_filename = filename + extension
		if ResourceLoader.exists(unpressed_filename):
			key_unpressed.texture = load(unpressed_filename)
		else:
			key_unpressed.texture = null
		if ResourceLoader.exists(pressed_filename):
			key_pressed.texture = load(pressed_filename)
		else:
			key_pressed.texture = null


@onready var key_unpressed = $Key_Unpressed
@onready var key_pressed = $Key_Pressed
@onready var path: String = "res://assets/ui/icons/input/keys_flat/"


func _input(event):
	if event is InputEventKey and event.pressed:
		if event.keycode == key_enum:
			key_unpressed.hide()
			key_pressed.show()
	if event is InputEventKey and not event.pressed:
		if event.keycode == key_enum:
			key_pressed.hide()
			key_unpressed.show()

This is an editor view of the items in the HUD:


This is what the game looks like when played from the editor (The ‘1’ key is being pressed, hence its different appearance.):

This is what the game looks like played from a Windows Export:

I’m open to alternate solutions or fixes to my code. As an added interesting piece, the keys can still receive mouse events. (That code isn’t included, it’s from another project.)