Godot GDEXTENSION build 環境構築のまとめ(その3)

Godot

本稿の内容に関して

本稿は GDEXTENSION の開発環境構築を行った際のメモの共有を意図した、一連の記事の3番目。1番目から順に読まれていくことを想定している。

対象としている Godot エンジンのバージョンは 4.2.1。

前稿(その2)では Godot エンジン及びエクスポートテンプレートのビルドを行った。本稿では Godot 公式が提供する GDEXTENSION サンプルを実際に Windows 11 PC で MinGW-w64 x86_64bit アーキテクチャでビルドを行う。

GDEXTENSION サンプルのビルド

公式の GDEXTENSION サンプルをビルドする。

godot-cpp のソースコードの取得

GDEXTENSIONの開発には godot-cpp を用いる。godot-cpp は以下の GitIT-HUB で公開されている。
https://github.com/godotengine/godot-cpp.git

godot-cpp は GODOTエンジンとバージョンを気にするようで、今のところGODOTエンジンとTAGを一致させると無難。

TAGは上の図の場合、”31 tags” と書かれているリンクから確認できる。

godot-4.2.1-stable のTAGを “.\godot-cpp-4.2.1-stable” というディレクトリに取得する場合は以下のようにする。

c:\dev>git clone --depth 1 https://github.com/godotengine/godot-cpp.git -b godot-4.2.1-stable godot-cpp-4.2.1-stable

extension_api.json と gdextension_interface.h の更新

godot-cpp で用いる extension_api.json と gdextension_interface.h を更新する。この二つのファイルは 以下の手順で Godot エンジンから取得することができる。

c:\dev>cd godot-cpp-4.2.1-stable
c:\dev\godot-cpp-4.2.1-stable>cd gdextension
c:\dev\godot-cpp-4.2.1-stable\gdextension>godot.windows.editor.x86_64.exe --dump-extension-api --dump-gdextension-interface --headless
c:\dev\godot-cpp-4.2.1-stable\gdextension>dir
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は 7C92-75B3 です

 c:\dev\godot-cpp-4.2.1-stable\gdextension のディレクトリ

2023/12/17  18:13    <DIR>          .
2023/12/17  18:09    <DIR>          ..
2023/12/27  20:51         5,502,554 extension_api.json
2023/12/27  20:50           107,142 gdextension_interface.h
2023/12/17  16:38               829 README.md
               3 個のファイル           5,896,982 バイト
               2 個のディレクトリ  34,019,409,920 バイトの空き領域

c:\dev\godot-4.2.1-stable\bin>

GDEXTENSION サンプルのビルド

“C:\dev\gdextension_test” というディレクトリにgdextensionのサンプルを作成することとする。設置するファイルは SConstruct と以下のソースコード4つ。

  • gdexample.cpp
  • gdexample.h
  • register_types.cpp
  • register_types.h

これらを以下のように設置する。

C:\dev
├─godot-cpp-4.2.1-stable\
│  
├─gdextension_test\
│  │  SConstruct
│  │
│  └─src\
│        gdexample.cpp
│        gdexample.h
│        register_types.cpp
│        register_types.h
│          
└─tools\

gdexample.h

#ifndef GDEXAMPLE_H
#define GDEXAMPLE_H

#include <godot_cpp/classes/sprite2d.hpp>

namespace godot {

class GDExample : public Sprite2D {
    GDCLASS(GDExample, Sprite2D)

private:
    double time_passed;

protected:
    static void _bind_methods();

public:
    GDExample();
    ~GDExample();

    void _process(double delta);
};
}

#endif

このヘッダファイルでは、Sprite2D クラスを継承する GDExample クラスを定義する。

gdexample.h(部分)

#include <godot_cpp/classes/sprite2d.hpp>

// ~省略~

class GDExample : public Sprite2D {
    GDCLASS(GDExample, Sprite2D)

// ~省略~

この GDEXTENSION は時間経過により位置を変える Sprite2D の実装サンプルで、”double time_passed;” は時間経過を管理するためのメンバ変数。

gdexample.h(部分)

// ~省略~

private:
    double time_passed;

// ~省略~

“_bind_methods()” は任意のユーザー定義メソッドを GDScript からアクセス可能にするためにバインドするメソッド。ただし、このサンプルでは未使用。

gdexample.h(部分)

// ~省略~

protected:
    static void _bind_methods();

// ~省略~

コンストラクタとデストラクタ。

// ~省略~

public:
    GDExample();
    ~GDExample();

// ~省略~

“_process(double delta)” メソッドはフレーム毎にコールされる。引数は前回コールからの差分時間 (msec) .

gdexample.h(部分)

// ~省略~

