mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-03 05:48:42 +03:00
Merge pull request #5604 from Calinou/gdnative-cpp-example-remove-nativescript-1.0
Remove references to NativeScript 1.0 in GDNative C++ example
This commit is contained in:
@@ -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);
|
||||
Next, we'll need to change our ``_process`` method:
|
||||
|
||||
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
|
||||
.. 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
|
||||
After a second has passed, we emit our signal and reset our counter. We can add
|
||||
our parameter values directly to ``emit_signal``.
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
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:
|
||||
|
||||
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
|
||||
.. 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.
|
||||
|
||||
Reference in New Issue
Block a user