תבנית 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
truea ו־b הם שני משתנים שמחזיקים אובייקט אחד, ולכן השינוי שנעשה דרך a מופיע גם דרך b. עבור אובייקטים, == הוא true רק כאשר שני הצדדים הם אותו אובייקט.
GDScript לא יכולה להפוך את _init() לפרטית, ולכן אין דבר שמונע מסקריפט לקרוא ל־GameSettings.new() ולקבל אובייקט שני. הקו התחתון ב־_instance והמתודה get_instance() הם ההסכמה: סקריפטים מבקשים את האובייקט, הם אף פעם לא יוצרים אותו.
בפרויקט Godot, הדרך המקובלת לקבל אובייקט משותף אחד היא באמצעות טעינה אוטומטית: סקריפט שרושמים בהגדרות הפרויקט, ו-Godot יוצר פעם אחת כשהמשחק מתחיל ומעמיד אותו לרשות כל סקריפט לפי שמו. זהו Singleton ש-Godot מנהל עבורך. מגדירים טעינות אוטומטיות בעורך, לכן האתגרים כאן בונים את התבנית בקוד באמצעות static var.
אתגר
קלכל מטבע במשחק מוסיף לניקוד משותף אחד. הפכו את 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()))
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח selfמתודותמאפייניםבנאי (_init)חזרה – מחשבון פשוט2מאפייני מחלקה
משתני מופע לעומת משתנים סטטייםפונקציות סטטיותקובעי מאפייניםמאחזרי מאפייניםפרטיים לפי מוסכמהסיכום – מנהל חשבון בנק5כימוס
איברים ציבוריים ופרטייםאימות באמצעות Settersהסתרת מידעמאפיינים לקריאה בלבדסיכום – מערכת לניהול רשומות תלמידיםתרגלו בעצמכם: קומפיילר GDScript אונליין