AddChild... and Godot hangs

Godot Version

4.5.1

Question

Game was working fine, made some changes, elsewhere, and now it hangs. Literally on stating the game I just get the Godot logo and then nothing. Basically have to force quit the game and sometimes even the editor.

I did a lot of tracing/commenting and determined that is is hanging on:

addchild(instance)

Researching and the only advice was to call deferred. I changed to:

call_deferred("add_child",instance)

But same issue. Game just hangs. It was working great all along so I have no idea why this decided to stop working or how refactoring a bunch of code elsewhere (other scripts) would somehow blow this line up. If I comment out the addchild line everything goes back to working normally. I’ve even changed almost all process, physics and ready to pass but that did not make a difference.

Show the instantiated scene structure. What’s happening inside _ready() functions in scene’s scripts? Also, who is calling that add_child()?

2 Likes

This is what calls the add child

func create_visitor():
	var instance = scene.instantiate()
	instance.position = Vector3(x,0,y)
	instance.name = "player"+str(global.visitor_number)
	global.visitor_number += 1
	call_deferred("add_child",instance)

And this is what calls the create. If I comment out the addchild line above, this all works perfectly, and then just stopped with no changes to this code or to the structure.


func _ready() -> void:
	for n in 5:
		create_visitor()
	pass

With additional testing I’ve now determined that if the agent is placed anywhere other than 0,0,0 it freezes. Any other transform starting point crashes it. So the add child is actually working fine. However it is still freezing… when the agents are created now (at 0,0,0) they start moving to their target paths and after a handful of seconds everything just freezes again.

What happens if you only create one instead of five? What happens when you remove navigation agent node? Where’s the code that sets targets? Provide more information.

Is that ā€˜y’ correct..? Should it not be ā€˜z’ instead (Vector3(x,0,z)…). Just a thought.

Y is correct. I refer to it as X and Y (2D array) knowing that I put the Y in the Z spot for Godot.

1 Like

Tried almost all of that already. After another lengthy comment/uncomment session tracing through the code literally line by line I found the offending line which is in a whole separate area of code that was never touched in my refactoring. It makes no sense how this line caused a freeze and even more confusing when I replace the IF conditions with a PRINT to see what the values actually are and everything looks exactly as expected. There is no reason I can think of that would cause a freeze here. (my 2D array is x/y while the 3D coordinates are x,0,z)

The offending line is:

if move_to_x != int(global_position.x) and move_to_y != int(global_position.z):

which is part of

func find_next_square():
	var found_good_spot = false
	while not found_good_spot:
		move_to_x = rng.randi_range(-24,24)
		move_to_y = rng.randi_range(-24,24)
		target = Vector3(move_to_x,0,move_to_y)
		if global.path_array.has(str(move_to_x) + "," + str(move_to_y)):
			if move_to_x != int(global_position.x) and move_to_y != int(global_position.z):
				set_target(target)
				found_good_spot = true

If I replace the ā€œif move_to_xā€¦ā€ line with

print (str(move_to_x) + " - " + str(int(global_position.x)) + " - " + str(move_to_y) + " - " + str(int(global_position.z)))

The output looks like this:

20 - 21 - 12 - 12
18 - 21 - 12 - 11
22 - 18 - 12 - 11
18 - 18 - 12 - 12
22 - 18 - 12 - 12
22 - 18 - 12 - 12
22 - 21 - 12 - 12
22 - 21 - 12 - 12
18 - 21 - 12 - 12
18 - 20 - 12 - 12
— Debugging process stopped —

Which is legit as only a few coordinates are valid to move to initially. It should be verifying the first two don’t match and the second two don’t match. If a match is found, then try to find new coordinates to move to. It’s worked perfectly for weeks. I guess something I did somewhere messed it up but in my decades of coding experience I’ve never been stumped this bad.

But at least the issue is found and I can work around it.

I’d first deal with that one

In what sense? I create an array of valid points that can be moved to as the player is placing path objects. Since most of the playing field won’t be paths I’m creating a simple array of the valid coordinates. Easy enough to append to and remove from as paths are created or destroyed.

Why conversion to string? How do you populate this array?
Also, shouldn’t that be or instead of and?

Initially there are only 3 (for now, may change) starting paths.

path_array.append("22,12")
path_array.append("20,12")
path_array.append("18,12")

And when a new path tile is placed

global.path_array.append(str(int(place_position.x)) + "," + str(int(place_position.z)))

The other option would be a 2D array however that only works if I stick with integers. I haven’t decided yet if I’ll allow for free form placement which would make the 2D array approach unusable. Instead it’s just a list. It may be slight less efficient to ā€œ.hasā€ the list as it stands now but if/when I allow decimal placement I feel this approach is the best I’ve been able to come up with.

Converting floating point numbers to strings and back is a really bad idea.

As to the and/or… yeah, probably will get changed to OR but with AND it causes the visitors to walk more than just essentially a straight line that could occur with OR.

Why?

I’m asking seriously. I only convert one way, not back. And even so, why is it bad to convert/cast/compare this way?

How do you convert only one way? Do you not convert to strings when populating the array?

It’s a bad idea because of floating point precision and rounding errors. The ā€œsameā€ number might have a slightly different value, depending on the way it was calculated, resulting in different strings. You should never do it.

Agreed if I went out to lots of digits. I don’t need super precise placement so rounding to a couple of decimal points would work fine for my application.

Correct. Everything is converted to a string and compared that way. But at no point do I convert the string back into integer (or decimal) to use anywhere. It’s literally just a ā€œreference listā€ of allowable locations that I check against.

Just don’t do it. It’s a very bad idea that will bite you in the butt sooner or later. For the same reason you don’t use operator == to compare floating point numbers. Consider this:

var foo = Vector2(0.0, 35.0) - Vector2(0.0, 34.9)
print(foo == Vector2(0.0, 0.1))

It should print true right? But try to run it and see what happens.

1 Like

As expected when extrapolated way out floating point gets squirrely.

First line is the result of foo

(0.0, 0.099998)
false

I do understand this ā€œissueā€ and rarely work to any level of precision that would need this. Usually I end up rounding to a few decimal places.

Interesting though in my day job, my company make highly precise measuring equipment. We’re talking the kind of equipment that measures so small it allows chip makers to use it in lining up the etchings on their chips. Currently the top of the line devices we offer are now measuring into pico meters. In that area we really do need ALL the decimal places. :slight_smile:

Regardless though, thanks for the feedback and insights. It’s always good to review.

It’s not about decimal places or precision in absolute sense. It’s the impossibility of some decimal numbers to be represented accurately in binary notation.
Here’s another example. Note that this is not some super-precision. It happens with only one decimal place:

var foo = Vector2(0.0, 50.0) - Vector2(0.0, 49.1)
var bar = Vector2(0.0, 50.0 - 49.1)
print(foo)
print(bar)

And here’s a particularly funky one:

var foo = Vector2(0.0, 1.0) - Vector2(0.0, 0.9)
var bar = Vector2(0.0, 0.1)
print(foo)
print(bar)
print(foo == bar)

Converting numbers to strings is in general an inefficient and bug prone way to handle things. Try to come up with a solution that doesn’t do it.

3 Likes

That one is amazing!

(0.0, 0.1)
(0.0, 0.1)
false

I suppose that is why we have:

	print(foo.is_equal_approx(bar))

true

But that example you gave is simpy brilliant!

It reminds me of how 0.999999999… is exactly equal to 1 mathematically. I know, you think ā€œbut it never gets to oneā€, but it is simple to prove mathematically. They are identical. (I know this is not the same thing, it just reminds me of it)

1 Like