Godot Version
4.4
Question
UI Items not showing on screen or in Remote tree, even though they appear in console using print_tree_pretty()
Context
I am creating a general menu made of two pages (left and right) that are emptied and filled when navigating through them. There are four sub-menus: Contacts (list of NPC you’ve met), Journal (list of current active quests), Observation (list of things you…well, observed) and Inventory (list of items you’ve picked up).
In terms of code, I have a simple Class PageComponent, that inherits Control, and that has a fill_page() and empty_page() method.
class_name PageComponent
extends Control
@export var left_page : VBoxContainer
@export var right_page : VBoxContainer
func fill_page():
pass
func empty_page():
for page in [left_page, right_page]:
for child in page.get_children():
child.queue_free()
Each page (Journal, Inventory etc) inherits that class and overwrites fill_page() with whatever they do for filling the pages.
Here is the code for my Observation page
class_name ObservationPageComponent
extends PageComponent
const ITEMS_PER_PAGE : int = 6
const ITEMS_PER_COLUMN : int = 3
var current_page : int = 0
func fill_page():
#Before filling the page we empty it
empty_page()
var start_index = current_page * ITEMS_PER_PAGE
var end_index = min(start_index + ITEMS_PER_PAGE, InteractableSingleton.observables.size())
for i in range (start_index, end_index):
var item = InteractableSingleton.observables[i]
#1-Generate an InventoryItem
var item_scene = preload("res://Scenes/UI/InventoryItem.tscn")
var new_inventory_item = item_scene.instantiate()
for child in new_inventory_item.get_children():
if child is TextureRect:
var texture_path = AppSettingsSingleton.images_folder_path + "UI/Observables/" + item.interactable_id + ".png"
child.texture = load(texture_path)
elif child is RichTextLabel:
child.text = item.interactable_data.description
#Select page to be displayed on
var local_index = i - start_index
if local_index < ITEMS_PER_COLUMN:
left_page.add_child(new_inventory_item)
else:
right_page.add_child(new_inventory_item)
And visually it looks like this:
Here you can see, three items that correspond to the three observable things I’ve interacted with in game.
Issue T_T
When switching to the Inventory or Journal submenu, the items that they should contain do not appear in game.
What works
A - I have a radial inventory system that displays the SAME items from the SAME array (stored in a singleton for ease of access) and when I pick up one or more item, I see these items, and when I click on them to select them, I print their properties properly in the console. Same for the quests, I can see them appear in the console thanks to the quest manager, so I know the issue is not in the objects I’m trying to display. The data is okay
B - More importantly, the Observation menu WORKS 100%, and Inventory is literally the same code using the same InventoryItem scene, but just with a different pool of items, which baffles me to no end.
Some more code samples
This is what the InventoryPageComponent looks like (identical to the Observation page)
class_name InventoryPageComponent
extends PageComponent
const ITEMS_PER_PAGE : int = 6
const ITEMS_PER_COLUMN : int = 3
var current_page : int = 0
func fill_page():
#Before filling the page we empty it
empty_page()
var start_index = current_page * ITEMS_PER_PAGE
var end_index = min(start_index + ITEMS_PER_PAGE, InteractableSingleton.pickables.size())
for i in range (start_index, end_index):
var item = InteractableSingleton.pickables[i]
#1-Generate an InventoryItem
var item_scene = preload("res://Scenes/UI/InventoryItem.tscn")
var new_inventory_item = item_scene.instantiate()
for child in new_inventory_item.get_children():
if child is TextureRect:
var texture_path = AppSettingsSingleton.images_folder_path + "UI/Pickables/" + item.interactable_id + ".png"
child.texture = load(texture_path)
elif child is RichTextLabel:
child.text = item.interactable_data.description
#Select page to be displayed on
var local_index = i - start_index
if local_index < ITEMS_PER_COLUMN:
left_page.add_child(new_inventory_item)
else:
right_page.add_child(new_inventory_item)
And this is what the print_tree_pretty() shows me in the console:
┠╴LeftPage
┃ ┠╴InventoryItem
┃ ┃ ┠╴ItemPhoto
┃ ┃ ┖╴ItemText
┃ ┃ ┖╴@VScrollBar@7
┃ ┖╴@Control@9
┃ ┠╴ItemPhoto
┃ ┖╴ItemText
┃ ┖╴@VScrollBar@8
And same for the Journal btw:
class_name JournalPageComponent
extends PageComponent
...
#region Methods
func fill_page():
#Before filling the page we empty it
empty_page()
if !journal_active_quests.is_empty():
for quest in journal_active_quests:
fill_quest_header(quest)
#generate_active_steps(quest)
...
func fill_quest_header(quest : QuestData):
#1-Generate an InventoryItem
var header_scene = preload("res://Scenes/UI/JournalHeader.tscn")
var header_item : Control = header_scene.instantiate()
for child in header_item.get_children():
if child is TextureRect:
var texture_path = AppSettingsSingleton.images_folder_path + "UI/Quests/" + quest.quest_id + ".png"
child.texture = load(texture_path)
elif child is VBoxContainer:
for item in child.get_children():
if item is Label:
if item.name.to_lower() == 'questtitlelabel':
item.text = quest.quest_title
if item.name.to_lower() == 'involvedlabel':
item.text = quest.quest_characters
if item is RichTextLabel:
item.text = quest.quest_description
left_page.add_child(header_item)
...
And this is what print_tree_pretty() shows…
┠╴LeftPage
┃ ┖╴JournalHeader
┃ ┠╴QuestPhoto
┃ ┖╴TitleBox
┃ ┠╴QuestTitleLabel
┃ ┠╴InvolvedLabel
┃ ┖╴InvolvedListLabel
┃ ┖╴@VScrollBar@6
What I tried:
-I tried setting up a custom_minimum_size for each main Controls
-I tried to force visible to true, even though they are all in visible = true in the editor
Conclusion
This is drinving me nuts. I can see that the different menus instantiate things right, and that they add the items to the proper page. But I cannot, for the love of me, understand what is happening.
Why is this not showing on screen, better yet in the Remote tab?? Like it’s in the tree, why isn’t it shown on the Remote tree???
And why does my code work for Observation but not Inventory, when the two are literally the same thing with the data changed (and the data exists é_è)
If someone could helping me out on this. UI in Godot is very new to me, so maybe there is something I don’t get about it.