From 80697b6ec4549cef2961c1b74f9f06e75b6e41d0 Mon Sep 17 00:00:00 2001 From: dementive <87823030+dementive@users.noreply.github.com> Date: Fri, 23 Aug 2024 11:41:29 -0400 Subject: [PATCH] Add GDExtension C++ code snippets to scripting_first_script.rst --- .../step_by_step/scripting_first_script.rst | 98 +++++++++++++++++++ .../step_by_step/scripting_player_input.rst | 74 ++++++++++++++ getting_started/step_by_step/signals.rst | 2 + .../gdextension/gdextension_cpp_example.rst | 2 + 4 files changed, 176 insertions(+) diff --git a/getting_started/step_by_step/scripting_first_script.rst b/getting_started/step_by_step/scripting_first_script.rst index 9c2bcc9a880..58cc7c9bcd6 100644 --- a/getting_started/step_by_step/scripting_first_script.rst +++ b/getting_started/step_by_step/scripting_first_script.rst @@ -30,6 +30,8 @@ The equivalent C# code has been included in another tab for convenience. .. seealso:: To learn more about C#, head to the :ref:`C# basics ` page. +.. seealso:: To learn more about GDExtension and godot-cpp, head to the :ref:`What is GDExtension? ` page. + Project setup ------------- @@ -107,6 +109,35 @@ the following line of code: { } + .. code-tab:: cpp C++ + + #ifndef MY_SPRITE_2D_H + #define MY_SPRITE_2D_H + + #include + + using namespace godot; + + namespace CustomNamespace { + + // MySprite2D also needs to be bound in your GDExtension's register_types.cpp file. + // More info on this in the GDExtension example setup tutorial. + class MySprite2D : public Sprite2D { + GDCLASS(MySprite2D, Sprite2D) + + protected: + // _bind_methods is required for all GDCLASS's or compilation will fail! + static void _bind_methods() {} + + public: + // Default constructor with no parameters is required or compilation will fail! + MySprite2D() {} + }; + + } // CustomNamespace + + #endif // MY_SPRITE_2D_H + Every GDScript file is implicitly a class. The ``extends`` keyword defines the class this script inherits or extends. In this case, it's ``Sprite2D``, meaning our script will get access to all the properties and functions of the Sprite2D @@ -150,6 +181,17 @@ Add the following code to your script: GD.Print("Hello, world!"); } + .. code-tab:: cpp C++ + + // Add this include at the top of your header file. + #include + + // Add this inside your MySprite2D class. + public: + MySprite2D() { + UtilityFunctions::print("Hello, world!"); + } + Let's break it down. The ``func`` keyword defines a new function named ``_init``. This is a special name for our class's constructor. The engine calls @@ -188,6 +230,11 @@ angular speed in radians per second. Add the following after the ``extends Spri private int _speed = 400; private float _angularSpeed = Mathf.Pi; + .. code-tab:: cpp C++ + + int speed = 400; + float angular_speed = Math_PI; + Member variables sit near the top of the script, after any "extends" lines, but before functions. Every node instance with this script attached to it will have its own copy of the ``speed`` @@ -231,6 +278,13 @@ At the bottom of the script, define the function: Rotation += _angularSpeed * (float)delta; } + .. code-tab:: cpp C++ + + void _process(double p_delta) override { + // Note that properties (like rotation) are accessed via setters and getters in godot-cpp. + set_rotation(get_rotation() + angular_speed * p_delta); + } + The ``func`` keyword defines a new function. After it, we have to write the function's name and arguments it takes in parentheses. A colon ends the definition, and the indented blocks that follow are the function's content or @@ -278,6 +332,14 @@ them. Position += velocity * (float)delta; + .. code-tab:: cpp C++ + + // Note that the directional Vector2 constants do not exist in godot-cpp. So Vector2(0, -1) must be used. + Vector2 velocity = Vector2(0, -1).rotated(get_rotation()) * speed; + + set_position(get_position() + velocity * p_delta); + + As we already saw, the ``var`` keyword defines a new variable. If you put it at the top of the script, it defines a property of the class. Inside a function, it defines a local variable: it only exists within the function's scope. @@ -343,3 +405,39 @@ Here is the complete ``sprite_2d.gd`` file for reference. Position += velocity * (float)delta; } } + + .. code-tab:: cpp C++ + + #ifndef MY_SPRITE_2D_H + #define MY_SPRITE_2D_H + + #include + #include + + using namespace godot; + + namespace CustomNamespace { + + class MySprite2D : public Sprite2D { + GDCLASS(MySprite2D, Sprite2D) + + int speed = 400; + float angular_speed = Math_PI; + + protected: + static void _bind_methods() {} + + public: + MySprite2D() {} + + void _process(double p_delta) override { + set_rotation(get_rotation() + angular_speed * p_delta); + + Vector2 velocity = Vector2(0, -1).rotated(get_rotation()) * speed; + set_position(get_position() + velocity * p_delta); + } + }; + + } // CustomNamespace + + #endif // MY_SPRITE_2D_H diff --git a/getting_started/step_by_step/scripting_player_input.rst b/getting_started/step_by_step/scripting_player_input.rst index 2a9cd9168e1..18942be04b5 100644 --- a/getting_started/step_by_step/scripting_player_input.rst +++ b/getting_started/step_by_step/scripting_player_input.rst @@ -56,6 +56,18 @@ code below. Rotation += _angularSpeed * direction * (float)delta; + .. code-tab:: cpp C++ + + int direction = 0; + if (Input::get_singleton()->is_action_pressed("ui_left")) { + direction = -1; + } + if (Input::get_singleton()->is_action_pressed("ui_right")) { + direction = 1; + } + + set_rotation(get_rotation() + angular_speed * direction * p_delta); + Our ``direction`` local variable is a multiplier representing the direction in which the player wants to turn. A value of ``0`` means the player isn't pressing the left or the right arrow key. A value of ``1`` means the player wants to turn @@ -95,6 +107,12 @@ Comment out the lines ``var velocity = Vector2.UP.rotated(rotation) * speed`` an //Position += velocity * (float)delta; + .. code-tab:: cpp C++ + + //Vector2 velocity = Vector2(0, -1).rotated(get_rotation()) * speed; + + //set_position(get_position() + velocity * p_delta); + This will ignore the code that moved the icon's position in a circle without user input from the previous exercise. If you run the scene with this code, the icon should rotate when you press @@ -121,6 +139,13 @@ velocity. Uncomment the code and replace the line starting with ``var velocity`` velocity = Vector2.Up.Rotated(Rotation) * _speed; } + .. code-tab:: cpp C++ + + Vector2 velocity = Vector2(0, 0); + if (Input::get_singleton()->is_action_pressed("ui_up")) { + velocity = Vector2(0, -1).rotated(get_rotation()) * speed; + } + We initialize the ``velocity`` with a value of ``Vector2.ZERO``, another constant of the built-in ``Vector`` type representing a 2D vector of length 0. @@ -189,6 +214,55 @@ Here is the complete ``sprite_2d.gd`` file for reference. } } + .. code-tab:: cpp C++ + + #ifndef MY_SPRITE_2D_H + #define MY_SPRITE_2D_H + + #include + #include + #include + + using namespace godot; + + namespace CustomNamespace { + + class MySprite2D : public Sprite2D { + GDCLASS(MySprite2D, Sprite2D) + + int speed = 400; + float angular_speed = Math_PI; + + protected: + static void _bind_methods() {} + + public: + MySprite2D() {} + + void _process(double p_delta) override { + int direction = 0; + if (Input::get_singleton()->is_action_pressed("ui_left")) { + direction = -1; + } + if (Input::get_singleton()->is_action_pressed("ui_right")) { + direction = 1; + } + + set_rotation(get_rotation() + angular_speed * p_delta); + + Vector2 velocity = Vector2(0, 0); + if (Input::get_singleton()->is_action_pressed("ui_up")) { + velocity = Vector2(0, -1).rotated(get_rotation()) * speed; + } + + set_position(get_position() + velocity * p_delta); + } + }; + + } //namespace CustomNamespace + + #endif // MY_SPRITE_2D_H + If you run the scene, you should now be able to rotate with the left and right arrow keys and move forward by pressing :kbd:`Up`. diff --git a/getting_started/step_by_step/signals.rst b/getting_started/step_by_step/signals.rst index 5a0e512632f..ace83fb8ac6 100644 --- a/getting_started/step_by_step/signals.rst +++ b/getting_started/step_by_step/signals.rst @@ -37,6 +37,8 @@ what you can do with the Signal type directly. observer pattern. You can learn more about it in `Game Programming Patterns `__. +.. note:: Signals in godot-cpp are used in slightly different ways than GDScript or CSharp. To learn more head to the :ref:`GDExtension C++ example ` page. + We will now use a signal to make our Godot icon from the previous lesson (:ref:`doc_scripting_player_input`) move and stop by pressing a button. diff --git a/tutorials/scripting/gdextension/gdextension_cpp_example.rst b/tutorials/scripting/gdextension/gdextension_cpp_example.rst index a16285185a7..bb1245fd97b 100644 --- a/tutorials/scripting/gdextension/gdextension_cpp_example.rst +++ b/tutorials/scripting/gdextension/gdextension_cpp_example.rst @@ -597,6 +597,8 @@ The first two arguments are the minimum and maximum value and the third is the s There are a lot more options to choose from. These can be used to further configure how properties are displayed and set on the Godot side. +.. _doc_gdextension_cpp_signals_example: + Signals -------