浏览代码

Update getting started C# code, remove randomize (#6811)

* Update getting started C# code, remove randomize

- Sync C# code with GDScript.
- Remove `randomize` calls.
- Rename members that have been renamed in Godot's C# API for 4.0.
- Change `delta` parameter type to `double` in C#.
- Follow our C# code style more closely.
- Other minor code fixes.

---------

Co-authored-by: RedworkDE <[email protected]>
Raul Santos 2 年之前
父节点
当前提交
a7637e0ef1

+ 1 - 1
getting_started/first_2d_game/01.project_setup.rst

@@ -25,7 +25,7 @@ Launch Godot and create a new project.
     and ``fonts/`` directories to your project's directory.
     and ``fonts/`` directories to your project's directory.
 
 
     Ensure that you have the required dependencies to use C# in Godot.
     Ensure that you have the required dependencies to use C# in Godot.
-    You need the .NET Core 3.1 SDK, and an editor such as VS Code.
+    You need the latest stable .NET SDK, and an editor such as VS Code.
     See :ref:`doc_c_sharp_setup`.
     See :ref:`doc_c_sharp_setup`.
 
 
  .. tab:: C++
  .. tab:: C++

+ 17 - 10
getting_started/first_2d_game/03.coding_the_player.rst

@@ -89,11 +89,18 @@ node and you'll see the property now appears in the "Script Variables" section
 of the Inspector. Remember, if you change the value here, it will override the
 of the Inspector. Remember, if you change the value here, it will override the
 value written in the script.
 value written in the script.
 
 
-.. warning:: If you're using C#, you need to (re)build the project assemblies
-             whenever you want to see new export variables or signals. This
-             build can be manually triggered by clicking the word "Mono" at the
-             bottom of the editor window to reveal the Mono Panel, then clicking
-             the "Build Project" button.
+.. warning::
+
+    If you're using C#, you need to (re)build the project assemblies
+    whenever you want to see new export variables or signals. This
+    build can be manually triggered by clicking the "Build" button at
+    the top right of the editor.
+
+    .. image:: img/build_dotnet.webp
+
+    A manual build can also be triggered from the MSBuild Panel. Click
+    the word "MSBuild" at the bottom of the editor window to reveal the
+    MSBuild Panel, then click the "Build" button.
 
 
 .. image:: img/export_variable.webp
 .. image:: img/export_variable.webp
 
 
@@ -390,7 +397,7 @@ movement. Let's place this code at the end of the ``_process()`` function:
 
 
            .. code-tab:: csharp
            .. code-tab:: csharp
 
 
-             if (velocity.x < 0)
+             if (velocity.X < 0)
              {
              {
                  animatedSprite2D.FlipH = true;
                  animatedSprite2D.FlipH = true;
              }
              }
@@ -490,12 +497,12 @@ this code to the function:
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void OnBodyEntered(PhysicsBody2D body)
+    private void OnBodyEntered(PhysicsBody2D body)
     {
     {
         Hide(); // Player disappears after being hit.
         Hide(); // Player disappears after being hit.
         EmitSignal(SignalName.Hit);
         EmitSignal(SignalName.Hit);
         // Must be deferred as we can't change physics properties on a physics callback.
         // Must be deferred as we can't change physics properties on a physics callback.
-        GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
+        GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred(CollisionShape2D.PropertyName.Disabled, true);
     }
     }
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
@@ -530,9 +537,9 @@ starting a new game.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void Start(Vector2 pos)
+    public void Start(Vector2 position)
     {
     {
-        Position = pos;
+        Position = position;
         Show();
         Show();
         GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
         GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
     }
     }

+ 6 - 13
getting_started/first_2d_game/04.creating_the_enemy.rst

@@ -108,18 +108,16 @@ and randomly choose one of the three animation types:
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
     func _ready():
     func _ready():
