Menu
CoddyTech

引数付きシグナル

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

シグナルは値を渡すことができます。関数のパラメーターのように、宣言に値を列挙します。

signal damaged(amount: int, hp_left: int)
signal died

値を同じ順序でemit()に渡します。

func take_damage(amount: int) -> void:
	hp = maxi(hp - amount, 0)
	damaged.emit(amount, hp)
	if hp == 0:
		died.emit()

接続された関数は値を引数として受け取るため、値ごとに1つの引数が必要です。

hero.damaged.connect(_on_hero_damaged)

func _on_hero_damaged(amount: int, hp_left: int) -> void:
	print("-%d HP, %d left" % [amount, hp_left])

宣言内の型は値を記録するものです。GDScriptはシグナルを発行するときに型をチェックしません。不一致は受信側の関数で明らかになります。パラメーターの数が間違っている関数や、値を格納できない型付きパラメーターがある場合、emit() の行でエラーになり、その関数は実行されません。

bind()は、発行された値の後に独自の値を追加します。1つのメソッドで複数のヒーローを監視しながら、誰がダメージを受けたのかを把握できます。

ada.damaged.connect(_on_damaged.bind("Ada"))
bo.damaged.connect(_on_damaged.bind("Bo"))

func _on_damaged(amount: int, hp_left: int, who: String) -> void:
	print(who, " lost ", amount, " HP")

引数を使うと、リスナーは必要なデータを受け取れるため、エミッターから読み取る必要がありません。hero.died.connect(func(): print(hero.hp)) のように、発行元オブジェクト自体を使うラムダを接続してはいけません。ラムダが hero を保持し、hero がラムダを保持するため、どちらも解放されず、プログラムの終了時に Godot がリークしたインスタンスを報告します。

challenge icon

チャレンジ

簡単

ウォレットは、数値を伴うシグナルで変更を通知します。Walletに、両方のシグナルのパラメーターを追加し、2つのメソッドを記述してください。

  • balance_changed(old_balance: int, new_balance: int)とpurchase_failed(price: int, missing: int)
  • add(amount)はコインを追加し、変更前と変更後の残高をbalance_changedで通知します。
  • buy(price):残高が足りている場合は、価格分を差し引き、balance_changedで通知します。それ以外の場合は何も変更せず、価格と不足しているコインの数をpurchase_failedで通知します。

用意されているコードは、各シグナルに1つずつ関数を接続し、後に続くコマンドの数を読み取り、各コマンド(add 20、buy 15)を実行して、最後に残っているコインを表示します。add 20、buy 15、buy 10、add 5、buy 10の場合、次のように表示されます。

Balance: 0 -> 20
Balance: 20 -> 5
Cannot buy for 10: 5 coins missing
Balance: 5 -> 10
Balance: 10 -> 0
Coins left: 0

コードはwallet.gdに記述してください。main.gdには用意されている入出力コードが含まれており、編集できません。

自分で試してみよう

extends Node

func _ready():
	# 提供された入出力コード:そのままにしてください
	var wallet := Wallet.new()
	wallet.balance_changed.connect(_on_balance_changed)
	wallet.purchase_failed.connect(_on_purchase_failed)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "add":
			wallet.add(int(parts[1]))
		else:
			wallet.buy(int(parts[1]))
	print("Coins left: ", wallet.balance)

func _on_balance_changed(old_balance: int, new_balance: int) -> void:
	print("Balance: %d -> %d" % [old_balance, new_balance])

func _on_purchase_failed(price: int, missing: int) -> void:
	print("Cannot buy for %d: %d coins missing" % [price, missing])
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: GDScriptオンラインコンパイラ