    void _process(double delta);

// ~省略~

尚、これらシステムからコールされるエントリメソッドは以下のようにいくつかあり、用途に応じて使い分ける。特にコンストラクタのコールタイミングはインスタンスの生成方法により様々となるため実装の初期化で問題がでてくる場合がありえる。よってこれらのエントリを使って適切なタイミングで行われるべく、初期化や運用時の処理を記述する。

void _physics_process(double delta)
// _process()と同様であるが物理演算付きのフレームごとのコール
void _enter_tree()
// シーンツリー配置の際、トップから順番にコールされる。
// 他のノードと初期化の順番を気にする場合有用か
void _exit_tree()
// シーンツリーから取り外される際、ボトムから順番にコールされる。
// 他のノードと後始末の順番を気にする場合有用か
void _ready()
// シーン上に配置され、実行可能となった場合にコールされる。

// 以下は入力関係のハンドルに用いられる
void _input(const Ref<InputEvent> &event)
void _shortcut_input(const Ref<InputEvent> &event)
void _unhandled_input(const Ref<InputEvent> &event)
void _unhandled_key_input(const Ref<InputEvent> &event)

注意:
同じクラスインスタンス中では、GDScript と GDEXTENSION とで、上記メソッドは排他利用となる。これは GDScript でのメソッドは、親クラスの同メソッドをオーバーライドしているため。GDEXTENSIONで _process() や _ready() の定義をしていながら、GDScript でデバッグコードを入れるため定義したら動かなくなった、というようなことが起こるので要注意。

gdexample.cpp

#include "gdexample.h"
#include <godot_cpp/core/class_db.hpp>

using namespace godot;

void GDExample::_bind_methods() {
}

GDExample::GDExample() {
    // Initialize any variables here.
    time_passed = 0.0;
}

GDExample::~GDExample() {
    // Add your cleanup here.
}

void GDExample::_process(double delta) {
    time_passed += delta;

    Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));

    set_position(new_position);
}

“gdexample.cpp” は GDExample クラスの実装。

コンストラクタは time_passed 変数の初期化だけ。デストラクタではなにもしない。

“GDExample::_process(double delta)” メソッドでは時間経過に応じて、new_position を計算し、position 変数の更新を行っている。set_position() メソッドは Sprite2Dクラスの継承元クラスである Node2D クラスが持つメソッドで、position を設定するためのもの。set_position() メソッド以外にも、Node2D クラスは set_texture()、set_centered() などのメンバ変数設定メソッドを持っていて Splite の操作を行うことができる。GDEXTENSION のもっともシンプルな実装として、これらメンバ設定メソッドを用いて、GDScript で記述するには厄介な処理(動的に texture を生成して差し替えるなど)を C++ で記述することが考えられる。

register_types.h

#ifndef GDEXAMPLE_REGISTER_TYPES_H
#define GDEXAMPLE_REGISTER_TYPES_H

void initialize_example_module();
void uninitialize_example_module();

#endif // GDEXAMPLE_REGISTER_TYPES_H

register_types.h は GDEXTENSION の Godot エンジンに登録するためのものであるが、テンプレートとして特に書き換える必要はないだろう。

register_types.cpp

#include "register_types.h"

#include "gdexample.h"

#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>

using namespace godot;

void initialize_example_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }

    ClassDB::register_class<GDExample>();
}

void uninitialize_example_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }
}

extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT example_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
    godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);

    init_obj.register_initializer(initialize_example_module);
    init_obj.register_terminator(uninitialize_example_module);
    init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);

    return init_obj.init();
}
}

register_types.cpp は GDEXTENSION の Godot エンジンに登録するための実装。

GDExample クラス定義ヘッダをインクルードする。

register_types.h(部分)

// ~省略~

#include "gdexample.h"

// ~省略~

initialize_example_module() は init_obj.register_initializer() に登録されるコールバック関数で、Godot エンジンから初期化レベルに応じて複数回コールされる。GDEXTENSION はこれを利用して、タイミング同期を行い、Godot エンジンに GDEXTENSION 実装を登録する。

register_types.h(部分)

// ~省略~

void initialize_example_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }

    ClassDB::register_class<GDExample>();
}

// ~省略~

