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

※ GodotEngine 4.3 を使用しています。.NET 版ではありません。
※スクリプトは自己責任でご使用ください。
テストシーン
メニュー「シーン」→「新規シーン」を選択してから、シーンドックで「インターフェース」を選んで Control クラスのルートノードを作成します。
ルートノードを選択した後 F2 キーを押して StudyPseudoVarargs に名前を変更します。
作成したルートノードを選択して、右上の+ボタンで study_pseudo_varargs.gd スクリプトを作成して割り当てたらシーンを study_pseudo_varargs.tscn で保存します。
※説明文の Label を子ノードに追加していますが、追加しなくても動作はします。

引数の個数が異なるとシグナルと関数の接続に失敗します
シグナルと受信側メソッドの引数が異なると、実行時に以下のように「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つの関数でそれらを受信した結果が出力されます。

出力結果から、以下の確認ができます。
※出力結果は、ハイライトで3つのブロックを分けて表示しています。
- 引数が異なるシグナルと1つの関数との connect (接続)
- 引数の個数が 0 ~ 9 まで異なる関数呼び出し
- テストシーンを起動後 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!
- Godot Engine – Free and open source 2D and 3D game engine
- @GlobalScope — Godot Engine (stable) documentation in English #globalscope-error
- Control — Godot Engine (4.x)の日本語のドキュメント
- godot/editor/editor_undo_redo_manager.h at master · godotengine/godot · GitHub
- GDExtension — Godot Engine (4.x)の日本語のドキュメント
- EditorUndoRedoManager — Godot Engine (stable) documentation in English #add_do_method
記事一覧 → Compota-Soft-Press
すぐに公開する。世界ではじめてかも。
コメント