Instances being set to null for reasons I don't understand

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

I’m currently trying my hand at procedural generation, which is something I’ve never really attempted before. I have a basic scene with a Node as root and a script attached to it. In the script, there is a Room class that (at the present time) is little more than a glorified Rect2. I intend to add more as time goes on, but right now I’m in a bit of a pickle.

I start in the _ready function of my Node script by calling my Room class and generating ten random rooms. I tested this before adding anything else and, indeed, all the rooms appeared in random sizes and at random positions. Once I had this working, I added some code to delete any overlapping rooms. This all goes according to plan, but when I run the game I now see nothing. No rooms. Just a gray background.

I added a dummy function where I call the _draw function on each of my Room objects, which I already know is unnecessary. I did this just so I could do some debugging. When this function is called, it throws an error because it says it’s trying to call previously removed instance:

Attempt to call function ‘_draw’ in base ‘previously freed instance’
on a null instance.

Setting up a breakpoint at the end of the _ready function shows the rooms array with all the instances intact. Their Instance IDs are present and clicking on each one shows me information about them.

Setting up a separate breakpoint at the start of the _process function, however, reveals the root of the problem: the array still has ten rooms, but they all have a value of NULL. Somewhere between _ready and _process, all the instances are being removed. I even made sure to add each instance as a child of the root node to see if that would help, but it did not. Here’s my script for reference:

extends Node
export var minimumSize := Vector2(0,0);
export var maximumSize := Vector2(0,0);
export var minimumPosition := Vector2(0,0);
export var maximumPosition := Vector2(0,0);
var rooms = [];

func _ready():
	var instance_count = 0;
	while instance_count < 10:
		var r : Room = Room.new();
		randomize();
		var x = floor(rand_range(minimumPosition.x,maximumPosition.x));
		var y = floor(rand_range(minimumPosition.y,maximumPosition.y));
		var sx = floor(rand_range(minimumSize.x, maximumSize.x));
		var sy = floor(rand_range(minimumSize.y, maximumSize.y));
		r.pos = Vector2(x,y);
		r.size = Vector2(sx,sy);
		r.setRect();
		if rooms.size() == 0:
			rooms.append(r);
			instance_count += 1;
		for room in rooms:
			if not room.checkIntersect(r.rect):
				rooms.append(r);
				add_child(r); #add to root node in attempt to preserve
				instance_count += 1;
			else:
				r.queue_free();
	pass; #added for the purposes of setting up a breakpoint here

func _process(delta):
	for room in rooms: #second breakpoint is here
		room._draw();
		
class Room:
	extends Node2D
	
	var rect : Rect2;
	var pos := Vector2(0,0);
	var size := Vector2(0,0);
	func generateRoom():
		pass;
	
	func setRect():
		rect = Rect2(pos, size);
		
	func checkIntersect(var r: Rect2) -> bool:
		if rect.intersects(r):
			return true;
		return false;
		
	func _draw():
		draw_rect(rect, Color.white);

Why are all of my instances being deleted? I don’t want them to be deleted! I never ask the engine to delete them. I do, in fact, store them in an array for the sole purpose of preserving them. What am I doing wrong here? Can someone out there lend a helping hand? Please?

I don’t know how you managed to hit those breakpoints as i’m getting into an infinite loop. That’s because you’re appending to an array while iterating over it.

for room in rooms:
            
                rooms.append(r);

That’s a bad practice in programming.

hilfazer | 2020-03-08 20:17

You’re right about that. I hadn’t noticed that I’d done that. I’ll change it, but I’m not hitting an infinite loop. Not sure why. Best to fix it, though. Thanks.

SawmillTurtle | 2020-03-08 20:24

:bust_in_silhouette: Reply From: volzhs
func _ready():
    var instance_count = 0;
    while instance_count < 10:
        # 1. Make Room "r"
        var r : Room = Room.new();
        .......
        # 2. rooms.size() == 0 so append "r" to rooms
        if rooms.size() == 0:
            rooms.append(r);
        # 3. now rooms has ["r"]
        for room in rooms:
            # 4. "room" and "r" points same instance. so it intersects.
            if not room.checkIntersect(r.rect):
                rooms.append(r);
                add_child(r); #add to root node in attempt to preserve
            else:
                # 5. finally you freed "r" in rooms, but rooms has "r" reference
                r.queue_free();