RefCounted לעומת Node
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GDScript של Coddy. שיעור 43 מתוך 65.
כל אובייקט תופס זיכרון עד שמשחררים אותו. את RefCounted ואת Node משחררים בדרכים שונות.
אובייקט RefCounted סופר את המשתנים, המערכים והאובייקטים שמתייחסים אליו. כשהמונה יורד לאפס, Godot משחרר אותו בעצמו. NOTIFICATION_PREDELETE, מתוך הפרק שיטות מיוחדות, מציג את הרגע:
# 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 נכתב, ואז Note שוחרר
print("Back in _ready")note היה המצביע היחיד, ולכן ההערה משתחררת ברגע ש-write() מסתיימת.
Node אינו נספר. צומת בעץ הסצנה משתחרר יחד עם ההורה שלו, וכל העץ משתחרר כשהמשחק מסתיים. צומת שמוציאים מהעץ, או שמעולם לא מוסיפים אליו, עליך לשחרר בעצמך:
crate.free() # משוחרר כעת, יחד עם כל הצאצאים שלו
crate.queue_free() # משוחרר בסוף הפריים הנוכחיאחרי free(), הצומת נעלם: is_instance_valid(crate) מחזירה false, וקריאה למתודה עליו היא שגיאה. queue_free() היא הבחירה הבטוחה כשייתכן שקוד אחר עדיין ישתמש בצומת בפריים הזה, אבל עד שהפריים מסתיים הצומת נשאר בעץ ועדיין נספר ב־get_child_count().
צומת שאף פעם לא משתחרר הוא דליפת זיכרון: Godot מדווח "ObjectDB instances were leaked at exit" כשהתוכנית מסתיימת. גם אובייקטים מסוג RefCounted דולפים כששניים מהם מפנים זה לזה, משום שאף אחד ממוני ההפניות לא יכול להגיע לאפס. אחסן את הקישור בכיוון אחד בלבד, או אחסן שם במקום את האובייקט.
השתמשו ב-RefCounted עבור נתונים: פריטים, ניקוד ורשומות. השתמשו ב-Node עבור דברים שחיים בעץ הסצנה.
אתגר
קלצומת Wave שומר את האויבים שלו כצמתי צאצא. הקובץ enemy.gd מלא: Enemy.new("Goblin", 5) יוצר צומת אויב בשם Goblin עם 5 נקודות חיים. כתוב את שלוש המתודות של Wave:
spawn(enemy_name, hp)מוסיפה אויב חדש כצומת צאצא. אם קיים צומת צאצא בשם הזה, היא מדפיסהOrc is already hereולא יוצרת דבר.hit(enemy_name, damage)מפחיתה את ה-hpשל אותו אויב ומדפיסהOrc has 8 HP. אם יש לו0נקודות חיים או פחות, היא מדפיסהOrc defeatedומסירה את האויב לתמיד, מיד: הקריאה הבאה ל-alive()לא חייבת לספור אותו. אם אין אויב בשם הזה, יודפסNo Orc here.alive()מחזירה את מספר האויבים שנותרו.
יש לשחרר כל צומת: כל דליפה בסיום תכשיל את הבדיקה. הקוד שסופק מוסיף את הגל לעץ הסצנה, קורא כמה פקודות יגיעו אחר כך (spawn Goblin 5, hit Goblin 6, count) ומדפיס את מספר האויבים שנותרו. עבור spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4 יודפס:
Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1הקוד שלך ייכתב בקבצים wave.gd ו-enemy.gd. הקובץ main.gd מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
extends Node
func _ready():
# קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
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())
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח selfמתודותמאפייניםבנאי (_init)חזרה – מחשבון פשוט2מאפייני מחלקה
משתני מופע לעומת משתנים סטטייםפונקציות סטטיותקובעי מאפייניםמאחזרי מאפייניםפרטיים לפי מוסכמהסיכום – מנהל חשבון בנק5כימוס
איברים ציבוריים ופרטייםאימות באמצעות Settersהסתרת מידעמאפיינים לקריאה בלבדסיכום – מערכת לניהול רשומות תלמידים8מושגי OOP מתקדמים
קומפוזיציה לעומת ירושהמחלקות פנימיותצמתים כרכיביםRefCounted לעומת NodeEnum במחלקותתרגלו בעצמכם: קומפיילר GDScript אונליין