p_level は以下の4種類。

  • GDEXTENSION_INITIALIZATION_CORE: Godot エンジンコアの初期化完了直後発生
  • GDEXTENSION_INITIALIZATION_SERVERS :Godot エンジンサーバーが初期化された直後に発生
  • GDEXTENSION_INITIALIZATION_SCENE: Godot エンジンのランタイムクラスが登録された直後に発生
  • GDEXTENSION_INITIALIZATION_EDITOR: Godot エディタークラスが登録された直後に発生

GDExample は通常の(エディター機能拡張用ではない)GDEXTENSION なので、GDEXTENSION_INITIALIZATION_SCENE を待って、ClassDB に登録している。もしエディッタ―の機能を拡張する GDEXTENSION ならば、GDEXTENSION_INITIALIZATION_EDITOR を待つ必要がある。

uninitialize_example_module() は init_obj.register_terminator() に登録されるコールバック関数で、Godot エンジンから終了時に複数回コールされる。GDEXTENSION はこれを利用して、ターミネーション時の処理同期タイミングを知る。このサンプルではなにも行っていない。p_level のコール順番は初期化時の逆順となる。

register_types.h(部分)

// ~省略~

void uninitialize_example_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }
}

// ~省略~

SConstruct

#!/usr/bin/env python
import os
import sys

project_name = 'libgdexample'
env = SConscript('c:/dev/godot-cpp-4.2.1-stable/SConstruct')

print('build target:', type(env['target']), env['target'])
print("env['CCFLAGS']:", type(env['CCFLAGS']), env['CCFLAGS'])
print("env['CFLAGS']:", type(env['CFLAGS']), env['CFLAGS'])
print("env['CXXFLAGS']:", type(env['CXXFLAGS']), env['CXXFLAGS'])
print("env['CPPDEFINES']:", type(env['CPPDEFINES']), env['CPPDEFINES'])
print("env['LINKFLAGS']:", type(env['LINKFLAGS']), env['LINKFLAGS'])
print("env['CPPPATH']:")
for i in env['CPPPATH']:
    print("  ",type(i), i)
print("env['LIBS']:")
for i in env['LIBS']:
    print("  ",type(i), i)
env.Append(CPPPATH=["src/"])
sources = Glob("src/*.cpp")

if env['platform'] == "windows" and env['use_mingw'] == True and env['arch'] == "x86_64":
    env.Append(LINKFLAGS = ['-static-libgcc', '-static-libstdc++','-static','-pthread'])
else:
    print("not suppoted conbination!")
    print("platform",env["platform"])
    print("use_mingw",env["use_mingw"])
    print("use_clang_cl",env["use_clang_cl"])
    print("use_static_cpp",env["use_static_cpp"])
    print("android_api_level",env["android_api_level"])
    print("ios_simulator",env["ios_simulator"])
    print("arch",env["arch"])
    sys.exit(1)

print("CPPDEFINES",env['CPPDEFINES'])
library = env.SharedLibrary(
    "project/bin/{}{}{}".format(project_name,env["suffix"], env["SHLIBSUFFIX"]),
    source=sources,
)

Default(library)

このファイルにおいて、以下の部分は、godot-cpp-4.2.1-stable のディレクトリを指定する。

env = SConscript('c:/dev/godot-cpp-4.2.1-stable/SConstruct')

以下の部分はこの SConstruct ファイルがサポートする対象を、Windows プラットフォーム、mingw ビルドツールチェーン、x86 64bit アーキテクチャの組み合わせにに限定している。

if env['platform'] == "windows" and env['use_mingw'] == True and env['arch'] == "x86_64":
    env.Append(LINKFLAGS = ['-static-libgcc', '-static-libstdc++','-static','-pthread'])

実際にやっているのはリンカオプションの指定。スタティックライブラリのリンク指定と、不足しているライブラリ指定を補う。

対象外の組み合わせの場合は、認識できる情報を表示して強制終了する。

else:
    print("not suppoted conbination!")
    print("platform",env["platform"])
    print("use_mingw",env["use_mingw"])
    print("use_clang_cl",env["use_clang_cl"])
    print("use_static_cpp",env["use_static_cpp"])
    print("android_api_level",env["android_api_level"])
    print("ios_simulator",env["ios_simulator"])
    print("arch",env["arch"])
    sys.exit(1)

設置が終われば、新規コマンドプロンプトを開き、以下のようにすることでビルドできる。

まず環境変数の定義とディレクトリの移動。

