RefCounted a Node
Część sekcji Programowanie obiektowe ścieżki GDScript w Coddy. Lekcja 43 z 65.
Każdy obiekt zajmuje pamięć, dopóki nie zostanie zwolniony. Obiekty RefCounted i Node są zwalniane na różne sposoby.
Obiekt RefCounted zlicza zmienne, tablice i obiekty, które się do niego odwołują. Gdy licznik spada do zera, Godot sam go zwalnia. NOTIFICATION_PREDELETE, omówione w rozdziale Metody specjalne, pokazuje ten 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() # Notatka zapisana, a następnie zwolniona
print("Back in _ready")note było jedynym odwołaniem, więc notatka zostaje zwolniona zaraz po zakończeniu write().
Node nie jest liczony. Węzeł w drzewie sceny jest zwalniany razem ze swoim elementem nadrzędnym, a całe drzewo jest zwalniane po zakończeniu gry. Węzeł, który usuniesz z drzewa lub którego nigdy do niego nie dodasz, musisz zwolnić samodzielnie:
crate.free() # zwolniony teraz, wraz ze wszystkimi swoimi elementami potomnymi
crate.queue_free() # zwolniony na końcu bieżącej klatkiPo wywołaniu free() węzeł znika: is_instance_valid(crate) zwraca false, a wywołanie na nim metody powoduje błąd. queue_free() to bezpieczny wybór, gdy inny kod może jeszcze używać węzła w tej klatce, ale do końca tej klatki węzeł pozostaje w drzewie i nadal jest uwzględniany przez get_child_count().
Węzeł, który nigdy nie jest zwalniany, powoduje wyciek pamięci: Godot zgłasza komunikat „ObjectDB instances were leaked at exit” po zakończeniu programu. Obiekty RefCounted również powodują wycieki, gdy dwa z nich odwołują się do siebie nawzajem, ponieważ żaden licznik nie może osiągnąć zera. Przechowuj powiązanie tylko w jednym kierunku albo przechowuj nazwę zamiast obiektu.
Używaj RefCounted do danych: przedmiotów, wyników, rekordów. Używaj Node dla obiektów, które znajdują się w drzewie sceny.
Wyzwanie
ŁatwyWęzeł Wave przechowuje swoich przeciwników jako węzły podrzędne. Plik enemy.gd jest kompletny: Enemy.new("Goblin", 5) tworzy węzeł przeciwnika o nazwie Goblin i 5 punktach HP. Napisz trzy metody klasy Wave:
spawn(enemy_name, hp)dodaje nowego przeciwnika jako węzeł podrzędny. Jeśli istnieje już węzeł podrzędny o tej nazwie, wypisujeOrc is already herei niczego nie tworzy.hit(enemy_name, damage)zmniejsza wartośćhptego przeciwnika i wypisujeOrc has 8 HP. Gdy HP wynosi0lub mniej, wypisujeOrc defeatedi natychmiast usuwa przeciwnika na stałe: następne wywołaniealive()nie może go już uwzględniać. Jeśli przeciwnika o podanej nazwie nie ma, wypisujeNo Orc here.alive()zwraca liczbę pozostałych przeciwników.
Każdy węzeł musi zostać zwolniony: każdy wyciek pamięci przy zakończeniu powoduje niezaliczenie testu. Dostarczony kod dodaje falę do drzewa sceny, odczytuje liczbę kolejnych poleceń (spawn Goblin 5, hit Goblin 6, count) i wypisuje liczbę pozostałych przeciwników. Dla poleceń spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4 wypisuje:
Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1Twój kod umieść w plikach wave.gd i enemy.gd. Plik main.gd zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
extends Node
func _ready():
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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())
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe selfMetodyWłaściwościKonstruktor (_init)Powtórka – prosty kalkulator4Polimorfizm
Ponowne omówienie przesłaniania metodDuck typingKlasy i metody abstrakcyjneProjektowanie interfejsówPowtórzenie – kalkulator kształtów7Sygnały
Czym jest sygnał?Deklarowanie i emitowanieŁączenie z sygnałamiSygnały z argumentamiPodsumowanie — system zdrowia10Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu2Właściwości klas
Zmienne instancji a zmienne statyczneFunkcje statyczneSettery właściwościGettery właściwościPrywatne z konwencjiPodsumowanie — menedżer kont bankowych5Enkapsulacja
Elementy publiczne i prywatneWalidacja za pomocą setterówUkrywanie informacjiWłaściwości tylko do odczytuPodsumowanie – system ewidencji uczniów8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieKlasy wewnętrzneWęzły jako komponentyRefCounted a NodeTypy wyliczeniowe w klasach11Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superNadpisywanie metodŁańcuchy dziedziczeniaSprawdzanie typów za pomocą isPodsumowanie – hierarchia pracowników6Metody specjalne
Wprowadzenie do metod wirtualnychMetoda _to_stringWłasne iteratoryPodsumowanie — własna lista9Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator GDScript online