How to not loose variables when using yield in loops?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By kubaxius

I am pretty new to using coroutines, so sorry if this is obvious, but I can’t find an answer to that anywhere.
When I try to use yield in any loop, variables created before yielding are just erased.

This:

func place_random_room(roomset):
	# something
	yield(VisualServer, 'frame_post_draw')
	emit_signal("room_placement_finished")

func generate_roomset(roomset: Array, desired_number: int) -> bool:
	var placed_rooms_number = 0
	while placed_rooms_number < desired_number:
		place_random_room(roomset)
		yield(self, "room_placement_finished")
		placed_rooms_number += 1
	return true

func _ready():
	var f = generate_roomset(roomset, desired_number)

Throws error “Invalid operands ‘Nil’ and ‘int’ in operator ‘+’.” This happens even with arguments passed to the function.

#EDIT:
Ok, so it turns out that all of this happens when I use this function to check if room can be placed:

# take room walls from its GridMap and try to collide them with any
# existing shapes on ENVIROMENT collision layer
func can_room_be_placed(room: Room) -> bool:
	var space_state = get_world().direct_space_state
	# example room_shapes_and_transforms:
	# [0]: Transform
	# [1]: ConvexPolygonShape
	# [2]: Transform
	# [3]: ConvexPolygonShape
	var room_shapes_and_transforms = room.get_array_of_shapes_and_their_transforms()
	var psqp
	for item in room_shapes_and_transforms:
		if typeof(item) == TYPE_TRANSFORM:
			# if this item is a transform
			psqp = PhysicsShapeQueryParameters.new()
			psqp.transform = item
			psqp.collision_mask = 1
		else:
			# if this item is a ConvexPolygonShape
			# set collision margin
			item.margin = rooms_collision_margin
			psqp.set_shape(item)
			if space_state.collide_shape(psqp).size() > 0:
				return false
	return true

I am working with 3D, but I don’t think it is very important.

#EDIT2:
When I change

yield(VisualServer, 'frame_post_draw')

to

yield(get_tree().create_timer(0.5), "timeout")

It no longer forgets variables, but instead, after few iterations, it throws “Stack Underflow (Engine Bug)”

For now, I just rewrote the whole generator to not use yields at all. But because I still don’t understand why this works like this I am leaving this thread open.

kubaxius | 2020-03-06 03:02

:bust_in_silhouette: Reply From: njamster

I cannot reproduce the error, works fine for me. Here’s what I did:

extends Node

signal room_placement_finished

func place_random_room(roomset, i):
	print(roomset[i])
	yield(get_tree().create_timer(1.0), "timeout")
	emit_signal("room_placement_finished")

func generate_roomset(roomset: Array, desired_number: int) -> bool:
	var placed_rooms_number = 0
	while placed_rooms_number < desired_number:
		place_random_room(roomset, placed_rooms_number)
		yield(self, "room_placement_finished")
		placed_rooms_number += 1
	return true

func _ready():
	generate_roomset(["Room1", "Room2", "Room3"], 3)

The only error I got was because your generate_roomset-function isn’t returning anything even though it’s explicitly set to return a bool.

I just narrowed down my problem to be this function that checks if a room can be placed. It uses collisions and can be quite heavy for CPU, but I would have never guessed that it is the cause of the problem. I added it to the post in the edit. Also added return true to generate_roomset, but in my code, I already had that, just forgot to copy it over.

kubaxius | 2020-03-05 21:07