9月29日、Conan Blogが「Using any C++ library in Godot」と題した記事を公開した。C++パッケージマネージャーのConanとgodot-cppを使い、Godotゲームエンジンに任意のC++ライブラリを組み込む手法を、10万パーティクルのECSシミュレーションという実例とともに解説している。
10万パーティクルで示す「C++統合」の威力
デモを先に見てほしい。10万個のパーティクルがマウスカーソルから逃げ回り、ウィンドウの端で跳ね返り続ける。GDScriptでこの規模の処理を書けば即座にフレームレートが崩壊するが、C++のECSライブラリflecsをGodotに組み込むことで、ゲームエンジン側の描画システムと組み合わせながら滑らかに動かせる。
GodotはMITライセンスのオープンソースゲームエンジンだ。2023年のUnity料金体系騒動を機に移行者が急増し、現在も開発コミュニティが急速に拡大している。GDScriptは習得しやすく小〜中規模のゲームには十分だが、大量エンティティの物理シミュレーションや既存のC++資産を転用したい場合には、どうしてもC++との橋渡しが必要になる。ところが「どのC++ライブラリでもGodotで使える」ワークフローは、これまで複雑なビルド管理を強いるものだった。この記事では、その課題をConanで一気に解決する方法を解説している。
GDExtension:エンジンを改造せずC++を追加する仕組み
GodotにC++コードを追加するルートは2つある。
- Engine modules: エンジン本体に組み込む。内部APIへの完全なアクセスが可能だが、エンジン自体とすべてのプラットフォーム向けエクスポートテンプレートを自前でビルドする必要がある。
- GDExtension: 公式バイナリに変更を加えずに共有ライブラリ(
.dll/.so/.dylib)を実行時ロードする。安定したCインターフェース経由でエンジンとやり取りするため、Godotのバージョンアップに対して比較的耐性がある。
ほとんどのプロジェクトにはGDExtensionが推奨される。godot-cppはそのCインターフェースをラップし、エンジン内部と近いAPIをC++から扱えるようにするライブラリだ。バージョン10.0からは、単一リリースでGodot 4.3以降のすべてのバージョンに対応できるようになった(api_versionビルドオプションで指定)。
godot-cppは以下の3ターゲット向けにコンパイルする必要がある。
| ターゲット | 用途 |
|---|---|
template_debug |
エディタおよびデバッグエクスポート(デフォルト) |
template_release |
リリースエクスポート |
editor |
エディタ専用ライブラリ |
どのライブラリを読み込むかは.gdextensionファイルのフィーチャータグで制御される。
[libraries]
macos.debug = "res://bin/libgdexample.template_debug.dylib"
macos.release = "res://bin/libgdexample.template_release.dylib"
linux.debug = "res://bin/libgdexample.template_debug.so"
linux.release = "res://bin/libgdexample.template_release.so"
Conanが解決するビルド地獄
ConanはC/C++向けのパッケージマネージャーで、vcpkgと並ぶ主要ツールだ。CMakeとの連携が強力で、クロスコンパイルやプロファイルによるビルド設定の切り替えを得意とする。
従来のGDExtensionワークフロー(Gitサブモジュール+SCons)では、プロジェクトごとにターゲット×プラットフォーム×アーキテクチャの組み合わせ分だけgodot-cppをコンパイルし直す必要があった。ConanCenterにgodot-cppのレシピが追加されたことで、godot-cppが通常のパッケージと同じように扱えるようになった。api_versionとtargetはConanオプションとして指定でき、ビルド済みバイナリはプロジェクト間で再利用される。ConanCenterには記事執筆時点で1,900以上のライブラリが登録されており、godot-cppと並べて依存関係に追加するだけで全プラットフォーム向けに一貫してビルドできる。
実装:flecsでECSシミュレーションをGodotに統合する
シミュレーションに使うのは**flecs**、C/C++向けのEntity Component System(ECS)ライブラリだ。ECSはゲーム開発でよく使われるアーキテクチャパターンで、エンティティはIDのみを持ち、コンポーネントはデータ構造体、システムはコンポーネントの集合に対して走る関数として分離される。同じ型のコンポーネントはメモリ上に連続して格納されるため、大量エンティティの反復処理が高速になる。
Godotのノードシステムは柔軟な階層構造を持つ一方、10万ノードをシーンツリーに並べることは現実的でない。flecsでロジックを処理し、描画は**MultiMesh**(同一メッシュを1回のドローコールで大量描画するGodotの仕組み)に任せることで、両者の得意領域を組み合わせている。
サンプルコードはConan examples2リポジトリに公開されている。
依存関係の宣言(conanfile.py)
def requirements(self):
self.requires("godot-cpp/10.0.0")
self.requires("flecs/4.1.6")
Godot固有の設定はgenerate()内のみで、godot-cppのtargetオプションをCMakeに渡して出力ライブラリ名を合わせるだけだ。
CMakeLists.txt
find_package(godot-cpp REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
add_library(gdexample SHARED
src/register_types.cpp
src/swarm.cpp
)
target_link_libraries(gdexample PRIVATE godot-cpp flecs::flecs_static)
標準的なCMakeプロジェクトそのものだ。godot-cppとflecsを静的リンクするため、配布物はライブラリファイル1つで済む。
ノードの実装
SwarmクラスはNode2Dを継承し、flecsのワールドを内包する。_bind_methods()でGodotに公開するプロパティ(countやflee_radius)を宣言すると、インスペクターに表示されGDScriptから操作できる。
void Swarm::_process(double p_delta) {
mouse = get_local_mouse_position();
world.progress(static_cast<float>(p_delta));
// flecsの計算結果をMultiMeshバッファにコピー
render_query.each([&](const Position &p, const Velocity &v) { ... });
multimesh->set_buffer(buffer);
}
ビルド
$ conan build . --build=missing
...
[100%] Linking CXX shared library .../demo/bin/libgdexample.template_debug.dylib
Conanがgodot-cppとflecsを解決し、ConanCenterに対応バイナリがあればダウンロード、なければソースからビルドし、CMake連携ファイルを生成してビルドまで一気に完了する。
注意: godot-cppはC++17が必要。MSVCのデフォルトプロファイルは古い規格を使うため、
-s compiler.cppstd=17の追加が必要になる場合がある。
Godot 4.7(記事執筆時点の最新安定版)でプロジェクトを開くと、Swarmが組み込みノードと同様に「Create New Node」ダイアログのNode2D配下に現れる。GDExtensionで登録したクラスはエディタ上ではプレースホルダーとして表示され、ゲーム実行時にのみ実体が初期化される仕様のため、シミュレーションもゲーム実行時に動き出す。
詳細はUsing any C++ library in Godotを参照していただきたい。