gdnative-cpp-example.rst 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. .. _doc_gdnative_cpp_example:
  2. GDNative C++ example
  3. ====================
  4. Introduction
  5. ------------
  6. This tutorial builds on top of the information given in the
  7. :ref:`GDNative C example <doc_gdnative_c_example>`, so we highly recommend you
  8. read that first.
  9. The C++ bindings for GDNative are built on top of the NativeScript GDNative API
  10. and provide a nicer way to "extend" nodes in Godot using C++. This is equivalent
  11. to writing scripts in GDScript, but in C++ instead.
  12. Godot 3.1 saw the introduction of the NativeScript 1.1 additions that enabled
  13. the GDNative team to build a nicer C++ bindings library. These changes have now
  14. been merged into the master branch and will be the way we go forward. If you
  15. want to write a C++ GDNative plugin that also supports Godot 3.0 you will need
  16. to use the 3.0 branch and the NativeScript 1.0 syntax. We'll be showing them
  17. side by side in this writeup.
  18. You can download the full example we'll be creating in this tutorial `on
  19. GitHub <https://github.com/BastiaanOlij/gdnative_cpp_example>`__.
  20. Setting up the project
  21. ----------------------
  22. There are a few prerequisites you'll need:
  23. - a Godot 3.x executable,
  24. - a C++ compiler,
  25. - SCons as a build tool,
  26. - a copy of the `godot-cpp
  27. repository <https://github.com/godotengine/godot-cpp>`__.
  28. See also :ref:`Compiling <toc-devel-compiling>` as the build tools are identical
  29. to the ones you need to compile Godot from source.
  30. You can download these repositories from GitHub or let Git do the work for you.
  31. Note that these repositories now have different branches for different versions
  32. of Godot. GDNative modules written for an earlier version of Godot will work in
  33. newer versions (with the exception of one breaking change in ARVR interfaces
  34. between 3.0 and 3.1) but not vice versa so make sure you download the correct
  35. branch. Also note that the version of Godot you use to generate the ``api.json``
  36. with becomes your minimum version.
  37. .. note::
  38. `GDExtension <https://godotengine.org/article/introducing-gd-extensions>`__ has been merged in the ``master`` branch of godot-cpp,
  39. but it is only compatible with the upcoming Godot 4.0.
  40. Therefore, you need to use the ``3.x`` branch of godot-cpp to use GDNative
  41. and follow this example.
  42. If you are versioning your project using Git, it is a good idea to add them as
  43. Git submodules:
  44. .. tabs::
  45. .. code-tab:: none Godot
  46. mkdir gdnative_cpp_example
  47. cd gdnative_cpp_example
  48. git init
  49. git submodule add -b 3.x https://github.com/godotengine/godot-cpp
  50. cd godot-cpp
  51. git submodule update --init
  52. .. code-tab:: none Godot 3.0
  53. mkdir gdnative_cpp_example
  54. cd gdnative_cpp_example
  55. git init
  56. git submodule add -b 3.0 https://github.com/godotengine/godot-cpp
  57. cd godot-cpp
  58. git submodule update --init
  59. If you decide to just download the repositories or clone them into your project
  60. folder, make sure to keep the folder layout identical to the one described here,
  61. as much of the code we'll be showcasing here assumes the project follows this
  62. layout.
  63. Do make sure you clone recursive to pull in both repositories:
  64. .. tabs::
  65. .. code-tab:: none Godot
  66. mkdir gdnative_cpp_example
  67. cd gdnative_cpp_example
  68. git clone --recursive -b 3.x https://github.com/godotengine/godot-cpp
  69. .. code-tab:: none Godot 3.0
  70. mkdir gdnative_cpp_example
  71. cd gdnative_cpp_example
  72. git clone --recursive -b 3.0 https://github.com/godotengine/godot-cpp
  73. .. note::
  74. ``godot-cpp`` now includes ``godot-headers`` as a nested submodule, if you've
  75. manually downloaded them please make sure to place ``godot-headers`` inside
  76. of the ``godot-cpp`` folder.
  77. You don't have to do it this way but we've found it easiest to manage. If you
  78. decide to just download the repositories or just clone them into your folder,
  79. make sure to keep the folder layout the same as we've setup here as much of
  80. the code we'll be showcasing here assumes the project has this layout.
  81. If you cloned the example from the link specified in the introduction, the
  82. submodules are not automatically initialized. You will need to execute the
  83. following commands:
  84. .. code-block:: none
  85. cd gdnative_cpp_example
  86. git submodule update --init --recursive
  87. This will clone these two repositories into your project folder.
  88. Building the C++ bindings
  89. -------------------------
  90. Now that we've downloaded our prerequisites, it is time to build the C++
  91. bindings.
  92. The repository contains a copy of the metadata for the current Godot release,
  93. but if you need to build these bindings for a newer version of Godot, simply
  94. call the Godot executable:
  95. .. code-block:: none
  96. godot --gdnative-generate-json-api api.json
  97. Place the resulting ``api.json`` file in the project folder and add
  98. ``use_custom_api_file=yes custom_api_file=../api.json`` to the scons command
  99. below.
  100. To generate and compile the bindings, use this command (replacing ``<platform>``
  101. with ``windows``, ``linux`` or ``osx`` depending on your OS):
  102. 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.
  103. .. code-block:: none
  104. cd godot-cpp
  105. scons platform=<platform> generate_bindings=yes -j4
  106. cd ..
  107. This step will take a while. When it is completed, you should have static
  108. libraries that can be compiled into your project stored in ``godot-cpp/bin/``.
  109. At some point in the future, compiled binaries will be available, making this
  110. step optional.
  111. .. note::
  112. You may need to add ``bits=64`` to the command on Windows or Linux. We're
  113. still working on better auto detection.
  114. Creating a simple plugin
  115. ------------------------
  116. Now it's time to build an actual plugin. We'll start by creating an empty Godot
  117. project in which we'll place a few files.
  118. Open Godot and create a new project. For this example, we will place it in a
  119. folder called ``demo`` inside our GDNative module's folder structure.
  120. In our demo project, we'll create a scene containing a Node called "Main" and
  121. we'll save it as ``main.tscn``. We'll come back to that later.
  122. Back in the top-level GDNative module folder, we're also going to create a
  123. subfolder called ``src`` in which we'll place our source files.
  124. You should now have ``demo``, ``godot-cpp``, ``godot-headers``, and ``src``
  125. directories in your GDNative module.
  126. In the ``src`` folder, we'll start with creating our header file for the
  127. GDNative node we'll be creating. We will name it ``gdexample.h``:
  128. .. tabs::
  129. .. code-tab:: C++ NativeScript 1.1
  130. #ifndef GDEXAMPLE_H
  131. #define GDEXAMPLE_H
  132. #include <Godot.hpp>
  133. #include <Sprite.hpp>
  134. namespace godot {
  135. class GDExample : public Sprite {
  136. GODOT_CLASS(GDExample, Sprite)
  137. private:
  138. float time_passed;
  139. public:
  140. static void _register_methods();
  141. GDExample();
  142. ~GDExample();
  143. void _init(); // our initializer called by Godot
  144. void _process(float delta);
  145. };
  146. }
  147. #endif
  148. .. code-tab:: C++ NativeScript 1.0
  149. #ifndef GDEXAMPLE_H
  150. #define GDEXAMPLE_H
  151. #include <Godot.hpp>
  152. #include <Sprite.hpp>
  153. namespace godot {
  154. class GDExample : public godot::GodotScript<Sprite> {
  155. GODOT_CLASS(GDExample)
  156. private:
  157. float time_passed;
  158. public:
  159. static void _register_methods();
  160. GDExample();
  161. ~GDExample();
  162. void _process(float delta);
  163. };
  164. }
  165. #endif
  166. There are a few things of note to the above. We're including ``Godot.hpp`` which
  167. contains all our basic definitions. After that, we include ``Sprite.hpp`` which
  168. contains bindings to the Sprite class. We'll be extending this class in our
  169. module.
  170. We're using the namespace ``godot``, since everything in GDNative is defined
  171. within this namespace.
  172. Then we have our class definition, which inherits from our Sprite through a
  173. container class. We'll see a few side effects of this later on. The
  174. ``GODOT_CLASS`` macro sets up a few internal things for us.
  175. After that, we declare a single member variable called ``time_passed``.
  176. In the next block we're defining our methods, we obviously have our constructor
  177. and destructor defined, but there are two other functions that will likely look
  178. familiar to some, and one new method.
  179. The first is ``_register_methods``, which is a static function that Godot will
  180. call to find out which methods can be called on our NativeScript and which
  181. properties it exposes. The second is our ``_process`` function, which will work
  182. exactly the same as the ``_process`` function you're used to in GDScript. The
  183. third is our ``_init`` function which is called after Godot has properly set up
  184. our object. It has to exist even if you don't place any code in it.
  185. Let's implement our functions by creating our ``gdexample.cpp`` file:
  186. .. tabs::
  187. .. code-tab:: C++ NativeScript 1.1
  188. #include "gdexample.h"
  189. using namespace godot;
  190. void GDExample::_register_methods() {
  191. register_method("_process", &GDExample::_process);
  192. }
  193. GDExample::GDExample() {
  194. }
  195. GDExample::~GDExample() {
  196. // add your cleanup here
  197. }
  198. void GDExample::_init() {
  199. // initialize any variables here
  200. time_passed = 0.0;
  201. }
  202. void GDExample::_process(float delta) {
  203. time_passed += delta;
  204. Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));
  205. set_position(new_position);
  206. }
  207. .. code-tab:: C++ NativeScript 1.0
  208. #include "gdexample.h"
  209. using namespace godot;
  210. void GDExample::_register_methods() {
  211. register_method((char *)"_process", &GDExample::_process);
  212. }
  213. GDExample::GDExample() {
  214. // Initialize any variables here
  215. time_passed = 0.0;
  216. }
  217. GDExample::~GDExample() {
  218. // Add your cleanup procedure here
  219. }
  220. void GDExample::_process(float delta) {
  221. time_passed += delta;
  222. Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));
  223. owner->set_position(new_position);
  224. }
  225. This one should be straightforward. We're implementing each method of our class
  226. that we defined in our header file. Note that the ``register_method`` call
  227. **must** expose the ``_process`` method, otherwise Godot will not be able to use
  228. it. However, we do not have to tell Godot about our constructor, destructor and
  229. ``_init`` functions.
  230. The other method of note is our ``_process`` function, which simply keeps track
  231. of how much time has passed and calculates a new position for our sprite using a
  232. sine and cosine function. What stands out is calling
  233. ``owner->set_position`` to call one of the built-in methods of our Sprite. This
  234. is because our class is a container class; ``owner`` points to the actual Sprite
  235. node our script relates to. In the upcoming NativeScript 1.1, ``set_position``
  236. can be called directly on our class.
  237. There is one more C++ file we need; we'll name it ``gdlibrary.cpp``. Our
  238. GDNative plugin can contain multiple NativeScripts, each with their own header
  239. and source file like we've implemented ``GDExample`` up above. What we need now
  240. is a small bit of code that tells Godot about all the NativeScripts in our
  241. GDNative plugin.
  242. .. code-block:: C++
  243. #include "gdexample.h"
  244. extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
  245. godot::Godot::gdnative_init(o);
  246. }
  247. extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
  248. godot::Godot::gdnative_terminate(o);
  249. }
  250. extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
  251. godot::Godot::nativescript_init(handle);
  252. godot::register_class<godot::GDExample>();
  253. }
  254. Note that we are not using the ``godot`` namespace here, since the three
  255. functions implemented here need to be defined without a namespace.
  256. The ``godot_gdnative_init`` and ``godot_gdnative_terminate`` functions get
  257. called respectively when Godot loads our plugin and when it unloads it. All
  258. we're doing here is parse through the functions in our bindings module to
  259. initialize them, but you might have to set up more things depending on your
  260. needs.
  261. The important function is the third function called ``godot_nativescript_init``.
  262. We first call a function in our bindings library that does its usual stuff.
  263. After that, we call the function ``register_class`` for each of our classes in
  264. our library.
  265. Compiling the plugin
  266. --------------------
  267. We cannot easily write by hand a ``SConstruct`` file that SCons would use for
  268. building. For the purpose of this example, just use
  269. :download:`this hardcoded SConstruct file <files/cpp_example/SConstruct>` we've
  270. prepared. We'll cover a more customizable, detailed example on how to use these
  271. build files in a subsequent tutorial.
  272. .. note::
  273. This ``SConstruct`` file was written to be used with the latest ``godot-cpp``
  274. master, you may need to make small changes using it with older versions or
  275. refer to the ``SConstruct`` file in the Godot 3.0 documentation.
  276. Once you've downloaded the ``SConstruct`` file, place it in your GDNative module
  277. folder besides ``godot-cpp``, ``godot-headers`` and ``demo``, then run:
  278. .. code-block:: none
  279. scons platform=<platform>
  280. You should now be able to find the module in ``demo/bin/<platform>``.
  281. .. note::
  282. Here, we've compiled both godot-cpp and our gdexample library as debug
  283. builds. For optimized builds, you should compile them using the
  284. ``target=release`` switch.
  285. Using the GDNative module
  286. -------------------------
  287. Before we jump back into Godot, we need to create two more files in
  288. ``demo/bin/``. Both can be created using the Godot editor, but it may be faster
  289. to create them directly.
  290. The first one is a file that lets Godot know what dynamic libraries should be
  291. loaded for each platform and is called ``gdexample.gdnlib``.
  292. .. code-block:: none
  293. [general]
  294. singleton=false
  295. load_once=true
  296. symbol_prefix="godot_"
  297. reloadable=false
  298. [entry]
  299. X11.64="res://bin/x11/libgdexample.so"
  300. Windows.64="res://bin/win64/libgdexample.dll"
  301. OSX.64="res://bin/osx/libgdexample.dylib"
  302. [dependencies]
  303. X11.64=[]
  304. Windows.64=[]
  305. OSX.64=[]
  306. This file contains a ``general`` section that controls how the module is loaded.
  307. It also contains a prefix section which should be left on ``godot_`` for now. If
  308. you change this, you'll need to rename various functions that are used as entry
  309. points. This was added for the iPhone platform because it doesn't allow dynamic
  310. libraries to be deployed, yet GDNative modules are linked statically.
  311. The ``entry`` section is the important bit: it tells Godot the location of the
  312. dynamic library in the project's filesystem for each supported platform. It will
  313. also result in *just* that file being exported when you export the project,
  314. which means the data pack won't contain libraries that are incompatible with the
  315. target platform.
  316. Finally, the ``dependencies`` section allows you to name additional dynamic
  317. libraries that should be included as well. This is important when your GDNative
  318. plugin implements someone else's library and requires you to supply a
  319. third-party dynamic library with your project.
  320. If you double click on the ``gdexample.gdnlib`` file within Godot, you'll see
  321. there are far more options to set:
  322. .. image:: img/gdnative_library.png
  323. The second file we need to create is a file used by each NativeScript we've
  324. added to our plugin. We'll name it ``gdexample.gdns`` for our gdexample
  325. NativeScript.
  326. .. code-block:: none
  327. [gd_resource type="NativeScript" load_steps=2 format=2]
  328. [ext_resource path="res://bin/gdexample.gdnlib" type="GDNativeLibrary" id=1]
  329. [resource]
  330. resource_name = "gdexample"
  331. class_name = "GDExample"
  332. library = ExtResource( 1 )
  333. This is a standard Godot resource; you could just create it directly in your
  334. scene, but saving it to a file makes it much easier to reuse it in other places.
  335. This resource points to our gdnlib file, so that Godot can know which dynamic
  336. library contains our NativeScript. It also defines the ``class_name`` which
  337. identifies the NativeScript in our plugin we want to use.
  338. Time to jump back into Godot. We load up the main scene we created way back in
  339. the beginning and now add a Sprite to our scene:
  340. .. image:: img/gdnative_cpp_nodes.png
  341. We're going to assign the Godot logo to this sprite as our texture, disable the
  342. ``centered`` property and drag our ``gdexample.gdns`` file onto the ``script``
  343. property of the sprite:
  344. .. image:: img/gdnative_cpp_sprite.png
  345. We're finally ready to run the project:
  346. .. image:: img/gdnative_cpp_animated.gif
  347. Adding properties
  348. -----------------
  349. GDScript allows you to add properties to your script using the ``export``
  350. keyword. In GDNative you have to register the properties and there are two ways
  351. of doing this. You can either bind directly to a member or use a setter and
  352. getter function.
  353. .. note::
  354. There is a third option, just like in GDScript you can directly implement the
  355. ``_get_property_list``, ``_get`` and ``_set`` methods of an object but that
  356. goes far beyond the scope of this tutorial.
  357. We'll examine both starting with the direct bind. Lets add a property that
  358. allows us to control the amplitude of our wave.
  359. In our ``gdexample.h`` file we simply need to add a member variable like so:
  360. .. code-block:: C++
  361. ...
  362. private:
  363. float time_passed;
  364. float amplitude;
  365. ...
  366. In our ``gdexample.cpp`` file we need to make a number of changes, we will only
  367. show the methods we end up changing, don't remove the lines we're omitting:
  368. .. tabs::
  369. .. code-tab:: C++ NativeScript 1.1
  370. void GDExample::_register_methods() {
  371. register_method("_process", &GDExample::_process);
  372. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  373. }
  374. void GDExample::_init() {
  375. // initialize any variables here
  376. time_passed = 0.0;
  377. amplitude = 10.0;
  378. }
  379. void GDExample::_process(float delta) {
  380. time_passed += delta;
  381. Vector2 new_position = Vector2(
  382. amplitude + (amplitude * sin(time_passed * 2.0)),
  383. amplitude + (amplitude * cos(time_passed * 1.5))
  384. );
  385. set_position(new_position);
  386. }
  387. .. code-tab:: C++ NativeScript 1.0
  388. void GDExample::_register_methods() {
  389. register_method((char *)"_process", &GDExample::_process);
  390. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  391. }
  392. GDExample::GDExample() {
  393. // initialize any variables here
  394. time_passed = 0.0;
  395. amplitude = 10.0;
  396. }
  397. void GDExample::_process(float delta) {
  398. time_passed += delta;
  399. Vector2 new_position = Vector2(
  400. amplitude + (amplitude * sin(time_passed * 2.0)),
  401. amplitude + (amplitude * cos(time_passed * 1.5))
  402. );
  403. owner->set_position(new_position);
  404. }
  405. Once you compile the module with these changes in place you will see that a
  406. property has been added to our interface. You can now change this property and
  407. when you run your project, you will see that our Godot icon travels along a
  408. larger figure.
  409. .. note::
  410. The ``reloadable`` property in the ``gdexample.gdnlib`` file must be set to
  411. ``true`` for the Godot editor to automatically pick up the newly added
  412. property.
  413. However, this setting should be used with care especially when tool classes
  414. are used, as the editor might hold objects then that have script instances
  415. attached to them that are managed by a GDNative library.
  416. Lets do the same but for the speed of our animation and use a setter and getter
  417. function. Our ``gdexample.h`` header file again only needs a few more lines of
  418. code:
  419. .. code-block:: C++
  420. ...
  421. float amplitude;
  422. float speed;
  423. ...
  424. void _process(float delta);
  425. void set_speed(float p_speed);
  426. float get_speed();
  427. ...
  428. This requires a few more changes to our ``gdexample.cpp`` file, again we're only
  429. showing the methods that have changed so don't remove anything we're omitting:
  430. .. tabs::
  431. .. code-tab:: C++ NativeScript 1.1
  432. void GDExample::_register_methods() {
  433. register_method("_process", &GDExample::_process);
  434. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  435. register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
  436. }
  437. void GDExample::_init() {
  438. // initialize any variables here
  439. time_passed = 0.0;
  440. amplitude = 10.0;
  441. speed = 1.0;
  442. }
  443. void GDExample::_process(float delta) {
  444. time_passed += speed * delta;
  445. Vector2 new_position = Vector2(
  446. amplitude + (amplitude * sin(time_passed * 2.0)),
  447. amplitude + (amplitude * cos(time_passed * 1.5))
  448. );
  449. set_position(new_position);
  450. }
  451. void GDExample::set_speed(float p_speed) {
  452. speed = p_speed;
  453. }
  454. float GDExample::get_speed() {
  455. return speed;
  456. }
  457. .. code-tab:: C++ NativeScript 1.0
  458. void GDExample::_register_methods() {
  459. register_method((char *)"_process", &GDExample::_process);
  460. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  461. register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
  462. }
  463. GDExample::GDExample() {
  464. // initialize any variables here
  465. time_passed = 0.0;
  466. amplitude = 10.0;
  467. speed = 1.0;
  468. }
  469. void GDExample::_process(float delta) {
  470. time_passed += speed * delta;
  471. Vector2 new_position = Vector2(
  472. amplitude + (amplitude * sin(time_passed * 2.0)),
  473. amplitude + (amplitude * cos(time_passed * 1.5))
  474. );
  475. owner->set_position(new_position);
  476. }
  477. void GDExample::set_speed(float p_speed) {
  478. speed = p_speed;
  479. }
  480. float GDExample::get_speed() {
  481. return speed;
  482. }
  483. Now when the project is compiled we'll see another property called speed.
  484. Changing its value will make the animation go faster or slower.
  485. For this example there is no obvious advantage of using a setter and getter. It
  486. is just more code to write. For a simple example as this there may be a good
  487. reason for a setter if you want to react on the variable being changed but in
  488. many cases just binding the variable will be enough.
  489. Getters and setters become far more useful in more complex scenarios where you
  490. need to make additional choices based on the state of your object.
  491. .. note::
  492. For simplicity we've left out the optional parameters in the
  493. register_property<class, type> method call. These parameters are
  494. ``rpc_mode``, ``usage``, ``hint`` and ``hint_string``. These can be used to
  495. further configure how properties are displayed and set on the Godot side.
  496. Modern C++ compilers are able to infer the class and variable type and allow
  497. you to omit the ``<GDExample, float>`` part of our ``register_property``
  498. method. We've had mixed experiences with this however.
  499. Signals
  500. -------
  501. Last but not least, signals fully work in GDNative as well. Having your module
  502. react to a signal given out by another object requires you to call ``connect``
  503. on that object. We can't think of a good example for our wobbling Godot icon, we
  504. would need to showcase a far more complete example.
  505. This however is the required syntax:
  506. .. tabs::
  507. .. code-tab:: C++ NativeScript 1.1
  508. some_other_node->connect("the_signal", this, "my_method");
  509. .. code-tab:: C++ NativeScript 1.0
  510. some_other_node->connect("the_signal", owner, "my_method");
  511. Note that you can only call ``my_method`` if you've previously registered it in
  512. your ``_register_methods`` method.
  513. Having your object sending out signals is far more common. For our wobbling
  514. Godot icon we'll do something silly just to show how it works. We're going to
  515. emit a signal every time a second has passed and pass the new location along.
  516. In our ``gdexample.h`` header file we just need to define a new member
  517. ``time_emit``:
  518. .. code-block:: C++
  519. ...
  520. float time_passed;
  521. float time_emit;
  522. float amplitude;
  523. ...
  524. The changes in ``gdexample.cpp`` are a bit more elaborate this time. First
  525. you'll need to set ``time_emit = 0.0;`` in either our ``_init`` method or in our
  526. constructor. But the other two needed changes we'll look at one by one.
  527. In our ``_register_methods`` method we need to declare our signal and we do this
  528. as follows:
  529. .. tabs::
  530. .. code-tab:: C++ NativeScript 1.1
  531. void GDExample::_register_methods() {
  532. register_method("_process", &GDExample::_process);
  533. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  534. register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
  535. register_signal<GDExample>((char *)"position_changed", "node", GODOT_VARIANT_TYPE_OBJECT, "new_pos", GODOT_VARIANT_TYPE_VECTOR2);
  536. }
  537. .. code-tab:: C++ NativeScript 1.0
  538. void GDExample::_register_methods() {
  539. register_method((char *)"_process", &GDExample::_process);
  540. register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
  541. register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
  542. Dictionary args;
  543. args[Variant("node")] = Variant(Variant::OBJECT);
  544. args[Variant("new_pos")] = Variant(Variant::VECTOR2);
  545. register_signal<GDExample>((char *)"position_changed", args);
  546. }
  547. Here we see a nice improvement in the latest version of godot-cpp where our
  548. ``register_signal`` method can be a single call first taking the signals name,
  549. then having pairs of values specifying the parameter name and type of each
  550. parameter we'll send along with this signal.
  551. For NativeScript 1.0 we first build a dictionary in which we tell Godot about
  552. the types of arguments we will pass to our signal, and then register it.
  553. Next we'll need to change our ``_process`` method:
  554. .. tabs::
  555. .. code-tab:: C++ NativeScript 1.1
  556. void GDExample::_process(float delta) {
  557. time_passed += speed * delta;
  558. Vector2 new_position = Vector2(
  559. amplitude + (amplitude * sin(time_passed * 2.0)),
  560. amplitude + (amplitude * cos(time_passed * 1.5))
  561. );
  562. set_position(new_position);
  563. time_emit += delta;
  564. if (time_emit > 1.0) {
  565. emit_signal("position_changed", this, new_position);
  566. time_emit = 0.0;
  567. }
  568. }
  569. .. code-tab:: C++ NativeScript 1.0
  570. void GDExample::_process(float delta) {
  571. time_passed += speed * delta;
  572. Vector2 new_position = Vector2(
  573. amplitude + (amplitude * sin(time_passed * 2.0)),
  574. amplitude + (amplitude * cos(time_passed * 1.5))
  575. );
  576. owner->set_position(new_position);
  577. time_emit += delta;
  578. if (time_emit > 1.0) {
  579. Array args;
  580. args.push_back(Variant(owner));
  581. args.push_back(Variant(new_position));
  582. owner->emit_signal("position_changed", args);
  583. time_emit = 0.0;
  584. }
  585. }
  586. After a second has passed we emit our signal and reset our counter. Again in the
  587. new version of godot-cpp we can add our parameter values directly to
  588. ``emit_signal``. In NativeScript 1.0 We first build an array of values and then
  589. call ``emit_signal``.
  590. Once compiled we can go into Godot and select our sprite node. On our ``Node``
  591. tab we find our new signal and link it up by pressing connect. We've added a
  592. script on our main node and implemented our signal like this:
  593. .. code-block:: none
  594. extends Node
  595. func _on_Sprite_position_changed(node, new_pos):
  596. print("The position of " + node.name + " is now " + str(new_pos))
  597. Every second we simply output our position to the console.
  598. NativeScript 1.1 vs NativeScript 1.0
  599. ------------------------------------
  600. So far in our example above there doesn't seem to be a lot of difference between
  601. the old and new syntax. The class is defined slightly differently and we no
  602. longer use the ``owner`` member to call methods on the Godot side of our object.
  603. A lot of the improvements are hidden under the hood.
  604. This example only deals with simple variables and simple methods. Especially
  605. once you start passing references to other objects or when you start calling
  606. methods that require more complex parameters, NativeScript 1.1 does start to
  607. show its benefits.
  608. Next steps
  609. ----------
  610. The above is only a simple example, but we hope it shows you the basics. You can
  611. build upon this example to create full-fledged scripts to control nodes in Godot
  612. using C++.
  613. You should be able to edit and recompile the plugin while the Godot editor
  614. remains open; just rerun the project after the library has finished building.