Menu
CoddyTech

シングルトンパターン

CoddyのGDScriptジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 46/65。

Singletonパターンを使うと、クラスが持つオブジェクトを必ず1つだけにでき、すべてのスクリプトからそのオブジェクトにアクセスする方法を1つに統一できます。設定、セーブシステム、スコア管理など、ゲーム内に1つだけあればよいものに使います。

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は同じオブジェクトを保持する2つの変数なので、aを通じて加えた変更はbにも反映されます。オブジェクトの場合、==がtrueになるのは、両辺が同じオブジェクトであるときだけです。

GDScriptでは_init()を非公開にできないため、スクリプトがGameSettings.new()を呼び出して2つ目のオブジェクトを取得するのを止めるものはありません。_instanceのアンダースコアとget_instance()メソッドが、その取り決めを示しています。スクリプトはオブジェクトを要求し、自分で作成することはありません。

Godotプロジェクトで、共有オブジェクトを1つ取得する一般的な方法はオートロードです。これはProject Settingsで登録するスクリプトで、Godotがゲーム開始時に1度だけ作成し、すべてのスクリプトから名前で利用できるようにします。これはGodotが管理してくれるSingletonです。オートロードはエディターで設定するため、ここでの課題ではstatic varを使ってコードでこのパターンを構築します。

challenge icon

チャレンジ

簡単

ゲーム内の各コインは、共有スコアに加算されます。ScoreManagerをシングルトンにしましょう。

  • 単一のオブジェクトを保持するstatic varを用意します。
  • static func get_instance()はオブジェクトを返し、最初の呼び出し時に作成します。作成直後にScore manager createdを出力します。
  • add_points(points)はscoreにポイントを加算します。

coin.gdでは、collect()がScoreManager.get_instance()を通じてコインのvalueをスコアに加算します。

用意されたコードは、後に続くイベントの数を読み取り、各行からイベントを1つ読み込みます。coin VALUEは新しいコインを回収し、bonus POINTSはポイントを直接加算します。各イベントの後にスコアを出力し、最後にget_instance()を2回呼び出したときに同じオブジェクトが返されるかどうかを出力します。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オンラインコンパイラ