Menu
CoddyTech

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 עבור דברים שחיים בעץ הסצנה.

challenge icon

אתגר

קל

צומת 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())
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר GDScript אונליין