🔩

【Godot4】GDExtensionの始め方

2024/06/26に公開

0. この記事の内容

大量の計算を行ったり、複雑なアルゴリズムをGodotで行いたい時、GDExtensionでは物足りない場合があります。そんな時はGDExtensionの出番です。エンジンをコンパイルせずともC++やその他言語で処理を書き、Godotで利用することができるのです。しかし、GDExtensionに関する情報(特に日本語)は少なく、なかなかハードルが高いのが現状です。今回GDExtensionを利用する機会があったので、GDExtensionの作り方と流れを備忘録としてまとめてみました。

1. 参考にした記事

2. 環境構築

C++でGDExtensionを作成しビルドするには次の3つが必要です。

  • MinGW
  • Python
  • SCons

SConsはビルド処理をPythonで記述できるツールで、MinGWはC++のコンパイルに使用します。

2.1 MinGWを準備する。

MinGWの公式サイトの左からDownload(のページに行き、Mingw-buildsの項目に記載されているGitHubのリポジトリから
x86_64-13.2.0-release-win32-seh-msvcrt-rt_v11-rev0.7z
をダウンロードします。

ダウンロードしたファイルを解凍し、適当なディレクトリに置きます。今回はCドライブ直下にしました。mingw64の中にbinというフォルダがあり、以下のようなパスになっていることを確認します。
C:\mingw64\bin\

次にこのパスを環境変数Pathに追加します。Windowsキーを押した検索欄にenvと入力すると、環境変数の設定画面にすぐ飛べます。便利。
ターミナルでwhere gccと入力し、C:\mingw64\bin\が出力されていればOKです。

2.2 Pythonを準備する

Pythonの公式サイトからダウンロードします。インストーラを使うのが簡単です。インストール時はAdd Python to PATHのチェックボックスを有効にします。インストーラを使うと同時にpipもインストールされます。ターミナルでwhere pythonwhere pipと入力し、どちらも正常にパスが表示されればインストール完了です。

2.3 SConsを準備する

ターミナルからpython -m pip install sconsと入力すると、SConsのインストールが始まります。where sconsでscons.exeのパスが表示されれば準備完了です。

3. プロジェクトを作成する

3.1 godot-cppを用意する

GitHubから対象のバージョンを選びダウンロードします。GDExtensionを使うGodotエディタのバージョンと揃えましょう。ダウンロードしたgodot-cpp-godot-◯.◯.◯-stable(以下godot-cpp)にはtestというフォルダがあります。これがGDExtensionのサンプルとなるプロジェクトフォルダで、このtestフォルダをコピペして編集することで簡単にGDExtensionが作成できます。

3.2 testフォルダの構造を確認する

testフォルダはGDExtension作成時の見本となるフォルダです。この構成を見本にしてオリジナルのGDExtensionプロジェクトを作成します。

test/
├ project/
│   ├ bin/
│   ├ example.gdextension
│   └ その他色々...
├ src/
│   ├ exmample.h
│   ├ exmample.cpp
│   ├ register_types.h
│   └ reister_types.cpp
├ SConstruct
└ その他色々...

実際にGDExtensionを作る際は、example.hexample.cppが自分の作ったスクリプトに置き換わるイメージです。一方、register_types.hregister_types.cppexample.gdextension、そしてSConstructはそのまま利用しますが、一部編集が必要です。

3.3 オリジナルGDExtensionのプロジェクトをつくる

testフォルダをgodot-cppの外の適当なところにコピペし、名前を変更します(今回はmy_gdextensionとします)。場所はどこでも良いですが、

Your folder structure should now look like this:

gdextension_cpp_example/
|
+--demo/                  # game example/demo to test the extension
|
+--godot-cpp/             # C++ bindings
|
+--src/                   # source code of the extension we are building

In the src folder, we'll start with creating our header file for the GDExtension node we'll be creating.

GDExtension C++ の例

とのことです。今回の場合、gdextension_cpp_examplemy_gdextensionフォルダに相当し、my_gdextensionフォルダの中に、Godotのプロジェクトとgodot-cppを入れる感じです。多分。

私は最初よくわからずに、

gdextensions/
├ my_gdextension/
└ godot-cpp/

という構成で初めてしまいましたが、問題ありませんでした。
この記事では公式の推奨する構成を前提とします。

3.4 SConstructを編集する

my_gdextensionのSConstructを開くと上の方に、

env = SConscript("../SConstruct")

と書いてあります。これは、godot-cppのSConstructを呼び出す処理で、そのパスを指定すれば良いわけです。今回の場合は

env = SConscript("./godot-cpp/SConstruct")

となります。godot-cppの部分は実際のフォルダ名:godot-cpp-godot-◯.◯.◯-stableに置き換えてください。今回は相対パスで指定していますが、絶対パスでもOKです。

次に必要なライブラリを静的にリンクしておきます。先ほど編集した行の次に

env.Append(
    LINKFLAGS=[
        "--static",
        "-static-libgcc",
        "-static-libstdc++",
    ]
)

を追加します。これをしないとMinGWに依存してしまうので、忘れず追記しましょう。

3.5 .gdextensionを編集する

exampe.gdextensionの中身を覗いてみましょう。

example.gdextension
[configuration]

entry_symbol = "example_library_init"
compatibility_minimum = "4.1"

[libraries]

macos.debug = "res://bin/libgdexample.macos.template_debug.framework"
macos.release = "res://bin/libgdexample.macos.template_release.framework"
windows.debug.x86_32 = "res://bin/libgdexample.windows.template_debug.x86_32.dll"
windows.release.x86_32 = "res://bin/libgdexample.windows.template_release.x86_32.dll"
windows.debug.x86_64 = "res://bin/libgdexample.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://bin/libgdexample.windows.template_release.x86_64.dll"
windows.debug.arm64 = "res://bin/libgdexample.windows.template_debug.arm64.dll"
windows.release.arm64 = "res://bin/libgdexample.windows.template_release.arm64.dll"
linux.debug.x86_32 = "res://bin/libgdexample.linux.template_debug.x86_32.so"
linux.release.x86_32 = "res://bin/libgdexample.linux.template_release.x86_32.so"
linux.debug.x86_64 = "res://bin/libgdexample.linux.template_debug.x86_64.so"
linux.release.x86_64 = "res://bin/libgdexample.linux.template_release.x86_64.so"
linux.debug.arm32 = "res://bin/libgdexample.linux.template_debug.arm32.so"
linux.release.arm32 = "res://bin/libgdexample.linux.template_release.arm32.so"
linux.debug.arm64 = "res://bin/libgdexample.linux.template_debug.arm64.so"
linux.release.arm64 = "res://bin/libgdexample.linux.template_release.arm64.so"
linux.debug.rv64 = "res://bin/libgdexample.linux.template_debug.rv64.so"
linux.release.rv64 = "res://bin/libgdexample.linux.template_release.rv64.so"
android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so"
android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so"
android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so"
android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so"
ios.debug = "res://bin/libgdexample.ios.template_debug.xcframework"
ios.release = "res://bin/libgdexample.ios.template_release.xcframework"
web.debug.wasm32 = "res://bin/libgdexample.web.template_debug.wasm32.wasm"
web.release.wasm32 = "res://bin/libgdexample.web.template_release.wasm32.wasm"

[dependencies]
ios.debug = {
  "res://bin/libgodot-cpp.ios.template_debug.xcframework": ""
}
ios.release = {
  "res://bin/libgodot-cpp.ios.template_release.xcframework": ""
}

entry_symbol = "example_library_init"ではregister_types.cppexample_library_initという関数を登録しています。これによりGDExtensionのクラスをGodotに登録しています。
[libraries]の項目では読み込むdllのパスを指定しています。今回はWindows環境を想定しているため、

windows.debug.x86_64 = "res://bin/libgdexample.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://bin/libgdexample.windows.template_release.x86_64.dll"

以外は消してしまって大丈夫です。dllの名前やパスが異なる場合は編集してください。

4. オリジナルGDExtension作成の流れ

ここまで来たら準備完了です。

  1. srcフォルダにソースファイルを作成する
  2. register_types.cppから作成したクラスを登録する
  3. コマンドscons use_mingw=yesでビルド
  4. my_gdextension/project/bin/のdllファイルをGodotのプロジェクトにコピペする(慣習的にres://bin/に置く)
  5. my_gdextension/project/example.gdextensionを同じフォルダに置く

Godotエディタを再起動して、追加したクラスが認識されていれば成功です。Godot4.2ではGDExtensionのホットリロードに対応しているそうですが、私の場合は再起動が必要でした(?)。

Discussion