var only works within func and not outside it

Godot Version

Godot v4.6 stable win64

Question

Hello! I’m trying to create an array of 8 buttons that get shuffled and then popped front. This is because I don’t want there to be any repeated calls of any button in the array.

This is the code so far, but running this gives the following error:

Invalid access to property or key ‘pressed’ on a base object of type ‘Nil’.

@onready var breaker_switch_1: Button = $breakerSwitch1
#...listed switch_1 through 8

var breakers = [
	breaker_switch_1,
	#...listed switch_1 through 8
	]

var flippedCount = 0
var flipped = breakers.pop_front()

func _ready() -> void:
	breakers.shuffle()

#...

func k_flipping():
	flipped.button_pressed = true # <--- point of error
	
	if flippedCount == 4:
		game_control.lights_flicker()
	elif flippedCount >= 9:
		game_control.lights_off()
	else:
		game_control.lights_on()

However, if I were to place the var breakers array into the function, shown below, it allows flipped.button_pressed = true. But of course it would not be shuffled, as it would be re-shuffling the array every time the function is called, allowing duplicates.

func k_flipping():
	var breakers = [
		breaker_switch_1,
		#...listed switch_1 through 8
		]
	breakers.shuffle() # moved out of _ready()
	var flipped = breakers.pop_front()
	flipped.button_pressed = true
	
	if flippedCount == 4:
		game_control.lights_flicker()
	elif flippedCount >= 9:
		game_control.lights_off()
		breakers.shuffle()
	else:
		game_control.lights_on()

Why does this happen? (I would love to learn). Additionally, how can I resolve this issue? I hope my query makes sense.
Many thanks!

What if you changed your flipped var to be this?

The “normal” variables are processed first. @onready variables are processed only when node has entered the scene tree. When you add breaker_switch_1 to the breakers array, a null is added because breaker_switch_1 is still null. Initialize the breakers array in _ready function:

var breakers: Array[Button]
var flipped: Button

func _ready() -> void:
	breakers.push_back($switch1)
	breakers.push_back($switch2)
	breakers.shuffle()
	flipped = breakers.pop_front()

Also, in your original code you are always popping the same first element from the breakers array as the var flipped = breakers.pop_front() is executed before the shuffle is done in the _ready function.

1 Like

Sadly it just gives me this error Expected end of statement after variable declaration, found “:” instead. when placed within the func k_flipping(). When placed above the code, it says Property with inline code must go to an indented block.

Thank you for your explanation! I managed to do as you suggested and I was successful. However, when implementing a line of code that allows me to reset it with flippedCount = 0 at the push of a button, this error occurs:

Invalid assignment of property or key ‘button_pressed’ with value of type ‘bool’ on a base object of type ‘Nil’.

## breaker_scene.gd

func k_flipping():
		flipped = breakers.pop_front() # moved here because this function is where I would want it called
		flipped.button_pressed = true # <--- point of error
	
	if flippedCount == 4:
		game_control.lights_flicker()
	elif flippedCount == 9:
		game_control.lights_off()
		breakers.shuffle()
		block_panel.visible = true
	elif flippedCount == 0:
		game_control.lights_on()

For additional context, k_flipping() is called through a separate script, shown below.

## k_AI.gd

func move():
	current_kitDis = kitDisruption.pick_random()
	
	if current_kitDis == "Door Peek":
		# ...
		if get_node("../BreakerScene").flippedCount < 9:
			breaker_scene.k_flipping()

Is it perhaps still not being initialized and returning as null? Are the popped front arrays perhaps no longer existent after they’ve been popped? Thank you for your time.

If the array is empty, pop_front() will return null. So check for that.
You can also print the array at the start of k_flipping() to see what’s inside.

1 Like

You’re right, it was returning as <null> when printed. Looking at the documentation, I’m unsure the best course of action for filling it again with the original breakers array once the reset button with flippedCount = 0 is pressed. I tried breakers.fill(breakers) in the reset button though as expected it did not work.

Why do you need to pop the array? You can simply shift the elements indefinitely. Or if you want to re-shuffle then put the initialization and shuffling into a dedicated function and call this function on first initialization and whenever the array gets emptied.

3 Likes

I’m relatively new to Godot and pop_front() was one of the first few Methods I learned how to use in Arrays. Shifting is a great idea! Though I can’t seem to find a shifting method that does not return it as null, or perhaps I’m not finding a good way to utilize appends?

Regardless, I took your suggestion in making a dedicated function and calling it on _ready() and it works! Further resulting bugs and errors are no longer on the topic of this original thread so I will say this is solved. Thank you all!

You don’t need to change the array. Just use an index variable to point the current breaker. Something like this:

var breakers: Array[Button]
var flipped: Button
var current_breaker: int = -1

func k_flipping():
	current_breaker += 1
	flipped = breakers[current_breaker]
	flipped.button_pressed = true
	
	if current_breaker == 0:
		...
	elif current_breaker == 4:
		...	
	elif current_breaker == breakers.size() - 1:
		# restart with a new shuffle
		current_breaker = -1
		breakers.shuffle()
1 Like

Thank you for this suggestion! This was very helpful. I will keep this method in mind for future projects. It actually did help with a different problem unrelated to this current thread’s topic.

To shift: pop front and then push the popped thing to back.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.