Godot 任意の個数の引数のシグナルを受け付ける疑似可変長引数を持つ関数の例

無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 で、デフォルト引数を用いて疑似的な可変長引数を持つ関数を実装するスクリプト例を紹介します。
関数呼び出しだけではなく、引数の個数が異なる複数のシグナル1つの関数で受信するテストシーンとその結果についても紹介します。

※ GodotEngine 4.3 を使用しています。.NET 版ではありません。
※スクリプトは自己責任でご使用ください。

テストシーン

メニュー「シーン」→「新規シーン」を選択してから、シーンドックで「インターフェース」を選んで Control クラスのルートノードを作成します。
ルートノードを選択した後 F2 キーを押して StudyPseudoVarargs に名前を変更します。

作成したルートノードを選択して、右上の+ボタンで study_pseudo_varargs.gd スクリプトを作成して割り当てたらシーンを study_pseudo_varargs.tscn で保存します。
※説明文の Label を子ノードに追加していますが、追加しなくても動作はします。

Godot4 疑似可変長引数の関数呼び出しのテストを行うためのシーンを作成

引数の個数が異なるとシグナルと関数の接続に失敗します

シグナルと受信側メソッドの引数が異なると、実行時に以下のように「Method expected 0 arguments, but called with 1.」というエラーメッセージがデバッガーボトムパネルに表示され、シグナルからの関数呼び出しが失敗します。

E 0:00:04:0229   emit_signalp: Error calling from signal 'value_changed' to callable: 'SpinBox(sc_min_max_constraint_handler.gd)::_on_node_small_value_changed': Method expected 0 arguments, but called with 1.
  <C++ ソース>      core/object/object.cpp:1200 @ emit_signalp()

疑似的な可変長引数の関数の型の例

以下の pseudo_varargs_function メンバ関数は、実際には有限な疑似的な可変長引数を持ちます。

デフォルト引数 _arg1 ~ _arg9 により、任意の引数の個数(例では 0 ~ 9 個)を持つさまざまなシグナル対応します。
処理で使用しない引数は、 UNUSED PARAMETER 警告が表示されるので、それを抑制するために名前の最初に _ をつけています。

## 引数が 0 ~ 9 個の任意の個数で呼び出される関数です。
func pseudo_varargs_function(_arg1 = null, _arg2 = null, _arg3 = null, _arg4 = null, _arg5 = null, _arg6 = null, _arg7 = null, _arg8 = null, _arg9 = null) -> void:
	pass

次の章では、この関数で、0 ~ 9 個の引数を持つ異なるシグナルとの接続・受信関数呼び出しができるかを確認します。

疑似可変長引数を持つ関数呼び出しとシグナル接続のテストコード

前述の疑似可変長引数を持つ関数1つで、0 ~ 9 個の引数を持つシグナルとの接続と受信、関数呼び出しをテストする以下のスクリプトを、先ほどテストシーンと一緒に作成した study_pseudo_varargs.gd に上書きします。
※詳細は、スクリプト内のコメントを参照してください。

extends Node
class_name ScStudyPseudoVarargs
## 実際には有限な引数を扱う疑似的な可変長引数の実装方法について調べるためのクラスです。
## メンバ関数呼び出しの他に、シグナルの引数にも対応できるかを確認します。
## [member pseudo_varargs_function] のようなインターフェースを持つことで疑似的に可変長引数を扱えます。