Microsoft Windows [Version 10.0.22631.2861]
(c) Microsoft Corporation. All rights reserved.

C:\Users\user>.\mingw64
C:\dev>cd gdextension_test

デバッグビルドならば以下を実行。

c:\dev\gdextension_test>scons platform=windows use_mingw=yes custom_api_file=c:/dev/godot-cpp-4.2.1-stable/gdextension/extension_api.json

ここで、”platform=windows” は、windows 用のビルドを指定し、”use_mingw=yes” は、ビルドツールチェーンに “mingw” を使うことを指定している。

custom_api_file の指定は GODOT エンジンから取得した extension_api.json のパスを指定している。ではあるが、今回の手順では “c:\dev\godot-cpp-4.2.1-stable\gdextension” ディレクトリの extension_api.json を上書きしているので指定しなくてもビルドは可能。

リリースビルドならば target=template_release を付け加える。

c:\dev\gdextension_test>scons platform=windows use_mingw=yes target=template_release custom_api_file=C:/dev/godot-4.2.1-stable/bin/extension_api.json

target=template_release を付けなければデフォルトでデバッグビルド指定となる。

初回ビルドでは godot-cpp のフルビルドが行われるため、かなり時間がかかる。

ビルドが成功すると以下のような成果物(および中間成果物)が生成される。

c:\dev\gdextension_test\
│  .sconsign.dblite
│  SConstruct
│
├─project
│  └─bin
│          libgdexample.windows.template_debug.x86_64.dll
│          liblibgdexample.windows.template_debug.x86_64.a
│
└─src
        gdexample.cpp
        gdexample.h
        gdexample.o
        register_types.cpp
        register_types.h
        register_types.o

“libgdexample.windows.template_debug.x86_64.dll” が目的の成果物。※ リリースビルドの場合は “libgdexample.windows.template_release.x86_64.dll” が生成される。

この GDEXTENSION は Sprite2D クラスを継承する GDExsample というノードとなる。つまり、 Sprite2D ノードの機能は全て持つうえで、position が以下の関数の規定に従い自動で動く性質をもつ。

gdexample.cpp(一部抜粋)

void GDExample::_process(double delta) {
    time_passed += delta;

    Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));

    set_position(new_position);
}

SConstruct の補足

SConstruct に以下の記述を入れている。

SConstruct(一部抜粋)

env = SConscript('c:/dev/godot-cpp-4.2.1-stable/SConstruct')

print('build target:', type(env['target']), env['target'])
print("env['CCFLAGS']:", type(env['CCFLAGS']), env['CCFLAGS'])
print("env['CFLAGS']:", type(env['CFLAGS']), env['CFLAGS'])
print("env['CXXFLAGS']:", type(env['CXXFLAGS']), env['CXXFLAGS'])
print("env['CPPDEFINES']:", type(env['CPPDEFINES']), env['CPPDEFINES'])
print("env['LINKFLAGS']:", type(env['LINKFLAGS']), env['LINKFLAGS'])
print("env['CPPPATH']:")
for i in env['CPPPATH']:
    print("  ",type(i), i)
print("env['LIBS']:")
for i in env['LIBS']:
    print("  ",type(i), i)

これは、godot-cpp-4.2.1-stable の scons 設定ファイル SConstruct の実行により得られる、各ビルドオプションの情報を出力するものであり、デバッグビルドの場合、結果は以下のようになる。

build target: <class 'str'> template_debug
env['CCFLAGS']: <class 'SCons.Util.CLVar'> -Wwrite-strings -O2 -fvisibility=hidden
env['CFLAGS']: <class 'SCons.Util.CLVar'>
env['CXXFLAGS']: <class 'SCons.Util.CLVar'> -fno-exceptions -std=c++17
env['CPPDEFINES']: <class 'collections.deque'> deque(['HOT_RELOAD_ENABLED', 'WINDOWS_ENABLED', 'DEBUG_ENABLED', 'DEBUG_METHODS_ENABLED', 'NDEBUG'])
env['LINKFLAGS']: <class 'SCons.Util.CLVar'> -Wl,--no-undefined -static -static-libgcc -static-libstdc++ -s -fvisibility=hidden
env['CPPPATH']:
   <class 'SCons.Node.FS.Dir'> C:\dev\godot-cpp-4.2.1-stable\gdextension
   <class 'SCons.Node.FS.Dir'> C:\dev\godot-cpp-4.2.1-stable\include
   <class 'SCons.Node.FS.Dir'> C:\dev\godot-cpp-4.2.1-stable\gen\include
