Failure at checking the array of child nodes of an object

Godot Version

3.5

Question

My code is failing when it comes to check if there exist child nodes this is my code
I’m relative new in this game engine so i don’t really know how to solve it

extends Node2D

export var maxcartas: int = 4
export var carta_scene: PackedScene # La escena de la carta a instanciar
var o1 = false
var o2 = false
var o3 = false
var o4 = false
var children = get_children()
var carta_1: Node2D
var carta_2: Node2D
var carta_3: Node2D
var carta_4: Node2D
var posiciones = [
Vector2(100, 100),
Vector2(160, 100),
Vector2(300, 100),
Vector2(360, 100)
]

var velocidad_movimiento = 200 # Velocidad en píxeles por segundo

func _ready():
_crear_cartas()
_distribuir_cartas()

func _crear_cartas():
# Asegúrate de que no haya cartas previas almacenadas
carta_1 = null
carta_2 = null
carta_3 = null
carta_4 = null

for i in range(maxcartas):
	var nueva_carta = carta_scene.instance()
	add_child(nueva_carta)
	_escala_carta(nueva_carta)
	_ubicar_carta(nueva_carta, i)
	
	# Asignamos la carta a la variable correspondiente
	if i == 0:
		carta_1 = nueva_carta
	elif i == 1:
		carta_2 = nueva_carta
	elif i == 2:
		carta_3 = nueva_carta
	elif i == 3:
		carta_4 = nueva_carta

func _escala_carta(carta):
carta.scale = Vector2(0.5, 0.5) # Escala las cartas a la mitad

func _ubicar_carta(carta, indice):
if indice < posiciones.size():
carta.position = posiciones[indice]

func _distribuir_cartas():
# Aquí puedes ajustar cómo distribuir las cartas si es necesario
pass

func repartir_carta():
if get_child_count() > 0:
var carta = get_child(0)
remove_child(carta)
carta.position = Vector2(rand_range(0, 300), rand_range(0, 300))
add_child(carta)
else:
print(“No hay más cartas en el mazo.”)

func _physics_process(delta):
_check_cartas()

func _check_cartas():
var children = get_children()
print(children)
var num_hijos = get_child_count()

# Reiniciamos los indicadores
o1 = false
o2 = false
o3 = false
o4 = false

# Revisamos la existencia de cada carta según su posición
if num_hijos >= 1 and not children[0] == null:
	o1 = true
if num_hijos >= 2 and not children[1] == null:
	o2 = true
if num_hijos >= 3 and not children[2] == null:
	o3 = true
if num_hijos >= 4 and not children[3] == null:
	o4 = true

move_cartas(children)

func move_cartas(hijos):
var tween = create_tween()

if not o2 and hijos.size() > 0:
	tween.tween_property(hijos[0], "position", posiciones[1], 1.0)
if not o3 and hijos.size() > 1:
	tween.tween_property(hijos[1], "position", posiciones[2], 1.0)
if not o4 and hijos.size() > 2:
	tween.tween_property(hijos[2], "position", posiciones[3], 1.0)

tween.play()

that’s a lot of code but please preformat it before posting as i can’t make sense of the code indentation.
Also please post the error as it should say what the problem is and maybe a line number

In addition this is the code of the cards
extends StaticBody2D

Variables existentes

var posiciondeseleccion = Vector2(0, -30)
var posicionoriginal = Vector2()
var sehapulsado = false

onready var ScaleTween = $ScaleTween
onready var tween = $Tween
onready var shader_material : ShaderMaterial = material
onready var Rt = $RotationTween
onready var timer = $Timer
onready var st = $T

var tamano_inicial = Vector2(1, 1)
var tamano_cambio = Vector2()
var tamano_final = Vector2(1, 1)

var rotando = false
var harotado = false

signal doble_click_detectado

func _ready():
posicionoriginal = position
shader_material.set_shader_param(“edge_threshold”, 1.0)

tamano_inicial = self.scale
tamano_final = self.scale * 0
tamano_cambio = self.scale * 1.25