-        $AnimatedSprite2D.play()
-        var mob_types = $AnimatedSprite2D.get_sprite_frames().get_animation_names()
-        $AnimatedSprite2D.animation = mob_types[randi() % mob_types.size()]
+        var mob_types = $AnimatedSprite2D.sprite_frames.get_animation_names()
+        $AnimatedSprite2D.play(mob_types[randi() % mob_types.size()])
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     public override void _Ready()
     public override void _Ready()
     {
     {
-        var animSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
-        animSprite2D.Play();
-        string[] mobTypes = animSprite2D.SpriteFrames.GetAnimationNames();
-        animSprite2D.Animation = mobTypes[GD.Randi() % mobTypes.Length];
+        var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
+        string[] mobTypes = animatedSprite2D.SpriteFrames.GetAnimationNames();
+        animatedSprite2D.Play(mobTypes[GD.Randi() % mobTypes.Length]);
     }
     }
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
@@ -132,7 +130,6 @@ and randomly choose one of the three animation types:
 
 
     void Mob::_ready() {
     void Mob::_ready() {
         godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
         godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
-        random->randomize();
         _animated_sprite = get_node<godot::AnimatedSprite2D>("AnimatedSprite2D");
         _animated_sprite = get_node<godot::AnimatedSprite2D>("AnimatedSprite2D");
         _animated_sprite->set_playing(true);
         _animated_sprite->set_playing(true);
         godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
         godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
@@ -147,10 +144,6 @@ We then need to pick a random number between ``0`` and ``2`` to select one of
 these names from the list (array indices start at ``0``). ``randi() % n``
 these names from the list (array indices start at ``0``). ``randi() % n``
 selects a random integer between ``0`` and ``n-1``.
 selects a random integer between ``0`` and ``n-1``.
 
 
-.. note:: You must use ``randomize()`` if you want your sequence of "random"
-            numbers to be different every time you run the scene. We're going to
-            use ``randomize()`` in our ``Main`` scene, so we won't need it here.
-
 The last piece is to make the mobs delete themselves when they leave the screen.
 The last piece is to make the mobs delete themselves when they leave the screen.
 Connect the ``screen_exited()`` signal of the ``VisibleOnScreenNotifier2D`` node and
 Connect the ``screen_exited()`` signal of the ``VisibleOnScreenNotifier2D`` node and
 add this code:
 add this code:
@@ -163,7 +156,7 @@ add this code:
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void OnVisibleOnScreenNotifier2DScreenExited()
+    private void OnVisibleOnScreenNotifier2DScreenExited()
     {
     {
         QueueFree();
         QueueFree();
     }
     }

+ 8 - 11
getting_started/first_2d_game/05.the_main_game_scene.rst

@@ -93,13 +93,10 @@ to instance.
     {
     {
         // Don't forget to rebuild the project so the editor knows about the new export variable.
         // Don't forget to rebuild the project so the editor knows about the new export variable.
 
 
-    #pragma warning disable 649
-        // We assign this in the editor, so we don't need the warning about not being assigned.
         [Export]
         [Export]
-        public PackedScene MobScene;
-    #pragma warning restore 649
+        public PackedScene MobScene { get; set; }
 
 
-        public int Score;
+        private int _score;
     }
     }
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
@@ -219,7 +216,7 @@ new game:
 
 
     public void NewGame()
     public void NewGame()
     {
     {
-        Score = 0;
+        _score = 0;
 
 
         var player = GetNode<Player>("Player");
         var player = GetNode<Player>("Player");
         var startPosition = GetNode<Marker2D>("StartPosition");
         var startPosition = GetNode<Marker2D>("StartPosition");
@@ -258,12 +255,12 @@ the other two timers. ``ScoreTimer`` will increment the score by 1.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void OnScoreTimerTimeout()
+    private void OnScoreTimerTimeout()
     {
     {
-        Score++;
+        _score++;
     }
     }
 
 
-    public void OnStartTimerTimeout()
+    private void OnStartTimerTimeout()
     {
     {
         GetNode<Timer>("MobTimer").Start();
         GetNode<Timer>("MobTimer").Start();
         GetNode<Timer>("ScoreTimer").Start();
         GetNode<Timer>("ScoreTimer").Start();
@@ -332,14 +329,14 @@ Note that a new instance must be added to the scene using ``add_child()``.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void OnMobTimerTimeout()
+    private void OnMobTimerTimeout()
     {
     {
         // Note: Normally it is best to use explicit types rather than the `var`
         // Note: Normally it is best to use explicit types rather than the `var`
         // keyword. However, var is acceptable to use here because the types are
         // keyword. However, var is acceptable to use here because the types are
         // obviously Mob and PathFollow2D, since they appear later on the line.
         // obviously Mob and PathFollow2D, since they appear later on the line.
 
 
         // Create a new instance of the Mob scene.
         // Create a new instance of the Mob scene.
-        var mob = MobScene.Instantiate<Mob>();
+        Mob mob = MobScene.Instantiate<Mob>();
 
 
         // Choose a random location on Path2D.
         // Choose a random location on Path2D.
         var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
         var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");

+ 7 - 7
getting_started/first_2d_game/06.heads_up_display.rst

@@ -233,13 +233,13 @@ We also need to process what happens when the player loses. The code below will
         ShowMessage("Game Over");
         ShowMessage("Game Over");
 
 
         var messageTimer = GetNode<Timer>("MessageTimer");
         var messageTimer = GetNode<Timer>("MessageTimer");
-        await ToSignal(messageTimer, "timeout");
+        await ToSignal(messageTimer, Timer.SignalName.Timeout);
 
 
         var message = GetNode<Label>("Message");
         var message = GetNode<Label>("Message");
         message.Text = "Dodge the\nCreeps!";
         message.Text = "Dodge the\nCreeps!";
         message.Show();
         message.Show();
 
 
-        await ToSignal(GetTree().CreateTimer(1), "timeout");
+        await ToSignal(GetTree().CreateTimer(1.0), SceneTreeTimer.SignalName.Timeout);
         GetNode<Button>("StartButton").Show();
         GetNode<Button>("StartButton").Show();
     }
     }
 
 
@@ -306,13 +306,13 @@ signal of ``StartButton``, and add the following code to the new functions:
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public void OnStartButtonPressed()
+    private void OnStartButtonPressed()
     {
     {
         GetNode<Button>("StartButton").Hide();
         GetNode<Button>("StartButton").Hide();
         EmitSignal(SignalName.StartGame);
         EmitSignal(SignalName.StartGame);
     }
     }
 
 
-    public void OnMessageTimerTimeout()
+    private void OnMessageTimerTimeout()
     {
     {
         GetNode<Label>("Message").Hide();
         GetNode<Label>("Message").Hide();
     }
     }
@@ -364,7 +364,7 @@ In ``new_game()``, update the score display and show the "Get Ready" message:
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
         var hud = GetNode<HUD>("HUD");
         var hud = GetNode<HUD>("HUD");
-        hud.UpdateScore(Score);
+        hud.UpdateScore(_score);
         hud.ShowMessage("Get Ready!");
         hud.ShowMessage("Get Ready!");
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
@@ -400,7 +400,7 @@ with the changing score:
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-        GetNode<HUD>("HUD").UpdateScore(Score);
+        GetNode<HUD>("HUD").UpdateScore(_score);
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
 
 
@@ -435,7 +435,7 @@ the ``new_game()`` function in ``Main``:
 
 
         // Note that for calling Godot-provided methods with strings,
         // Note that for calling Godot-provided methods with strings,
         // we have to use the original Godot snake_case name.
         // we have to use the original Godot snake_case name.
-        GetTree().CallGroup("mobs", "queue_free");
+        GetTree().CallGroup("mobs", Node.MethodName.QueueFree);
 
 
  .. code-tab:: cpp
  .. code-tab:: cpp
 
 

二进制
getting_started/first_2d_game/img/build_dotnet.webp


+ 49 - 35
getting_started/first_3d_game/03.player_movement_code.rst

@@ -38,12 +38,12 @@ character.
 
 
         // How fast the player moves in meters per second.
         // How fast the player moves in meters per second.
         [Export]
         [Export]
-        public int Speed = 14;
+        public int Speed { get; set; } = 14;
         // The downward acceleration when in the air, in meters per second squared.
         // The downward acceleration when in the air, in meters per second squared.
         [Export]
         [Export]
-        public int FallAcceleration = 75;
+        public int FallAcceleration { get; set; } = 75;
 
 
-        private Vector3 _velocity = Vector3.Zero;
+        private Vector3 _targetVelocity = Vector3.Zero;
     }
     }
 
 
 
 
@@ -81,29 +81,29 @@ using the global ``Input`` object, in ``_physics_process()``.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // We create a local variable to store the input direction.
         // We create a local variable to store the input direction.
         var direction = Vector3.Zero;
         var direction = Vector3.Zero;
 
 
-        // We check for each move input and update the direction accordingly
+        // We check for each move input and update the direction accordingly.
         if (Input.IsActionPressed("move_right"))
         if (Input.IsActionPressed("move_right"))
         {
         {
-            direction.x += 1f;
+            direction.X += 1.0f;
         }
         }
         if (Input.IsActionPressed("move_left"))
         if (Input.IsActionPressed("move_left"))
         {
         {
-            direction.x -= 1f;
+            direction.X -= 1.0f;
         }
         }
         if (Input.IsActionPressed("move_back"))
         if (Input.IsActionPressed("move_back"))
         {
         {
-            // Notice how we are working with the vector's x and z axes.
+            // Notice how we are working with the vector's X and Z axes.
             // In 3D, the XZ plane is the ground plane.
             // In 3D, the XZ plane is the ground plane.
-            direction.z += 1f;
+            direction.Z += 1.0f;
         }
         }
         if (Input.IsActionPressed("move_forward"))
         if (Input.IsActionPressed("move_forward"))
         {
         {
-            direction.z -= 1f;
+            direction.Z -= 1.0f;
         }
         }
     }
     }
 
 
@@ -141,14 +141,14 @@ call its ``normalize()`` method.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
 
 
         if (direction != Vector3.Zero)
         if (direction != Vector3.Zero)
         {
         {
             direction = direction.Normalized();
             direction = direction.Normalized();
-            GetNode<Node3D>("Pivot").LookAt(position + direction, Vector3.Up);
+            GetNode<Node3D>("Pivot").LookAt(Position + direction, Vector3.Up);
         }
         }
     }
     }
 
 
@@ -192,19 +192,30 @@ fall speed separately. Be sure to go back one tab so the lines are inside the
         # Moving the Character
         # Moving the Character
         velocity = target_velocity
         velocity = target_velocity
         move_and_slide()
         move_and_slide()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
+        if (direction != Vector3.Zero)
+        {
+            // ...
+        }
 
 
         // Ground velocity
         // Ground velocity
-        _velocity.x = direction.x * Speed;
-        _velocity.z = direction.z * Speed;
+        _targetVelocity.X = direction.X * Speed;
+        _targetVelocity.Z = direction.Z * Speed;
+
         // Vertical velocity
         // Vertical velocity
-        _velocity.y -= FallAcceleration * delta;
+        if (!IsOnFloor()) // If in the air, fall towards the floor. Literally gravity
+        {
+            _targetVelocity.Y -= FallAcceleration * (float)delta;
+        }
+
         // Moving the character
         // Moving the character
-        _velocity = MoveAndSlide(_velocity, Vector3.Up);
+        Velocity = _targetVelocity;
+        MoveAndSlide();
     }
     }
 
 
 The ``CharacterBody3D.is_on_floor()`` function returns ``true`` if the body collided with the floor in this frame. That's why
 The ``CharacterBody3D.is_on_floor()`` function returns ``true`` if the body collided with the floor in this frame. That's why
@@ -269,11 +280,12 @@ Here is the complete ``Player.gd`` code for reference.
 
 
         # Vertical Velocity
         # Vertical Velocity
         if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
         if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
-	        target_velocity.y = target_velocity.y - (fall_acceleration * delta)
+            target_velocity.y = target_velocity.y - (fall_acceleration * delta)
 
 
         # Moving the Character
         # Moving the Character
         velocity = target_velocity
         velocity = target_velocity
         move_and_slide()
         move_and_slide()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -282,51 +294,53 @@ Here is the complete ``Player.gd`` code for reference.
     {
     {
         // How fast the player moves in meters per second.
         // How fast the player moves in meters per second.
         [Export]
         [Export]
-        public int Speed = 14;
+        public int Speed { get; set; } = 14;
         // The downward acceleration when in the air, in meters per second squared.
         // The downward acceleration when in the air, in meters per second squared.
         [Export]
         [Export]
-        public int FallAcceleration = 75;
+        public int FallAcceleration { get; set; } = 75;
 
 
-        private Vector3 _velocity = Vector3.Zero;
+        private Vector3 _targetVelocity = Vector3.Zero;
 
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
-            // We create a local variable to store the input direction.
             var direction = Vector3.Zero;
             var direction = Vector3.Zero;
 
 
-            // We check for each move input and update the direction accordingly
             if (Input.IsActionPressed("move_right"))
             if (Input.IsActionPressed("move_right"))
             {
             {
-                direction.x += 1f;
+                direction.X += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_left"))
             if (Input.IsActionPressed("move_left"))
             {
             {
-                direction.x -= 1f;
+                direction.X -= 1.0f;
             }
             }
             if (Input.IsActionPressed("move_back"))
             if (Input.IsActionPressed("move_back"))
             {
             {
-                // Notice how we are working with the vector's x and z axes.
-                // In 3D, the XZ plane is the ground plane.
-                direction.z += 1f;
+                direction.Z += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_forward"))
             if (Input.IsActionPressed("move_forward"))
             {
             {
-                direction.z -= 1f;
+                direction.Z -= 1.0f;
             }
             }
 
 
             if (direction != Vector3.Zero)
             if (direction != Vector3.Zero)
             {
             {
                 direction = direction.Normalized();
                 direction = direction.Normalized();
-                GetNode<Node3D>("Pivot").LookAt(Translation + direction, Vector3.Up);
+                GetNode<Node3D>("Pivot").LookAt(Position + direction, Vector3.Up);
             }
             }
 
 
             // Ground velocity
             // Ground velocity
-            _velocity.x = direction.x * Speed;
-            _velocity.z = direction.z * Speed;
+            _targetVelocity.X = direction.X * Speed;
+            _targetVelocity.Z = direction.Z * Speed;
+
             // Vertical velocity
             // Vertical velocity
-            _velocity.y -= FallAcceleration * delta;
+            if (!IsOnFloor()) // If in the air, fall towards the floor. Literally gravity
+            {
+                _targetVelocity.Y -= FallAcceleration * (float)delta;
+            }
+
             // Moving the character
             // Moving the character
-            _velocity = MoveAndSlide(_velocity, Vector3.Up);
+            Velocity = _targetVelocity;
+            MoveAndSlide();
         }
         }
     }
     }
 
 

+ 40 - 29
getting_started/first_3d_game/04.mob_scene.rst

@@ -125,12 +125,12 @@ and ``max_speed``, to define a random speed range, which we will later use to de
 
 
         // Minimum speed of the mob in meters per second
         // Minimum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MinSpeed = 10;
+        public int MinSpeed { get; set; } = 10;
         // Maximum speed of the mob in meters per second
         // Maximum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MaxSpeed = 18;
+        public int MaxSpeed { get; set; } = 18;
 
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
             MoveAndSlide();
             MoveAndSlide();
         }
         }