env['LIBS']:
   <class 'SCons.Node.FS.File'> C:\dev\godot-cpp-4.2.1-stable\bin\libgodot-cpp.windows.template_debug.x86_64.a

これを見れば godot-cpp が何を提供するかが分かるし、これだけの情報があれば、godot-cpp をスタティックリンクライブラリとして、GDEXTENSION ダイナミックリンクライブラリをビルドするための CMakeLists.txt を書き下すこともできるだろう。

特筆すべきは、$CXXFLAGS に “-fno-exceptions” が付いていること。このオプションはスタティックリンクしようとする C++ ライブラリへの枷となるので注意が必要。”-fno-exceptions” を付与すると例外 throw, catch を禁止する代わりに例外のハンドルテーブル生成や、例外発生時のスタック巻き戻しコードを生成しなくなる。つまり例外 throw を使えなくする不便さの代償として、生成コードを小さくするとともに実行速度を早くするメリットを享受するためのコンパイルオプションと言える。GAME アプリケーションなど、パフォーマンス重視の場合、必要となるのだろう。
https://github.com/godotengine/godot-proposals/issues/3302

とはいえ、GDEXTENSION に OSS ライブラリをリンクしようとしている場合、-fno-exceptions を付けてしまうと殆どの OSS ライブラリはコンパイルエラーとなってしまう。それでは都合が悪いので -fno-exceptions 指定でコンパイルしたオブジェクトと、-fexceptions 指定でコンパイルしたオブジェクトは混在可能であることを利用し、GDEXTENSION のビルド部分では例えば以下のサンプルコードのように -fno-exceptions のオプション指定を剥いでしまうと良いだろう。

SConstruct(変更例部分)

import SCons.Util  # To use "SCons.Util.CLVar".

# Strip "-fno-exceptions" from "$CXXFLAGS".
env['CXXFLAGS'] = SCons.Util.CLVar(str(env['CXXFLAGS']).replace("-fno-exceptions", ""))

デバッグビルド時に定義されるプリデファインマクロは以下の通り。

env['CPPDEFINES']: <class 'collections.deque'> deque(['HOT_RELOAD_ENABLED', 'WINDOWS_ENABLED', 'DEBUG_ENABLED', 'DEBUG_METHODS_ENABLED', 'NDEBUG'])

同じくリリースビルド時に定義されるプリデファインマクロは以下の通り。

env['CPPDEFINES']: <class 'collections.deque'> deque(['WINDOWS_ENABLED', 'NDEBUG'])

差分は以下となる。

'HOT_RELOAD_ENABLED', 'DEBUG_ENABLED', 'DEBUG_METHODS_ENABLED'

‘HOT_RELOAD_ENABLED’ は Godot 4.2 で追加になったもの。これは GDEXTENSION ダイナミックリンクライブラリを Godot エンジンを停止させずに差し替えられる機能を有効とする。このプリデファインマクロはリリースビルドでは剝ぎ取られる。

‘DEBUG_ENABLED’ はデバッグビルドの識別に使える。デバッグコードは以下のサンプルコードのように DEBUG_ENABLED を用いて差分コーディングすると良いだろう。

サンプルコード

#ifdef DEBUG_ENABLED
#include <godot_cpp/variant/utility_functions.hpp> // To use "UtilityFunctions::print()".
#endif // DEBUG_ENABLED

Sample::Sample()
{
#ifdef DEBUG_ENABLED
    UtilityFunctions::print("construct.");
#endif // DEBUG_ENABLED
}

UtilityFunctions::print() メソッドは Godot エンジンエディッタのターミナルに文字列を出力する。これを用いて簡易的にデバッグできる。

‘DEBUG_METHODS_ENABLED’ はクラスメソッドの振る舞いをデバッグ用で変えたい場合に用いられる。

プラットフォーム毎に定義されるプリデファインマクロは以下の通り。

android: "ANDROID_ENABLED", "UNIX_ENABLED"
ios:     "IOS_ENABLED", "UNIX_ENABLED"
linux:   "LINUX_ENABLED", "UNIX_ENABLED"
macos:   "MACOS_ENABLED", "UNIX_ENABLED"
web:     "WEB_ENABLED", "UNIX_ENABLED"
windows: "WINDOWS_ENABLED"

これらを用いてプラットフォーム毎の差分コーディングを行えばよい。

GDEXTENSION サンプルの設置

