Move plugins/gdnative to scripting/

Closes #4114
This commit is contained in:
Nathan Lovato
2020-10-08 17:16:22 -06:00
parent 1778b89945
commit 6bca7ed4d6
24 changed files with 101 additions and 5 deletions

View File

@@ -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))

View File

@@ -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

View File

@@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,9 +0,0 @@
GDNative
========
.. toctree::
:maxdepth: 1
:name: toc-tutorials-gdnative
gdnative-c-example
gdnative-cpp-example

View File

@@ -6,5 +6,4 @@ Plugins
:name: toc-tutorials-extending
editor/index
gdnative/index
running_code_in_the_editor