@@ -161,17 +161,20 @@ between ``-PI / 4`` radians and ``PI / 4`` radians.
         # We position the mob by placing it at start_position
         # We position the mob by placing it at start_position
         # and rotate it towards player_position, so it looks at the player.
         # and rotate it towards player_position, so it looks at the player.
         look_at_from_position(start_position, player_position, Vector3.UP)
         look_at_from_position(start_position, player_position, Vector3.UP)
-        # In this rotation^, the mob will move directly towards the player
-        # so we rotate it randomly within range of -90 and +90 degrees.
+        # Rotate this mob randomly within range of -90 and +90 degrees,
+        # so that it doesn't move directly towards the player.
         rotate_y(randf_range(-PI / 4, PI / 4))
         rotate_y(randf_range(-PI / 4, PI / 4))
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    // We will call this function from the Main scene
+    // This function will be called from the Main scene.
     public void Initialize(Vector3 startPosition, Vector3 playerPosition)
     public void Initialize(Vector3 startPosition, Vector3 playerPosition)
     {
     {
-        // We position the mob and turn it so that it looks at the player.
+        // We position the mob by placing it at startPosition
+        // and rotate it towards playerPosition, so it looks at the player.
         LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
         LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
-        // And rotate it randomly so it doesn't move exactly toward the player.
+        // Rotate this mob randomly within range of -90 and +90 degrees,
+        // so that it doesn't move directly towards the player.
         RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
         RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
     }
     }
 
 
@@ -198,12 +201,13 @@ We got a random position, now we need a ``random_speed``. ``randi_range()`` will
     {
     {
         // ...
         // ...
 
 
-        // We calculate a random speed.
-        float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
+        // We calculate a random speed (integer).
+        int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
         // We calculate a forward velocity that represents the speed.
         // We calculate a forward velocity that represents the speed.
-        _velocity = Vector3.Forward * randomSpeed;
-        // We then rotate the vector based on the mob's Y rotation to move in the direction it's looking
-        _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+        Velocity = Vector3.Forward * randomSpeed;
+        // We then rotate the velocity vector based on the mob's Y rotation
+        // in order to move in the direction the mob is looking.
+        Velocity = Velocity.Rotated(Vector3.Up, Rotation.Y);
     }
     }
 
 
 Leaving the screen
 Leaving the screen
@@ -239,7 +243,7 @@ method. This function destroy the instance it's called on.
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     // We also specified this function name in PascalCase in the editor's connection window
     // We also specified this function name in PascalCase in the editor's connection window
-    public void OnVisibilityNotifierScreenExited()
+    private void OnVisibilityNotifierScreenExited()
     {
     {
         QueueFree();
         QueueFree();
     }
     }
@@ -268,8 +272,8 @@ Here is the complete ``Mob.gd`` script for reference.
         # We position the mob by placing it at start_position
         # We position the mob by placing it at start_position
         # and rotate it towards player_position, so it looks at the player.
         # and rotate it towards player_position, so it looks at the player.
         look_at_from_position(start_position, player_position, Vector3.UP)
         look_at_from_position(start_position, player_position, Vector3.UP)
-        # In this rotation^, the mob will move directly towards the player
-        # so we rotate it randomly within range of -90 and +90 degrees.
+        # Rotate this mob randomly within range of -90 and +90 degrees,
+        # so that it doesn't move directly towards the player.
         rotate_y(randf_range(-PI / 4, PI / 4))
         rotate_y(randf_range(-PI / 4, PI / 4))
 
 
         # We calculate a random speed (integer)
         # We calculate a random speed (integer)
@@ -282,39 +286,46 @@ Here is the complete ``Mob.gd`` script for reference.
 
 
     func _on_visible_on_screen_notifier_3d_screen_exited():
     func _on_visible_on_screen_notifier_3d_screen_exited():
         queue_free()
         queue_free()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
 
 
     public partial class Mob : CharacterBody3D
     public partial class Mob : CharacterBody3D
     {
     {
-        // Minimum speed of the mob in meters per second
+        // Minimum speed of the mob in meters per second.
         [Export]
         [Export]
-        public int MinSpeed = 10;
-        // Maximum speed of the mob in meters per second
+        public int MinSpeed { get; set; } = 10;
+        // Maximum speed of the mob in meters per second.
         [Export]
         [Export]
-        public int MaxSpeed = 18;
-
-        private Vector3 _velocity = Vector3.Zero;
+        public int MaxSpeed { get; set; } = 18;
 
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
-            MoveAndSlide(_velocity);
+            MoveAndSlide();
         }
         }
 
 
-        // We will call this function from the Main scene
+        // This function will be called from the Main scene.
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         {
         {
+            // We position the mob by placing it at startPosition
+            // and rotate it towards playerPosition, so it looks at the player.
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
+            // Rotate this mob randomly within range of -90 and +90 degrees,
+            // so that it doesn't move directly towards the player.
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
 
 
-            var randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
-            _velocity = Vector3.Forward * randomSpeed;
-            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+            // We calculate a random speed (integer).
+            int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
+            // We calculate a forward velocity that represents the speed.
+            Velocity = Vector3.Forward * randomSpeed;
+            // We then rotate the velocity vector based on the mob's Y rotation
+            // in order to move in the direction the mob is looking.
+            Velocity = Velocity.Rotated(Vector3.Up, Rotation.Y);
         }
         }
 
 
         // We also specified this function name in PascalCase in the editor's connection window
         // We also specified this function name in PascalCase in the editor's connection window
-        public void OnVisibilityNotifierScreenExited()
+        private void OnVisibilityNotifierScreenExited()
         {
         {
             QueueFree();
             QueueFree();
         }
         }

+ 20 - 41
getting_started/first_3d_game/05.spawning_mobs.rst

@@ -143,7 +143,7 @@ Your path should look like this.
 |image18|
 |image18|
 
 
 To sample random positions on it, we need a :ref:`PathFollow3D <class_PathFollow3D>` node. Add a
 To sample random positions on it, we need a :ref:`PathFollow3D <class_PathFollow3D>` node. Add a
-:ref:`PathFollow3D <class_PathFollow3D>` as a child of the ``Path3d``. Rename the two nodes to ``SpawnPath`` and
+:ref:`PathFollow3D <class_PathFollow3D>` as a child of the ``Path3D``. Rename the two nodes to ``SpawnPath`` and
 ``SpawnLocation``, respectively. It's more descriptive of what we'll use them for.
 ``SpawnLocation``, respectively. It's more descriptive of what we'll use them for.
 
 
 |image19|
 |image19|
@@ -158,10 +158,6 @@ Right-click on the ``Main`` node and attach a new script to it.
 We first export a variable to the *Inspector* so that we can assign ``Mob.tscn``
 We first export a variable to the *Inspector* so that we can assign ``Mob.tscn``
 or any other monster to it.
 or any other monster to it.
 
 
-Then, as we're going to spawn the monsters procedurally, we want to randomize
-numbers every time we play the game. If we don't do that, the monsters will
-always spawn following the same sequence.
-
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
@@ -169,10 +165,6 @@ always spawn following the same sequence.
 
 
    @export var mob_scene: PackedScene
    @export var mob_scene: PackedScene
 
 
