Godot Version
4.3
So what I’m trying to do is to basically create a variable that contains nodes and edit them all through this, here’s the script
var scene = 0
var child = [$Panel1,$Panel2]
func _on_buttonscene_pressed() -> void:
scene += 1
print (scene)
if scene == 0:
for panel in child:
child.visible = false
$Panel2.visible = true
elif scene == 1:
for panel in child:
child.visible = false
$Panel2.visible = true
You have a number of issues in your code:
- Whether scene equals 1 or 2, you are always showing panel 2. So all those statements have no effect.
- You cannot use a reference like $Panel in a variable assignment at the beginning of a file without using @onready
- Using an @onready variable in another @onready variable assignment would be dicey.
- The use of
child as a variable name is really confusing and should be avoided. Case in point, you are trying to operate on the array instead of the members of the array in your for/each loop.
- If you fix all the above issues, this code will only result in the second if statement being executed once.
Here’s how you can restructure your code. I assumed you wanted Panel1 to be visible in the first condition.
var scene = 0
var panels: Array
@onready var panel_1: Panel = $Panel1
@onready var panel_2: Panel = $Panel2
func _ready() -> void:
panels.append(panel_1)
panels.append(panel_2)
func _on_buttonscene_pressed() -> void:
scene += 1
print (scene)
if scene == 0:
for panel in panels:
panel.visible = false
panel_1.visible = true
elif scene == 1:
for panel in panels:
panel.visible = false
panel_2.visible = true
However, like I said in #5, this code will only work the first time you click the button. Assuming you want the button to toggle between the two:
- In the Inspector, turn the button’s toggle_mode to true by checking the box.
- Connect the `toggled(toggled_on: bool) signal of the button to your code.
- Disconnect the
pressed() signal.
- Use this code:
@onready var panel_1: Panel = $Panel1
@onready var panel_2: Panel = $Panel2
func _on_button_toggled(toggle_mode: bool) -> void:
panel_1.visible = toggle_mode
panel_2.visible = !toggle_mode
If they’re not toggling in the order you want, change the function to this:
func _on_button_toggled(toggle_mode: bool) -> void:
panel_1.visible = !toggle_mode
panel_2.visible = toggle_mode
You don’t need all that other stuff.
Aren’t you doing exactly that in your code?
This:
func _ready() -> void:
panels.append(panel_1)
panels.append(panel_2)
is no different than this:
@onready var panels:Array[Panel] = [panel_1, panel_2]
You’re right, thanks. Neither would work. It’d have to be:
var scene = 0
var panels: Array
var panel_1: Panel
var panel_2: Panel
func _ready() -> void:
panel_1 = $Panel1
panel_2 = $Panel2
panels.append(panel_1)
panels.append(panel_2)
func _on_buttonscene_pressed() -> void:
scene += 1
print (scene)
if scene == 0:
for panel in panels:
panel.visible = false
panel_1.visible = true
elif scene == 1:
for panel in panels:
panel.visible = false
panel_2.visible = true
Hello there,
You haven’t really mentioned what help you need regarding you code. I assume you wanted to share your code for improvements.
One improvement I would like to share is to only use the child list to change the visibility of panels and remove the conditional check.
func _on_buttonscene_pressed() -> void:
scene += 1
print (scene)
for panel in child:
child.visible = false # Hide all the panels
child[scene].visible = true; # Show the current panel
Further it can be improved by adding a _ready() function that already hides all the panels except the first one on startup.
func _ready() -> void:
for panel in child:
panel.visible = false; # Hide all the panels at startup
child[scene].visible = true; # Show the default panel
func _on_buttonscene_pressed() -> void:
scene += 1
print (scene)
child[scene - 1].visible = false; # Hide the previous panel
child[scene].visible = true; # Show the next panel
Please note that I code in C# and haven’t used gdScript in a long time. Kindly forgive me if I had made any mistake above.
(Not trying to badger you but) I don’t understand why the other method wouldn’t work.
@onready var panel_1: Panel = $Panel1
@onready var panel_2: Panel = $Panel2
@onready var panels:Array[Panel] = [panel_1, panel_2]
I use that method often enough that I know it seems to work, however if I am doing something that isn’t recommended, I wonder if you can explain why (or point me to some docs)?
And I apologize to the OP here for this bit of sidetrack.
It will work sometimes. But it isn’t guaranteed to work. If your file is large enough, Godot will load it faster by using multiple threads. At that point you will get a race condition and you may end up with panels being constructed by a different thread and faster than panels 1 and 2. In which case it will throw a null exception at some point because panels is an array containing two nulls.
Do not trust any onready variable or variable constructed in _ready() until the ready signal has been emitted by your object. This bites you in the behind most often when you have something in _process or _physics_process that uses one of those variables. They can get called before the node is fully constructed.
If this happens you can create a variable is_ready to track it.
var is_ready = false
func _ready() -> void:
ready.connect(_on_ready)
func _on_ready() -> void:
is_ready = true
func _physics_process(delta: float) -> void:
if not is_ready:
return
#regular stuff to execute after we are ready
I could have also used is_ready as an example above - it’s just kind of convoluted for that and in that case not good practice.