Menu
CoddyTech

シグナルへの接続

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

connect() は、実行する関数を Callable として受け取ります。括弧を付けずにメソッド名を書くか、ラムダを書きます。

chest.opened.connect(_on_chest_opened)          # このスクリプトのメソッド
chest.opened.connect(quest_log.add_entry)       # 別のオブジェクトのメソッド
chest.opened.connect(func(): print("Creak!"))   # ラムダ式

Godotでは、リスニングメソッドを_on_、次に発信元、最後にシグナルの順に命名します。たとえば_on_chest_openedです。1つのシグナルに複数の関数を接続でき、それらは接続された順に実行されます。

イベントを監視するオブジェクトは通常、発信元を受け取るメソッド内で、自身を接続します:

# quest_log.gd
class_name QuestLog
extends RefCounted

var chests_opened := 0

func track(chest: Chest) -> void:
	if not chest.opened.is_connected(_on_chest_opened):
		chest.opened.connect(_on_chest_opened)

func untrack(chest: Chest) -> void:
	if chest.opened.is_connected(_on_chest_opened):
		chest.opened.disconnect(_on_chest_opened)

func _on_chest_opened() -> void:
	chests_opened += 1

同じ関数を同じシグナルに2回接続するのはエラーです。また、接続されていない関数を切断するのもエラーです。まずis_connected()で確認します。disconnect()は監視を停止し、次にシグナルを発行してもその関数は実行されなくなります。

2 番目の引数として CONNECT_ONE_SHOT を渡すと、接続は最初の呼び出しの後に解除されます: chest.opened.connect(_on_first_open, CONNECT_ONE_SHOT)。

ラムダは、作成された時点でローカル変数が持っていた値を保持します。接続されたラムダ内の count += 1 はそのコピーを変更しますが、_ready() 内の count は 0 のままです。QuestLog のように、リスナーオブジェクトのメンバー変数にカウンターを保持してください。

challenge icon

チャレンジ

簡単

射撃場には、撃たれると hit を発するターゲットがあります。ターゲットを監視する ScoreBoard を作成してください。

  • watch(target) は、ターゲットの hit シグナルを _on_target_hit に接続します。すでに監視中のターゲットを監視しても、何も変わりません。
  • ignore(target) は接続を解除します。監視していないターゲットを無視しても、何も変わりません。
  • _on_target_hit() は points に 10 を加算し、Hit! Score: N を出力します。

同じターゲットを2回監視したり、監視していないターゲットを無視したりしても、エラーが発生してはいけません。エラーがあるとテストに失敗するためです。target.gd は完成しています。

提供されたコードはターゲットの数を読み込んだ後、watch 0、ignore 0、shoot 0 のようなコマンドを読み込み、最終スコアを出力します。ターゲットが2つあり、コマンドが watch 0、shoot 0、shoot 1、watch 1、watch 1、shoot 1、ignore 0、shoot 0 の場合、次のように出力されます。

Shot target 0
Hit! Score: 10
Shot target 1
Shot target 1
Hit! Score: 20
Shot target 0
Final score: 20

コードは score_board.gd と target.gd に記述します。main.gd には提供された入出力コードがあり、編集できません。

自分で試してみよう

extends Node

func _ready():
	# 提供された入出力コード: そのままにしておいてください
	var board := ScoreBoard.new()
	var targets: Array[Target] = []
	for i in int(OS.read_string_from_stdin()):
		targets.append(Target.new())
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		var target := targets[int(parts[1])]
		if parts[0] == "watch":
			board.watch(target)
		elif parts[0] == "ignore":
			board.ignore(target)
		else:
			print("Shot target ", parts[1])
			target.shoot()
	print("Final score: ", board.points)
quiz icon腕試し

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

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

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