GDNative Cの例¶
はじめに¶
このチュートリアルでは、GDNativeモジュールの作成に最低限必要なものを紹介します。これがGDNativeの世界への出発点になるはずです。このチュートリアルの内容を理解することは、この後のすべてを理解するのに役立ちます。
Before we begin, you can download the source code to the example object we describe below in the GDNative-demos repository.
このサンプルプロジェクトには、コンパイルを少し簡単にするSConstructファイルも含まれていますが、このチュートリアルでは、プロセスを理解するために手動で作業を行います。
GDNativeを使用すると、PluginScriptやARVRInterfaceGDNativeなどのインターフェースを利用して、Godotに追加する複数のタイプの要素を作成できます。このチュートリアルでは、NativeScriptモジュールの作成について見ていきます。 NativeScriptを使用すると、GDScriptファイルを作成するのと同様の方法でCまたはC++でロジックを作成できます。このGDScriptに相当するCを作成します。
extends Reference
var data
func _ready():
data = "World from GDScript!"
func get_data():
return data
今後のチュートリアルでは、他のタイプのGDNativeモジュールに焦点を当て、各モジュールをいつどのように使用するかを説明します。
前提条件¶
始める前に、いくつかのものが必要です:
ターゲットバージョンのGodot実行可能ファイル。
Cコンパイラ。 Linuxでは、パッケージマネージャーから
gcc
またはclang
をインストールします。 macOSでは、Mac App StoreからXcodeをインストールできます。 Windowsでは、Visual Studio 2015以降またはMinGW-w64を使用できます。A Git clone of the godot-headers repository: these are the C headers for Godot's public API exposed to GDNative.
次に行う作業については、このGDNativeサンプルプロジェクト専用のフォルダを作成し、そのフォルダ内でターミナルを開いて実行することをお勧めします。
git clone https://github.com/godotengine/godot-headers.git --branch=3.4
これにより、必要なファイルがそのフォルダにダウンロードされます。
ちなみに
If you plan to use Git for your GDNative project, you can also add
godot-headers
as a Git submodule.
注釈
The godot-headers
repository has different branches. As Godot evolves,
so does GDNative. While we try to preserve compatibility between version,
you should always build your GDNative module against headers matching the
Godot stable branch (e.g. 3.4
) and ideally actual release (e.g.
3.4.4-stable
) that you use.
GDNative modules built against older versions of the Godot headers may
work with newer versions of the engine, but not the other way around.
The master
branch of the godot-headers
repository is kept in line with
the master
branch of Godot and thus contains the GDNative class and
structure definitions that will work with the latest development builds.
If you want to write a GDNative module for a stable version of Godot, look at
the available Git tags (with git tags
) for the one matching your engine
version. In the godot-headers
repository, such tags are prefixed with
godot-
, so you can e.g. checkout the godot-3.4.4-stable
tag for use with
Godot 3.4.4. In your cloned repository, you can do:
git checkout godot-3.4.4-stable
If a tag matching your stable release is missing for any reason, you can fall
back to the matching stable branch (e.g. 3.4
), which you would also check
out with git checkout 3.4
.
GDNativeに影響する独自の変更を使用してソースからGodotをビルドしている場合、更新されたクラスと構造の定義は次の場所にあります。<godotsource>/modules/gdnative/include
私たちのCソース¶
メインコードを書くことから始めましょう。最終的には、下の図の各行と同様なファイル構造を作成します。
+ <your development folder>
+ godot-headers
- <lots of files here>
+ simple
+ bin
- libsimple.dll/so/dylib
- libsimple.gdnlib
- simple.gdns
main.tscn
project.godot
+ src
- simple.c
Open up Godot and create a new project called "simple" alongside your
godot-headers
Git clone. This will create the simple
folder and
project.godot
file. Then manually create a src
folder alongside the
simple
folder, and a bin
subfolder in the simple
folder.
まず、 simple.c
ファイルに何が含まれているかを見てみましょう。ここで例では、説明を単純にするためにヘッダーを用意しない単一の C ソース ファイルを作成しています。大きなプロジェクトの作成を開始したら、プロジェクトを複数のファイルに分割することをお勧めします。ただし、それはこのチュートリアルの範囲外です。
ソースコードを少しずつ見ていきますので、以下のすべての部分を1つの大きなファイルにまとめて行く必要があります。各セクションの説明は、それを追加するときに行います。
#include <gdnative_api_struct.gen.h>
#include <string.h>
const godot_gdnative_core_api_struct *api = NULL;
const godot_gdnative_ext_nativescript_api_struct *nativescript_api = NULL;
上記のコードには、GDNative API構造体ヘッダーと、文字列操作のために使用する標準ヘッダーが含まれています。次に、2つの異なる構造体への2つのポインターを定義します。 GDNativeは、メインのGodot実行可能ファイルにコールバックするための機能の、大規模なコレクションをサポートしています。モジュールがこれらの関数にアクセスできるようにするため、GDNativeはこれらすべての関数へのポインターを含む構造体をアプリケーションに提供します。
この実装をモジュール化し、簡単に拡張できるようにするため、コア関数は「コア」API構造体から直接使用できますが、追加の関数には独自の「GDNative構造体」があり、拡張機能からアクセスできます。
この例では、これらの拡張機能の1つにアクセスして、NativeScriptに特に必要な関数にアクセスします。
NativeScriptは、Godotの他のスクリプトと同様に動作します。NativeScript APIはかなり低レベルのため、ライブラリはGDScript などの他のスクリプトシステムよりも冗長に多くのことを指定する必要があります。NativeScriptインスタンスが作成されると、ライブラリ指定のコンストラクターが呼び出されます。そのインスタンスが破棄されると、指定されたデストラクターが実行されます。
void *simple_constructor(godot_object *p_instance, void *p_method_data);
void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data);
godot_variant simple_get_data(godot_object *p_instance, void *p_method_data,
void *p_user_data, int p_num_args, godot_variant **p_args);
これらは、オブジェクトに実装する関数の前方宣言です。コンストラクターとデストラクターが必要です。さらに、オブジェクトには get_data
と呼ばれる単一のメソッドがあります。
次は、動的ライブラリがロードされたときにGodotが呼び出す最初のエントリポイントです。これらのメソッドにはすべて godot_
というプレフィックスが付いており(後から変更できます)名前が後に続きます。 gdnative_init
は動的ライブラリを初期化する関数です。 Godotは、API構造体へのポインターが、役に立つと思われるさまざまな情報を含む構造体へのポインターを提供します。
追加のAPI構造体については、拡張機能配列をループ処理し、拡張機能の種類を確認する必要があります。
void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options) {
api = p_options->api_struct;
// Now find our extensions.
for (int i = 0; i < api->num_extensions; i++) {
switch (api->extensions[i]->type) {
case GDNATIVE_EXT_NATIVESCRIPT: {
nativescript_api = (godot_gdnative_ext_nativescript_api_struct *)api->extensions[i];
}; break;
default: break;
}
}
}
次は、ライブラリがアンロードされる前に呼び出される gdnative_terminate
です。 Godotは、オブジェクトがライブラリを使用しなくなったときにライブラリをアンロードします。 ここで、必要なクリーンアップ処理を実行できます。 この例では、単にAPIポインタをクリアします。
void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options) {
api = NULL;
nativescript_api = NULL;
}
Finally, we have nativescript_init
which is the most important function we'll
need today. This function will be called by Godot as part of loading a GDNative
library and communicates back to the engine what objects we make available.
void GDN_EXPORT godot_nativescript_init(void *p_handle) {
godot_instance_create_func create = { NULL, NULL, NULL };
create.create_func = &simple_constructor;
godot_instance_destroy_func destroy = { NULL, NULL, NULL };
destroy.destroy_func = &simple_destructor;
nativescript_api->godot_nativescript_register_class(p_handle, "SIMPLE", "Reference",
create, destroy);
godot_instance_method get_data = { NULL, NULL, NULL };
get_data.method = &simple_get_data;
godot_method_attributes attributes = { GODOT_METHOD_RPC_MODE_DISABLED };
nativescript_api->godot_nativescript_register_method(p_handle, "SIMPLE", "get_data",
attributes, get_data);
}
まず、 nativescript_register_class
を呼び出すことで、どのクラスが実装されているかをエンジンに伝えます。ここでの最初のパラメーターは、エンジンから与えられたハンドルポインターです。 2番目はオブジェクトクラスの名前です。 3番目は、「継承」するGodotのオブジェクトのタイプです。これは真の継承ではありませんが、十分に近いものです。最後に、4番目と5番目のパラメーターは、コンストラクターとデストラクターの説明です。
We then tell Godot about our methods (well our one method in this case), by
calling nativescript_register_method
for each method of our class. In our
case, that is just get_data
. Our first parameter is yet again our handle
pointer. The second is again the name of the object class we're registering. The
third is the name of our function as it will be known to GDScript. The fourth is
our attributes setting (see godot_method_rpc_mode
enum in
godot-headers/nativescript/godot_nativescript.h
for possible values). The
fifth and final parameter is a description of which function to call when the
method gets called.
構造体 instance_method
の記述には、関数自体への関数ポインターが最初のフィールドとして含まれています。これらの構造体の他の2つのフィールドは、メソッドごとのユーザーデータを指定するためのものです。 2つ目は method_data
フィールドで、これはすべての関数呼び出しで p_method_data
引数として渡されます。これはおそらく、1つの関数を複数の異なるスクリプト クラスの異なるメソッドで再利用する場合に便利です。 method_data
値が解放する必要のあるメモリへのポインタである場合、3つ目の free_func
フィールドにはそのメモリを解放する関数へのポインタを含めることができます。このフリー関数は、スクリプト自体(インスタンスではありません!)がアンロードされると呼び出されます(つまり、通常はライブラリのアンロード時に)。
それでは、オブジェクトの機能に関する作業を始めましょう。 まず、GDNativeクラスのインスタンスのメンバーデータを格納するために使用する構造体を定義します。
typedef struct user_data_struct {
char data[256];
} user_data_struct;
次に、コンストラクターを定義します。コンストラクターで行うことは、構造体にメモリを割り当て、そのメモリにデータを書き込むことだけです。ここではGodotのメモリ関数を使ってメモリを追跡し、新しい構造体へのポインターを返すことに注意してください。このポインターは、複数のオブジェクトがインスタンス化された場合に、インスタンスIDとして機能します。
このポインタは、 p_user_data
というパラメーターとしてオブジェクトに関連する関数のいずれかに渡され、インスタンスの識別とメンバーデータへのアクセスの両方に使用できます。
void *simple_constructor(godot_object *p_instance, void *p_method_data) {
user_data_struct *user_data = api->godot_alloc(sizeof(user_data_struct));
strcpy(user_data->data, "World from GDNative!");
return user_data;
}
オブジェクトに対してGodotが実行され、インスタンスのメンバデータが解放されると、デストラクタが呼び出されます。
void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data) {
api->godot_free(p_user_data);
}
最後に、 get_data
関数を実装します。データは常にバリアント型(Variant)として送受信されるため、文字列のデータを返すには、まずCの文字列をGodotの文字列オブジェクトに変換し、次にその文字列オブジェクトを戻り値のバリアントにコピーする必要があります。
godot_variant simple_get_data(godot_object *p_instance, void *p_method_data,
void *p_user_data, int p_num_args, godot_variant **p_args) {
godot_string data;
godot_variant ret;
user_data_struct *user_data = (user_data_struct *)p_user_data;
api->godot_string_new(&data);
api->godot_string_parse_utf8(&data, user_data->data);
api->godot_variant_new_string(&ret, &data);
api->godot_string_destroy(&data);
return ret;
}
文字列はGodotではヒープに割り当てられるため、そのメモリを解放するデストラクタがあります。 デストラクタには godot_TYPENAME_destroy
という名前が付けられます。 バリアントが文字列で作成されると、その文字列を参照します。 つまり、元の文字列を「破棄」して参照カウントを減らすことができます。 そうしないと、参照カウントがゼロにならず、メモリの割り当てが解除されないため、文字列のメモリがリークします。 返されたバリアントは、Godotによって自動的に破棄されます。
注釈
より複雑な操作では、どの値の割り当てを解放するべきなのかそれとも保持するべきなのか、混乱しないように追跡する必要があります。一般的な規則として、C++で デストラクターが呼び出されるときには godot_TYPENAME_destroy
を呼び出します。文字列デストラクターは、バリアント型 (Variant) の作成後に C++ で呼び出されるため、C でも同じことが必要です。
戻り値として返されるバリアントは、Godotによって自動的に破棄されます。
これがモジュールのソースコード全体です。
コンパイル¶
次に、ソースコードをコンパイルする必要があります。前述のとおり、GitHubのサンプルプロジェクトには、すべての面倒な作業を行う「SCons configuration」が含まれていますが、ここでのチュートリアルでは、コンパイラを直接呼び出します。
上記のフォルダ構造に揃えてあると仮定すると、 src
フォルダでターミナルセッションを開き、そこからコマンドを実行するのが最善です。先に進む前に ``bin``フォルダを作成してください。
Linux:
gcc -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.o
gcc -rdynamic -shared simple.o -o ../simple/bin/libsimple.so
macOS:
clang -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.os
clang -dynamiclib simple.os -o ../simple/bin/libsimple.dylib
Windows:
cl /Fosimple.obj /c simple.c /nologo -EHsc -DNDEBUG /MD /I. /I..\godot-headers
link /nologo /dll /out:..\simple\bin\libsimple.dll /implib:..\simple\bin\libsimple.lib simple.obj
注釈
Windowsでのビルドでは、 libsimple.lib
ライブラリも作成されます。これは、(一般的には)DLLにアクセスするためにプロジェクトのコンパイルで生成されるライブラリです。Godotにとってはそれは単に副産物なので必要ではありません。リリースのためにゲームをエクスポートするとき、このファイルは無視されエクスポートには含まれませんが心配はいりません。
GDNativeLibrary(.gdnlib
)ファイルの作成¶
モジュールをコンパイルしたら、動的ライブラリに対応するGDNativeLibraryリソースを作成し、それを .gdnlib
拡張子を付けて一緒に配置する必要があります。このファイルは、どの動的ライブラリがモジュールの一部であり、プラットフォームごとにロードする必要があるかをGodotに伝えます。
Godotを使用してこのファイルを生成できるため、エディタでシンプルなプロジェクトを開きます。
まず、インスペクタで 「リソースの作成」 ボタンをクリックします:
そして、 GDNativeLibrary
を選択します:
下部のパネルにコンテキストエディタが表示されます。右下にある[下部パネルを展開]ボタンを使用して、目いっぱいの高さにパネルを拡張します。
一般的なプロパティ¶
インスペクタには、ライブラリの読み込みを制御するためのさまざまなプロパティがあります。
Load Once が有効な場合、ライブラリは一度だけ読み込まれ、ライブラリを使用する個々のスクリプトは同じデータを使用します。グローバルに定義した変数は、作成されるオブジェクトのインスタンスからアクセスできます。Load Once を無効にすると、スクリプトがライブラリにアクセスするたびに、ライブラリの新しいコピーがメモリに読み込まれます。
Singleton が有効になっている場合はライブラリが自動的に読み込まれ、 godot_gdneative_singleton
という関数が呼び出されます。これに関しては別のチュートリアルで説明を行ないます。
Symbol Prefix は、これまでに見た godot_nativescript_init
の godot_
のようなコア関数用のプレフィックスです。静的にリンクする複数のGDNativeライブラリを使用する場合は、異なるプレフィックスを使用する必要があります。これもまた別のチュートリアルでさらに掘り下げる対象です。iOSは動的ライブラリを好まないため、現時点ではこのプラットフォーム(iOS)への展開にのみ必要です。
Reloadable は、エディタがフォーカスを失ったときにライブラリをリロードする必要があるかどうかを定義します。通常は、外部で変更を加えられたライブラリから、新しいシンボルまたは変更されたシンボルを取得します。
プラットフォームライブラリ¶
GDNativeLibraryエディタプラグインを使用すると、サポートするプラットフォームとアーキテクチャごとに2つの設定を行うことができます。
ダイナミック ライブラリ列(保存されたファイルの entry
セクション)は、プラットフォームと機能の組み合わせごとに、どのダイナミックライブラリをロードする必要があるかを示しています。これは、特定のプラットフォームにエクスポートするときに、どのファイルをエクスポートする必要があるかをエクスポーターに通知します。
依存関係列(dependencies
セクション)は、ライブラリが機能するために各プラットフォームにエクスポートする必要がある他のファイルをGodotに伝えます。 例えばGDNativeモジュールが別のDLLを使用してサードパーティライブラリの機能を実装する場合には、そのDLLをここにリストアップします。
この例では、Linux、macOS、Windows用のライブラリのみを構築しました。フォルダボタンをクリックして、関連するフィールドでそれらをリンクできます。 3つのライブラリをすべて作成した場合、次のようなものが必要です:
リソースの保存¶
次に、インスペクタの[保存]ボタンを使用して、GDNativeLibraryリソースを bin/libsimple.gdnlib
として保存できます:
ファイルはテキストベースの形式で保存され、次のような内容が含まれている必要があります:
[general]
singleton=false
load_once=true
symbol_prefix="godot_"
reloadable=true
[entry]
OSX.64="res://bin/libsimple.dylib"
OSX.32="res://bin/libsimple.dylib"
Windows.64="res://bin/libsimple.dll"
X11.64="res://bin/libsimple.so"
[dependencies]
OSX.64=[ ]
OSX.32=[ ]
Windows.64=[ ]
X11.64=[ ]
NativeScript( .gdns
)ファイルの作成¶
With our .gdnlib
file we've told Godot how to load our library, now we need
to tell it about our "SIMPLE" object class. We do this by creating a
NativeScript resource file with .gdns
extension.
GDNativeLibraryリソースの場合と同様に、インスペクタで新しいリソースを作成するボタンをクリックし、 NativeScript
を選択します:
The inspector will show a few properties that we need to fill. As Class Name
we enter "SIMPLE" which is the object class name that we declared in our C
source when calling godot_nativescript_register_class
. We also need to
select our .gdnlib
file by clicking on Library and selecting Load:
注釈
The Class Name must have the same spelling as the one given in godot_nativescript_init
when registering the class.
Finally, click on the save icon and save this as bin/simple.gdns
:
次に、シーンを構築します。ルートとしてシーンにコントロールノードを追加し、 main
と名付けます。次に、ボタンとラベルを子ノードとして追加します。画面上のどこかに適当な場所にボタンを配置して、名前を付けます。
コントロール ノードを選択し、スクリプトをアタッチします:
次に、ボタンの pressed
シグナルをスクリプトにリンクします:
main.tscn
と名付けてシーンを保存することを忘れないでください。
main.gd
コードを実装できます:
extends Control
# load the Simple library
onready var data = preload("res://bin/simple.gdns").new()
func _on_Button_pressed():
$Label.text = "Data = " + data.get_data()
これで、プロジェクトは機能するはずです。 Godotを初めて実行すると、メインシーンが何であるかを尋ねられるので、 main.tscn
ファイルを選択して実行します。