🏷️

GDScriptでネームスペースっぽい使い方をする

2022/09/27に公開

概説

独自のクラスを沢山実装していると、クラス名の衝突などが心配になることがあるかと思います。そんな時のために、GDScriptでネームスペースっぽいものを実現してみる実験です[1][2]

実装例

以下の例では、NameSpaceANameSpaceBというクラスを実装します。どちらにもChildという名前の定数を宣言し、スクリプトファイルを読み込みます。NameSpaceA, Bの違いはクラス名と、参照しているスクリプトファイルのみです[3]

ネームスペースA
extends Node
class_name NameSpaceA

const Child = preload("res://namespace_a_child.gd") # A用のスクリプトを読み込む
ネームスペースB
extends Node
class_name NameSpaceB

const Child = preload("res://namespace_b_child.gd") # B用のスクリプトを読み込む 

NameSpaceA, Bがそれぞれ参照しているスクリプトは以下の通りです。ログを出力するtest()関数を宣言しただけのすごく単純なものになっています。ABでは出力される文字列だけが違うことがわかると思います。

namespace_a_child.gd
extends Node # A用のスクリプト

func test():
    print("foo")
namespace_b_child.gd
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にしてみても良さそうです。

namespace_a_child.gd
# 上述の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に限定されるため、スクリプトを読み込む順番次第ではエラーが出る可能性もあります。

ネームスペースA
extends Node
class_name NameSpaceA

class Child extends Node: # インナークラスを宣言
    func test():
        print("foo")

おわり

今すぐ役に立つのかと言われれば微妙かもしれませんが、クラス名が衝突しそうな状況では使えそうです。ちなみに、Godot 4.0時点では定数内部クラスのどちらを使った実装でもコード補完も動きます。

脚注
  1. 一応ネームスペース機能のリクエストは出ていますが、活発に議論されている様子はなく4.0時点では実装される予定はないです。https://github.com/godotengine/godot-proposals/issues/1566 ↩︎

  2. 重複しないクラス名を付ければいいだけですが…。 ↩︎

  3. ちなみにGodotのスタイルガイドでは、loadpreloadでスクリプトを読み込む際には(変数や)定数名をPascalCaseで宣言することとなっています。それ以外の場合、定数はCONSTANT_CASEです。 ↩︎

Discussion