-
-   func _ready():
-       randomize()
-
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -181,16 +173,8 @@ always spawn following the same sequence.
     {
     {
         // Don't forget to rebuild the project so the editor knows about the new export variable.
         // Don't forget to rebuild the project so the editor knows about the new export variable.
 
 
-    #pragma warning disable 649
-        // We assign this in the editor, so we don't need the warning about not being assigned.
         [Export]
         [Export]
-        public PackedScene MobScene;
-    #pragma warning restore 649
-
-        public override void _Ready()
-        {
-            GD.Randomize();
-        }
+        public PackedScene MobScene { get; set; }
     }
     }
 
 
 We want to spawn mobs at regular time intervals. To do this, we need to go back
 We want to spawn mobs at regular time intervals. To do this, we need to go back
@@ -259,22 +243,22 @@ Let's code the mob spawning logic. We're going to:
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     // We also specified this function name in PascalCase in the editor's connection window
     // We also specified this function name in PascalCase in the editor's connection window
-    public void OnMobTimerTimeout()
+    private void OnMobTimerTimeout()
     {
     {
         // Create a new instance of the Mob scene.
         // Create a new instance of the Mob scene.
-        Mob mob = (Mob)MobScene.Instantiate();
+        Mob mob = MobScene.Instantiate<Mob>();
 
 
         // Choose a random location on the SpawnPath.
         // Choose a random location on the SpawnPath.
         // We store the reference to the SpawnLocation node.
         // We store the reference to the SpawnLocation node.
         var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
         var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
         // And give it a random offset.
         // And give it a random offset.
-        mobSpawnLocation.UnitOffset = GD.Randf();
+        mobSpawnLocation.ProgressRatio = GD.Randf();
 
 
-        Vector3 playerPosition = GetNode<Player>("Player").position;
-        mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+        Vector3 playerPosition = GetNode<Player>("Player").Position;
+        mob.Initialize(mobSpawnLocation.Position, playerPosition);
 
 
+        // Spawn the mob by adding it to the Main scene.
         AddChild(mob);
         AddChild(mob);
-
     }
     }
 
 
 Above, ``randf()`` produces a random value between ``0`` and ``1``, which is
 Above, ``randf()`` produces a random value between ``0`` and ``1``, which is
@@ -292,9 +276,6 @@ Here is the complete ``Main.gd`` script so far, for reference.
 
 
     @export var mob_scene: PackedScene
     @export var mob_scene: PackedScene
 
 
-    func _ready():
-        randomize()
-
 
 
     func _on_mob_timer_timeout():
     func _on_mob_timer_timeout():
         # Create a new instance of the Mob scene.
         # Create a new instance of the Mob scene.
@@ -318,26 +299,24 @@ Here is the complete ``Main.gd`` script so far, for reference.
 
 
     public partial class Main : Node
     public partial class Main : Node
     {
     {
-    #pragma warning disable 649
         [Export]
         [Export]
-        public PackedScene MobScene;
-    #pragma warning restore 649
-
-        public override void _Ready()
-        {
-            GD.Randomize();
-        }
+        public PackedScene MobScene { get; set; }
 
 
-        public void OnMobTimerTimeout()
+        private void OnMobTimerTimeout()
         {
         {
-            Mob mob = (Mob)MobScene.Instantiate();
+            // Create a new instance of the Mob scene.
+            Mob mob = MobScene.Instantiate<Mob>();
 
 
-            var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
-            mobSpawnLocation.UnitOffset = GD.Randf();
+            // Choose a random location on the SpawnPath.
+            // We store the reference to the SpawnLocation node.
+            var mobSpawnLocation = GetNode<PathFollow3D>("SpawnPath/SpawnLocation");
+            // And give it a random offset.
+            mobSpawnLocation.ProgressRatio = GD.Randf();
 
 
-            Vector3 playerPosition = GetNode<Player>("Player").position;
-            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+            Vector3 playerPosition = GetNode<Player>("Player").Position;
+            mob.Initialize(mobSpawnLocation.Position, playerPosition);
 
 
+            // Spawn the mob by adding it to the Main scene.
             AddChild(mob);
             AddChild(mob);
         }
         }
     }
     }

+ 17 - 14
getting_started/first_3d_game/06.jump_and_squash.rst

@@ -122,7 +122,7 @@ the ``jump_impulse``.
     // ...
     // ...
     // Vertical impulse applied to the character upon jumping in meters per second.
     // Vertical impulse applied to the character upon jumping in meters per second.
     [Export]
     [Export]
-    public int JumpImpulse = 20;
+    public int JumpImpulse { get; set; } = 20;
 
 
 Inside ``_physics_process()``, add the following code before the ``move_and_slide()`` codeblock.
 Inside ``_physics_process()``, add the following code before the ``move_and_slide()`` codeblock.
 
 
@@ -140,14 +140,14 @@ Inside ``_physics_process()``, add the following code before the ``move_and_slid
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
 
 
         // Jumping.
         // Jumping.
         if (IsOnFloor() && Input.IsActionJustPressed("jump"))
         if (IsOnFloor() && Input.IsActionJustPressed("jump"))
         {
         {
-            _velocity.y = JumpImpulse;
+            _targetVelocity.Y = JumpImpulse;
         }
         }
 
 
         // ...
         // ...
@@ -218,7 +218,7 @@ when jumping.
 
 
     // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
     // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
     [Export]
     [Export]
-    public int BounceImpulse = 16;
+    public int BounceImpulse { get; set; } = 16;
 
 
 Then, after the **Jumping** codeblock we added above in ``_physics_process()``, add the following loop. With
 Then, after the **Jumping** codeblock we added above in ``_physics_process()``, add the following loop. With
 ``move_and_slide()``, Godot makes the body move sometimes multiple times in a
 ``move_and_slide()``, Godot makes the body move sometimes multiple times in a
@@ -235,6 +235,7 @@ With this code, if no collisions occurred on a given frame, the loop won't run.
 
 
    func _physics_process(delta):
    func _physics_process(delta):
         #...
         #...
+
         # Iterate through all collisions that occurred this frame
         # Iterate through all collisions that occurred this frame
         # in C this would be for(int i = 0; i < collisions.Count; i++)
         # in C this would be for(int i = 0; i < collisions.Count; i++)
         for index in range(get_slide_collision_count()):
         for index in range(get_slide_collision_count()):
@@ -256,23 +257,25 @@ With this code, if no collisions occurred on a given frame, the loop won't run.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
 
 
-        for (int index = 0; index < GetSlideCount(); index++)
+        // Iterate through all collisions that occurred this frame.
+        for (int index = 0; index < GetSlideCollisionCount(); index++)
         {
         {
-            // We check every collision that occurred this frame.
+            // We get one of the collisions with the player.
             KinematicCollision3D collision = GetSlideCollision(index);
             KinematicCollision3D collision = GetSlideCollision(index);
-            // If we collide with a monster...
-            if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+
+            // If the collision is with a mob.
+            if (collision.GetCollider() is Mob mob)
             {
             {
-                // ...we check that we are hitting it from above.
-                if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                // We check that we are hitting it from above.
+                if (Vector3.Up.Dot(collision.GetNormal()) > 0.1f)
                 {
                 {
                     // If so, we squash it and bounce.
                     // If so, we squash it and bounce.
                     mob.Squash();
                     mob.Squash();
-                    _velocity.y = BounceImpulse;
+                    _targetVelocity.Y = BounceImpulse;
                 }
                 }
             }
             }
         }
         }
@@ -328,7 +331,7 @@ destroy the mob.
 
 
     // Don't forget to rebuild the project so the editor knows about the new signal.
     // Don't forget to rebuild the project so the editor knows about the new signal.
 
 
-    // Emitted when the played jumped on the mob.
+    // Emitted when the player jumped on the mob.
     [Signal]
     [Signal]
     public delegate void SquashedEventHandler();
     public delegate void SquashedEventHandler();
 
 
@@ -336,7 +339,7 @@ destroy the mob.
 
 
     public void Squash()
     public void Squash()
     {
     {
-        EmitSignal(nameof(Squashed));
+        EmitSignal(SignalName.Squashed);
         QueueFree();
         QueueFree();
     }
     }
 
 

+ 80 - 57
getting_started/first_3d_game/07.killing_player.rst

@@ -90,12 +90,12 @@ a ``die()`` function that helps us put a descriptive label on the code.
 
 
     private void Die()
     private void Die()
     {
     {
-        EmitSignal(nameof(Hit));
+        EmitSignal(SignalName.Hit);
         QueueFree();
         QueueFree();
     }
     }
 
 
     // We also specified this function name in PascalCase in the editor's connection window
     // We also specified this function name in PascalCase in the editor's connection window
-    public void OnMobDetectorBodyEntered(Node body)
+    private void OnMobDetectorBodyEntered(Node3D body)
     {
     {
         Die();
         Die();
     }
     }
@@ -108,6 +108,11 @@ the character should die when an enemy runs into the collider. Note that without
 
 
     var player_position = $Player.position
     var player_position = $Player.position
 
 
