Game breaks after setting up the GameOverRetry button action

I am 60% through the " Master Mobile Game Development with Godot 4: From Concept to App Stores" course. My jump game was working well until I implemented the code for the GameOverRetry button (Putting it all Together/Game Over Retry lecture ). I now receive the following errors. If I change the variable name from scale to pSpriteScale, the first error is resolved.

Ok, I see three things I readily recognize. Yay!

The warning about “shadowing” means you have two variables called scale declared in different scopes on the same script. While you can sometimes get away with that, it’s a very bad idea not to fix it by changing one (or both, if that seems more sensible) of the names, because it’s ambiguous otherwise. Sounds like you already figured that one out, but that’s what it means under the hood =)

The warning about await basically means it’s being used on something that happens “instantly” and doesn’t need to be waited for. It’s most likely because you’re not specifying that you’re awaiting the timeout callable on the timer you’re creating. As it is now, you’re creating a timer and it influences nothing. Pretty sure what you want there is

  • await get_tree().create_timer(0.75).timeout

Finally, invalid get index on a “previously freed” node means you’re trying to get a value from something that you’ve called queue_free() on, or has otherwise been removed from the scene tree (neither of which, btw, mean that the node is completely deleted at this time). In this case, most likely what’s happening is you’re reloading the scene, and your reference to the player scene is not being updated to reflect [the new player instance sitting on the new level instance], so it’s trying to influence the old, dead player object and it can’t.

To fix that, could you show your code in game.reset_game()?

Wow! Thanks for those explanations. My reset_game code is below:

func reset_game():
	groundSprite.visible = false
	if player != null:
		player = null
		levelGenerator.player = null
	if camera != null:
		camera = null

Cheers =)

I have an approach for you, but I’m going to wait and see what @OboShape suggests first, as it will probably be easier to do XD

absolutely on the money and alot quicker than i am hehe, nice one :slight_smile:

yea, its sort of like an override in other languages, since the script inherits all its base class properties and methods, were basically redefining one that already exists.

if you Ctrl + Click on the Node2D at the top, where it says extends. that will bring up the docs and you can see there in the properties section, theres already a property for scale defined as part of Node2Ds functionality.

yea spot on again, await needs a callback or an event notifer to wait for completion, so the timeout provides that to the await call to allow it to say ’ ah, ok your done, ill carry on’

@BH67 go for it, im still in the middle of the UI lectures and not quite got that far yet :slight_smile:

1 Like

RE reset_game():

Interesting. It looks like this function tries to reset important references manually.

At first glance, this looks like it should work fine because it’s setting the references to null, but the problem is, queue_free() doesn’t delete stuff immediately; instead, it does exactly what its name suggests: it marks stuff for deletion, and that stuff gets deleted… basically whenever your program feels like it, lol XD

Behind the scenes, based on what I’ve observed in my own projects, what’s almost certainly happening with your broken reference is that you’re queuing the old player (and whatever else - could just as easily happen to your camera too) for deletion, and then you’re presumably re-instancing the level scene in levelGenerator.reset_level(). The problem is, when that new level is instanced, it builds a reference to a player object, but your old player object is still sitting in the queue and hasn’t been deleted yet, so your reference ends up pointing to that instead of the new player object (get_first_in_group() will do this, as will get_nodes_in_group() because it’s the oldest child, and if you were to check the Remote tab, your new player object would have a nonsense name because “Player” is already taken, so it doesn’t matter exactly how you’re filling your reference). Then, by the time you go to act on it with physics or whatever else, it’s finally actually been deleted. There’s a chance this isn’t what’s happening because it would depend on exactly what levelGenerator.reset_level() does, but your error is consistent with the ones I’ve had.

The approach I’ve used to fix this (which works on the entire level scene, but you can do it for just the player or anything else) is to do the following:

  • Ensure the level instance is not null, then call queue_free(), exactly as you’ve done.
  • In the frame’s idle time after _process() and similar functions are done for the frame (call_deferred()), decouple [the level to be destroyed] from the scene tree (remove_child()) and don’t do anything else until that’s finished (await)
    • await call_deferred("remove_child", old_level_instance)
  • Instance the new scene and add it as a child to the scene tree
  • Set old_level_instance to new_level_instance so that the next time you perform all of this, the function knows what to delete. Like a conveyor belt!

It looks a bit nasty, but it’s actually pretty intuitive once you understand it. It’s basically just putting all the old stuff to one side so that its pedantic deletion procedure doesn’t affect the construction of your new stuff.

If you need further help implementing this, just ask (this was a good opportunity to note an explanation out, as I implemented this just today to fix a bug with my original solution).

Of course! Scale! I hadn’t even considered that this would happen if you try to re-declare an inherited variable. Great that you pointed this out :+1:

1 Like

Thanks guys. I am new to Godot, so not aware of how things work behind the scenes. I’ll try to implement the fix and see how I go. I’ll probably be back :stuck_out_tongue:


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

Privacy & Terms