Why is an global array empty after switching the scene?

Godot Version

4.2

Question

I’m currently programming a [Battleship (game) - Wikipedia]
In my game I’m having a class Ship. There is also a Player class that contains an array of Ship. In one scene (“add_ships.tscn”) the player is adding the ships to the grid. Everything is stored in an autoload called Globals.
In “add_ships.gd” it looks like this:

print("Current Player: ", Globals.current_player.p_name)
Globals.current_player.p_ships.append(carrier)
... # repeat for all ships
print(Globals.current_player.p_ships)
	for i in Globals.current_player.p_ships:
		print("Player Ship: ", i.ship_type)

So, this works all as expected and the for-loop at the end prints correctly all ship types. But after the array p_ships has been created I’m switching the scene to the actual game scene. Here I’m having also a grid and want to add all the ships stored previously in a function that is called by _ready():

func build_players_grid():
	if Globals.current_player != null:
		print(Globals.current_player.p_name)
		if Globals.current_player.p_ships.size() > 0:
			for i in Globals.current_player.p_ships:
				print("Player Ship: ", i.ship_type) # ERROR: Invalid get index 'ship_type' (on base: 'previously freed')

Globals.current_player.p_name is “Player 1”, so not null. The array has got the correct size, so it is > 0 (I’m having 10 ships total). But when the game crashes, I see in debug mode, that all 10 items of Globals.current_player.p_ships are actually null, although the same code worked in the other scene. I want to store exact copies of the ships, including position, rotation, type and so on.
How can I make the objects live from one scene to the other (I guess that “previously freed” means that the objects have been deleted in memory while the scene has changed). How can I prevent this?

Thanks a lot for helping me out!

The easy way: Globals.current_player.p_ships.append(carrier.duplicate())
This, of course, makes a copy of the object, which is not ideal, but also not terrible for a simple game.
If you want to do it in a cleaner way, you’d have to redesign your game so you have the carrier instances cached on global and only use references in scenes, but that’s more complex than it’s worth for battleship.

1 Like

Thanks a lot. I’m now having the issue that it is not the exact position of the ship as it was originally in add_ships.tscn, but that might be because I did some re-positioning magic on the grid. But at least the array is not empty any longer and the first ship is showing up in the game itself. So, thanks for your reply.

I tried it out and found that some of the class variables of a ship are still empty or set to the default value after using append(ship.duplicate()).
It’s now working if I do the following, but I thought that it should be an exact copy…:

Globals.current_player.p_ships.append(battleship.duplicate())
Globals.current_player.p_ships[0].angle = battleship.angle
Globals.current_player.p_ships[0].center_field = battleship.center_field

Any idea what might be the issue here?

Because there are many ways of duplicating a Node depending on what “a copy” means in the context you need:
https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-duplicate
https://docs.godotengine.org/en/stable/classes/class_node.html#enum-node-duplicateflags

Remember you can sum flags like these to combine them.

1 Like

Ah, ok. I didn’t know that. And yes, my Ship class uses an _init() method, so your answer was again very helpful. Thanks again!

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