ScaleTween.connect("tween_completed", self, "_on_scale_tween_completed")
tween.connect("tween_completed", self, "_on_tween_completed")
Rt.connect("tween_completed", self, "_on_rotacion_completada")

timer.wait_time = 0.1 # Cada 0.1 segundos
timer.one_shot = false  # Para que se repita
timer.start()
timer.connect("timeout", self, "_on_Timer_timeout")

func _input_event(viewport, event, shape_idx):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and event.pressed:
_manejar_click_izquierdo()
elif event.button_index == BUTTON_RIGHT and event.pressed:
_manejar_click_derecho()

func _manejar_click_izquierdo():
if not sehapulsado:
_mover_a_posicion_deseleccion()
else:
_mover_a_posicion_original()

func _manejar_click_derecho():
if position == posicionoriginal + posiciondeseleccion:
emit_signal(“doble_click_detectado”)
cambiar_tamano_final()
print(“¡Doble clic detectado!”)
else:
print(“No se puede emitir señal de doble clic porque no está en la posición de selección.”)

func _mover_a_posicion_deseleccion():
tween.interpolate_property(self, “position”, position, posicionoriginal + posiciondeseleccion, 0.5, Tween.TRANS_SINE, Tween.EASE_IN)
tween.start()

_aplicar_shader_de_borde()
sehapulsado = true
cambiar_tamano()

func _mover_a_posicion_original():
tween.interpolate_property(self, “position”, position, posicionoriginal, 0.7, Tween.TRANS_ELASTIC, Tween.EASE_IN_OUT)
tween.start()

