【Godot】Editor拡張する (Inspector Plugin編)
みなさんEditor拡張沼に浸かっていますか?
ゲームエンジンと言ったらEditor拡張ですよね。
Godot Engineも例に漏れず、強力なEditor拡張が行えます。
↓こんなこともできました。
GodotのEditor拡張。InspectorPluginでEffekseerのプレビューをインスペクタで出来るようにしてみた。Effekseer開いて編集も楽ちん #GodotEngine pic.twitter.com/GEwfak52Qy
— うえした (@ueshita) 2021年10月1日
今回は↑のようなインスペクタ拡張を行う方法を紹介したいと思います。
公式の参考ページ
キャラを増やすボタンをインスペクタに置く
ボタンを押してキャラを増やすシンプルなEditor拡張を行います。

1. プラグインの体裁を整える
インスペクタを拡張するのでInspector Pluginを作成します。
Inspector PluginをGodotエディタで実行するためには、Godotプラグインの体裁を整える必要があります。
今回はMyPluginという名前で作りました。
まずプロジェクトルートから次のディレクトリを掘ります。
- res://addons/MyPlugin/
ここに次のファイルを追加します。
- plugin.cfg
[plugin] name="MyPlugin" description="A fantastic editor plugin." author="" version="1.0.0" script="plugin.gd"
- plugin.gd
tool
extends EditorPlugin
func _enter_tree():
pass
func _exit_tree():
pass
そうすると、プロジェクト設定のプラグイン一覧に追加されるので 有効 にチェックを入れます。

2. Inspector Pluginを作成する
Inspector Pluginのスクリプトを書きます。
今回はインスペクタにカスタムコントロールとしてボタンを追加して、ボタンが押されたらキャラをランダムに配置するようにしました。
- MyInspectorPlugin.gd
tool extends EditorInspectorPlugin class_name MyInspectorPlugin var target: MyCharactor # 選択されたノード、ファイルがこのスクリプトの対象かどうか判定する func can_handle(object: Object) -> bool: # オブジェクトのクラスがMyCharactorだったら対象とする return object is MyCharactor # インスペクタに表示されるときに1回呼ばれる func parse_begin(object: Object) -> void: # インスペクターに表示しているオブジェクトを保存 target = object # ボタンを作る var button = Button.new() button.text = "押すと増える" # イベント登録 button.connect("pressed", self, "_button_pressed") # UIのカスタムコントロールとして追加 add_custom_control(button) # ボタンが押されると呼ばれる func _button_pressed(): # エディタのルートノード var root = target.get_tree().edited_scene_root # キャラをロードしてインスタンス化する var chara = load("res://models/Dman.fbx").instance() as Spatial # 位置をランダムに配置する chara.transform.origin = Vector3(rand_range(-3, 3), rand_range(-3, 3), rand_range(-3, 3)) # ルートの子として追加 root.add_child(chara) # キャラのオーナーを設定 (Editorでは必要) chara.set_owner(root) print("増えたぞ")
今回、Insupector Pluginの対象判定のためにクラス型を使っているため、対象になる空のクラスを作りました。
- MyCharactor.gd
extends Node class_name MyCharactor
3. 動作確認する

適当なノードに先ほどの空クラスのスクリプトをアタッチしています。
このノードをクリックするとインスペクタにボタンが追加されていると思います。
このボタンを押しまくると…

D言語くんが大量増殖しました!
なんか上手くいかないときは、プラグインの有効チェックを外して再度有効にするとリロードが行われます。
インスペクタで3Dレンダリングを行う
EditorInspectorPluginのadd_custom_controlにはControlノードを突っ込めるため、ぶっちゃけ何でもできます。
例えばノードやアセットの3Dプレビュー機能が欲しかったりしたら、ViewportContainerを使って3Dレンダリングを行うこともできます。

1. Controlシーンを作る
今回はレスポンシブルにするため、ルートノードの型をVBoxContainerにしました。

ViewportContainer

Stretchにチェックを入れるMin SizeのYを設定しないと、Viewportがゼロサイズになってしまうので適当な値を入れる
Viewport

Own Worldにチェックを入れる
3Dシーン
Camera, DirectionalLight, 適当なキャラを配置します。
2. スクリプトを作る
CharaPreview.gd
動かないのは面白くないので、tool属性を付けたスクリプトをキャラにアタッチしました。
tool extends Spatial func _process(delta: float): transform = transform.rotated(Vector3.FORWARD, deg2rad(360.0) * delta)
MyInspectorPlugin.gd
作成したシーンをロードしてadd_custom_controlに突っ込むスクリプトを書きます。
tool extends EditorInspectorPlugin class_name MyInspectorPlugin var target: MyCharactor var scene: Control # 選択されたノード、ファイルがこのスクリプトの対象かどうか判定する func can_handle(object: Object) -> bool: # オブジェクトのクラスがMyCharactorだったら対象とする return object is MyCharactor # インスペクタに表示されるときに1回呼ばれる func parse_begin(object: Object) -> void: # インスペクターに表示しているオブジェクトを保存 target = object # シーンを追加する scene = load("res://addons/MyPlugin/MyInspectorPlugin.tscn").instance() # ボタン押下のイベント登録 #scene.get_node("Button").connect("pressed", self, "_button_pressed") # UIのカスタムコントロールとして追加 add_custom_control(scene)
3. 動作確認する
インスペクタで荒ぶるD言語くんが見れました。

おわりに
Godot EngineではInspector Pluginでなんでも出来ることが分かりました。
この調子でゲーム制作がどんどん効率化できそうです。
しかし、効率よくゲームを作るためEditor拡張していたら夢中になってしまい、いつまで経ってもゲームが出来上がらないなんてことは避けましょうね。
おまけ
Effekseerプラグインに実例があるので、よかったらご参考ください。 https://github.com/effekseer/EffekseerForGodot3/tree/main/Dev/Godot/addons/effekseer