GDExtensionシンプルに環境構築するよ Pt.4
前回の続き
godot-cppを使ってDLLをビルドします。godot-cppは、GDExtensionを利用するソースコードと、SCons用ビルドスクリプトからなる、ファイル一式です。
godot-cppのセットアップ
ダウンロード
godot-cppはGitHubにあります。godotengine/godot-cppというリポジトリです。
【godot-cpp in GitHub】
https://github.com/godotengine/godot-cpp
リポジトリではなく、特定バージョンのファイルだけをzipとしてダウンロードします。たぶんGitHub Desktopからクローンするほうがスマートなんでしょうけど、筆者はGitHubやGitに苦手意識があるので、Webからダウンロードします。
で、注意点なんですけど、このgodot-cppのバージョンは、godotエディタのバージョンと一致させないといけません。4.1.2のgodot-cppでビルドしたdllを、4.1.1のGodotで使おうとすると、バージョンが違うという旨のエラーが出ます。バージョンが0.0.1でも違うとだめってことです。
これ、公式ドキュメントでも口酸っぱく言われてます。
この記事では、4.1.1のgodot-cppを使います。
下のスクショの、オレンジ枠の個所をクリックして、tagを選びます。
"godot-4.1.1-stable"のzipを選んでダウンロードします。
godot-cpp-godot-4.1.1-stable.zip (1MB未満)
配置
zipを解凍して、次の場所に配置しました。
<C:\godot-cpp-godot-4.1.1-stable>
この時点では6.3MB。
ところで、godot-cppフォルダは不変ではなく、ビルド実行によって中身が変化します。なので、元のzipか、解凍した直後のフォルダは、別の場所に取っておいたほうが、トラブル時にやり直しがはかどると思います。
中には、SConstructという名前のファイルがあります。これがgodot-cppの肝心かなめのスクリプトです。
公式ドキュメント等では、このgodot-cppフォルダを次のように配置することが推奨(?)されています。
【推奨?のフォルダ構成】
総合プロジェクト ←リポジトリ
Godotプロジェクト
godot-cpp(submodule)
GDExtensionプロジェクト
・Godotプロジェクト
・GDExtension用のマイcppファイル一式
・godot-cpp
の3つを1つのリポジトリにするようです。さらに、godot-cppをGitのsubmodule機能で管理しています。submoduleは、自分のリポジトリの中に他人のリポジトリを埋め込む機能のようです。これをすることで、バージョンの更新をしやすくする意図があるように思います。
でもこの記事では、それぞれのフォルダをバラバラに配置することにしました。なぜなら、
Godotのバージョンを頻繁に変えない。
一人で作ってるので、環境を統一させる仕組みが不要。
GDExtensionを頻繁に更新しない。
C++部分にゲームロジックを処理させるような作り方はしない。submoduleがよくわからない。
この記事の環境を構成する3つのフォルダ
バラバラ
1. Godotプロジェクト ←リポジトリ
2. godot-cpp(コピペ)
3. GDExtensionプロジェクト ←リポジトリ
GDExtensionプロジェクトの用意
さあさあ、ついに、サンプルコードをビルドして、(サンプルの)マイDLLを作成します。
C++ソースコードなどを置くフォルダを作ります。ここではひとまずこんなフォルダを作りました。GDE(xtension)のテストなのでGDETest。
<C:\GDETest>
この記事では、このフォルダを、「GDExtensionのプロジェクトフォルダ」として扱います。
サンプルをコピペする
さて、godot-cppの中にはtestという名前のフォルダがあります。これはサンプルプロジェクトのような位置づけのようです。このフォルダの中の、SConstructファイルと、srcフォルダを、先ほど作ったプロジェクトフォルダにコピペします。
紛らわしいですが、godot-cpp直下のSConstructではありません。godot-cppフォルダの中には、少なくとも2つのSConstructファイルがあります。
srcフォルダの中身はcppファイルです。最終的には自分で書くことになるものです。今回はこれはいじりません。
SConstructはSconsから実行させるPythonスクリプトです。テキストエディタで開いて中を見てみましょうー。
Pythonがわからなくても、コードを眺めていると、なんとなく処理が見えてきます。下記のようになことを、順にやってるはずです。
Environmentのインスタンスenvを作成する。
envにCPPPATHとしてsrcフォルダを追加する。
env.SharedLibraryを実行する。
このままではビルドできないので、SConstructの中身を書き換えていきます。
SConstructのパスを変える
SConstructにおいて、envを生成しているのはこの行です。
env = SConscript("../SConstruct")
SConscript関数の意味はわかりませんが、SConstructファイルを引数にしていることが見て取れます。このSConstructは、testのひとつ上のフォルダ、つまりgodot-cpp直下のSConstructです。あっちのSConstructの中身も見てみましょう。
godot-cpp/SConstructの最後の行
Return("env")
内容はさっぱりわかりませんが、最後の行でenvをReturnしてます。C系の言語のreturnと同じだと思います。つまりgodot-cppのSConstructは、envを作って返すスクリプトです。envのひな型を作っているのです。
このenvをアレンジするのが、マイSConstructの役目の一つです。
ところが、互いのSConstructの位置が変わっているので、マイSConstruct側のgodot-cppパスを書き換えてやる必要があります。次の通りです。
# これが元々
env = SConscript("../SConstruct")
↓
# パスを絶対パスにする
env = SConscript("C:/godot-cpp-godot-4.1.1-stable/SConstruct")
# または相対パスにする。
env = SConscript("../godot-cpp-godot-4.1.1-stable/SConstruct")
LINKFLAGSを設定する
実はこのままでもビルドはできます。でも、出来上がるDLLは、MinGW付属のdllに依存したDLLになります。(Pt. 2参照)
ゲームにdllを添付したくないので、できれば埋め込んでおきたい。
そこで、可能な限りライブラリを静的にリンクするように設定を変えます。
マイSConstructに次の一文を追加します。
env.Append(
LINKFLAGS=[
"--static",
"-static-libgcc",
"-static-libstdc++",
]
)
意味はなんとなく分かると思います。これによって、C/C++標準ライブラリと、その他のライブラリが静的にリンクされるようになるようです。
C/C++標準ライブラリだけではだめです。なぜなら、C++の機能そのもの(例外やスレッド)にも、libwinpthread-1.dllなどのDLLを使っているからです。
batファイルを作る
筆者はコマンドプロンプト(cmd.exe)のようなコンソールアプリが苦手です。こういうのはバッチファイル(.bat)を作ることで乗り切ってきました。なので今回もbatを作ります。
<C:\GDETest\BuildGDETest.bat>
.bat拡張子のテキストファイルを作り、本来コマンドプロンプトに打ち込むはずの内容を、各行に書き込みます。
バッチファイルの中身(3行だけ)
cd "C:\GDETest\"
"C:\python-3.12.0-embed-amd64\Scripts\scons.exe" use_mingw=yes
pause
内容は、GDExtensionのプロジェクトフォルダにカレントディレクトリを移動(cd)して、scons.exeを実行するだけです。
カレントディレクトリの移動は必須です。他の場所から実行するとうまく動きません。たぶんgodot-cppのスクリプトの中で、カレントディレクトリに依存するところがあるのだと思います。
sconsにつけているuse_mingw=yesという引数は、sconsではなく、godot-cppスクリプトに対するパラメータです。これを付けないと、godot-cppスクリプトは、デフォルトとしてVCを使ってビルドしようとします。
ビルド実行
さあ、実行!
batファイルをダブルクリックします。ぽちぽちっ!
初回は長い長いビルドが始まります。
筆者のパソコンでは、15分~20分くらいかかります。いいグレードのCPUだと短いと思います。
ところで、ビルドを実行すると、godot-cppフォルダの容量がもりもり増えていきます。これ何してるかというと、中にソースコードを作ってるようです。
いつのまにか、srcとgen(ついでにbin)というフォルダが作られてます。
<C:\godot-cpp-4.1\gen\src>
<C:\godot-cpp-4.1\gen\gen>
これの中身、C++ファイルです。
そうかー、これをするためのSConsなんだなあ、という気がします。
しばらく待つとビルドが終わります。
C:\GDETest>cd "C:\GDETest\"
C:\GDETest>"C:\python-3.12.0-embed-amd64\Scripts\scons.exe" use_mingw=yes
scons: Reading SConscript files ...
C:\GDETest\SConstruct:7: SyntaxWarning: invalid escape sequence '\g'
env = SConscript("C:\godot-cpp-godot-4.1.1-stable/SConstruct")
Auto-detected 8 CPU cores available for build parallelism. Using 7 cores by default. You can override it with the -j argument.
Building for architecture x86_64 on platform windows
scons: done reading SConscript files.
scons: Building targets ...
略
scons: done building targets.
C:\GDETest>pause
続行するには何かキーを押してください . . .
<project\bin>フォルダが作成されていて、その中に成果物が入っています。
<C:\GDETest\project\bin>
libgdexample.windows.template_debug.x86_64.dll
liblibgdexample.windows.template_debug.x86_64.a
6.5MB程度です。
このDLLが、GDExtensionのマイDLLです。やった!
念のため、lucasg/Dependenciesで、依存dllを調べます。
依存dllが、kernel32.dllとMSVCRT.dllしかないことがわかります。これはgodotエディタ本体も使ってるdllです。
また、本DLLを選択状態にすると、右側に公開関数一覧が表示されます。この中に"example_library_init"が含まれていればオッケーです。この関数が、GDExtensionで最初に呼ばれる関数です。
この時点でのパソコンの状態の変化は次の通りです。
【手動で配置したツール】
C:\mingw64\bin -MinGW
C:\python-3.12.0-embed-amd64 -Python
C:\godot-cpp-godot-4.1.1-stable -godot-cpp
【インストールしたPython Package】
C:\python-3.12.0-embed-amd64\Scripts -SCons
【プロジェクト】
C:\GDETest
【環境変数 "Path"】
C:\mingw64\bin\
【一時ファイルやキャッシュの類】
C:\Users\ユーザ名\AppData\Local\pip
なので、パソコンを元に戻したければ、これらフォルダや"Path"を消せば元に戻れるはずです。
つづく~!