|
@@ -15,10 +15,13 @@ 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.
|
|
|
|
|
|
-We'll be looking at NativeScript 1.0 which is available in Godot 3.0.
|
|
|
-Godot 3.1 will see the introduction of NativeScript 1.1, which comes with a
|
|
|
-number of improvements. We'll update this tutorial once it is
|
|
|
-officially released, but the overall structure remains similar.
|
|
|
+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>`_.
|
|
@@ -31,7 +34,6 @@ 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_headers repository <https://github.com/GodotNativeTools/godot_headers>`_,
|
|
|
- a copy of the `godot-cpp repository <https://github.com/GodotNativeTools/godot-cpp>`_.
|
|
|
|
|
|
See also :ref:`Compiling <toc-devel-compiling>` as the build tools are identical
|
|
@@ -39,21 +41,35 @@ 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 vise versa so make sure you download the correct branch.
|
|
|
+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 vise 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.
|
|
|
|
|
|
If you are versioning your project using Git,
|
|
|
it is a good idea to add them as Git submodules:
|
|
|
|
|
|
-.. code-block:: none
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: none Godot
|
|
|
+
|
|
|
+ mkdir gdnative_cpp_example
|
|
|
+ cd gdnative_cpp_example
|
|
|
+ git init
|
|
|
+ git submodule add https://github.com/GodotNativeTools/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/GodotNativeTools/godot-cpp
|
|
|
cd godot-cpp
|
|
|
- git submodule init
|
|
|
- git submodule update
|
|
|
- cd ..
|
|
|
+ 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
|
|
@@ -61,16 +77,23 @@ 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:
|
|
|
-.. code-block:: none
|
|
|
+
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: none Godot
|
|
|
+
|
|
|
+ mkdir gdnative_cpp_example
|
|
|
+ cd gdnative_cpp_example
|
|
|
+ git clone --recursive https://github.com/GodotNativeTools/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/GodotNativeTools/godot-cpp
|
|
|
|
|
|
-.. note:: The ``-b 3.0`` I've added as an example to show how to select a specific branch for a specific version of Godot.
|
|
|
- Also ``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.
|
|
|
+.. 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 I've found it easiest to manage. If you decide to just download the repositories or just clone them into your folder, makes sure to keep the folder layout the same as I've setup here as much of the code we'll be showcasing here assumes the project has this layout.
|
|
|
+ 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.
|
|
@@ -114,6 +137,8 @@ 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. We're still working on better auto detection.
|
|
|
+
|
|
|
Creating a simple plugin
|
|
|
------------------------
|
|
|
|
|
@@ -135,7 +160,39 @@ 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``:
|
|
|
|
|
|
-.. code:: C++
|
|
|
+.. 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
|
|
@@ -174,24 +231,57 @@ 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.
|
|
|
-This is also the main bit that is going to improve in NativeScript 1.1.
|
|
|
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.
|
|
|
+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,
|
|
|
+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.
|
|
|
|
|
|
-So, let's implement our functions by creating our ``gdexample.cpp`` file:
|
|
|
+Let's implement our functions by creating our ``gdexample.cpp`` file:
|
|
|
|
|
|
-.. code:: C++
|
|
|
+.. 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"
|
|
|
|
|
@@ -222,7 +312,7 @@ 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 and destructor.
|
|
|
+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
|
|
@@ -280,6 +370,8 @@ use for building. For the purpose of this example, just use
|
|
|
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:
|
|
@@ -313,6 +405,7 @@ should be loaded for each platform and is called ``gdexample.gdnlib``.
|
|
|
singleton=false
|
|
|
load_once=true
|
|
|
symbol_prefix="godot_"
|
|
|
+ reloadable=false
|
|
|
|
|
|
[entry]
|
|
|
|
|
@@ -367,7 +460,7 @@ gdexample NativeScript.
|
|
|
_sections_unfolded = [ "Resource" ]
|
|
|
|
|
|
This is a standard Godot resource; you could just create it directly
|
|
|
-in of your scene, but saving it to a file makes it much easier to reuse it
|
|
|
+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
|
|
@@ -388,6 +481,356 @@ 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:: 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.
|
|
|
+
|
|
|
+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:: 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:: 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 specificying 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 v.s. 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
|
|
|
----------
|
|
|
|