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 actualDespué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.
Desafío
FácilUn 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, imprimeOrc is already herey no crea nada.hit(enemy_name, damage)reduce loshpde ese enemigo e imprimeOrc has 8 HP. Si tiene0HP o menos, imprimeOrc defeatedy elimina al enemigo definitivamente, de inmediato: la siguiente llamada aalive()no debe contarlo. Si el nombre no está presente, imprimeNo 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: 1Tu 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())
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
1Fundamentos de la programación orientada a objetos
Archivos externosIntroducción a la programación orientada a objetosClases vs. objetosLa palabra clave selfMétodosPropiedadesConstructor (_init)Repaso: calculadora sencilla4Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas7Señales
¿Qué es una señal?Declarar y emitirConectarse a las señalesSeñales con argumentosRepaso: sistema de salud10Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite2Propiedades de clase
Variables de instancia y estáticasFunciones estáticasMétodos setter de propiedadesMétodos getter de propiedadesPrivado por convenciónRepaso: administrador de cuentas bancarias5Encapsulación
Miembros públicos y privadosValidación con settersOcultación de informaciónPropiedades de solo lecturaRepaso: sistema de registros de estudiantes8Conceptos avanzados de POO
Composición frente a herenciaClases internasNodos como componentesRefCounted frente a NodeEnums en clases3Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosCadenas de herenciaComprobaciones de tipo con isRepaso: jerarquía de empleados6Métodos especiales
Introducción a los métodos virtualesEl método _to_stringIteradores personalizadosRepaso: lista personalizada9Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de GDScript online