+   .. code-tab:: csharp
+
+    Vector3 playerPosition = GetNode<Player>("Player").Position;
+
+
 gives error because there is no $Player!
 gives error because there is no $Player!
 
 
 Also note that the enemy colliding with the player and dying depends on the size and position of the
 Also note that the enemy colliding with the player and dying depends on the size and position of the
@@ -137,7 +142,7 @@ Get the timer, and stop it, in the ``_on_player_hit()`` function.
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     // We also specified this function name in PascalCase in the editor's connection window
     // We also specified this function name in PascalCase in the editor's connection window
-    public void OnPlayerHit()
+    private void OnPlayerHit()
     {
     {
         GetNode<Timer>("MobTimer").Stop();
         GetNode<Timer>("MobTimer").Stop();
     }
     }
@@ -167,9 +172,6 @@ Starting with ``Main.gd``.
 
 
     @export var mob_scene: PackedScene
     @export var mob_scene: PackedScene
 
 
-    func _ready():
-        randomize()
-
 
 
     func _on_mob_timer_timeout():
     func _on_mob_timer_timeout():
         # Create a new instance of the Mob scene.
         # Create a new instance of the Mob scene.
@@ -189,42 +191,35 @@ Starting with ``Main.gd``.
 
 
     func _on_player_hit():
     func _on_player_hit():
         $MobTimer.stop()
         $MobTimer.stop()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
 
 
     public partial class Main : Node
     public partial class Main : Node
     {
     {
-    #pragma warning disable 649
         [Export]
         [Export]
-        public PackedScene MobScene;
-    #pragma warning restore 649
+        public PackedScene MobScene { get; set; }
 
 
-        public override void _Ready()
-        {
-            GD.Randomize();
-        }
-
-        public void OnMobTimerTimeout()
+        private void OnMobTimerTimeout()
         {
         {
             // Create a new instance of the Mob scene.
             // Create a new instance of the Mob scene.
-            var mob = (Mob)MobScene.Instantiate();
+            Mob mob = MobScene.Instantiate<Mob>();
 
 
             // Choose a random location on the SpawnPath.
             // Choose a random location on the SpawnPath.
             // We store the reference to the SpawnLocation node.
             // We store the reference to the SpawnLocation node.
             var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
             var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
             // And give it a random offset.
             // And give it a random offset.
-            mobSpawnLocation.UnitOffset = GD.Randf();
+            mobSpawnLocation.ProgressRatio = GD.Randf();
 
 
-            // Communicate the spawn location and the player's location to the mob.
-            Vector3 playerPosition = GetNode<Player>("Player").position;
-            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+            Vector3 playerPosition = GetNode<Player>("Player").Position;
+            mob.Initialize(mobSpawnLocation.Position, playerPosition);
 
 
             // Spawn the mob by adding it to the Main scene.
             // Spawn the mob by adding it to the Main scene.
             AddChild(mob);
             AddChild(mob);
         }
         }
 
 
-        public void OnPlayerHit()
+        private void OnPlayerHit()
         {
         {
             GetNode<Timer>("MobTimer").Stop();
             GetNode<Timer>("MobTimer").Stop();
         }
         }
@@ -253,8 +248,8 @@ Next is ``Mob.gd``.
         # We position the mob by placing it at start_position
         # We position the mob by placing it at start_position
         # and rotate it towards player_position, so it looks at the player.
         # and rotate it towards player_position, so it looks at the player.
         look_at_from_position(start_position, player_position, Vector3.UP)
         look_at_from_position(start_position, player_position, Vector3.UP)
-        # In this rotation^, the mob will move directly towards the player
-        # so we rotate it randomly within range of -90 and +90 degrees.
+        # Rotate this mob randomly within range of -90 and +90 degrees,
+        # so that it doesn't move directly towards the player.
         rotate_y(randf_range(-PI / 4, PI / 4))
         rotate_y(randf_range(-PI / 4, PI / 4))
 
 
         # We calculate a random speed (integer)
         # We calculate a random speed (integer)
@@ -271,6 +266,7 @@ Next is ``Mob.gd``.
     func squash():
     func squash():
         squashed.emit()
         squashed.emit()
         queue_free() # Destroy this node
         queue_free() # Destroy this node
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -283,35 +279,42 @@ Next is ``Mob.gd``.
 
 
         // Minimum speed of the mob in meters per second
         // Minimum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MinSpeed = 10;
+        public int MinSpeed { get; set; } = 10;
         // Maximum speed of the mob in meters per second
         // Maximum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MaxSpeed = 18;
+        public int MaxSpeed { get; set; } = 18;
 
 
-        private Vector3 _velocity = Vector3.Zero;
-
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
-            MoveAndSlide(_velocity);
+            MoveAndSlide();
         }
         }
 
 
+        // This function will be called from the Main scene.
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         {
         {
+            // We position the mob by placing it at startPosition
+            // and rotate it towards playerPosition, so it looks at the player.
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
+            // Rotate this mob randomly within range of -90 and +90 degrees,
+            // so that it doesn't move directly towards the player.
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
 
 
-            float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
-            _velocity = Vector3.Forward * randomSpeed;
-            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+            // We calculate a random speed (integer)
+            int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
+            // We calculate a forward velocity that represents the speed.
+            Velocity = Vector3.Forward * randomSpeed;
+            // We then rotate the velocity vector based on the mob's Y rotation
+            // in order to move in the direction the mob is looking.
+            Velocity = Velocity.Rotated(Vector3.Up, Rotation.Y);
         }
         }
 
 
         public void Squash()
         public void Squash()
         {
         {
-            EmitSignal(nameof(Squashed));
-            QueueFree();
+            EmitSignal(SignalName.Squashed);
+            QueueFree(); // Destroy this node
         }
         }
 
 
-        public void OnVisibilityNotifierScreenExited()
+        private void OnVisibilityNotifierScreenExited()
         {
         {
             QueueFree();
             QueueFree();
         }
         }
@@ -402,6 +405,7 @@ Finally, the longest script, ``Player.gd``:
 
 
     func _on_mob_detector_body_entered(body):
     func _on_mob_detector_body_entered(body):
         die()
         die()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -414,79 +418,98 @@ Finally, the longest script, ``Player.gd``:
 
 
         // How fast the player moves in meters per second.
         // How fast the player moves in meters per second.
         [Export]
         [Export]
-        public int Speed = 14;
+        public int Speed { get; set; } = 14;
         // The downward acceleration when in the air, in meters per second squared.
         // The downward acceleration when in the air, in meters per second squared.
         [Export]
         [Export]
-        public int FallAcceleration = 75;
+        public int FallAcceleration { get; set; } = 75;
         // Vertical impulse applied to the character upon jumping in meters per second.
         // Vertical impulse applied to the character upon jumping in meters per second.
         [Export]
         [Export]
-        public int JumpImpulse = 20;
+        public int JumpImpulse { get; set; } = 20;
         // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
         // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
         [Export]
         [Export]
-        public int BounceImpulse = 16;
+        public int BounceImpulse { get; set; } = 16;
 
 
-        private Vector3 _velocity = Vector3.Zero;
+        private Vector3 _targetVelocity = Vector3.Zero;
 
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
+            // We create a local variable to store the input direction.
             var direction = Vector3.Zero;
             var direction = Vector3.Zero;
 
 
