RefCounted ou Node
Fait partie de la section Programmation orientée objet du Journey GDScript de Coddy. Leçon 43 sur 65.
Chaque objet occupe de la mémoire jusqu’à sa libération. RefCounted et Node sont libérés de différentes manières.
Un objet RefCounted compte les variables, les tableaux et les objets qui y font référence. Lorsque ce nombre tombe à zéro, Godot le libère de lui-même. NOTIFICATION_PREDELETE, dans le chapitre Méthodes spéciales, indique le moment :
# 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 écrit, puis Note libéré
print("Back in _ready")note était la seule référence, donc la note est libérée dès que write() se termine.
Un Node n'est pas comptabilisé. Un nœud de l'arbre de scène est libéré avec son parent, et l'arbre entier est libéré lorsque le jeu se termine. Un nœud que tu retires de l'arbre, ou que tu n'y ajoutes jamais, doit être libéré par tes soins :
crate.free() # libéré immédiatement, ainsi que tous ses enfants
crate.queue_free() # libéré à la fin de la frame actuelleAprès free(), le nœud n’existe plus : is_instance_valid(crate) renvoie false, et appeler une méthode sur celui-ci provoque une erreur. queue_free() est le choix sûr lorsque d’autres portions de code peuvent encore utiliser le nœud pendant cette frame, mais jusqu’à la fin de celle-ci, le nœud reste dans l’arbre et est toujours comptabilisé par get_child_count().
Un nœud qui n’est jamais libéré constitue une fuite de mémoire : Godot signale « ObjectDB instances were leaked at exit » à la fin du programme. Les objets RefCounted fuient également lorsque deux d’entre eux se référencent mutuellement, car aucun des deux compteurs ne peut atteindre zéro. Stockez le lien dans un seul sens, ou stockez un nom plutôt que l’objet.
Utilisez RefCounted pour les données : objets, scores, enregistrements. Utilisez Node pour les éléments qui vivent dans l’arbre de scène.
Défi
FacileUn nœud Wave conserve ses ennemis comme nœuds enfants. enemy.gd est complet : Enemy.new("Goblin", 5) crée un nœud ennemi nommé Goblin avec 5 HP. Écris les trois méthodes de Wave :
spawn(enemy_name, hp)ajoute un nouvel ennemi comme enfant. S’il existe un enfant portant ce nom, il afficheOrc is already hereet ne crée rien.hit(enemy_name, damage)diminue lehpde cet ennemi et afficheOrc has 8 HP. À0HP ou moins, il afficheOrc defeatedet supprime définitivement l’ennemi, immédiatement : le prochainalive()ne doit pas le compter. Si le nom n’existe pas, il afficheNo Orc here.alive()renvoie le nombre d’ennemis restants.
Chaque nœud doit être libéré : toute fuite à la fin entraîne l’échec du test. Le code fourni ajoute la vague à l’arbre de scènes, lit le nombre de commandes qui suivent (spawn Goblin 5, hit Goblin 6, count) et affiche le nombre d’ennemis restants. Pour spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4, le résultat affiché est :
Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1Ton code va dans wave.gd et enemy.gd. main.gd contient le code d’entrée/sortie fourni et ne peut pas être modifié.
Essayez vous-même
extends Node
func _ready():
# Code d’entrée/sortie fourni : conservez-le tel quel
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())
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses et objetsLe mot-clé selfMéthodesPropriétésConstructeur (_init)Récapitulatif - Calculatrice simple4Polymorphisme
Retour sur la redéfinition des méthodesTypage canardClasses et méthodes abstraitesConception d’interfacesRécapitulatif – Calculateur de formes7Signaux
Qu’est-ce qu’un signal ?Déclarer et émettreSe connecter aux signauxSignaux avec argumentsRécapitulatif - système de santé10Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode modèlePatron ÉtatPatron Composite2Propriétés de classe
Variables d’instance et variables statiquesFonctions statiquesSetters de propriétésGetters de propriétésPrivé par conventionRécapitulatif - Gestionnaire de comptes bancaires5Encapsulation
Membres publics et privésValidation avec les méthodes setterMasquage de l’informationPropriétés en lecture seuleRécapitulatif - Système de dossiers étudiants8Concepts avancés de la POO
Composition ou héritageClasses internesLes nœuds comme composantsRefCounted ou NodeEnums dans les classes3Héritage
Héritage de baseLe mot-clé superRedéfinition de méthodesChaînes d’héritageVérifier les types avec isRécapitulatif – Hiérarchie des employés6Méthodes spéciales
Introduction aux méthodes virtuellesLa méthode _to_stringItérateurs personnalisésRécapitulatif – Liste personnalisée9Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron StrategyEntraînez-vous par vous-même : Compilateur GDScript en ligne