Menu
CoddyTech

RefCounted frente a Node

Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 43 de 65.

Cada objeto ocupa memoria hasta que se libera. RefCounted y Node se liberan de distintas maneras.

Un objeto RefCounted cuenta las variables, los arreglos y los objetos que hacen referencia a él. Cuando el recuento llega a cero, Godot lo libera por sí solo. NOTIFICATION_PREDELETE, del capítulo Métodos especiales, muestra el momento:

# note.gd
class_name Note
extends RefCounted

func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		print("Note freed")

# main.gd
func write() -> void:
	var note := Note.new()
	print("Note written")

func _ready():
	write()          # Note escrito, luego Note liberado
	print("Back in _ready")

note era la única referencia, así que la nota se libera en cuanto termina write().

Un Node no se cuenta. Un nodo del árbol de escenas se libera junto con su padre, y todo el árbol se libera cuando termina el juego. Debes liberar tú cualquier nodo que saques del árbol o que nunca añadas a él:

crate.free()         # se libera ahora, junto con todos sus hijos
crate.queue_free()   # se libera al final del fotograma actual

Después de free(), el nodo desaparece: is_instance_valid(crate) devuelve false, y llamar a un método en él es un error. queue_free() es la opción segura mientras otro código todavía pueda usar el nodo en este fotograma, pero hasta que termine ese fotograma, el nodo permanece en el árbol y sigue contando en get_child_count().

Un nodo que nunca se libera es una fuga de memoria: Godot informa «ObjectDB instances were leaked at exit» cuando termina el programa. Los objetos RefCounted también tienen fugas cuando dos de ellos se refieren entre sí, porque ninguno de los contadores puede llegar a cero. Almacena el vínculo en una sola dirección o almacena un nombre en lugar del objeto.

Usa RefCounted para los datos: elementos, puntuaciones y registros. Usa Node para las cosas que viven en el árbol de escena.

challenge icon

Desafío

Fácil

Un nodo Wave mantiene a sus enemigos como nodos secundarios. enemy.gd está completo: Enemy.new("Goblin", 5) crea un nodo enemigo llamado Goblin con 5 HP. Escribe los tres métodos de Wave:

  • spawn(enemy_name, hp) añade un nuevo enemigo como nodo secundario. Si ya existe un nodo secundario con ese nombre, imprime Orc is already here y no crea nada.
  • hit(enemy_name, damage) reduce los hp de ese enemigo e imprime Orc has 8 HP. Si tiene 0 HP o menos, imprime Orc defeated y elimina al enemigo definitivamente, de inmediato: la siguiente llamada a alive() no debe contarlo. Si el nombre no está presente, imprime No Orc here.
  • alive() devuelve cuántos enemigos quedan.

Todos los nodos deben liberarse: cualquier fuga al salir hace que la prueba falle. El código proporcionado añade la ola al árbol de escenas, lee cuántos comandos vienen después (spawn Goblin 5, hit Goblin 6, count) e imprime los enemigos que quedan. Para spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4, imprime:

Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1

Tu código va en wave.gd y enemy.gd. main.gd contiene el código de entrada/salida proporcionado y no se puede editar.

Pruébalo tú mismo

extends Node

func _ready():
	# Código de entrada/salida proporcionado: mantenlo tal como está
	var wave := Wave.new()
	add_child(wave)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "spawn":
			wave.spawn(parts[1], int(parts[2]))
		elif parts[0] == "hit":
			wave.hit(parts[1], int(parts[2]))
		else:
			print("Enemies: ", wave.alive())
	print("Enemies left: ", wave.alive())
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de GDScript online