Menu
CoddyTech

Паттерн «Одиночка»

Часть раздела Объектно-ориентированное программирование путешествия по 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 создаёт его один раз при запуске игры и предоставляет по имени в каждом скрипте. Это одиночка, управляемая 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