@@ -1,109 +0,0 @@
|
||||
#!python
|
||||
import os
|
||||
|
||||
opts = Variables([], ARGUMENTS)
|
||||
|
||||
# Gets the standard flags CC, CCX, etc.
|
||||
env = DefaultEnvironment()
|
||||
|
||||
# Define our options
|
||||
opts.Add(EnumVariable('target', "Compilation target", 'debug', ['d', 'debug', 'r', 'release']))
|
||||
opts.Add(EnumVariable('platform', "Compilation platform", '', ['', 'windows', 'x11', 'linux', 'osx']))
|
||||
opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", '', ['', 'windows', 'x11', 'linux', 'osx']))
|
||||
opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no'))
|
||||
opts.Add(PathVariable('target_path', 'The path where the lib is installed.', 'demo/bin/'))
|
||||
opts.Add(PathVariable('target_name', 'The library name.', 'libgdexample', PathVariable.PathAccept))
|
||||
|
||||
# Local dependency paths, adapt them to your setup
|
||||
godot_headers_path = "godot-cpp/godot-headers/"
|
||||
cpp_bindings_path = "godot-cpp/"
|
||||
cpp_library = "libgodot-cpp"
|
||||
|
||||
# only support 64 at this time..
|
||||
bits = 64
|
||||
|
||||
# Updates the environment with the option variables.
|
||||
opts.Update(env)
|
||||
|
||||
# Process some arguments
|
||||
if env['use_llvm']:
|
||||
env['CC'] = 'clang'
|
||||
env['CXX'] = 'clang++'
|
||||
|
||||
if env['p'] != '':
|
||||
env['platform'] = env['p']
|
||||
|
||||
if env['platform'] == '':
|
||||
print("No valid target platform selected.")
|
||||
quit();
|
||||
|
||||
# For the reference:
|
||||
# - CCFLAGS are compilation flags shared between C and C++
|
||||
# - CFLAGS are for C-specific compilation flags
|
||||
# - CXXFLAGS are for C++-specific compilation flags
|
||||
# - CPPFLAGS are for pre-processor flags
|
||||
# - CPPDEFINES are for pre-processor defines
|
||||
# - LINKFLAGS are for linking flags
|
||||
|
||||
# Check our platform specifics
|
||||
if env['platform'] == "osx":
|
||||
env['target_path'] += 'osx/'
|
||||
cpp_library += '.osx'
|
||||
env.Append(CCFLAGS=['-arch', 'x86_64'])
|
||||
env.Append(CXXFLAGS=['-std=c++17'])
|
||||
env.Append(LINKFLAGS=['-arch', 'x86_64'])
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CCFLAGS=['-g', '-O2'])
|
||||
else:
|
||||
env.Append(CCFLAGS=['-g', '-O3'])
|
||||
|
||||
elif env['platform'] in ('x11', 'linux'):
|
||||
env['target_path'] += 'x11/'
|
||||
cpp_library += '.linux'
|
||||
env.Append(CCFLAGS=['-fPIC'])
|
||||
env.Append(CXXFLAGS=['-std=c++17'])
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CCFLAGS=['-g3', '-Og'])
|
||||
else:
|
||||
env.Append(CCFLAGS=['-g', '-O3'])
|
||||
|
||||
elif env['platform'] == "windows":
|
||||
env['target_path'] += 'win64/'
|
||||
cpp_library += '.windows'
|
||||
# This makes sure to keep the session environment variables on windows,
|
||||
# that way you can run scons in a vs 2017 prompt and it will find all the required tools
|
||||
env.Append(ENV=os.environ)
|
||||
|
||||
env.Append(CPPDEFINES=['WIN32', '_WIN32', '_WINDOWS', '_CRT_SECURE_NO_WARNINGS'])
|
||||
env.Append(CCFLAGS=['-W3', '-GR'])
|
||||
env.Append(CXXFLAGS='/std:c++17')
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CPPDEFINES=['_DEBUG'])
|
||||
env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI'])
|
||||
env.Append(LINKFLAGS=['-DEBUG'])
|
||||
else:
|
||||
env.Append(CPPDEFINES=['NDEBUG'])
|
||||
env.Append(CCFLAGS=['-O2', '-EHsc', '-MD'])
|
||||
|
||||
if env['target'] in ('debug', 'd'):
|
||||
cpp_library += '.debug'
|
||||
else:
|
||||
cpp_library += '.release'
|
||||
|
||||
cpp_library += '.' + str(bits)
|
||||
|
||||
# make sure our binding library is properly includes
|
||||
env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/'])
|
||||
env.Append(LIBPATH=[cpp_bindings_path + 'bin/'])
|
||||
env.Append(LIBS=[cpp_library])
|
||||
|
||||
# tweak this if you want to use different folders, or more folders, to store your source code in.
|
||||
env.Append(CPPPATH=['src/'])
|
||||
sources = Glob('src/*.cpp')
|
||||
|
||||
library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)
|
||||
|
||||
Default(library)
|
||||
|
||||
# Generates help for the -h scons option.
|
||||
Help(opts.GenerateHelpText(env))
|
||||
@@ -1,555 +0,0 @@
|
||||
.. _doc_gdnative_c_example:
|
||||
|
||||
GDNative C example
|
||||
==================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial will introduce you to the bare minimum required to create GDNative
|
||||
modules. This should be your starting point into the world of GDNative.
|
||||
Understanding the contents of this tutorial will help you in understanding all
|
||||
that is to come after this.
|
||||
|
||||
Before we begin, you can download the source code to the example object we
|
||||
describe below in the `GDNative-demos repository
|
||||
<https://github.com/godotengine/gdnative-demos/tree/master/c/simple>`_.
|
||||
|
||||
This example project also contains a SConstruct file that makes compiling a
|
||||
little easier, but in this tutorial we'll be doing things by hand to
|
||||
understand the process.
|
||||
|
||||
:ref:`GDNative <class_GDNative>` can be used to create several types of
|
||||
additions to Godot, using interfaces such as
|
||||
:ref:`PluginScript <class_PluginScript>` or
|
||||
:ref:`ARVRInterfaceGDNative <class_ARVRInterfaceGDNative>`. In this tutorial we
|
||||
are going to look at creating a :ref:`NativeScript <class_NativeScript>`
|
||||
module. NativeScript allows you to write logic in C or C++ in a similar fashion
|
||||
as you would write a GDScript file. We'll be creating the C equivalent of this
|
||||
GDScript:
|
||||
|
||||
::
|
||||
|
||||
extends Reference
|
||||
|
||||
var data
|
||||
|
||||
func _ready():
|
||||
data = "World from GDScript!"
|
||||
|
||||
func get_data():
|
||||
return data
|
||||
|
||||
Future tutorials will focus on the other types of GDNative modules and explain
|
||||
when and how to use each of them.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
Before we start you'll need a few things:
|
||||
|
||||
1) A Godot executable for your target version.
|
||||
2) A C compiler. On Linux, install ``gcc`` or ``clang`` from your package
|
||||
manager. On macOS, you can install Xcode from the Mac App Store. On Windows,
|
||||
you can use Visual Studio 2015 or later, or MinGW-w64.
|
||||
3) A Git clone of the `godot-headers
|
||||
repository <https://github.com/godotengine/godot-headers.git>`_: these are
|
||||
the C headers for Godot's public API exposed to GDNative.
|
||||
|
||||
For the latter, we suggest that you create a dedicated folder for this GDNative
|
||||
example project, open a terminal in that folder and execute:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
git clone https://github.com/godotengine/godot-headers.git
|
||||
|
||||
This will download the required files into that folder.
|
||||
|
||||
.. tip::
|
||||
|
||||
If you plan to use Git for your GDNative project, you can also add
|
||||
``godot-headers`` as a Git submodule.
|
||||
|
||||
.. note::
|
||||
|
||||
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.1``) and ideally actual release (e.g.
|
||||
``3.1.1-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.1.1-stable`` tag for use with
|
||||
Godot 3.1.1. In your cloned repository, you can do:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
git checkout godot-3.1.1-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.1``), which you would also check
|
||||
out with ``git checkout 3.1``.
|
||||
|
||||
If you are building Godot from source with your own changes that impact
|
||||
GDNative, you can find the updated class and structure definition in
|
||||
``<godotsource>/modules/gdnative/include``
|
||||
|
||||
Our C source
|
||||
------------
|
||||
|
||||
Let's start by writing our main code. Eventually, we want to end up with a file
|
||||
structure that looks along those lines:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
+ <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.
|
||||
|
||||
We're going to start by having a look at what our ``simple.c`` file contains.
|
||||
Now, for our example here we're making a single C source file without a header
|
||||
to keep things simple. Once you start writing bigger projects it is advisable
|
||||
to break your project up into multiple files. That however falls outside of the
|
||||
scope of this tutorial.
|
||||
|
||||
We'll be looking at the source code bit by bit so all the parts below should all
|
||||
be put together into one big file. Each section will be explained as we add it.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#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;
|
||||
|
||||
The above code includes the GDNative API struct header and a standard header
|
||||
that we will use further down for string operations.
|
||||
It then defines two pointers to two different structs. GDNative supports a large
|
||||
collection of functions for calling back into the main Godot executable. In
|
||||
order for your module to have access to these functions, GDNative provides your
|
||||
application with a struct containing pointers to all these functions.
|
||||
|
||||
To keep this implementation modular and easily extendable, the core functions
|
||||
are available directly through the "core" API struct, but additional functions
|
||||
have their own "GDNative structs" that are accessible through extensions.
|
||||
|
||||
In our example, we access one of these extension to gain access to the functions
|
||||
specifically needed for NativeScript.
|
||||
|
||||
A NativeScript behaves like any other script in Godot. Because the NativeScript
|
||||
API is rather low level, it requires the library to specify many things more
|
||||
verbosely than other scripting systems, such as GDScript. When a NativeScript
|
||||
instance gets created, a library-given constructor gets called. When that
|
||||
instance gets destroyed, the given destructor will be executed.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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);
|
||||
|
||||
These are forward declarations for the functions we'll be implementing for our
|
||||
object. A constructor and destructor is needed. Additionally, the object will
|
||||
have a single method called ``get_data``.
|
||||
|
||||
Next up is the first of the entry points Godot will call when our dynamic
|
||||
library is loaded. These methods are all prefixed with ``godot_`` (you can
|
||||
change this later on) followed by their name. ``gdnative_init`` is a function
|
||||
that initializes our dynamic library. Godot will give it a pointer to a
|
||||
structure that contains various bits of information we may find useful among
|
||||
which the pointers to our API structures.
|
||||
|
||||
For any additional API structures we need to loop through our extensions array
|
||||
and check the type of extension.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Next up is ``gdnative_terminate`` which is called before the library is
|
||||
unloaded. Godot will unload the library when no object uses it anymore. Here,
|
||||
you can do any cleanup you may need to do. For our example, we're simply going
|
||||
to clear our API pointers.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
We first tell the engine which classes are implemented by calling
|
||||
``nativescript_register_class``. The first parameter here is the handle pointer
|
||||
given to us. The second is the name of our object class. The third is the type
|
||||
of object in Godot that we 'inherit' from; this is not true inheritance but it's
|
||||
close enough. Finally, our fourth and fifth parameters are descriptions for our
|
||||
constructor and destructor.
|
||||
|
||||
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.
|
||||
|
||||
The description struct ``instance_method`` contains the function pointer to the
|
||||
function itself as first field. The other two fields in these structs are for
|
||||
specifying per-method userdata. The second is the ``method_data`` field which is
|
||||
passed on every function call as the ``p_method_data`` argument. This is useful
|
||||
to reuse one function for different methods on possibly multiple different
|
||||
script-classes. If the ``method_data`` value is a pointer to memory that needs
|
||||
to be freed, the third ``free_func`` field can contain a pointer to a function
|
||||
that will free that memory. That free function gets called when the script
|
||||
itself (not instance!) gets unloaded (so usually at library-unload time).
|
||||
|
||||
Now, it's time to start working on the functions of our object. First, we define
|
||||
a structure that we use to store the member data of an instance of our GDNative
|
||||
class.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
typedef struct user_data_struct {
|
||||
char data[256];
|
||||
} user_data_struct;
|
||||
|
||||
And then, we define our constructor. All we do in our constructor is allocate
|
||||
memory for our structure and fill it with some data. Note that we use Godot's
|
||||
memory functions so the memory gets tracked and then return the pointer to our
|
||||
new structure. This pointer will act as our instance identifier in case multiple
|
||||
objects are instantiated.
|
||||
|
||||
This pointer will be passed to any of our functions related to our object as a
|
||||
parameter called ``p_user_data``, and can both be used to identify our instance
|
||||
and to access its member data.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Our destructor is called when Godot is done with our object and we free our
|
||||
instances' member data.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data) {
|
||||
api->godot_free(p_user_data);
|
||||
}
|
||||
|
||||
And finally, we implement our ``get_data`` function. Data is always sent and
|
||||
returned as variants so in order to return our data, which is a string, we first
|
||||
need to convert our C string to a Godot string object, and then copy that string
|
||||
object into the variant we are returning.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Strings are heap-allocated in Godot, so they have a destructor which frees the
|
||||
memory. Destructors are named ``godot_TYPENAME_destroy``. When a Variant gets
|
||||
created with a String, it references the String. That means that the original
|
||||
String can be "destroyed" to decrease the ref-count. If that does not happen the
|
||||
String memory will leak since the ref-count will never be zero and the memory
|
||||
never deallocated. The returned variant gets automatically destroyed by Godot.
|
||||
|
||||
.. note::
|
||||
|
||||
In more complex operations it can be confusing the keep track of which value
|
||||
needs to be deallocated and which does not. As a general rule: call
|
||||
``godot_TYPENAME_destroy`` when a C++ destructor would be called instead.
|
||||
The String destructor would be called in C++ after the Variant was created,
|
||||
so the same is necessary in C.
|
||||
|
||||
The variant we return is destroyed automatically by Godot.
|
||||
|
||||
And that is the whole source code of our module.
|
||||
|
||||
Compiling
|
||||
---------
|
||||
|
||||
We now need to compile our source code. As mentioned our example project on
|
||||
GitHub contains a SCons configuration that does all the hard work for you, but
|
||||
for our tutorial here we are going to call the compilers directly.
|
||||
|
||||
Assuming you are sticking to the folder structure suggested above, it is best to
|
||||
open a terminal session in the ``src`` folder and execute the commands from
|
||||
there. Make sure to create the ``bin`` folder before you proceed.
|
||||
|
||||
On Linux:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
gcc -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.o
|
||||
gcc -rdynamic -shared simple.o -o ../simple/bin/libsimple.so
|
||||
|
||||
On macOS:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
clang -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.os
|
||||
clang -dynamiclib simple.os -o ../simple/bin/libsimple.dylib
|
||||
|
||||
On Windows:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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
|
||||
|
||||
.. note::
|
||||
|
||||
On the Windows build you also end up with a ``libsimple.lib`` library. This
|
||||
is a library that you can compile into a project to provide access to the
|
||||
DLL. We get it as a byproduct and we do not need it :)
|
||||
When exporting your game for release this file will be ignored.
|
||||
|
||||
Creating the GDNativeLibrary (``.gdnlib``) file
|
||||
-----------------------------------------------
|
||||
|
||||
With our module compiled, we now need to create a corresponding
|
||||
:ref:`GDNativeLibrary <class_GDNativeLibrary>` resource with ``.gdnlib``
|
||||
extension which we place alongside our dynamic libraries. This file tells Godot
|
||||
what dynamic libraries are part of our module and need to be loaded per
|
||||
platform.
|
||||
|
||||
We can use Godot to generate this file, so open the "simple" project in the
|
||||
editor.
|
||||
|
||||
Start by clicking the create resource button in the Inspector:
|
||||
|
||||
.. image:: img/new_resource.gif
|
||||
|
||||
And select ``GDNativeLibrary``:
|
||||
|
||||
.. image:: img/gdnativelibrary_resource.png
|
||||
|
||||
You should see a contextual editor appear in the bottom panel. Use the "Expand
|
||||
Bottom Panel" button in the bottom right to expand it to full height:
|
||||
|
||||
.. image:: img/gdnativelibrary_editor.png
|
||||
|
||||
General properties
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the Inspector, you have various properties to control loading the library.
|
||||
|
||||
If *Load Once* is enabled, our library is loaded only once and each individual
|
||||
script that uses our library will use the same data. Any variable you define
|
||||
globally will be accessible from any instance of your object you create. If
|
||||
*Load Once* is disabled, a new copy of the library is loaded into memory each
|
||||
time a script accesses the library.
|
||||
|
||||
If *Singleton* is enabled, our library is automatically loaded and a function
|
||||
called ``godot_gdnative_singleton`` is called. We'll leave that for another
|
||||
tutorial.
|
||||
|
||||
The *Symbol Prefix* is a prefix for our core functions, such as ``godot_`` in
|
||||
``godot_nativescript_init`` seen earlier. If you use multiple GDNative libraries
|
||||
that you wish to statically link, you will have to use different prefixes. This
|
||||
again is a subject to dive into deeper in a separate tutorial, it is only needed
|
||||
at this time for deployment to iOS as this platform does not like dynamic
|
||||
libraries.
|
||||
|
||||
*Reloadable* defines whether the library should be reloaded when the editor
|
||||
loses and gains focus, typically to pick up new or modified symbols from any
|
||||
change made to the library externally.
|
||||
|
||||
Platform libraries
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The GDNativeLibrary editor plugin lets you configure two things for each
|
||||
platform and architecture that you aim to support.
|
||||
|
||||
The *Dynamic Library* column (``entry`` section in the saved file) tells us for
|
||||
each platform and feature combination which dynamic library has to be loaded.
|
||||
This also informs the exporter which files need to be exported when exporting to
|
||||
a specific platform.
|
||||
|
||||
The *Dependencies* column (also ``dependencies`` section) tells Godot what other
|
||||
files need to be exported for each platform in order for our library to work.
|
||||
Say that your GDNative module uses another DLL to implement functionality from a
|
||||
3rd party library, this is where you list that DLL.
|
||||
|
||||
For our example, we only built libraries for Linux, macOS and/or Windows, so you
|
||||
can link them in the relevant fields by clicking the folder button. If you built
|
||||
all three libraries, you should have something like this:
|
||||
|
||||
.. image:: img/gdnativelibrary_editor_complete.png
|
||||
|
||||
Saving the resource
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We can then save our GDNativeLibrary resource as ``bin/libsimple.gdnlib`` with
|
||||
the Save button in the Inspector:
|
||||
|
||||
.. image:: img/gdnativelibrary_save.png
|
||||
|
||||
The file is saved in a text-based format and should have contents similar to
|
||||
this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[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=[ ]
|
||||
|
||||
Creating the NativeScript (``.gdns``) file
|
||||
------------------------------------------
|
||||
|
||||
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
|
||||
:ref:`NativeScript <class_NativeScript>` resource file with ``.gdns`` extension.
|
||||
|
||||
Like done for the GDNativeLibrary resource, click the button to create a new
|
||||
resource in the Inspector and select ``NativeScript``:
|
||||
|
||||
.. image:: img/nativescript_resource.png
|
||||
|
||||
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*:
|
||||
|
||||
.. image:: img/nativescript_library.png
|
||||
|
||||
.. note::
|
||||
|
||||
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``:
|
||||
|
||||
.. image:: img/save_gdns.gif
|
||||
|
||||
Now it's time to build our scene. Add a Control node to your scene as your root
|
||||
and call it ``main``. Then add a Button and a Label as child nodes. Place them
|
||||
somewhere nice on screen and give your button a name.
|
||||
|
||||
.. image:: img/c_main_scene_layout.png
|
||||
|
||||
Select the control node and attach a script to it:
|
||||
|
||||
.. image:: img/add_main_script.gif
|
||||
|
||||
Next link up the ``pressed`` signal on the button to your script:
|
||||
|
||||
.. image:: img/connect_button_signal.gif
|
||||
|
||||
Don't forget to save your scene, call it ``main.tscn``.
|
||||
|
||||
Now we can implement our ``main.gd`` code:
|
||||
|
||||
::
|
||||
|
||||
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()
|
||||
|
||||
After all that, our project should work. The first time you run it Godot will
|
||||
ask you what your main scene is and you select your ``main.tscn`` file and
|
||||
presto:
|
||||
|
||||
.. image:: img/c_sample_result.png
|
||||
@@ -1,863 +0,0 @@
|
||||
.. _doc_gdnative_cpp_example:
|
||||
|
||||
GDNative C++ example
|
||||
====================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial builds on top of the information given in the
|
||||
:ref:`GDNative C example <doc_gdnative_c_example>`, so we highly recommend you
|
||||
read that first.
|
||||
|
||||
The C++ bindings for GDNative are built on top of the NativeScript GDNative API
|
||||
and provide a nicer way to "extend" nodes in Godot using C++. This is equivalent
|
||||
to writing scripts in GDScript, but in C++ instead.
|
||||
|
||||
Godot 3.1 saw the introduction of the NativeScript 1.1 additions that enabled
|
||||
the GDNative team to build a nicer C++ bindings library. These changes have now
|
||||
been merged into the master branch and will be the way we go forward. If you
|
||||
want to write a C++ GDNative plugin that also supports Godot 3.0 you will need
|
||||
to use the 3.0 branch and the NativeScript 1.0 syntax. We'll be showing them
|
||||
side by side in this writeup.
|
||||
|
||||
You can download the full example we'll be creating in this tutorial `on
|
||||
GitHub <https://github.com/BastiaanOlij/gdnative_cpp_example>`__.
|
||||
|
||||
Setting up the project
|
||||
----------------------
|
||||
|
||||
There are a few prerequisites you'll need:
|
||||
|
||||
- a Godot 3.x executable,
|
||||
- a C++ compiler,
|
||||
- SCons as a build tool,
|
||||
- a copy of the `godot-cpp
|
||||
repository <https://github.com/godotengine/godot-cpp>`__.
|
||||
|
||||
See also :ref:`Compiling <toc-devel-compiling>` as the build tools are identical
|
||||
to the ones you need to compile Godot from source.
|
||||
|
||||
You can download these repositories from GitHub or let Git do the work for you.
|
||||
Note that these repositories now have different branches for different versions
|
||||
of Godot. GDNative modules written for an earlier version of Godot will work in
|
||||
newer versions (with the exception of one breaking change in ARVR interfaces
|
||||
between 3.0 and 3.1) but not vice versa so make sure you download the correct
|
||||
branch. Also note that the version of Godot you use to generate the ``api.json``
|
||||
with becomes your minimum version.
|
||||
|
||||
.. note::
|
||||
|
||||
`GDExtension <https://godotengine.org/article/introducing-gd-extensions>`__ has been merged in the ``master`` branch of godot-cpp,
|
||||
but it is only compatible with the upcoming Godot 4.0.
|
||||
Therefore, you need to use the ``3.x`` branch of godot-cpp to use GDNative
|
||||
and follow this example.
|
||||
|
||||
If you are versioning your project using Git, it is a good idea to add them as
|
||||
Git submodules:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: none Godot
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git init
|
||||
git submodule add -b 3.x https://github.com/godotengine/godot-cpp
|
||||
cd godot-cpp
|
||||
git submodule update --init
|
||||
|
||||
.. code-tab:: none Godot 3.0
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git init
|
||||
git submodule add -b 3.0 https://github.com/godotengine/godot-cpp
|
||||
cd godot-cpp
|
||||
git submodule update --init
|
||||
|
||||
If you decide to just download the repositories or clone them into your project
|
||||
folder, make sure to keep the folder layout identical to the one described here,
|
||||
as much of the code we'll be showcasing here assumes the project follows this
|
||||
layout.
|
||||
|
||||
Do make sure you clone recursive to pull in both repositories:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: none Godot
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git clone --recursive -b 3.x https://github.com/godotengine/godot-cpp
|
||||
|
||||
.. code-tab:: none Godot 3.0
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git clone --recursive -b 3.0 https://github.com/godotengine/godot-cpp
|
||||
|
||||
.. note::
|
||||
|
||||
``godot-cpp`` now includes ``godot-headers`` as a nested submodule, if you've
|
||||
manually downloaded them please make sure to place ``godot-headers`` inside
|
||||
of the ``godot-cpp`` folder.
|
||||
|
||||
You don't have to do it this way but we've found it easiest to manage. If you
|
||||
decide to just download the repositories or just clone them into your folder,
|
||||
make sure to keep the folder layout the same as we've setup here as much of
|
||||
the code we'll be showcasing here assumes the project has this layout.
|
||||
|
||||
If you cloned the example from the link specified in the introduction, the
|
||||
submodules are not automatically initialized. You will need to execute the
|
||||
following commands:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
cd gdnative_cpp_example
|
||||
git submodule update --init --recursive
|
||||
|
||||
This will clone these two repositories into your project folder.
|
||||
|
||||
Building the C++ bindings
|
||||
-------------------------
|
||||
|
||||
Now that we've downloaded our prerequisites, it is time to build the C++
|
||||
bindings.
|
||||
|
||||
The repository contains a copy of the metadata for the current Godot release,
|
||||
but if you need to build these bindings for a newer version of Godot, simply
|
||||
call the Godot executable:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
godot --gdnative-generate-json-api api.json
|
||||
|
||||
Place the resulting ``api.json`` file in the project folder and add
|
||||
``use_custom_api_file=yes custom_api_file=../api.json`` to the scons command
|
||||
below.
|
||||
|
||||
To generate and compile the bindings, use this command (replacing ``<platform>``
|
||||
with ``windows``, ``linux`` or ``osx`` depending on your OS):
|
||||
|
||||
To speed up compilation, add `-jN` at the end of the SCons command line where `N` is the number of CPU threads you have on your system. The example below uses 4 threads.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
cd godot-cpp
|
||||
scons platform=<platform> generate_bindings=yes -j4
|
||||
cd ..
|
||||
|
||||
This step will take a while. When it is completed, you should have static
|
||||
libraries that can be compiled into your project stored in ``godot-cpp/bin/``.
|
||||
|
||||
At some point in the future, compiled binaries will be available, making this
|
||||
step optional.
|
||||
|
||||
.. note::
|
||||
|
||||
You may need to add ``bits=64`` to the command on Windows or Linux. We're
|
||||
still working on better auto detection.
|
||||
|
||||
Creating a simple plugin
|
||||
------------------------
|
||||
|
||||
Now it's time to build an actual plugin. We'll start by creating an empty Godot
|
||||
project in which we'll place a few files.
|
||||
|
||||
Open Godot and create a new project. For this example, we will place it in a
|
||||
folder called ``demo`` inside our GDNative module's folder structure.
|
||||
|
||||
In our demo project, we'll create a scene containing a Node called "Main" and
|
||||
we'll save it as ``main.tscn``. We'll come back to that later.
|
||||
|
||||
Back in the top-level GDNative module folder, we're also going to create a
|
||||
subfolder called ``src`` in which we'll place our source files.
|
||||
|
||||
You should now have ``demo``, ``godot-cpp``, ``godot-headers``, and ``src``
|
||||
directories in your GDNative module.
|
||||
|
||||
In the ``src`` folder, we'll start with creating our header file for the
|
||||
GDNative node we'll be creating. We will name it ``gdexample.h``:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
#ifndef GDEXAMPLE_H
|
||||
#define GDEXAMPLE_H
|
||||
|
||||
#include <Godot.hpp>
|
||||
#include <Sprite.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
class GDExample : public Sprite {
|
||||
GODOT_CLASS(GDExample, Sprite)
|
||||
|
||||
private:
|
||||
float time_passed;
|
||||
|
||||
public:
|
||||
static void _register_methods();
|
||||
|
||||
GDExample();
|
||||
~GDExample();
|
||||
|
||||
void _init(); // our initializer called by Godot
|
||||
|
||||
void _process(float delta);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
#ifndef GDEXAMPLE_H
|
||||
#define GDEXAMPLE_H
|
||||
|
||||
#include <Godot.hpp>
|
||||
#include <Sprite.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
class GDExample : public godot::GodotScript<Sprite> {
|
||||
GODOT_CLASS(GDExample)
|
||||
|
||||
private:
|
||||
float time_passed;
|
||||
|
||||
public:
|
||||
static void _register_methods();
|
||||
|
||||
GDExample();
|
||||
~GDExample();
|
||||
|
||||
void _process(float delta);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
There are a few things of note to the above. We're including ``Godot.hpp`` which
|
||||
contains all our basic definitions. After that, we include ``Sprite.hpp`` which
|
||||
contains bindings to the Sprite class. We'll be extending this class in our
|
||||
module.
|
||||
|
||||
We're using the namespace ``godot``, since everything in GDNative is defined
|
||||
within this namespace.
|
||||
|
||||
Then we have our class definition, which inherits from our Sprite through a
|
||||
container class. We'll see a few side effects of this later on. The
|
||||
``GODOT_CLASS`` macro sets up a few internal things for us.
|
||||
|
||||
After that, we declare a single member variable called ``time_passed``.
|
||||
|
||||
In the next block we're defining our methods, we obviously have our constructor
|
||||
and destructor defined, but there are two other functions that will likely look
|
||||
familiar to some, and one new method.
|
||||
|
||||
The first is ``_register_methods``, which is a static function that Godot will
|
||||
call to find out which methods can be called on our NativeScript and which
|
||||
properties it exposes. The second is our ``_process`` function, which will work
|
||||
exactly the same as the ``_process`` function you're used to in GDScript. The
|
||||
third is our ``_init`` function which is called after Godot has properly set up
|
||||
our object. It has to exist even if you don't place any code in it.
|
||||
|
||||
Let's implement our functions by creating our ``gdexample.cpp`` file:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
#include "gdexample.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
}
|
||||
|
||||
GDExample::GDExample() {
|
||||
}
|
||||
|
||||
GDExample::~GDExample() {
|
||||
// add your cleanup here
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float 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);
|
||||
}
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
#include "gdexample.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method((char *)"_process", &GDExample::_process);
|
||||
}
|
||||
|
||||
GDExample::GDExample() {
|
||||
// Initialize any variables here
|
||||
time_passed = 0.0;
|
||||
}
|
||||
|
||||
GDExample::~GDExample() {
|
||||
// Add your cleanup procedure here
|
||||
}
|
||||
|
||||
void GDExample::_process(float 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)));
|
||||
|
||||
owner->set_position(new_position);
|
||||
}
|
||||
|
||||
This one should be straightforward. We're implementing each method of our class
|
||||
that we defined in our header file. Note that the ``register_method`` call
|
||||
**must** expose the ``_process`` method, otherwise Godot will not be able to use
|
||||
it. However, we do not have to tell Godot about our constructor, destructor and
|
||||
``_init`` functions.
|
||||
|
||||
The other method of note is our ``_process`` function, which simply keeps track
|
||||
of how much time has passed and calculates a new position for our sprite using a
|
||||
simple sine and cosine function. What stands out is calling
|
||||
``owner->set_position`` to call one of the build in methods of our Sprite. This
|
||||
is because our class is a container class; ``owner`` points to the actual Sprite
|
||||
node our script relates to. In the upcoming NativeScript 1.1, ``set_position``
|
||||
can be called directly on our class.
|
||||
|
||||
There is one more C++ file we need; we'll name it ``gdlibrary.cpp``. Our
|
||||
GDNative plugin can contain multiple NativeScripts, each with their own header
|
||||
and source file like we've implemented ``GDExample`` up above. What we need now
|
||||
is a small bit of code that tells Godot about all the NativeScripts in our
|
||||
GDNative plugin.
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
#include "gdexample.h"
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
|
||||
godot::Godot::gdnative_init(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
|
||||
godot::Godot::gdnative_terminate(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
|
||||
godot::Godot::nativescript_init(handle);
|
||||
|
||||
godot::register_class<godot::GDExample>();
|
||||
}
|
||||
|
||||
Note that we are not using the ``godot`` namespace here, since the three
|
||||
functions implemented here need to be defined without a namespace.
|
||||
|
||||
The ``godot_gdnative_init`` and ``godot_gdnative_terminate`` functions get
|
||||
called respectively when Godot loads our plugin and when it unloads it. All
|
||||
we're doing here is parse through the functions in our bindings module to
|
||||
initialize them, but you might have to set up more things depending on your
|
||||
needs.
|
||||
|
||||
The important function is the third function called ``godot_nativescript_init``.
|
||||
We first call a function in our bindings library that does its usual stuff.
|
||||
After that, we call the function ``register_class`` for each of our classes in
|
||||
our library.
|
||||
|
||||
Compiling the plugin
|
||||
--------------------
|
||||
|
||||
We cannot easily write by hand a ``SConstruct`` file that SCons would use for
|
||||
building. For the purpose of this example, just use
|
||||
:download:`this hardcoded SConstruct file <files/cpp_example/SConstruct>` we've
|
||||
prepared. We'll cover a more customizable, detailed example on how to use these
|
||||
build files in a subsequent tutorial.
|
||||
|
||||
.. note::
|
||||
|
||||
This ``SConstruct`` file was written to be used with the latest ``godot-cpp``
|
||||
master, you may need to make small changes using it with older versions or
|
||||
refer to the ``SConstruct`` file in the Godot 3.0 documentation.
|
||||
|
||||
Once you've downloaded the ``SConstruct`` file, place it in your GDNative module
|
||||
folder besides ``godot-cpp``, ``godot-headers`` and ``demo``, then run:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
scons platform=<platform>
|
||||
|
||||
You should now be able to find the module in ``demo/bin/<platform>``.
|
||||
|
||||
.. note::
|
||||
|
||||
Here, we've compiled both godot-cpp and our gdexample library as debug
|
||||
builds. For optimized builds, you should compile them using the
|
||||
``target=release`` switch.
|
||||
|
||||
Using the GDNative module
|
||||
-------------------------
|
||||
|
||||
Before we jump back into Godot, we need to create two more files in
|
||||
``demo/bin/``. Both can be created using the Godot editor, but it may be faster
|
||||
to create them directly.
|
||||
|
||||
The first one is a file that lets Godot know what dynamic libraries should be
|
||||
loaded for each platform and is called ``gdexample.gdnlib``.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[general]
|
||||
|
||||
singleton=false
|
||||
load_once=true
|
||||
symbol_prefix="godot_"
|
||||
reloadable=false
|
||||
|
||||
[entry]
|
||||
|
||||
X11.64="res://bin/x11/libgdexample.so"
|
||||
Windows.64="res://bin/win64/libgdexample.dll"
|
||||
OSX.64="res://bin/osx/libgdexample.dylib"
|
||||
|
||||
[dependencies]
|
||||
|
||||
X11.64=[]
|
||||
Windows.64=[]
|
||||
OSX.64=[]
|
||||
|
||||
This file contains a ``general`` section that controls how the module is loaded.
|
||||
It also contains a prefix section which should be left on ``godot_`` for now. If
|
||||
you change this, you'll need to rename various functions that are used as entry
|
||||
points. This was added for the iPhone platform because it doesn't allow dynamic
|
||||
libraries to be deployed, yet GDNative modules are linked statically.
|
||||
|
||||
The ``entry`` section is the important bit: it tells Godot the location of the
|
||||
dynamic library in the project's filesystem for each supported platform. It will
|
||||
also result in *just* that file being exported when you export the project,
|
||||
which means the data pack won't contain libraries that are incompatible with the
|
||||
target platform.
|
||||
|
||||
Finally, the ``dependencies`` section allows you to name additional dynamic
|
||||
libraries that should be included as well. This is important when your GDNative
|
||||
plugin implements someone else's library and requires you to supply a
|
||||
third-party dynamic library with your project.
|
||||
|
||||
If you double click on the ``gdexample.gdnlib`` file within Godot, you'll see
|
||||
there are far more options to set:
|
||||
|
||||
.. image:: img/gdnative_library.png
|
||||
|
||||
The second file we need to create is a file used by each NativeScript we've
|
||||
added to our plugin. We'll name it ``gdexample.gdns`` for our gdexample
|
||||
NativeScript.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[gd_resource type="NativeScript" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://bin/gdexample.gdnlib" type="GDNativeLibrary" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
resource_name = "gdexample"
|
||||
class_name = "GDExample"
|
||||
library = ExtResource( 1 )
|
||||
|
||||
This is a standard Godot resource; you could just create it directly in your
|
||||
scene, but saving it to a file makes it much easier to reuse it in other places.
|
||||
This resource points to our gdnlib file, so that Godot can know which dynamic
|
||||
library contains our NativeScript. It also defines the ``class_name`` which
|
||||
identifies the NativeScript in our plugin we want to use.
|
||||
|
||||
Time to jump back into Godot. We load up the main scene we created way back in
|
||||
the beginning and now add a Sprite to our scene:
|
||||
|
||||
.. image:: img/gdnative_cpp_nodes.png
|
||||
|
||||
We're going to assign the Godot logo to this sprite as our texture, disable the
|
||||
``centered`` property and drag our ``gdexample.gdns`` file onto the ``script``
|
||||
property of the sprite:
|
||||
|
||||
.. image:: img/gdnative_cpp_sprite.png
|
||||
|
||||
We're finally ready to run the project:
|
||||
|
||||
.. image:: img/gdnative_cpp_animated.gif
|
||||
|
||||
Adding properties
|
||||
-----------------
|
||||
|
||||
GDScript allows you to add properties to your script using the ``export``
|
||||
keyword. In GDNative you have to register the properties and there are two ways
|
||||
of doing this. You can either bind directly to a member or use a setter and
|
||||
getter function.
|
||||
|
||||
.. note::
|
||||
|
||||
There is a third option, just like in GDScript you can directly implement the
|
||||
``_get_property_list``, ``_get`` and ``_set`` methods of an object but that
|
||||
goes far beyond the scope of this tutorial.
|
||||
|
||||
We'll examine both starting with the direct bind. Lets add a property that
|
||||
allows us to control the amplitude of our wave.
|
||||
|
||||
In our ``gdexample.h`` file we simply need to add a member variable like so:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
...
|
||||
private:
|
||||
float time_passed;
|
||||
float amplitude;
|
||||
...
|
||||
|
||||
In our ``gdexample.cpp`` file we need to make a number of changes, we will only
|
||||
show the methods we end up changing, don't remove the lines we're omitting:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
}
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method((char *)"_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
}
|
||||
|
||||
GDExample::GDExample() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
owner->set_position(new_position);
|
||||
}
|
||||
|
||||
Once you compile the module with these changes in place you will see that a
|
||||
property has been added to our interface. You can now change this property and
|
||||
when you run your project, you will see that our Godot icon travels along a
|
||||
larger figure.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``reloadable`` property in the ``gdexample.gdnlib`` file must be set to
|
||||
``true`` for the Godot editor to automatically pick up the newly added
|
||||
property.
|
||||
|
||||
However, this setting should be used with care especially when tool classes
|
||||
are used, as the editor might hold objects then that have script instances
|
||||
attached to them that are managed by a GDNative library.
|
||||
|
||||
Lets do the same but for the speed of our animation and use a setter and getter
|
||||
function. Our ``gdexample.h`` header file again only needs a few more lines of
|
||||
code:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
...
|
||||
float amplitude;
|
||||
float speed;
|
||||
...
|
||||
void _process(float delta);
|
||||
void set_speed(float p_speed);
|
||||
float get_speed();
|
||||
...
|
||||
|
||||
This requires a few more changes to our ``gdexample.cpp`` file, again we're only
|
||||
showing the methods that have changed so don't remove anything we're omitting:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
speed = 1.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
}
|
||||
|
||||
void GDExample::set_speed(float p_speed) {
|
||||
speed = p_speed;
|
||||
}
|
||||
|
||||
float GDExample::get_speed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method((char *)"_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
}
|
||||
|
||||
GDExample::GDExample() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
speed = 1.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
owner->set_position(new_position);
|
||||
}
|
||||
|
||||
void GDExample::set_speed(float p_speed) {
|
||||
speed = p_speed;
|
||||
}
|
||||
|
||||
float GDExample::get_speed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
Now when the project is compiled we'll see another property called speed.
|
||||
Changing its value will make the animation go faster or slower.
|
||||
|
||||
For this example there is no obvious advantage of using a setter and getter. It
|
||||
is just more code to write. For a simple example as this there may be a good
|
||||
reason for a setter if you want to react on the variable being changed but in
|
||||
many cases just binding the variable will be enough.
|
||||
|
||||
Getters and setters become far more useful in more complex scenarios where you
|
||||
need to make additional choices based on the state of your object.
|
||||
|
||||
.. note::
|
||||
|
||||
For simplicity we've left out the optional parameters in the
|
||||
register_property<class, type> method call. These parameters are
|
||||
``rpc_mode``, ``usage``, ``hint`` and ``hint_string``. These can be used to
|
||||
further configure how properties are displayed and set on the Godot side.
|
||||
|
||||
Modern C++ compilers are able to infer the class and variable type and allow
|
||||
you to omit the ``<GDExample, float>`` part of our ``register_property``
|
||||
method. We've had mixed experiences with this however.
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
Last but not least, signals fully work in GDNative as well. Having your module
|
||||
react to a signal given out by another object requires you to call ``connect``
|
||||
on that object. We can't think of a good example for our wobbling Godot icon, we
|
||||
would need to showcase a far more complete example.
|
||||
|
||||
This however is the required syntax:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
some_other_node->connect("the_signal", this, "my_method");
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
some_other_node->connect("the_signal", owner, "my_method");
|
||||
|
||||
Note that you can only call ``my_method`` if you've previously registered it in
|
||||
your ``_register_methods`` method.
|
||||
|
||||
Having your object sending out signals is far more common. For our wobbling
|
||||
Godot icon we'll do something silly just to show how it works. We're going to
|
||||
emit a signal every time a second has passed and pass the new location along.
|
||||
|
||||
In our ``gdexample.h`` header file we just need to define a new member
|
||||
``time_emit``:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
...
|
||||
float time_passed;
|
||||
float time_emit;
|
||||
float amplitude;
|
||||
...
|
||||
|
||||
The changes in ``gdexample.cpp`` are a bit more elaborate this time. First
|
||||
you'll need to set ``time_emit = 0.0;`` in either our ``_init`` method or in our
|
||||
constructor. But the other two needed changes we'll look at one by one.
|
||||
|
||||
In our ``_register_methods`` method we need to declare our signal and we do this
|
||||
as follows:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
|
||||
register_signal<GDExample>((char *)"position_changed", "node", GODOT_VARIANT_TYPE_OBJECT, "new_pos", GODOT_VARIANT_TYPE_VECTOR2);
|
||||
}
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method((char *)"_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
|
||||
Dictionary args;
|
||||
args[Variant("node")] = Variant(Variant::OBJECT);
|
||||
args[Variant("new_pos")] = Variant(Variant::VECTOR2);
|
||||
register_signal<GDExample>((char *)"position_changed", args);
|
||||
}
|
||||
|
||||
Here we see a nice improvement in the latest version of godot-cpp where our
|
||||
``register_signal`` method can be a single call first taking the signals name,
|
||||
then having pairs of values specifying the parameter name and type of each
|
||||
parameter we'll send along with this signal.
|
||||
|
||||
For NativeScript 1.0 we first build a dictionary in which we tell Godot about
|
||||
the types of arguments we will pass to our signal, and then register it.
|
||||
|
||||
Next we'll need to change our ``_process`` method:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: C++ NativeScript 1.1
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
|
||||
time_emit += delta;
|
||||
if (time_emit > 1.0) {
|
||||
emit_signal("position_changed", this, new_position);
|
||||
|
||||
time_emit = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: C++ NativeScript 1.0
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
owner->set_position(new_position);
|
||||
|
||||
time_emit += delta;
|
||||
if (time_emit > 1.0) {
|
||||
Array args;
|
||||
args.push_back(Variant(owner));
|
||||
args.push_back(Variant(new_position));
|
||||
owner->emit_signal("position_changed", args);
|
||||
|
||||
time_emit = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
After a second has passed we emit our signal and reset our counter. Again in the
|
||||
new version of godot-cpp we can add our parameter values directly to
|
||||
``emit_signal``. In NativeScript 1.0 We first build an array of values and then
|
||||
call ``emit_signal``.
|
||||
|
||||
Once compiled we can go into Godot and select our sprite node. On our ``Node``
|
||||
tab we find our new signal and link it up by pressing connect. We've added a
|
||||
script on our main node and implemented our signal like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
extends Node
|
||||
|
||||
func _on_Sprite_position_changed(node, new_pos):
|
||||
print("The position of " + node.name + " is now " + str(new_pos))
|
||||
|
||||
Every second we simply output our position to the console.
|
||||
|
||||
NativeScript 1.1 vs NativeScript 1.0
|
||||
------------------------------------
|
||||
|
||||
So far in our example above there doesn't seem to be a lot of difference between
|
||||
the old and new syntax. The class is defined slightly differently and we no
|
||||
longer use the ``owner`` member to call methods on the Godot side of our object.
|
||||
A lot of the improvements are hidden under the hood.
|
||||
|
||||
This example only deals with simple variables and simple methods. Especially
|
||||
once you start passing references to other objects or when you start calling
|
||||
methods that require more complex parameters, NativeScript 1.1 does start to
|
||||
show its benefits.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
The above is only a simple example, but we hope it shows you the basics. You can
|
||||
build upon this example to create full-fledged scripts to control nodes in Godot
|
||||
using C++.
|
||||
|
||||
You should be able to edit and recompile the plugin while the Godot editor
|
||||
remains open; just rerun the project after the library has finished building.
|
||||
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 625 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 149 KiB |
@@ -1,9 +0,0 @@
|
||||
GDNative
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-tutorials-gdnative
|
||||
|
||||
gdnative-c-example
|
||||
gdnative-cpp-example
|
||||
@@ -6,5 +6,4 @@ Plugins
|
||||
:name: toc-tutorials-extending
|
||||
|
||||
editor/index
|
||||
gdnative/index
|
||||
running_code_in_the_editor
|
||||
|
||||