GDEXTENSION は Godot エンジンの拡張機能であるので、Godot エンジンプロジェクトを作成し、その中に組み込む必要がある。参考例として “C:\dev\demos\” というディレクトリ配下に “C:\dev\demos\demo_01” という Godot エンジンプロジェクトを作成する。ここでは説明を簡単にするため、コマンドプロンプトを用いた方法を説明する。

先ずディレクトリの作成。

c:\dev>mkdir demos
c:\dev>cd demos
c:\dev\demos>mkdir demo_01
c:\dev\demos>cd demo_01

空の Godot エンジンプロジェクトを作成。

c:\dev\demos\demo_01>copy nul .\project.godot
        1 個のファイルをコピーしました。

c:\dev\demos\demo_01>dir
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は 7C92-75B3 です

 c:\dev\demos\demo_01 のディレクトリ

2023/12/29  14:14    <DIR>          .
2023/12/29  14:13    <DIR>          ..
2023/12/29  14:14                 0 project.godot
               1 個のファイル                   0 バイト
               2 個のディレクトリ  62,520,664,064 バイトの空き領域

bin ディレクトリを作成し、GDEXTENSION のダイナミックリンクライブラリを設置する。

c:\dev\demos\demo_01>mkdir bin
c:\dev\demos\demo_01>cd bin
c:\dev\demos\demo_01\bin>copy c:\dev\gdextension_test\project\bin\*.dll .

C:\dev\dems\demo_01\bin ディレクトリに以下のような gdextension ファイルを作成する。

gdexample.gdextension

[configuration]

entry_symbol = "example_library_init"
compatibility_minimum = "4.1"

[libraries]

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

ここまでの手順で、ディレクトリ構成は以下のようになる。

C:\dev\demos\demo_01
│  project.godot
│
└─bin\
        gdexample.gdextension
        libgdexample.windows.template_debug.x86_64.dll

デバッグビルドの場合は以上の構成となるが、リリースビルドの場合は gdexample.gdextension は以下を付け加える。

gdexample.gdextension(差分)

  [libraries]

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

同じくリリースビルドを加えた場合のディレクトリ構造は以下のようになる。

C:\dev\demos\demo_01
│  project.godot
│
└─bin\
        gdexample.gdextension
        libgdexample.windows.template_debug.x86_64.dll
        libgdexample.windows.template_release.x86_64.dll  <- 追加 

リリースビルド用の GDEXTENSION ダイナミックリンクライブラリはエクスポート時にしか用いられない。

Godot エンジンプロジェクトを起動する。

c:\dev\demos\demo_01>godot.windows.editor.x86_64.exe -e .

名無しのプロジェクトが起動する。

GDEXTENSION が読み込まれている場合は、左下のリソースツリーに gdexample.gdextension が存在する。

res://
└─bin/
        gdexample.gdextension

先ずシーンを設定する。メニューの「シーン」を選ぶとメニューが現れるので「新規シーン」を選択する。

左ペインの「シーン」のタブに「ルートノードを生成:」という欄が現れるので、「その他のノード」を選ぶ。

検索用のノードツリーが現れるので、「Node2D」を選択し、下にある「作成」を押下する。

画面が 3D 表示から 2D 表示に切り替わる。

ここで左ペインの「シーン」のタブ直下にある「+」を選択するとメニューが現れるので、「子ノードを追加」を選択する。

検索用のノードツリーが現れるので、検索欄に “gdexample” と入力すると、”GDExample” というノードが現れるのが期待値。

“GDExample” を選択し、下部の「作成」を押下すると左ペインのシーンのノードツリーに GDExample のノードが組み込まれ、これを選択すると中央の画面のギズモが自動で動く様が確認できるのが期待値。

“GDExample”は Splite2D のプロパティを持っているので、右ペインのインスペクターでTexture を設定することができ、設定した画像が自動で動く様が確認できる。

本稿の振り返りと次稿について

本稿では Godot 公式が提供する GDEXTENSION のビルドを試した。ただし、Godot 公式の説明とはかなり異なる方法なので、両方を読んでいる方は戸惑われたかもしれない。無論意図が有って変えているが、両者の違いに関して興味を持っていただければ幸いである。

次稿(その4)では C++ ライブラリとして opencv 4.8.1 を選び、それを使って mp4 動画を Godot エンジンで再生する GDEXTENSION サンプルを動かす。socns の一風変わった人となりの紹介でもある。

ということで(その4)に続く。

コメント

タイトルとURLをコピーしました