ScaleTween.interpolate_property(self, "scale", tamano_cambio, tamano_inicial, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
ScaleTween.start()

_quitar_shader_de_borde()
sehapulsado = false

func _aplicar_shader_de_borde():
if shader_material and shader_material is ShaderMaterial:
shader_material.set_shader_param(“edge_threshold”, 0.0)
else:
print(“El material no es un ShaderMaterial o no está definido.”)

func _quitar_shader_de_borde():
if shader_material and shader_material is ShaderMaterial:
shader_material.set_shader_param(“edge_threshold”, 1.0)
else:
print(“El material no es un ShaderMaterial o no está definido.”)

func rotar():
if not rotando: # Solo iniciar si no está rotando
rotando = true
Rt.interpolate_property(self, “rotation”, rotation, 0.05, 2, Tween.TRANS_SINE, Tween.EASE_IN)
Rt.start()

func _on_rotacion_completada(object = null, key = “”):
if harotado: # Solo se ejecuta una vez que la rotación inicial ha terminado
harotado = false
rotando = false
else: # La primera vez que se completa la rotación
harotado = true
Rt.interpolate_property(self, “rotation”, self.rotation, -0.05, 2, Tween.TRANS_QUAD, Tween.EASE_IN)
Rt.start()

func _on_Timer_timeout():
if not rotando and not harotado:
rotar()
elif harotado and not rotando: # Este bloque ahora está claro cuando se debe llamar a la segunda rotación
rotar()

func cambiar_tamano():
ScaleTween.interpolate_property(self, “scale”, tamano_inicial, tamano_cambio, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
ScaleTween.start()

func cambiar_tamano_final():
Rt.stop_all()
self.rotation_degrees = 0
ScaleTween.interpolate_property(self, “scale”, tamano_inicial, tamano_final, 1.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
ScaleTween.start()

# Ajuste: conecta el temporizador para destruir la carta
st.wait_time = 0.8
st.one_shot = true  # Asegúrate de que sea una única vez
st.start()
st.connect("timeout", self, "Destroy")

func Destroy():
# Emite una señal antes de destruir para actualizar el conteo de cartas
emit_signal(“carta_destruida”)

# Verifica si el nodo está en un árbol de nodos
if get_parent() != null:
	# Si está en un árbol, lo elimina
	get_parent().remove_child(self)
else:
	# Si no está en un árbol, imprime un mensaje
	print("La carta no está en ningún árbol de nodos.")

Método adicional para deslizar la carta a una nueva posición

func deslizar_a_posicion(nueva_posicion, velocidad):
# Define el nuevo destino
var distancia = nueva_posicion.distance_to(position)
var tiempo = distancia / velocidad
tween.interpolate_property(self, “position”, position, nueva_posicion, tiempo, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
func _process(delta):
if get_parent() == null:
print(“La carta no está en ningún árbol de nodos.”)

how can preformat it?

it’s this button here
grafik

it simply doesn’t work there is no code error
when i destroy the card th code does anything

extends Node2D

export var maxcartas: int = 4
export var carta_scene: PackedScene  # La escena de la carta a instanciar

var posiciones = [
	Vector2(100, 100),
	Vector2(160, 100),
	Vector2(300, 100),
	Vector2(360, 100)
]

var velocidad_movimiento = 200  # Velocidad en píxeles por segundo

func _ready():
	_crear_cartas()
	_distribuir_cartas()

func _crear_cartas():
	# Limpia las cartas existentes antes de crear nuevas
	for child in get_children():
		remove_child(child)
		child.queue_free()

	for i in range(maxcartas):
		var nueva_carta = carta_scene.instance()
		add_child(nueva_carta)
		_escala_carta(nueva_carta)
		_ubicar_carta(nueva_carta, i)

func _escala_carta(carta):
	carta.scale = Vector2(0.5, 0.5)  # Escala las cartas a la mitad

func _ubicar_carta(carta, indice):
	if indice < posiciones.size():
		carta.position = posiciones[indice]

func _distribuir_cartas():
	_check_cartas()

func _physics_process(delta):
	_check_cartas()

func _check_cartas():
	var children = get_children()
	var num_hijos = children.size()

	# Reiniciamos los indicadores
	var o1 = num_hijos > 0 and children[0] != null
	var o2 = num_hijos > 1 and children[1] != null
	var o3 = num_hijos > 2 and children[2] != null
	var o4 = num_hijos > 3 and children[3] != null

	move_cartas(children)

func move_cartas(hijos):
	for i in range(hijos.size()):
		var target_position = posiciones[i]
		if hijos[i].position != target_position:
			var tween = create_tween()
			tween.tween_property(hijos[i], "position", target_position, 1.0)
			tween.play()

func destruir_carta(indice):
	if indice < get_child_count():
		var carta = get_child(indice)
		remove_child(carta)
		carta.queue_free()
		_distribuir_cartas()

and this is the card code

extends StaticBody2D

# Variables existentes
var posiciondeseleccion = Vector2(0, -30)
var posicionoriginal = Vector2()
var sehapulsado = false

onready var ScaleTween = $ScaleTween
onready var tween = $Tween
onready var shader_material : ShaderMaterial = material
onready var Rt = $RotationTween
onready var timer = $Timer
onready var st = $T

var tamano_inicial = Vector2(1, 1)
var tamano_cambio = Vector2()
var tamano_final = Vector2(1, 1)

var rotando = false
var harotado = false

signal doble_click_detectado

func _ready():
	posicionoriginal = position
	shader_material.set_shader_param("edge_threshold", 1.0)


	tamano_inicial = self.scale
	tamano_final = self.scale * 0
	tamano_cambio = self.scale * 1.25

	ScaleTween.connect("tween_completed", self, "_on_scale_tween_completed")
	tween.connect("tween_completed", self, "_on_tween_completed")
	Rt.connect("tween_completed", self, "_on_rotacion_completada")
	
	timer.wait_time = 0.1 # Cada 0.1 segundos
	timer.one_shot = false  # Para que se repita
	timer.start()
	timer.connect("timeout", self, "_on_Timer_timeout")


func _input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton:
		if event.button_index == BUTTON_LEFT and event.pressed:
			_manejar_click_izquierdo()
		elif event.button_index == BUTTON_RIGHT and event.pressed:
			_manejar_click_derecho()

func _manejar_click_izquierdo():
	if not sehapulsado:
		_mover_a_posicion_deseleccion()
	else:
		_mover_a_posicion_original()

func _manejar_click_derecho():
	if position == posicionoriginal + posiciondeseleccion:
		emit_signal("doble_click_detectado")
		cambiar_tamano_final()
		print("¡Doble clic detectado!")
	else:
		print("No se puede emitir señal de doble clic porque no está en la posición de selección.")

func _mover_a_posicion_deseleccion():
	tween.interpolate_property(self, "position", position, posicionoriginal + posiciondeseleccion, 0.5, Tween.TRANS_SINE, Tween.EASE_IN)
	tween.start()

	_aplicar_shader_de_borde()
	sehapulsado = true
	cambiar_tamano()

func _mover_a_posicion_original():
	tween.interpolate_property(self, "position", position, posicionoriginal, 0.7, Tween.TRANS_ELASTIC, Tween.EASE_IN_OUT)
	tween.start()

	ScaleTween.interpolate_property(self, "scale", tamano_cambio, tamano_inicial, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()

	_quitar_shader_de_borde()
	sehapulsado = false

func _aplicar_shader_de_borde():
	if shader_material and shader_material is ShaderMaterial:
		shader_material.set_shader_param("edge_threshold", 0.0)
	else:
		print("El material no es un ShaderMaterial o no está definido.")

func _quitar_shader_de_borde():
	if shader_material and shader_material is ShaderMaterial:
		shader_material.set_shader_param("edge_threshold", 1.0)
	else:
		print("El material no es un ShaderMaterial o no está definido.")

func rotar():
	if not rotando:  # Solo iniciar si no está rotando
		rotando = true
		Rt.interpolate_property(self, "rotation", rotation, 0.05, 2, Tween.TRANS_SINE, Tween.EASE_IN)
		Rt.start()

func _on_rotacion_completada(object = null, key = ""):
	if harotado:  # Solo se ejecuta una vez que la rotación inicial ha terminado
		harotado = false
		rotando = false
	else:  # La primera vez que se completa la rotación
		harotado = true
		Rt.interpolate_property(self, "rotation", self.rotation, -0.05, 2, Tween.TRANS_QUAD, Tween.EASE_IN)
		Rt.start()

func _on_Timer_timeout():
	if not rotando and not harotado:
		rotar()
	elif harotado and not rotando:  # Este bloque ahora está claro cuando se debe llamar a la segunda rotación
		rotar()

func cambiar_tamano():
	ScaleTween.interpolate_property(self, "scale", tamano_inicial, tamano_cambio, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()

func cambiar_tamano_final():
	Rt.stop_all()
	self.rotation_degrees = 0
	ScaleTween.interpolate_property(self, "scale", tamano_inicial, tamano_final, 1.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()
	
	# Ajuste: conecta el temporizador para destruir la carta
	st.wait_time = 0.8
	st.one_shot = true  # Asegúrate de que sea una única vez
	st.start()
	st.connect("timeout", self, "Destroy")

func Destroy():
	# Emite una señal antes de destruir para actualizar el conteo de cartas
	emit_signal("carta_destruida")
	
	# Verifica si el nodo está en un árbol de nodos
	if get_parent() != null:
		# Si está en un árbol, lo elimina
		get_parent().remove_child(self)
	else:
		# Si no está en un árbol, imprime un mensaje
		print("La carta no está en ningún árbol de nodos.")




# Método adicional para deslizar la carta a una nueva posición
func deslizar_a_posicion(nueva_posicion, velocidad):
	# Define el nuevo destino
	var distancia = nueva_posicion.distance_to(position)
	var tiempo = distancia / velocidad
	tween.interpolate_property(self, "position", position, nueva_posicion, tiempo, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()
func _process(delta):
		if get_parent() == null:
			print("La carta no está en ningún árbol de nodos.")

I see, as of now and understanding your problem, your code sections

func _crear_cartas():
	# Limpia las cartas existentes antes de crear nuevas
	for child in get_children():
		remove_child(child)
		child.queue_free()

or

func _check_cartas():
	var children = get_children()
	var num_hijos = children.size()

	# Reiniciamos los indicadores
	var o1 = num_hijos > 0 and children[0] != null
	var o2 = num_hijos > 1 and children[1] != null
	var o3 = num_hijos > 2 and children[2] != null
	var o4 = num_hijos > 3 and children[3] != null

	move_cartas(children)

Could have an error.

i would like you to set a breakpoint at var children = get_children() within the _check_cartas() method.
You will then check if children returns an array that is not empty.

If it is empty, then you need to check the SceneTree and see if the object, which the script is attached to, has any node children.

also:

	# Reiniciamos los indicadores
	var o1 = num_hijos > 0 and children[0] != null
	var o2 = num_hijos > 1 and children[1] != null
	var o3 = num_hijos > 2 and children[2] != null
	var o4 = num_hijos > 3 and children[3] != null

this will most likely run into an error if children is empty or its size is less than 3 because it cannot reference children[x]

I have already do this and it prints [cartadegravedad:[Node2D:1305], @cartadegravedad@2:[Node2D:1316], @cartadegravedad@3:[Node2D:1327], @cartadegravedad@4:[Node2D:1338]]
so it is not empty

and I can move the cards if I change the value of o1 o2 o3 o4
but the function check cartas does not work at seeing if a card has been destroyed

what happens in the code when the card is destroyed?
Could you run the game, look at the remote tab and then destroy a card?
Does the SceneTree change and is the card removed from the SceneTree?

remote tab?

how can I acces to it?

do yo mean the scene tree?

because th cards are created only when the game is running and in the editor you only see the parent node of the cards

Once you start your game, your left side will produce a Remote tab which displays the current state of the SceneTree as your game is running. You can see exactly the values of get_children and other variables for your scripts
grafik

it does not destroy and it does not exit the node tree

Which method is executed when a card is destroyed? I don’t understand the portuguese language.
Is it repartir_carta()?
You need to set a breakpoint there and check if the card is removed from the SceneTree upon being destroyed.

the code of the card is this

extends StaticBody2D

# Variables existentes
var posiciondeseleccion = Vector2(0, -30)
var posicionoriginal = Vector2()
var sehapulsado = false

onready var ScaleTween = $ScaleTween
onready var tween = $Tween
onready var shader_material : ShaderMaterial = material
onready var Rt = $RotationTween
onready var timer = $Timer
onready var st = $T

var tamano_inicial = Vector2(1, 1)
var tamano_cambio = Vector2()
var tamano_final = Vector2(1, 1)

var rotando = false
var harotado = false

signal doble_click_detectado

func _ready():
	posicionoriginal = position
	shader_material.set_shader_param("edge_threshold", 1.0)



	tamano_inicial = self.scale
	tamano_final = self.scale * 0
	tamano_cambio = self.scale * 1.25

	ScaleTween.connect("tween_completed", self, "_on_scale_tween_completed")
	tween.connect("tween_completed", self, "_on_tween_completed")
	Rt.connect("tween_completed", self, "_on_rotacion_completada")
	
	timer.wait_time = 0.1 # Cada 0.1 segundos
	timer.one_shot = false  # Para que se repita
	timer.start()
	timer.connect("timeout", self, "_on_Timer_timeout")


func _input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton:
		if event.button_index == BUTTON_LEFT and event.pressed:
			_manejar_click_izquierdo()
		elif event.button_index == BUTTON_RIGHT and event.pressed:
			_manejar_click_derecho()

func _manejar_click_izquierdo():
	if not sehapulsado:
		_mover_a_posicion_deseleccion()
	else:
		_mover_a_posicion_original()

func _manejar_click_derecho():
	if position == posicionoriginal + posiciondeseleccion:
		emit_signal("doble_click_detectado")
		cambiar_tamano_final()
		print("¡Doble clic detectado!")
	else:
		print("No se puede emitir señal de doble clic porque no está en la posición de selección.")

func _mover_a_posicion_deseleccion():
	tween.interpolate_property(self, "position", position, posicionoriginal + posiciondeseleccion, 0.5, Tween.TRANS_SINE, Tween.EASE_IN)
	tween.start()

	_aplicar_shader_de_borde()
	sehapulsado = true
	cambiar_tamano()

func _mover_a_posicion_original():
	tween.interpolate_property(self, "position", position, posicionoriginal, 0.7, Tween.TRANS_ELASTIC, Tween.EASE_IN_OUT)
	tween.start()

	ScaleTween.interpolate_property(self, "scale", tamano_cambio, tamano_inicial, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()

	_quitar_shader_de_borde()
	sehapulsado = false

func _aplicar_shader_de_borde():
	if shader_material and shader_material is ShaderMaterial:
		shader_material.set_shader_param("edge_threshold", 0.0)
	else:
		print("El material no es un ShaderMaterial o no está definido.")

func _quitar_shader_de_borde():
	if shader_material and shader_material is ShaderMaterial:
		shader_material.set_shader_param("edge_threshold", 1.0)
	else:
		print("El material no es un ShaderMaterial o no está definido.")

func rotar():
	if not rotando:  # Solo iniciar si no está rotando
		rotando = true
		Rt.interpolate_property(self, "rotation", rotation, 0.05, 2, Tween.TRANS_SINE, Tween.EASE_IN)
		Rt.start()

func _on_rotacion_completada(object = null, key = ""):
	if harotado:  # Solo se ejecuta una vez que la rotación inicial ha terminado
		harotado = false
		rotando = false
	else:  # La primera vez que se completa la rotación
		harotado = true
		Rt.interpolate_property(self, "rotation", self.rotation, -0.05, 2, Tween.TRANS_QUAD, Tween.EASE_IN)
		Rt.start()

func _on_Timer_timeout():
	if not rotando and not harotado:
		rotar()
	elif harotado and not rotando:  # Este bloque ahora está claro cuando se debe llamar a la segunda rotación
		rotar()

func cambiar_tamano():
	ScaleTween.interpolate_property(self, "scale", tamano_inicial, tamano_cambio, 2.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()

func cambiar_tamano_final():
	Rt.stop_all()
	self.rotation_degrees = 0
	ScaleTween.interpolate_property(self, "scale", tamano_inicial, tamano_final, 1.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()
	
	# Ajuste: conecta el temporizador para destruir la carta
	st.wait_time = 0.8
	st.one_shot = true  # Asegúrate de que sea una única vez
	st.start()
	st.connect("timeout", self, "Destroy")

func Destroy():
	# Emite una señal antes de destruir para actualizar el conteo de cartas
	emit_signal("carta_destruida")
	
	# Verifica si el nodo está en un árbol de nodos
	if get_parent() != null:
		# Si está en un árbol, lo elimina
		get_parent().remove_child(self)
	else:
		# Si no está en un árbol, imprime un mensaje
		print("La carta no está en ningún árbol de nodos.")




# Método adicional para deslizar la carta a una nueva posición
func deslizar_a_posicion(nueva_posicion, velocidad):
	# Define el nuevo destino
	var distancia = nueva_posicion.distance_to(position)
	var tiempo = distancia / velocidad
	tween.interpolate_property(self, "position", position, nueva_posicion, tiempo, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()
func _process(delta):
		if get_parent() == null:
			print("La carta no está en ningún árbol de nodos.")

and the method to destroy the card is this

func cambiar_tamano_final():
	Rt.stop_all()
	self.rotation_degrees = 0
	ScaleTween.interpolate_property(self, "scale", tamano_inicial, tamano_final, 1.0, Tween.TRANS_QUINT, Tween.EASE_IN_OUT)
	ScaleTween.start()
	
	# Ajuste: conecta el temporizador para destruir la carta
	st.wait_time = 0.8
	st.one_shot = true  # Asegúrate de que sea una única vez
	st.start()
	st.connect("timeout", self, "Destroy")

func Destroy():
	# Emite una señal antes de destruir para actualizar el conteo de cartas
	emit_signal("carta_destruida")
	
	# Verifica si el nodo está en un árbol de nodos
	if get_parent() != null:
		# Si está en un árbol, lo elimina
		get_parent().remove_child(self)
		queue_free()
	else:
		# Si no está en un árbol, imprime un mensaje
		print("La carta no está en ningún árbol de nodos.")