From e2f9034f8d93c25138ea7083cb97f15a23881745 Mon Sep 17 00:00:00 2001 From: CHM <56677134+chocola-mint@users.noreply.github.com> Date: Tue, 14 Nov 2023 23:00:33 +0800 Subject: [PATCH] Remove outdated C++ tutorial in "Your first 2D game" --- .../first_2d_game/03.coding_the_player.rst | 124 --------------- .../first_2d_game/04.creating_the_enemy.rst | 52 ------- .../first_2d_game/05.the_main_game_scene.rst | 146 ------------------ .../first_2d_game/06.heads_up_display.rst | 126 --------------- .../first_2d_game/07.finishing-up.rst | 19 --- 5 files changed, 467 deletions(-) diff --git a/getting_started/first_2d_game/03.coding_the_player.rst b/getting_started/first_2d_game/03.coding_the_player.rst index eec2789c72d..f49af6c34fc 100644 --- a/getting_started/first_2d_game/03.coding_the_player.rst +++ b/getting_started/first_2d_game/03.coding_the_player.rst @@ -45,43 +45,6 @@ Start by declaring the member variables this object will need: public Vector2 ScreenSize; // Size of the game window. } - .. code-tab:: cpp - - // A `player.gdns` file has already been created for you. Attach it to the Player node. - - // Create two files `player.cpp` and `player.hpp` next to `entry.cpp` in `src`. - // This code goes in `player.hpp`. We also define the methods we'll be using here. - #ifndef PLAYER_H - #define PLAYER_H - - #include - #include - #include - #include - #include - - class Player : public godot::Area2D { - GODOT_CLASS(Player, godot::Area2D) - - godot::AnimatedSprite2D *_animated_sprite; - godot::CollisionShape2D *_collision_shape; - godot::Input *_input; - godot::Vector2 _screen_size; // Size of the game window. - - public: - real_t speed = 400; // How fast the player will move (pixels/sec). - - void _init() {} - void _ready(); - void _process(const double p_delta); - void start(const godot::Vector2 p_position); - void _on_body_entered(godot::Node2D *_body); - - static void _register_methods(); - }; - - #endif // PLAYER_H - Using the ``export`` keyword on the first variable ``speed`` allows us to set its value in the Inspector. This can be handy for values that you want to be able to adjust just like a node's built-in properties. Click on the ``Player`` @@ -121,18 +84,6 @@ a good time to find the size of the game window: ScreenSize = GetViewportRect().Size; } - .. code-tab:: cpp - - // This code goes in `player.cpp`. - #include "player.hpp" - - void Player::_ready() { - _animated_sprite = get_node("AnimatedSprite2D"); - _collision_shape = get_node("CollisionShape2D"); - _input = godot::Input::get_singleton(); - _screen_size = get_viewport_rect().size; - } - Now we can use the ``_process()`` function to define what the player will do. ``_process()`` is called every frame, so we'll use it to update elements of our game, which we expect will change often. For the player, we need to do the @@ -245,23 +196,6 @@ which returns ``true`` if it's pressed or ``false`` if it isn't. } } - .. code-tab:: cpp - - // This code goes in `player.cpp`. - void Player::_process(const double p_delta) { - godot::Vector2 velocity(0, 0); - - velocity.x = _input->get_action_strength("move_right") - _input->get_action_strength("move_left"); - velocity.y = _input->get_action_strength("move_down") - _input->get_action_strength("move_up"); - - if (velocity.length() > 0) { - velocity = velocity.normalized() * speed; - _animated_sprite->play(); - } else { - _animated_sprite->stop(); - } - } - We start by setting the ``velocity`` to ``(0, 0)`` - by default, the player should not be moving. Then we check each input and add/subtract from the ``velocity`` to obtain a total direction. For example, if you hold ``right`` and @@ -308,14 +242,6 @@ the ``_process`` function (make sure it's not indented under the `else`): y: Mathf.Clamp(Position.Y, 0, ScreenSize.Y) ); - .. code-tab:: cpp - - godot::Vector2 position = get_position(); - position += velocity * (real_t)p_delta; - position.x = godot::Math::clamp(position.x, (real_t)0.0, _screen_size.x); - position.y = godot::Math::clamp(position.y, (real_t)0.0, _screen_size.y); - set_position(position); - .. tip:: The `delta` parameter in the `_process()` function refers to the *frame length* - the amount of time that the previous frame took to complete. Using this value ensures that your movement will remain consistent even @@ -370,18 +296,6 @@ movement. Let's place this code at the end of the ``_process()`` function: animatedSprite2D.FlipV = velocity.Y > 0; } - .. code-tab:: cpp - - if (velocity.x != 0) { - _animated_sprite->set_animation("walk"); - _animated_sprite->set_flip_v(false); - // See the note below about boolean assignment. - _animated_sprite->set_flip_h(velocity.x < 0); - } else if (velocity.y != 0) { - _animated_sprite->set_animation("up"); - _animated_sprite->set_flip_v(velocity.y > 0); - } - .. Note:: The boolean assignments in the code above are a common shorthand for programmers. Since we're doing a comparison test (boolean) and also *assigning* a boolean value, we can do both at the same time. Consider @@ -426,10 +340,6 @@ When you're sure the movement is working correctly, add this line to Hide(); - .. code-tab:: cpp - - hide(); - Preparing for collisions ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -452,21 +362,6 @@ Add the following at the top of the script. If you're using GDScript, add it aft [Signal] public delegate void HitEventHandler(); - .. code-tab:: cpp - - // This code goes in `player.cpp`. - // We need to register the signal here, and while we're here, we can also - // register the other methods and register the speed property. - void Player::_register_methods() { - godot::register_method("_ready", &Player::_ready); - godot::register_method("_process", &Player::_process); - godot::register_method("start", &Player::start); - godot::register_method("_on_body_entered", &Player::_on_body_entered); - godot::register_property("speed", &Player::speed, (real_t)400.0); - // This below line is the signal. - godot::register_signal("hit", godot::Dictionary()); - } - This defines a custom signal called "hit" that we will have our player emit (send out) when it collides with an enemy. We will use ``Area2D`` to detect the collision. Select the ``Player`` node and click the "Node" tab next to the @@ -505,16 +400,6 @@ this code to the function: GetNode("CollisionShape2D").SetDeferred(CollisionShape2D.PropertyName.Disabled, true); } - .. code-tab:: cpp - - // This code goes in `player.cpp`. - void Player::_on_body_entered(godot::Node2D *_body) { - hide(); // Player disappears after being hit. - emit_signal("hit"); - // Must be deferred as we can't change physics properties on a physics callback. - _collision_shape->set_deferred("disabled", true); - } - Each time an enemy hits the player, the signal is going to be emitted. We need to disable the player's collision so that we don't trigger the ``hit`` signal more than once. @@ -544,13 +429,4 @@ starting a new game. GetNode("CollisionShape2D").Disabled = false; } - .. code-tab:: cpp - - // This code goes in `player.cpp`. - void Player::start(const godot::Vector2 p_position) { - set_position(p_position); - show(); - _collision_shape->set_disabled(false); - } - With the player working, we'll work on the enemy in the next lesson. diff --git a/getting_started/first_2d_game/04.creating_the_enemy.rst b/getting_started/first_2d_game/04.creating_the_enemy.rst index 7a790e55609..0275c0419cd 100644 --- a/getting_started/first_2d_game/04.creating_the_enemy.rst +++ b/getting_started/first_2d_game/04.creating_the_enemy.rst @@ -78,35 +78,6 @@ Add a script to the ``Mob`` like this: // Don't forget to rebuild the project. } - .. code-tab:: cpp - - // Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`. - // Attach the `mob.gdns` file to the Mob node. - - // Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`. - // This code goes in `mob.hpp`. We also define the methods we'll be using here. - #ifndef MOB_H - #define MOB_H - - #include - #include - #include - - class Mob : public godot::RigidBody2D { - GODOT_CLASS(Mob, godot::RigidBody2D) - - godot::AnimatedSprite2D *_animated_sprite; - - public: - void _init() {} - void _ready(); - void _on_visible_on_screen_notifier_2d_screen_exited(); - - static void _register_methods(); - }; - - #endif // MOB_H - Now let's look at the rest of the script. In ``_ready()`` we play the animation and randomly choose one of the three animation types: @@ -126,22 +97,6 @@ and randomly choose one of the three animation types: animatedSprite2D.Play(mobTypes[GD.Randi() % mobTypes.Length]); } - .. code-tab:: cpp - - // This code goes in `mob.cpp`. - #include "mob.hpp" - - #include - #include - - void Mob::_ready() { - godot::Ref random = godot::RandomNumberGenerator::_new(); - _animated_sprite = get_node("AnimatedSprite2D"); - _animated_sprite->set_playing(true); - godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names(); - _animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]); - } - First, we get the list of animation names from the AnimatedSprite2D's ``sprite_frames`` property. This returns an Array containing all three animation names: ``["walk", "swim", "fly"]``. @@ -167,13 +122,6 @@ to the ``Mob`` and add this code: QueueFree(); } - .. code-tab:: cpp - - // This code goes in `mob.cpp`. - void Mob::_on_visible_on_screen_notifier_2d_screen_exited() { - queue_free(); - } - This completes the `Mob` scene. With the player and enemies ready, in the next part, we'll bring them together diff --git a/getting_started/first_2d_game/05.the_main_game_scene.rst b/getting_started/first_2d_game/05.the_main_game_scene.rst index 0a3f57c716c..9e7f47ad48d 100644 --- a/getting_started/first_2d_game/05.the_main_game_scene.rst +++ b/getting_started/first_2d_game/05.the_main_game_scene.rst @@ -99,80 +99,6 @@ to instance. private int _score; } - .. code-tab:: cpp - - // Copy `player.gdns` to `main.gdns` and replace `Player` with `Main`. - // Attach the `main.gdns` file to the Main node. - - // Create two files `main.cpp` and `main.hpp` next to `entry.cpp` in `src`. - // This code goes in `main.hpp`. We also define the methods we'll be using here. - #ifndef MAIN_H - #define MAIN_H - - #include - #include - #include - #include - #include - #include - #include - #include - - #include "hud.hpp" - #include "player.hpp" - - class Main : public godot::Node { - GODOT_CLASS(Main, godot::Node) - - int score; - HUD *_hud; - Player *_player; - godot::Node2D *_start_position; - godot::PathFollow2D *_mob_spawn_location; - godot::Timer *_mob_timer; - godot::Timer *_score_timer; - godot::Timer *_start_timer; - godot::AudioStreamPlayer *_music; - godot::AudioStreamPlayer *_death_sound; - godot::Ref _random; - - public: - godot::Ref mob_scene; - - void _init() {} - void _ready(); - void game_over(); - void new_game(); - void _on_MobTimer_timeout(); - void _on_ScoreTimer_timeout(); - void _on_StartTimer_timeout(); - - static void _register_methods(); - }; - - #endif // MAIN_H - - // This code goes in `main.cpp`. - #include "main.hpp" - - #include - - #include "mob.hpp" - - void Main::_ready() { - _hud = get_node("HUD"); - _player = get_node("Player"); - _start_position = get_node("StartPosition"); - _mob_spawn_location = get_node("MobPath/MobSpawnLocation"); - _mob_timer = get_node("MobTimer"); - _score_timer = get_node("ScoreTimer"); - _start_timer = get_node("StartTimer"); - // Uncomment these after adding the nodes in the "Sound effects" section of "Finishing up". - //_music = get_node("Music"); - //_death_sound = get_node("DeathSound"); - _random = (godot::Ref)godot::RandomNumberGenerator::_new(); - } - Click the ``Main`` node and you will see the ``Mob Scene`` property in the Inspector under "Script Variables". @@ -227,20 +153,6 @@ everything up for a new game: GetNode("StartTimer").Start(); } - .. code-tab:: cpp - - // This code goes in `main.cpp`. - void Main::game_over() { - _score_timer->stop(); - _mob_timer->stop(); - } - - void Main::new_game() { - score = 0; - _player->start(_start_position->get_position()); - _start_timer->start(); - } - Now connect the ``timeout()`` signal of each of the Timer nodes (``StartTimer``, ``ScoreTimer``, and ``MobTimer``) to the main script. ``StartTimer`` will start the other two timers. ``ScoreTimer`` will increment the score by 1. @@ -268,29 +180,6 @@ the other two timers. ``ScoreTimer`` will increment the score by 1. GetNode("ScoreTimer").Start(); } - .. code-tab:: cpp - - // This code goes in `main.cpp`. - void Main::_on_ScoreTimer_timeout() { - score += 1; - } - - void Main::_on_StartTimer_timeout() { - _mob_timer->start(); - _score_timer->start(); - } - - // Also add this to register all methods and the mob scene property. - void Main::_register_methods() { - godot::register_method("_ready", &Main::_ready); - godot::register_method("game_over", &Main::game_over); - godot::register_method("new_game", &Main::new_game); - godot::register_method("_on_MobTimer_timeout", &Main::_on_MobTimer_timeout); - godot::register_method("_on_ScoreTimer_timeout", &Main::_on_ScoreTimer_timeout); - godot::register_method("_on_StartTimer_timeout", &Main::_on_StartTimer_timeout); - godot::register_property("mob_scene", &Main::mob_scene, (godot::Ref)nullptr); - } - In ``_on_mob_timer_timeout()``, we will create a mob instance, pick a random starting location along the ``Path2D``, and set the mob in motion. The ``PathFollow2D`` node will automatically rotate as it follows the path, so we @@ -362,34 +251,6 @@ Note that a new instance must be added to the scene using ``add_child()``. AddChild(mob); } - .. code-tab:: cpp - - // This code goes in `main.cpp`. - void Main::_on_MobTimer_timeout() { - // Create a new instance of the Mob scene. - godot::Node *mob = mob_scene->instance(); - - // Choose a random location on Path2D. - _mob_spawn_location->set_progress_ratio((real_t)_random->randf()); - - // Set the mob's direction perpendicular to the path direction. - real_t direction = _mob_spawn_location->get_rotation() + (real_t)Math_PI / 2; - - // Set the mob's position to a random location. - mob->set("position", _mob_spawn_location->get_position()); - - // Add some randomness to the direction. - direction += _random->randf_range((real_t)-Math_PI / 4, (real_t)Math_PI / 4); - mob->set("rotation", direction); - - // Choose the velocity for the mob. - godot::Vector2 velocity = godot::Vector2(_random->randf_range(150.0, 250.0), 0.0); - mob->set("linear_velocity", velocity.rotated(direction)); - - // Spawn the mob by adding it to the Main scene. - add_child(mob); - } - .. important:: Why ``PI``? In functions requiring angles, Godot uses *radians*, not degrees. Pi represents a half turn in radians, about ``3.1415`` (there is also ``TAU`` which is equal to ``2 * PI``). @@ -416,13 +277,6 @@ call to ``_ready()``: NewGame(); } - .. code-tab:: cpp - - // This code goes in `main.cpp`. - void Main::_ready() { - new_game(); - } - Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically when the game launches. Press the "Play" button and select ``main.tscn`` when prompted. diff --git a/getting_started/first_2d_game/06.heads_up_display.rst b/getting_started/first_2d_game/06.heads_up_display.rst index a36f1c28681..95b561c6f22 100644 --- a/getting_started/first_2d_game/06.heads_up_display.rst +++ b/getting_started/first_2d_game/06.heads_up_display.rst @@ -105,47 +105,6 @@ Now add this script to ``HUD``: public delegate void StartGameEventHandler(); } - .. code-tab:: cpp - - // Copy `player.gdns` to `hud.gdns` and replace `Player` with `HUD`. - // Attach the `hud.gdns` file to the HUD node. - - // Create two files `hud.cpp` and `hud.hpp` next to `entry.cpp` in `src`. - // This code goes in `hud.hpp`. We also define the methods we'll be using here. - #ifndef HUD_H - #define HUD_H - - #include - #include - #include - #include - #include - - class HUD : public godot::CanvasLayer { - GODOT_CLASS(HUD, godot::CanvasLayer) - - godot::Label *_score_label; - godot::Label *_message_label; - godot::Timer *_start_message_timer; - godot::Timer *_get_ready_message_timer; - godot::Button *_start_button; - godot::Timer *_start_button_timer; - - public: - void _init() {} - void _ready(); - void show_get_ready(); - void show_game_over(); - void update_score(const int score); - void _on_StartButton_pressed(); - void _on_StartMessageTimer_timeout(); - void _on_GetReadyMessageTimer_timeout(); - - static void _register_methods(); - }; - - #endif // HUD_H - We now want to display a message temporarily, such as "Get Ready", so we add the following code @@ -168,31 +127,6 @@ such as "Get Ready", so we add the following code GetNode("MessageTimer").Start(); } - .. code-tab:: cpp - - // This code goes in `hud.cpp`. - #include "hud.hpp" - - void HUD::_ready() { - _score_label = get_node("ScoreLabel"); - _message_label = get_node("MessageLabel"); - _start_message_timer = get_node("StartMessageTimer"); - _get_ready_message_timer = get_node("GetReadyMessageTimer"); - _start_button = get_node("StartButton"); - _start_button_timer = get_node("StartButtonTimer"); - } - - void HUD::_register_methods() { - godot::register_method("_ready", &HUD::_ready); - godot::register_method("show_get_ready", &HUD::show_get_ready); - godot::register_method("show_game_over", &HUD::show_game_over); - godot::register_method("update_score", &HUD::update_score); - godot::register_method("_on_StartButton_pressed", &HUD::_on_StartButton_pressed); - godot::register_method("_on_StartMessageTimer_timeout", &HUD::_on_StartMessageTimer_timeout); - godot::register_method("_on_GetReadyMessageTimer_timeout", &HUD::_on_GetReadyMessageTimer_timeout); - godot::register_signal("start_game", godot::Dictionary()); - } - We also need to process what happens when the player loses. The code below will show "Game Over" for 2 seconds, then return to the title screen and, after a brief pause, show the "Start" button. .. tabs:: @@ -226,23 +160,6 @@ We also need to process what happens when the player loses. The code below will GetNode