# Called when the node enters the scene tree for the first time.
func _ready():
	# connect のエラーコードを得る変数。
	# 参照 https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-error
	var error_code = OK	# OK = 0
	
	# 引数 0 ~ 9 を持つ各シグナルを同じ関数に接続するテスト
	error_code = self.connect("study_pseudo_varargs_signal_arg_0", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_0 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_1", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_1 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_2", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_2 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_3", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_3 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_4", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_4 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_5", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_5 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_6", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_6 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_7", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_7 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_8", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_8 error_code = " + str(error_code))
	error_code = self.connect("study_pseudo_varargs_signal_arg_9", Callable(self, "pseudo_varargs_function"))
	print("connect study_pseudo_varargs_signal_arg_9 error_code = " + str(error_code))
	# 通常の関数呼び出しのテスト
	print("pseudo_varargs_function(0 args) call")
	pseudo_varargs_function()
	print("pseudo_varargs_function(1 args) call")
	pseudo_varargs_function("A")
	print("pseudo_varargs_function(2 args) call")
	pseudo_varargs_function("A", "B")
	print("pseudo_varargs_function(3 args) call")
	pseudo_varargs_function("A", "B", "C")
	print("pseudo_varargs_function(4 args) call")
	pseudo_varargs_function("A", "B", "C", "D")
	print("pseudo_varargs_function(5 args) call")
	pseudo_varargs_function("A", "B", "C", "D", "E")
	print("pseudo_varargs_function(6 args) call")
	pseudo_varargs_function("A", "B", "C", "D", "E", "F")
	print("pseudo_varargs_function(7 args) call")
	pseudo_varargs_function("A", "B", "C", "D", "E", "F", "G")
	print("pseudo_varargs_function(8 args) call")
	pseudo_varargs_function("A", "B", "C", "D", "E", "F", "G", "H")
	print("pseudo_varargs_function(9 args) call")
	pseudo_varargs_function("A", "B", "C", "D", "E", "F", "G", "H", "I")
	return

func _input(event):
	# key 0 ~ 9 がおされた直後のフレームに signal を emit します。
	# signal の呼び出しのため、エディタでこのスクリプトを割り当てたノードを持つシーンを実行して
	# 0 ~ 9 のキーを押下してください。
	var just_pressed = event.is_pressed() and not event.is_echo()
	if Input.is_key_pressed(KEY_0) and just_pressed:
		print("study_pseudo_varargs_signal_arg_0.emit")
		study_pseudo_varargs_signal_arg_0.emit()
	elif Input.is_key_pressed(KEY_1) and just_pressed:
		print("study_pseudo_varargs_signal_arg_1.emit")
		study_pseudo_varargs_signal_arg_1.emit(1)
	elif Input.is_key_pressed(KEY_2) and just_pressed:
		print("study_pseudo_varargs_signal_arg_2.emit")
		study_pseudo_varargs_signal_arg_2.emit(1, 2)
	elif Input.is_key_pressed(KEY_3) and just_pressed:
		print("study_pseudo_varargs_signal_arg_3.emit")
		study_pseudo_varargs_signal_arg_3.emit(1, 2, 3)
	elif Input.is_key_pressed(KEY_4) and just_pressed:
		print("study_pseudo_varargs_signal_arg_4.emit")
		study_pseudo_varargs_signal_arg_4.emit(1, 2, 3, 4)
	elif Input.is_key_pressed(KEY_5) and just_pressed:
		print("study_pseudo_varargs_signal_arg_5.emit")
		study_pseudo_varargs_signal_arg_5.emit(1, 2, 3, 4, 5)
	elif Input.is_key_pressed(KEY_6) and just_pressed:
		print("study_pseudo_varargs_signal_arg_6.emit")
		study_pseudo_varargs_signal_arg_6.emit(1, 2, 3, 4, 5, 6)
	elif Input.is_key_pressed(KEY_7) and just_pressed:
		print("study_pseudo_varargs_signal_arg_7.emit")
		study_pseudo_varargs_signal_arg_7.emit(1, 2, 3, 4, 5, 6, 7)
	elif Input.is_key_pressed(KEY_8) and just_pressed:
		print("study_pseudo_varargs_signal_arg_8.emit")
		study_pseudo_varargs_signal_arg_8.emit(1, 2, 3, 4, 5, 6, 7, 8)
	elif Input.is_key_pressed(KEY_9) and just_pressed:
		print("study_pseudo_varargs_signal_arg_9.emit")
		study_pseudo_varargs_signal_arg_9.emit(1, 2, 3, 4, 5, 6, 7, 8, 9)
	return

## 引数が 0 ~ 9 個のそれぞれの数字のキーを押されたときに emit されるシグナルです。
signal study_pseudo_varargs_signal_arg_0
signal study_pseudo_varargs_signal_arg_1(arg1)
signal study_pseudo_varargs_signal_arg_2(arg1, arg2)
signal study_pseudo_varargs_signal_arg_3(arg1, arg2, arg3)
signal study_pseudo_varargs_signal_arg_4(arg1, arg2, arg3, arg4)
signal study_pseudo_varargs_signal_arg_5(arg1, arg2, arg3, arg4, arg5)
signal study_pseudo_varargs_signal_arg_6(arg1, arg2, arg3, arg4, arg5, arg6)
signal study_pseudo_varargs_signal_arg_7(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
signal study_pseudo_varargs_signal_arg_8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
signal study_pseudo_varargs_signal_arg_9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

## 引数が 0 ~ 9 個の任意の個数で呼び出される関数です。引数を, 区切りで出力します。
## signal, 関数呼び出しで利用できることを確認します。
func pseudo_varargs_function(_arg1 = null, _arg2 = null, _arg3 = null, _arg4 = null, _arg5 = null, _arg6 = null, _arg7 = null, _arg8 = null, _arg9 = null) -> void:
	var string_args = str(_arg1)
	string_args += ", " + str(_arg2)
	string_args += ", " + str(_arg3)
	string_args += ", " + str(_arg4)
	string_args += ", " + str(_arg5)
	string_args += ", " + str(_arg6)
	string_args += ", " + str(_arg7)
	string_args += ", " + str(_arg8)
	string_args += ", " + str(_arg9)
	print("study_pseudo_varargs.gd : pseudo_varargs_function called.")
	print("string_args = " + string_args)
	return

_input イベント関数の just_pressed 変数による判定については、以下の記事を参照してください。

 https://compota-soft.work/wp1/wp-admin/post.php?post=47669&action=edit

テスト

F6 キーテストシーン実行すると、起動直後に、引数の個数が異なる各シグナルとの接続、引数の個数が異なる関数呼び出しが行われ、その結果が出力されます。
その後、 0 ~ 9 までのキーを入力すると、それに応じたシグナルが emit されて、1つの関数でそれらを受信した結果が出力されます。

Godot4 疑似可変長引数の関数呼び出しのテストを行うためのシーンの起動

出力結果から、以下の確認ができます。
※出力結果は、ハイライトで3つのブロックを分けて表示しています。

  1. 引数が異なるシグナルと1つの関数との connect (接続)
  2. 引数の個数が 0 ~ 9 まで異なる関数呼び出し
  3. テストシーンを起動後 0 ~ 9 までキーを押して、複数のシグナルから1つの関数へ emit (呼び出し

が正常に行われ、受信側の疑似可変長引数の関数で、引数も受け取れていることが確認できました。

Godot Engine v4.3.stable.official.77dcf97d8 - https://godotengine.org
OpenGL API 3.3.0 NVIDIA 536.67 - Compatibility - Using Device: NVIDIA - NVIDIA GeForce RTX 4060

connect study_pseudo_varargs_signal_arg_0 error_code = 0
connect study_pseudo_varargs_signal_arg_1 error_code = 0
connect study_pseudo_varargs_signal_arg_2 error_code = 0
connect study_pseudo_varargs_signal_arg_3 error_code = 0
connect study_pseudo_varargs_signal_arg_4 error_code = 0
connect study_pseudo_varargs_signal_arg_5 error_code = 0
connect study_pseudo_varargs_signal_arg_6 error_code = 0
connect study_pseudo_varargs_signal_arg_7 error_code = 0
connect study_pseudo_varargs_signal_arg_8 error_code = 0
connect study_pseudo_varargs_signal_arg_9 error_code = 0
pseudo_varargs_function(0 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>
pseudo_varargs_function(1 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>
pseudo_varargs_function(2 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, <null>, <null>, <null>, <null>, <null>, <null>, <null>
pseudo_varargs_function(3 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, <null>, <null>, <null>, <null>, <null>, <null>
pseudo_varargs_function(4 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, <null>, <null>, <null>, <null>, <null>
pseudo_varargs_function(5 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, E, <null>, <null>, <null>, <null>
pseudo_varargs_function(6 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, E, F, <null>, <null>, <null>
pseudo_varargs_function(7 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, E, F, G, <null>, <null>
pseudo_varargs_function(8 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, E, F, G, H, <null>
pseudo_varargs_function(9 args) call
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = A, B, C, D, E, F, G, H, I
study_pseudo_varargs_signal_arg_0.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_1.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, <null>, <null>, <null>, <null>, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_2.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, <null>, <null>, <null>, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_3.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, <null>, <null>, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_4.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, <null>, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_5.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, 5, <null>, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_6.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, 5, 6, <null>, <null>, <null>
study_pseudo_varargs_signal_arg_7.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, 5, 6, 7, <null>, <null>
study_pseudo_varargs_signal_arg_8.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, 5, 6, 7, 8, <null>
study_pseudo_varargs_signal_arg_9.emit
study_pseudo_varargs.gd : pseudo_varargs_function called.
string_args = 1, 2, 3, 4, 5, 6, 7, 8, 9
--- Debugging process stopped ---

EditorUndoRedoManager add_do_method などの可変長引数

EditorUndoRedoManager クラスのメンバ関数 add_do_method などは以下のように可変長引数を、引数の最後に任意の個数、指定できます。

void add_do_method(object: Object, method: StringName, …) vararg

EditorUndoRedoManager — Godot Engine (stable) documentation in English #add_do_method

GodotEngine の GitHub にある editor_undo_redo_manager.h を見ると C++ の GDExtension のような形で可変長引数を実装しているように思います。
こちらの、ある意味王道ともいえる方法を使えば、個数を気にしない本当の可変長引数が実現できると思います。

しかし、 GD Script で手軽に実装できる、デフォルト引数を使った今回紹介した疑似的な可変長引数も、上限はあるけれど現実的に多くのシグナルをサポートできる意味で有用だと思い紹介しました。

まとめ

今回は、無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 で、デフォルト引数を用いて疑似的な可変長引数を持つ関数を実装するスクリプト例を紹介しました。
関数呼び出しだけではなく、引数の個数が異なる複数のシグナル1つの関数で受信するテストシーンとその結果についても紹介しました。

参照サイト Thank You!

記事一覧 → Compota-Soft-Press

すぐに公開する。世界ではじめてかも。

コメント

Ads Blocker Image Powered by Code Help Pro

お願い - Ads Blocker Detected

このサイトは広告を掲載して運営しています。

ポップアップを閉じて閲覧できますが、よろしければ

このサイト内の広告を非表示にする拡張機能をオフにしていただけませんか?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

タイトルとURLをコピーしました