+            // We check for each move input and update the direction accordingly.
             if (Input.IsActionPressed("move_right"))
             if (Input.IsActionPressed("move_right"))
             {
             {
-                direction.x += 1f;
+                direction.X += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_left"))
             if (Input.IsActionPressed("move_left"))
             {
             {
-                direction.x -= 1f;
+                direction.X -= 1.0f;
             }
             }
             if (Input.IsActionPressed("move_back"))
             if (Input.IsActionPressed("move_back"))
             {
             {
-                direction.z += 1f;
+                // Notice how we are working with the vector's X and Z axes.
+                // In 3D, the XZ plane is the ground plane.
+                direction.Z += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_forward"))
             if (Input.IsActionPressed("move_forward"))
             {
             {
-                direction.z -= 1f;
+                direction.Z -= 1.0f;
             }
             }
 
 
+            // Prevent diagonal moving fast af
             if (direction != Vector3.Zero)
             if (direction != Vector3.Zero)
             {
             {
                 direction = direction.Normalized();
                 direction = direction.Normalized();
-                GetNode<Node3D>("Pivot").LookAt(Translation + direction, Vector3.Up);
+                GetNode<Node3D>("Pivot").LookAt(Position + direction, Vector3.Up);
             }
             }
 
 
-            _velocity.x = direction.x * Speed;
-            _velocity.z = direction.z * Speed;
+            // Ground Velocity
+            _targetVelocity.X = direction.X * Speed;
+            _targetVelocity.Z = direction.Z * Speed;
+
+            // Vertical Velocity
+            if (!IsOnFloor()) // If in the air, fall towards the floor. Literally gravity
+            {
+                _targetVelocity.Y -= FallAcceleration * (float)delta;
+            }
 
 
             // Jumping.
             // Jumping.
             if (IsOnFloor() && Input.IsActionJustPressed("jump"))
             if (IsOnFloor() && Input.IsActionJustPressed("jump"))
             {
             {
-                _velocity.y += JumpImpulse;
+                _targetVelocity.Y = JumpImpulse;
             }
             }
 
 
-            _velocity.y -= FallAcceleration * delta;
-            _velocity = MoveAndSlide(_velocity, Vector3.Up);
-
+            // Iterate through all collisions that occurred this frame.
             for (int index = 0; index < GetSlideCount(); index++)
             for (int index = 0; index < GetSlideCount(); index++)
             {
             {
+                // We get one of the collisions with the player.
                 KinematicCollision3D collision = GetSlideCollision(index);
                 KinematicCollision3D collision = GetSlideCollision(index);
-                if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+
+                // If the collision is with a mob.
+                if (collision.GetCollider() is Mob mob)
                 {
                 {
-                    if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                    // We check that we are hitting it from above.
+                    if (Vector3.Up.Dot(collision.GetNormal()) > 0.1f)
                     {
                     {
+                        // If so, we squash it and bounce.
                         mob.Squash();
                         mob.Squash();
-                        _velocity.y = BounceImpulse;
+                        _targetVelocity.Y = BounceImpulse;
                     }
                     }
                 }
                 }
             }
             }
+
+            // Moving the Character
+            Velocity = _targetVelocity;
+            MoveAndSlide();
         }
         }
 
 
         private void Die()
         private void Die()
         {
         {
-            EmitSignal(nameof(Hit));
+            EmitSignal(SignalName.Hit);
             QueueFree();
             QueueFree();
         }
         }
 
 
-        public void OnMobDetectorBodyEntered(Node body)
+        private void OnMobDetectorBodyEntered(Node3D body)
         {
         {
             Die();
             Die();
         }
         }

+ 24 - 20
getting_started/first_3d_game/08.score_and_replay.rst

@@ -121,7 +121,7 @@ line:
 
 
   .. code-tab:: csharp
   .. code-tab:: csharp
 
 
-    public void OnMobTimerTimeout()
+    private void OnMobTimerTimeout()
     {
     {
         // ...
         // ...
         // We connect the mob to the score label to update the score upon squashing one.
         // We connect the mob to the score label to update the score upon squashing one.
@@ -148,7 +148,7 @@ There, we increment the score and update the displayed text.
     public void OnMobSquashed()
     public void OnMobSquashed()
     {
     {
         _score += 1;
         _score += 1;
-        Text = string.Format("Score: {0}", _score);
+        Text = $"Score: {_score}";
     }
     }
 
 
 The second line uses the value of the ``score`` variable to replace the
 The second line uses the value of the ``score`` variable to replace the
@@ -159,6 +159,7 @@ function.
 .. seealso::
 .. seealso::
 
 
     You can learn more about string formatting here: :ref:`doc_gdscript_printf`.
     You can learn more about string formatting here: :ref:`doc_gdscript_printf`.
+    In C#, consider using `string interpolation with "$" <https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated>`_.
 
 
 
 
 .. note::
 .. note::
@@ -237,15 +238,13 @@ the game. Add this line to the ``_ready()`` function.
 .. tabs::
 .. tabs::
   .. code-tab:: gdscript GDScript
   .. code-tab:: gdscript GDScript
 
 
-    func _ready():
-        #...
-        $UserInterface/Retry.hide()
+   func _ready():
+       $UserInterface/Retry.hide()
 
 
   .. code-tab:: csharp
   .. code-tab:: csharp
 
 
     public override void _Ready()
     public override void _Ready()
     {
     {
-        // ...
         GetNode<Control>("UserInterface/Retry").Hide();
         GetNode<Control>("UserInterface/Retry").Hide();
     }
     }
 
 
@@ -260,7 +259,7 @@ Then, when the player gets hit, we show the overlay.
 
 
   .. code-tab:: csharp
   .. code-tab:: csharp
 
 
-    public void OnPlayerHit()
+    private void OnPlayerHit()
     {
     {
         //...
         //...
         GetNode<Control>("UserInterface/Retry").Show();
         GetNode<Control>("UserInterface/Retry").Show();
@@ -316,7 +315,7 @@ or by using the *+* icon next to your currently opened scene.
 
 
 |image15|
 |image15|
 
 
-Click the *Other Node* button to create an :ref:`AudioStreamPlayer2D <class_AudioStreamPlayer2D>` and rename it to
+Click the *Other Node* button to create an :ref:`AudioStreamPlayer <class_AudioStreamPlayer>` and rename it to
 ``MusicPlayer``.
 ``MusicPlayer``.
 
 
 |image16|
 |image16|
@@ -370,7 +369,6 @@ Here is the complete ``Main.gd`` script for reference.
     @export var mob_scene: PackedScene
     @export var mob_scene: PackedScene
 
 
     func _ready():
     func _ready():
-        randomize()
         $UserInterface/Retry.hide()
         $UserInterface/Retry.hide()
 
 
 
 
@@ -401,20 +399,18 @@ Here is the complete ``Main.gd`` script for reference.
         if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
         if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
             # This restarts the current scene.
             # This restarts the current scene.
             get_tree().reload_current_scene()
             get_tree().reload_current_scene()
-  .. code-tab:: csharp
+
+ .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
 
 
     public partial class Main : Node
     public partial class Main : Node
     {
     {
-    #pragma warning disable 649
         [Export]
         [Export]
-        public PackedScene MobScene;
-    #pragma warning restore 649
+        public PackedScene MobScene { get; set; }
 
 
         public override void _Ready()
         public override void _Ready()
         {
         {
-            GD.Randomize();
             GetNode<Control>("UserInterface/Retry").Hide();
             GetNode<Control>("UserInterface/Retry").Hide();
         }
         }
 
 
@@ -422,25 +418,33 @@ Here is the complete ``Main.gd`` script for reference.
         {
         {
             if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
             if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
             {
             {
+                // This restarts the current scene.
                 GetTree().ReloadCurrentScene();
                 GetTree().ReloadCurrentScene();
             }
             }
         }
         }
 
 
-        public void OnMobTimerTimeout()
+        private void OnMobTimerTimeout()
         {
         {
-            Mob mob = (Mob)MobScene.Instantiate();
+            // Create a new instance of the Mob scene.
+            Mob mob = MobScene.Instantiate<Mob>();
 
 
-            var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
-            mobSpawnLocation.UnitOffset = GD.Randf();
+            // Choose a random location on the SpawnPath.
+            // We store the reference to the SpawnLocation node.
+            var mobSpawnLocation = GetNode<PathFollow3D>("SpawnPath/SpawnLocation");
+            // And give it a random offset.
+            mobSpawnLocation.ProgressRatio = GD.Randf();
 
 
             Vector3 playerPosition = GetNode<Player>("Player").position;
             Vector3 playerPosition = GetNode<Player>("Player").position;
-            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+            mob.Initialize(mobSpawnLocation.Position, playerPosition);
 
 
+            // Spawn the mob by adding it to the Main scene.
             AddChild(mob);
             AddChild(mob);
+
+            // We connect the mob to the score label to update the score upon squashing one.
             mob.Squashed += GetNode<ScoreLabel>("UserInterface/ScoreLabel").OnMobSquashed;
             mob.Squashed += GetNode<ScoreLabel>("UserInterface/ScoreLabel").OnMobSquashed;
         }
         }
 
 
-        public void OnPlayerHit()
+        private void OnPlayerHit()
         {
         {
             GetNode<Timer>("MobTimer").Stop();
             GetNode<Timer>("MobTimer").Stop();
             GetNode<Control>("UserInterface/Retry").Show();
             GetNode<Control>("UserInterface/Retry").Show();

+ 79 - 50
getting_started/first_3d_game/09.adding_animations.rst

@@ -77,7 +77,7 @@ click any of them to create a keyframe, a time and value pair for the
 corresponding property. The keyframe gets inserted where your time cursor is in
 corresponding property. The keyframe gets inserted where your time cursor is in
 the timeline.
 the timeline.
 
 
-Let's insert our first keys. Here, we will animate both the translation and the
+Let's insert our first keys. Here, we will animate both the position and the
 rotation of the ``Character`` node.
 rotation of the ``Character`` node.
 
 
 Select the ``Character`` and in the *Inspector* expand the *Transform* section. Click the key icon next to *Position*, and *Rotation*.
 Select the ``Character`` and in the *Inspector* expand the *Transform* section. Click the key icon next to *Position*, and *Rotation*.
@@ -93,7 +93,7 @@ Two tracks appear in the editor with a diamond icon representing each keyframe.
 |image11|
 |image11|
 
 
 You can click and drag on the diamonds to move them in time. Move the
 You can click and drag on the diamonds to move them in time. Move the
-translation key to ``0.2`` seconds and the rotation key to ``0.1`` seconds.
+position key to ``0.2`` seconds and the rotation key to ``0.1`` seconds.
 
 
 |image12|
 |image12|
 
 
@@ -163,7 +163,7 @@ Apply an ease-out to the second keyframe in the rotation track.
 
 
 |image19|
 |image19|
 
 
-Do the opposite for the second translation keyframe, dragging it to the right.
+Do the opposite for the second position keyframe, dragging it to the right.
 
 
 |image20|
 |image20|
 
 
@@ -211,7 +211,7 @@ vector, add the following code.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
         if (direction != Vector3.Zero)
         if (direction != Vector3.Zero)
@@ -241,11 +241,11 @@ at the end of ``_physics_process()``.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
     {
         // ...
         // ...
         var pivot = GetNode<Node3D>("Pivot");
         var pivot = GetNode<Node3D>("Pivot");
-        pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
+        pivot.Rotation = new Vector3(Mathf.Pi / 6.0f * Velocity.Y / JumpImpulse, pivot.Rotation.Y, pivot.Rotation.Z);
     }
     }
 
 
 Animating the mobs
 Animating the mobs
@@ -293,15 +293,16 @@ Here's the *Player* script.
 
 
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
-    
+
     extends CharacterBody3D
     extends CharacterBody3D
 
 
     signal hit
     signal hit
 
 
-    # How fast the player moves in meters per second
+    # How fast the player moves in meters per second.
     @export var speed = 14
     @export var speed = 14
     # The downward acceleration while in the air, in meters per second squared.
     # The downward acceleration while in the air, in meters per second squared.
     @export var fall_acceleration = 75
     @export var fall_acceleration = 75
+    # Vertical impulse applied to the character upon jumping in meters per second.
     @export var jump_impulse = 20
     @export var jump_impulse = 20
     # Vertical impulse applied to the character upon bouncing over a mob
     # Vertical impulse applied to the character upon bouncing over a mob
     # in meters per second.
     # in meters per second.
@@ -330,9 +331,9 @@ Here's the *Player* script.
         if direction != Vector3.ZERO:
         if direction != Vector3.ZERO:
             direction = direction.normalized()
             direction = direction.normalized()
             $Pivot.look_at(position + direction,Vector3.UP)
             $Pivot.look_at(position + direction,Vector3.UP)
-            $AnimationPlayer.playback_speed = 4
+            $AnimationPlayer.speed_scale = 4
         else:
         else:
-            $AnimationPlayer.playback_speed = 1
+            $AnimationPlayer.speed_scale = 1
 
 
         # Ground Velocity
         # Ground Velocity
         target_velocity.x = direction.x * speed
         target_velocity.x = direction.x * speed
@@ -378,6 +379,7 @@ Here's the *Player* script.
 
 
     func _on_mob_detector_body_entered(body):
     func _on_mob_detector_body_entered(body):
         die()
         die()
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -390,44 +392,49 @@ Here's the *Player* script.
 
 
         // How fast the player moves in meters per second.
         // How fast the player moves in meters per second.
         [Export]
         [Export]
-        public int Speed = 14;
+        public int Speed { get; set; } = 14;
         // The downward acceleration when in the air, in meters per second squared.
         // The downward acceleration when in the air, in meters per second squared.
         [Export]
         [Export]
-        public int FallAcceleration = 75;
+        public int FallAcceleration { get; set; } = 75;
         // Vertical impulse applied to the character upon jumping in meters per second.
         // Vertical impulse applied to the character upon jumping in meters per second.
         [Export]
         [Export]
-        public int JumpImpulse = 20;
+        public int JumpImpulse { get; set; } = 20;
         // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
         // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
         [Export]
         [Export]
-        public int BounceImpulse = 16;
+        public int BounceImpulse { get; set; } = 16;
 
 
-        private Vector3 _velocity = Vector3.Zero;
+        private Vector3 _targetVelocity = Vector3.Zero;
 
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
+            // We create a local variable to store the input direction.
             var direction = Vector3.Zero;
             var direction = Vector3.Zero;
 
 
+            // We check for each move input and update the direction accordingly.
             if (Input.IsActionPressed("move_right"))
             if (Input.IsActionPressed("move_right"))
             {
             {
-                direction.x += 1f;
+                direction.X += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_left"))
             if (Input.IsActionPressed("move_left"))
             {
             {
-                direction.x -= 1f;
+                direction.X -= 1.0f;
             }
             }
             if (Input.IsActionPressed("move_back"))
             if (Input.IsActionPressed("move_back"))
             {
             {
-                direction.z += 1f;
+                // Notice how we are working with the vector's X and Z axes.
+                // In 3D, the XZ plane is the ground plane.
+                direction.Z += 1.0f;
             }
             }
             if (Input.IsActionPressed("move_forward"))
             if (Input.IsActionPressed("move_forward"))
             {
             {
-                direction.z -= 1f;
+                direction.Z -= 1.0f;
             }
             }
 
 
+            // Prevent diagonal movement being very fast.
             if (direction != Vector3.Zero)
             if (direction != Vector3.Zero)
             {
             {
                 direction = direction.Normalized();
                 direction = direction.Normalized();
-                GetNode<Node3D>("Pivot").LookAt(Translation + direction, Vector3.Up);
+                GetNode<Node3D>("Pivot").LookAt(Position + direction, Vector3.Up);
                 GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
                 GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
             }
             }
             else
             else
@@ -435,42 +442,56 @@ Here's the *Player* script.
                 GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
                 GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
             }
             }
 
 
-            _velocity.x = direction.x * Speed;
-            _velocity.z = direction.z * Speed;
+            // Ground velocity
+            _targetVelocity.X = direction.X * Speed;
+            _targetVelocity.Z = direction.Z * Speed;
+
+            // Vertical velocity
+            if (!IsOnFloor())
+            {
+                _targetVelocity.Y -= FallAcceleration * (float)delta;
+            }
 
 
             // Jumping.
             // Jumping.
             if (IsOnFloor() && Input.IsActionJustPressed("jump"))
             if (IsOnFloor() && Input.IsActionJustPressed("jump"))
             {
             {
-                _velocity.y += JumpImpulse;
+                _targetVelocity.y += JumpImpulse;
             }
             }
 
 
-            _velocity.y -= FallAcceleration * delta;
-            _velocity = MoveAndSlide(_velocity, Vector3.Up);
-
-            for (int index = 0; index < GetSlideCount(); index++)
+            // Iterate through all collisions that occurred this frame.
+            for (int index = 0; index < GetSlideCollisionCount(); index++)
             {
             {
+                // We get one of the collisions with the player.
                 KinematicCollision3D collision = GetSlideCollision(index);
                 KinematicCollision3D collision = GetSlideCollision(index);
-                if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+
+                // If the collision is with a mob.
+                if (collision.GetCollider() is Mob mob)
                 {
                 {
-                    if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                    // We check that we are hitting it from above.
+                    if (Vector3.Up.Dot(collision.GetNormal()) > 0.1f)
                     {
                     {
+                        // If so, we squash it and bounce.
                         mob.Squash();
                         mob.Squash();
-                        _velocity.y = BounceImpulse;
+                        _targetVelocity.Y = BounceImpulse;
                     }
                     }
                 }
                 }
             }
             }
 
 
+            // Moving the character
+            Velocity = _targetVelocity;
+            MoveAndSlide();
+
             var pivot = GetNode<Node3D>("Pivot");
             var pivot = GetNode<Node3D>("Pivot");
-            pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
+            pivot.Rotation = new Vector3(Mathf.Pi / 6.0f * Velocity.Y / JumpImpulse, pivot.Rotation.Y, pivot.Rotation.Z);
         }
         }
 
 
         private void Die()
         private void Die()
         {
         {
-            EmitSignal(nameof(Hit));
+            EmitSignal(SignalName.Hit);
             QueueFree();
             QueueFree();
         }
         }
 
 
-        public void OnMobDetectorBodyEntered(Node body)
+        private void OnMobDetectorBodyEntered(Node body)
         {
         {
             Die();
             Die();
         }
         }
@@ -500,8 +521,8 @@ And the *Mob*'s script.
         # We position the mob by placing it at start_position
         # We position the mob by placing it at start_position
         # and rotate it towards player_position, so it looks at the player.
         # and rotate it towards player_position, so it looks at the player.
         look_at_from_position(start_position, player_position, Vector3.UP)
         look_at_from_position(start_position, player_position, Vector3.UP)
-        # In this rotation^, the mob will move directly towards the player
-        # so we rotate it randomly within range of -90 and +90 degrees.
+        # Rotate this mob randomly within range of -90 and +90 degrees,
+        # so that it doesn't move directly towards the player.
         rotate_y(randf_range(-PI / 4, PI / 4))
         rotate_y(randf_range(-PI / 4, PI / 4))
 
 
         # We calculate a random speed (integer)
         # We calculate a random speed (integer)
@@ -512,7 +533,7 @@ And the *Mob*'s script.
         # in order to move in the direction the mob is looking.
         # in order to move in the direction the mob is looking.
         velocity = velocity.rotated(Vector3.UP, rotation.y)
         velocity = velocity.rotated(Vector3.UP, rotation.y)
 
 
-        $AnimationPlayer.playback_speed = random_speed / min_speed
+        $AnimationPlayer.speed_scale = random_speed / min_speed
 
 
     func _on_visible_on_screen_notifier_3d_screen_exited():
     func _on_visible_on_screen_notifier_3d_screen_exited():
         queue_free()
         queue_free()
@@ -520,6 +541,7 @@ And the *Mob*'s script.
     func squash():
     func squash():
         squashed.emit()
         squashed.emit()
         queue_free() # Destroy this node
         queue_free() # Destroy this node
+
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     using Godot;
     using Godot;
@@ -532,37 +554,44 @@ And the *Mob*'s script.
 
 
         // Minimum speed of the mob in meters per second
         // Minimum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MinSpeed = 10;
+        public int MinSpeed { get; set; } = 10;
         // Maximum speed of the mob in meters per second
         // Maximum speed of the mob in meters per second
         [Export]
         [Export]
-        public int MaxSpeed = 18;
+        public int MaxSpeed { get; set; } = 18;
 
 
-        private Vector3 _velocity = Vector3.Zero;
-
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
         {
-            MoveAndSlide(_velocity);
+            MoveAndSlide();
         }
         }
 
 
+        // This function will be called from the Main scene.
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         public void Initialize(Vector3 startPosition, Vector3 playerPosition)
         {
         {
+            // We position the mob by placing it at startPosition
+            // and rotate it towards playerPosition, so it looks at the player.
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
             LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
+            // Rotate this mob randomly within range of -90 and +90 degrees,
+            // so that it doesn't move directly towards the player.
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
             RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
 
 
-            float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
-            _velocity = Vector3.Forward * randomSpeed;
-            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+            // We calculate a random speed (integer).
+            int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
+            // We calculate a forward velocity that represents the speed.
+            Velocity = Vector3.Forward * randomSpeed;
+            // We then rotate the velocity vector based on the mob's Y rotation
+            // in order to move in the direction the mob is looking.
+            Velocity = Velocity.Rotated(Vector3.Up, Rotation.Y);
 
 
-            GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
+            GetNode<AnimationPlayer>("AnimationPlayer").SpeedScale = randomSpeed / MinSpeed;
         }
         }
 
 
         public void Squash()
         public void Squash()
         {
         {
-            EmitSignal(nameof(Squashed));
-            QueueFree();
+            EmitSignal(SignalName.Squashed);
+            QueueFree(); // Destroy this node
         }
         }
 
 
-        public void OnVisibilityNotifierScreenExited()
+        private void OnVisibilityNotifierScreenExited()
         {
         {
             QueueFree();
             QueueFree();
         }
         }

+ 26 - 26
getting_started/step_by_step/signals.rst

@@ -157,7 +157,7 @@ the ``not`` keyword to invert the value.
 
 
  .. code-tab:: csharp C#
  .. code-tab:: csharp C#
 
 
-    public void OnButtonPressed()
+    private void OnButtonPressed()
     {
     {
         SetProcess(!IsProcessing());
         SetProcess(!IsProcessing());
     }
     }
@@ -181,8 +181,8 @@ following code, which we saw two lessons ago:
 
 
     public override void _Process(double delta)
     public override void _Process(double delta)
     {
     {
-        Rotation += AngularSpeed * (float)delta;
-        var velocity = Vector2.Up.Rotated(Rotation) * Speed;
+        Rotation += _angularSpeed * (float)delta;
+        var velocity = Vector2.Up.Rotated(Rotation) * _speed;
         Position += velocity * (float)delta;
         Position += velocity * (float)delta;
     }
     }
 
 
@@ -210,19 +210,19 @@ Your complete ``Sprite2D.gd`` code should look like the following.
 
 
     using Godot;
     using Godot;
 
 
-    public partial class Sprite : Sprite2D
+    public partial class MySprite2D : Sprite2D
     {
     {
-        private float Speed = 400;
-        private float AngularSpeed = Mathf.Pi;
+        private float _speed = 400;
+        private float _angularSpeed = Mathf.Pi;
 
 
         public override void _Process(double delta)
         public override void _Process(double delta)
         {
         {
-            Rotation += AngularSpeed * (float)delta;
-            var velocity = Vector2.Up.Rotated(Rotation) * Speed;
+            Rotation += _angularSpeed * (float)delta;
+            var velocity = Vector2.Up.Rotated(Rotation) * _speed;
             Position += velocity * (float)delta;
             Position += velocity * (float)delta;
         }
         }
 
 
-        public void OnButtonPressed()
+        private void OnButtonPressed()
         {
         {
             SetProcess(!IsProcessing());
             SetProcess(!IsProcessing());
         }
         }
@@ -323,7 +323,7 @@ bottom of our script and use it to toggle our sprite's visibility.
 
 
  .. code-tab:: csharp C#
  .. code-tab:: csharp C#
 
 
-    public void OnTimerTimeout()
+    private void OnTimerTimeout()
     {
     {
         Visible = !Visible;
         Visible = !Visible;
     }
     }
@@ -372,10 +372,10 @@ Here is the complete ``Sprite2D.gd`` file for reference.
 
 
     using Godot;
     using Godot;
 
 
-    public partial class Sprite : Sprite2D
+    public partial class MySprite2D : Sprite2D
     {
     {
-        private float Speed = 400;
-        private float AngularSpeed = Mathf.Pi;
+        private float _speed = 400;
+        private float _angularSpeed = Mathf.Pi;
 
 
         public override void _Ready()
         public override void _Ready()
         {
         {
@@ -385,17 +385,17 @@ Here is the complete ``Sprite2D.gd`` file for reference.
 
 
         public override void _Process(double delta)
         public override void _Process(double delta)
         {
         {
-            Rotation += AngularSpeed * (float)delta;
-            var velocity = Vector2.Up.Rotated(Rotation) * Speed;
+            Rotation += _angularSpeed * (float)delta;
+            var velocity = Vector2.Up.Rotated(Rotation) * _speed;
             Position += velocity * (float)delta;
             Position += velocity * (float)delta;
         }
         }
 
 
-        public void OnButtonPressed()
+        private void OnButtonPressed()
         {
         {
             SetProcess(!IsProcessing());
             SetProcess(!IsProcessing());
         }
         }
 
 
-        public void OnTimerTimeout()
+        private void OnTimerTimeout()
         {
         {
             Visible = !Visible;
             Visible = !Visible;
         }
         }
@@ -425,12 +425,12 @@ reaches 0.
 
 
     using Godot;
     using Godot;
 
 
-    public partial class CustomSignal : Node2D
+    public partial class MyNode2D : Node2D
     {
     {
         [Signal]
         [Signal]
         public delegate void HealthDepletedEventHandler();
         public delegate void HealthDepletedEventHandler();
 
 
-        private int Health = 10;
+        private int _health = 10;
     }
     }
 
 
 .. note:: As signals represent events that just occurred, we generally use an
 .. note:: As signals represent events that just occurred, we generally use an
@@ -455,9 +455,9 @@ To emit a signal in your scripts, call ``emit()`` on the signal.
 
 
     public void TakeDamage(int amount)
     public void TakeDamage(int amount)
     {
     {
-        Health -= amount;
+        _health -= amount;
 
 
-        if (Health < 0)
+        if (_health <= 0)
         {
         {
             EmitSignal(SignalName.HealthDepleted);
             EmitSignal(SignalName.HealthDepleted);
         }
         }
@@ -479,12 +479,12 @@ names between parentheses:
 
 
     using Godot;
     using Godot;
 
 
-    public partial class CustomSignal : Node
+    public partial class MyNode : Node
     {
     {
         [Signal]
         [Signal]
         public delegate void HealthChangedEventHandler(int oldValue, int newValue);
         public delegate void HealthChangedEventHandler(int oldValue, int newValue);
 
 
-        private int Health = 10;
+        private int _health = 10;
     }
     }
 
 
 .. note::
 .. note::
@@ -509,9 +509,9 @@ To emit values along with the signal, add them as extra arguments to the
 
 
     public void TakeDamage(int amount)
     public void TakeDamage(int amount)
     {
     {
-        var oldHealth = Health;
-        Health -= amount;
-        EmitSignal(SignalName.HealthChanged, oldHealth, Health);
+        int oldHealth = _health;
+        _health -= amount;
+        EmitSignal(SignalName.HealthChanged, oldHealth, _health);
     }
     }
 
 
 Summary
 Summary