Menu
CoddyTech

תבנית Singleton

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GDScript של Coddy. שיעור 46 מתוך 65.

תבנית Singleton מבטיחה שלמחלקה יש בדיוק אובייקט אחד, ומספקת לכל סקריפט דרך אחת לגשת אליו. השתמשו בה עבור דברים שיש מהם רק אחד במשחק: ההגדרות, מערכת השמירה, מנהל הניקוד.

ב-GDScript, ‏static var מחזיק את האובייקט היחיד, ו-static func יוצרת אותו בפעם הראשונה שמישהו מבקש אותו:

# game_settings.gd
class_name GameSettings
extends RefCounted

static var _instance: GameSettings = null

var volume := 50

static func get_instance() -> GameSettings:
	if _instance == null:
		_instance = GameSettings.new()
		print("Creating settings")
	return _instance

_instance שייך למחלקה, ולא לאובייקט, ולכן כל סקריפט רואה את אותו משתנה. הקריאה הראשונה מוצאת אותו ריק ויוצרת את האובייקט; כל קריאה מאוחרת יותר מחזירה את אותו אובייקט.

# main.gd
var a := GameSettings.get_instance()
var b := GameSettings.get_instance()
a.volume = 80
print(b.volume)
print(a == b)
Creating settings
80
true

a ו־b הם שני משתנים שמחזיקים אובייקט אחד, ולכן השינוי שנעשה דרך a מופיע גם דרך b. עבור אובייקטים, == הוא true רק כאשר שני הצדדים הם אותו אובייקט.

GDScript לא יכולה להפוך את _init() לפרטית, ולכן אין דבר שמונע מסקריפט לקרוא ל־GameSettings.new() ולקבל אובייקט שני. הקו התחתון ב־_instance והמתודה get_instance() הם ההסכמה: סקריפטים מבקשים את האובייקט, הם אף פעם לא יוצרים אותו.

בפרויקט Godot, הדרך המקובלת לקבל אובייקט משותף אחד היא באמצעות טעינה אוטומטית: סקריפט שרושמים בהגדרות הפרויקט, ו-Godot יוצר פעם אחת כשהמשחק מתחיל ומעמיד אותו לרשות כל סקריפט לפי שמו. זהו Singleton ש-Godot מנהל עבורך. מגדירים טעינות אוטומטיות בעורך, לכן האתגרים כאן בונים את התבנית בקוד באמצעות static var.

challenge icon

אתגר

קל

כל מטבע במשחק מוסיף לניקוד משותף אחד. הפכו את ScoreManager לסינגלטון:

  • static var מחזיק את האובייקט היחיד.
  • static func get_instance() מחזירה אותו, ויוצרת אותו בקריאה הראשונה. מיד לאחר יצירתו, היא מדפיסה Score manager created.
  • add_points(points) מוסיפה ל־score.

ב־coin.gd, הפונקציה collect() מוסיפה את ה־value של המטבע לניקוד באמצעות ScoreManager.get_instance().

הקוד שסופק קורא כמה אירועים מופיעים בהמשך, ואז אירוע אחד בכל שורה: coin VALUE אוסף מטבע חדש, bonus POINTS מוסיף נקודות ישירות. אחרי כל אירוע הוא מדפיס את הניקוד, ובסוף מציג אם שתי קריאות של get_instance() מחזירות את אותו אובייקט. עבור coin 5 ו־bonus 20 הפלט הוא:

Score manager created
Score: 5
Score: 25
Same object: true

כתבו את הקוד שלכם ב־score_manager.gd וב־coin.gd. הקובץ main.gd מכיל את קוד הקלט/פלט שסופק, ואי אפשר לערוך אותו.

נסו בעצמכם

extends Node

func _ready():
	# קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "coin":
			Coin.new(int(parts[1])).collect()
		elif parts[0] == "bonus":
			ScoreManager.get_instance().add_points(int(parts[1]))
		print("Score: %d" % ScoreManager.get_instance().score)
	print("Same object: %s" % (ScoreManager.get_instance() == ScoreManager.get_instance()))
quiz iconבחנו את עצמכם

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

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

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