|
@@ -14,13 +14,6 @@ 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>`__.
|
|
|
|
|
@@ -48,16 +41,18 @@ with becomes your minimum version.
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
- `GDExtension <https://godotengine.org/article/introducing-gd-extensions>`__ has been merged in the ``master`` branch of godot-cpp,
|
|
|
+ `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.
|
|
|
|
|
|
+ This tutorial covers only GDNative in Godot 3.x, *not* GDExtension in Godot 4.0.
|
|
|
+
|
|
|
If you are versioning your project using Git, it is a good idea to add them as
|
|
|
Git submodules:
|
|
|
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: none Godot
|
|
|
+.. code-block:: none
|
|
|
|
|
|
mkdir gdnative_cpp_example
|
|
|
cd gdnative_cpp_example
|
|
@@ -66,15 +61,6 @@ Git submodules:
|
|
|
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
|
|
@@ -82,28 +68,21 @@ layout.
|
|
|
|
|
|
Do make sure you clone recursive to pull in both repositories:
|
|
|
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: none Godot
|
|
|
+.. code-block:: none
|
|
|
|
|
|
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
|
|
|
+ You don't have to do it this way, but we've found it easiest to manage. If you
|
|
|
+ decide to download the repositories or clone them into your folder,
|
|
|
+ make sure to keep the folder layout the same as we've setup here. 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
|
|
@@ -138,7 +117,8 @@ 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.
|
|
|
+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
|
|
|
|
|
@@ -149,13 +129,9 @@ To speed up compilation, add `-jN` at the end of the SCons command line where `N
|
|
|
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.
|
|
|
+ You may need to add ``bits=64`` to the command on Windows or Linux.
|
|
|
|
|
|
Creating a simple plugin
|
|
|
------------------------
|
|
@@ -178,8 +154,7 @@ 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
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
#ifndef GDEXAMPLE_H
|
|
|
#define GDEXAMPLE_H
|
|
@@ -210,35 +185,6 @@ GDNative node we'll be creating. We will name it ``gdexample.h``:
|
|
|
|
|
|
#endif
|
|
|
|
|
|
- .. code-tab:: C++ NativeScript 1.0
|
|
|
-
|
|
|
- #ifndef GDEXAMPLE_H
|
|
|
- #define GDEXAMPLE_H
|
|
|
-
|
|
|
- #include <Godot.hpp>
|
|
|
- #include <Sprite2D.hpp>
|
|
|
-
|
|
|
- namespace godot {
|
|
|
-
|
|
|
- class GDExample : public godot::GodotScript<Sprite2D> {
|
|
|
- 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 ``Sprite2D.hpp`` which
|
|
|
contains bindings to the Sprite2D class. We'll be extending this class in our
|
|
@@ -271,8 +217,7 @@ our object.
|
|
|
|
|
|
Let's implement our functions by creating our ``gdexample.cpp`` file:
|
|
|
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: C++ NativeScript 1.1
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
#include "gdexample.h"
|
|
|
|
|
@@ -302,33 +247,6 @@ Let's implement our functions by creating our ``gdexample.cpp`` file:
|
|
|
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
|
|
@@ -340,8 +258,7 @@ of how much time has passed and calculates a new position for our sprite using a
|
|
|
sine and cosine function. What stands out is calling
|
|
|
``owner->set_position`` to call one of the build in methods of our Sprite2D. This
|
|
|
is because our class is a container class; ``owner`` points to the actual Sprite2D
|
|
|
-node our script relates to. Since NativeScript 1.1, ``set_position``
|
|
|
-can be called directly on our class.
|
|
|
+node our script relates to.
|
|
|
|
|
|
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
|
|
@@ -531,8 +448,7 @@ In our ``gdexample.h`` file we simply need to add a member variable like so:
|
|
|
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
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
void GDExample::_register_methods() {
|
|
|
register_method("_process", &GDExample::_process);
|
|
@@ -556,31 +472,7 @@ show the methods we end up changing, don't remove the lines we're omitting:
|
|
|
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
|
|
|
+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.
|
|
@@ -591,11 +483,11 @@ larger figure.
|
|
|
``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
|
|
|
+ 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
|
|
|
+Let's 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:
|
|
|
|
|
@@ -613,8 +505,7 @@ code:
|
|
|
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
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
void GDExample::_register_methods() {
|
|
|
register_method("_process", &GDExample::_process);
|
|
@@ -648,53 +539,19 @@ showing the methods that have changed so don't remove anything we're omitting:
|
|
|
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.
|
|
|
+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.
|
|
|
-A good reason for a setter would be if you wanted to react on the variable being changed, but in
|
|
|
-many cases binding the variable is enough.
|
|
|
+For this example, there is no obvious advantage of using a setter and getter.
|
|
|
+A good reason for a setter would be if you wanted to react on the variable being changed.
|
|
|
+If you don't need to do something like that, binding the variable is 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
|
|
|
+ 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.
|
|
@@ -711,26 +568,20 @@ 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:
|
|
|
+This is the required syntax:
|
|
|
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: C++ NativeScript 1.1
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
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
|
|
|
+Having your object sending out signals is 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``:
|
|
|
+In our ``gdexample.h`` header file, we need to define a new member ``time_emit``:
|
|
|
|
|
|
.. code-block:: C++
|
|
|
|
|
@@ -740,15 +591,14 @@ In our ``gdexample.h`` header file we just need to define a new member
|
|
|
float amplitude;
|
|
|
...
|
|
|
|
|
|
-The changes in ``gdexample.cpp`` are a bit more elaborate this time. First
|
|
|
+This time, the changes in ``gdexample.cpp`` are more elaborate. 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.
|
|
|
+constructor. We'll look at the other 2 needed changes one by one.
|
|
|
|
|
|
-In our ``_register_methods`` method we need to declare our signal and we do this
|
|
|
+In our ``_register_methods`` method, we need to declare our signal. This is done
|
|
|
as follows:
|
|
|
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: C++ NativeScript 1.1
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
void GDExample::_register_methods() {
|
|
|
register_method("_process", &GDExample::_process);
|
|
@@ -758,31 +608,13 @@ as follows:
|
|
|
register_signal<GDExample>((char *)"position_changed", "node", GODOT_VARIANT_TYPE_OBJECT, "new_pos", GODOT_VARIANT_TYPE_VECTOR2);
|
|
|
}
|
|
|
|
|
|
- .. code-tab:: C++ NativeScript 1.0
|
|
|
+Here, 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.
|
|
|
|
|
|
- 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:
|
|
|
|
|
|
-Next we'll need to change our ``_process`` method:
|
|
|
-
|
|
|
-.. tabs::
|
|
|
- .. code-tab:: C++ NativeScript 1.1
|
|
|
+.. code-block:: C++
|
|
|
|
|
|
void GDExample::_process(float delta) {
|
|
|
time_passed += speed * delta;
|
|
@@ -802,59 +634,22 @@ Next we'll need to change our ``_process`` method:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .. 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``.
|
|
|
+After a second has passed, we emit our signal and reset our counter. We can add
|
|
|
+our parameter values directly to ``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:
|
|
|
+Once the GDNative library is compiled, we can go into Godot and select our sprite
|
|
|
+node. In the **Node** dock, we can find our new signal and link it up by pressing
|
|
|
+the **Connect** button or double-clicking the signal. We've added a script on
|
|
|
+our main node and implemented our signal like this:
|
|
|
|
|
|
-.. code-block:: none
|
|
|
+.. code-block:: GDScript
|
|
|
|
|
|
extends Node
|
|
|
|
|
|
func _on_Sprite2D_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 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.
|
|
|
+Every second, we output our position to the console.
|
|
|
|
|
|
Next steps
|
|
|
----------
|
|
@@ -864,4 +659,4 @@ build upon this example to create full-fledged scripts to control nodes in Godot
|
|
|
using C++.
|
|
|
|
|
|
To edit and recompile the plugin while the Godot editor
|
|
|
-remains open, rerun the project after the library has finished building.
|
|
|
+remains open, re-run the project after the library has finished building.
|