GDScriptでネームスペースっぽい使い方をする
概説
独自のクラスを沢山実装していると、クラス名の衝突などが心配になることがあるかと思います。そんな時のために、GDScript
でネームスペースっぽいものを実現してみる実験です[1][2]。
実装例
以下の例では、NameSpaceA
とNameSpaceB
というクラスを実装します。どちらにもChild
という名前の定数
を宣言し、スクリプトファイルを読み込みます。NameSpaceA, B
の違いはクラス名と、参照しているスクリプトファイルのみです[3]。
extends Node
class_name NameSpaceA
const Child = preload("res://namespace_a_child.gd") # A用のスクリプトを読み込む
extends Node
class_name NameSpaceB
const Child = preload("res://namespace_b_child.gd") # B用のスクリプトを読み込む
NameSpaceA, B
がそれぞれ参照しているスクリプトは以下の通りです。ログを出力するtest()
関数を宣言しただけのすごく単純なものになっています。A
とB
では出力される文字列だけが違うことがわかると思います。
extends Node # A用のスクリプト
func test():
print("foo")
extends Node # B用のスクリプト
func test():
print("bar")
動かしてみる
実際に動かしてみましょう。以下のノードは読み込まれると、NameSpaceA, B
の両方のChild
を初期化し、test()
関数を実行します。
extends Node
func _ready():
var a = NameSpaceA.Child.new()
a.test() # "foo"と出力されます
var b = NameSpaceB.Child.new()
b.test() # "bar"と出力されます
ちゃんと動きました。
static
関数を使ってみる
読込先に変数を保持しないのであれば、関数をstatic
にしてみても良さそうです。
# 上述のA用のスクリプトの関数をstaticにしたもの
# B用のスクリプトは割愛します
extends Node
static func test():
print("foo")
これを動かしてみます。
extends Node
func _ready():
var a = NameSpaceA.Child.test() # "foo"と出力されます
var b = NameSpaceB.Child.test() # "bar"と出力されます
こちらもちゃんと動きました。
内部クラスでもできる
定数 + スクリプトファイル
ではなく、内部クラス(インナークラス / サブクラス)でも同じように実装ができます。以下のコードでは、冒頭で示したNameSpaceA
& 参照先のスクリプト
と全く同じ処理を、内部クラスを使って実装しています。
単純な実装ならば、スクリプトファイルが別れずに済むし内部クラスを使う方がいいかもしれません。定数
を使った実装は、フォルダ構造の変更に弱いというデメリットもあります。また、定数
でのスクリプト読み込みはload
が使えず、preload
に限定されるため、スクリプトを読み込む順番次第ではエラーが出る可能性もあります。
extends Node
class_name NameSpaceA
class Child extends Node: # インナークラスを宣言
func test():
print("foo")
おわり
今すぐ役に立つのかと言われれば微妙かもしれませんが、クラス名が衝突しそうな状況では使えそうです。ちなみに、Godot 4.0
時点では定数
と内部クラス
のどちらを使った実装でもコード補完も動きます。
-
一応ネームスペース機能のリクエストは出ていますが、活発に議論されている様子はなく
4.0
時点では実装される予定はないです。https://github.com/godotengine/godot-proposals/issues/1566 ↩︎ -
重複しないクラス名を付ければいいだけですが…。 ↩︎
-
ちなみにGodotのスタイルガイドでは、
load
やpreload
でスクリプトを読み込む際には(変数や)定数名をPascalCase
で宣言することとなっています。それ以外の場合、定数はCONSTANT_CASE
です。 ↩︎
Discussion