How can I delete a node instance from an array?

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

Hello!

I already almost finished my memory game in Godot, but I can’t delete a node instance from an array without this error:
Invalid get index ‘24’ (on base: ‘Array’).
I have created this card nodes with code and an array contains these:

var c=card.instance()
(...)
cards.append(c)

Then, this piece of code checks whether need to delete two cards or not:

func _process(delta):
    if (Input.is_mouse_button_pressed(BUTTON_LEFT)):
        var mousex=get_global_mouse_position().x
        var mousey=get_global_mouse_position().y
        for j in range(0,cards.size()):
            if mousex>cards[j].position.x and mousex<cards[j].position.x+cardwidth and mousey>cards[j].position.y and mousey<cards[j].position.y+cardheight:    
                var k: Sprite=cards[j].get_node("cardsprite")
                if card1==null:
                    card1=cards[j]
                    k.texture=sprchanger(cards[j].itssprite)
                elif card2==null and card1!=cards[j]:
                    card2=cards[j]
                    k.texture=sprchanger(cards[j].itssprite)
                    if card1.itssprite==card2.itssprite:
                        for m in range(0,cards.size()):
                            if cards[m]==card1:
                                card1.queue_free()
                                card1=null
                            else:
                                card2.queue_free()
                                card2=null
                            cards.remove(m)
                            break
                    else:
                        card1.get_node("cardsprite").texture=load("res://cardbackground.png")
                        card2.get_node("cardsprite").texture=load("res://cardbackground.png")
                        card1=null
                        card2=null

But something is wrong, because I get that error message at mouse and card position check line when my code uses the queue_free() and remove(m) commands.
Maybe I overcomplicated my code at deleting lines? But then which is the best method to remove a node instance from an array?

I think your cards.remove and break in the for loop don’t make sense. Few comments in code would help when you have code this complex.

wyattb | 2021-05-22 11:33

Hello Wyattb!

Should I use cards.remove after queue_free(), or one of the two is unnecessary?

Tomi | 2021-05-23 08:49

:bust_in_silhouette: Reply From: Afely

I think the problem isn’t the deleting of cards, but your loop itself. You used range(0,cards.size()) for the loop, but Array.size() returns a value 1 higher than the index of the last element in the array, because it outputs the number of items in the array (including the item at index 0). So, you should change your loop to for x in range(0, cards.size() - 1).
But there’s a better way to do this: you can just write for card in cards. This will loop through every element of the array, and inside the loop card is a reference to the item the loop is currently on.

Now I rebuild the structure of my program a bit.

func _process(delta):
	if (Input.is_mouse_button_pressed(BUTTON_LEFT) and can_click==true):
		var mousex=get_global_mouse_position().x
		var mousey=get_global_mouse_position().y
		for j in cards.size(): #Loop on all card instances:
			if mousex>cards[j].position.x and mousex<cards[j].position.x+cardwidth and mousey>cards[j].position.y and mousey<cards[j].position.y+cardheight:	
				var k: Sprite=cards[j].get_node("cardsprite")
				if card1==null:
					card1=cards[j]
					k.texture=sprchanger(cards[j].itssprite) #Change its sprite from background image to another
					card1index=j
				elif card2==null and card1!=cards[j]:
					card2=cards[j]
					k.texture=sprchanger(cards[j].itssprite)
					card2index=j
					if card1.itssprite==card2.itssprite:
						can_click=false
						cards.remove(card1index) #Remove cards from array if matching
						cards.remove(card2index)
						card1.queue_free() #and delete them in this case.
						card2.queue_free()
						card1=null
						card2=null
					else:
						card1.get_node("cardsprite").texture=load("res://cardbackground.png")
						card2.get_node("cardsprite").texture=load("res://cardbackground.png")
						card1=null
						card2=null

As you can see, I use a new boolean variable named “can_click” to check mouse button pressing. This caused me half-success, because after I find a pair of cards, the program doesn’t throw error message but I can’t click anymore.
So, the problem maybe not the loop itself, but the mouse button pressing, that is too fast.
Maybe should I check cards after the player releases the mouse button? But how? I tried these:

func _process(delta):
	if (Input.is_mouse_button_pressed(BUTTON_LEFT) and can_click==true):
	(...)
	elif (!(Input.is_mouse_button_pressed(BUTTON_LEFT)) and can_click==false):
		can_click=true

and:

elif Input.is_action_just_released("click"):
		can_click=true

but these didn’t work. How can I solve this program with better control over the mouse?

Tomi | 2021-05-23 08:45

First off, your for loops still aren’t right. Read this documentation for a better explanation and some examples of how to use them.
I don’t know if the incorrect for loops are related to your mouse problem, but it’s best to fix them first anyways.

Afely | 2021-05-23 09:01

I tried set the j loop so:
for j in cards:
but in this case I got the following error message after a click:
Invalid get index ‘[Node2D:1376]’ (on base: ‘Array’).
Then in case of
for j in cards.size()-1:
the last card is not clickable.

Tomi | 2021-05-23 12:47