Browse Source

Merge pull request #4208 from aaronfranke/dtc-consistency

Change Dodge the Creeps to be more consistent with the demo projects
Aaron Franke 4 years ago
parent
commit
7bcfdafdc0
1 changed files with 74 additions and 63 deletions
  1. 74 63
      getting_started/step_by_step/your_first_game.rst

+ 74 - 63
getting_started/step_by_step/your_first_game.rst

@@ -176,17 +176,17 @@ Start by declaring the member variables this object will need:
 
 
     extends Area2D
     extends Area2D
 
 
-    export var speed = 400  # How fast the player will move (pixels/sec).
-    var screen_size  # Size of the game window.
+    export var speed = 400 # How fast the player will move (pixels/sec).
+    var screen_size # Size of the game window.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     public class Player : Area2D
     public class Player : Area2D
     {
     {
         [Export]
         [Export]
-        public int Speed = 400; // How fast the player will move (pixels/sec).
+        public int speed = 400; // How fast the player will move (pixels/sec).
 
 
-        private Vector2 _screenSize; // Size of the game window.
+        public Vector2 screenSize; // Size of the game window.
     }
     }
 
 
 
 
@@ -213,12 +213,14 @@ which is a good time to find the size of the game window:
 
 
     func _ready():
     func _ready():
         screen_size = get_viewport_rect().size
         screen_size = get_viewport_rect().size
+        hide() # Start the player hidden.
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     public override void _Ready()
     public override void _Ready()
     {
     {
-        _screenSize = GetViewport().Size;
+        screenSize = GetViewportRect().Size;
+        Hide(); // Start the player hidden.
     }
     }
 
 
 Now we can use the ``_process()`` function to define what the player will do.
 Now we can use the ``_process()`` function to define what the player will do.
@@ -233,9 +235,9 @@ need to do the following:
 First, we need to check for input - is the player pressing a key? For
 First, we need to check for input - is the player pressing a key? For
 this game, we have 4 direction inputs to check. Input actions are defined
 this game, we have 4 direction inputs to check. Input actions are defined
 in the Project Settings under "Input Map". Here, you can define custom events and
 in the Project Settings under "Input Map". Here, you can define custom events and
-assign different keys, mouse events, or other inputs to them. For this demo,
-we will use the default events that are assigned to the arrow keys on the
-keyboard.
+assign different keys, mouse events, or other inputs to them.
+For this game, we will just use the default events called
+"ui_right" etc that are assigned to the arrow keys on the keyboard.
 
 
 You can detect whether a key is pressed using
 You can detect whether a key is pressed using
 ``Input.is_action_pressed()``, which returns ``true`` if it's pressed
 ``Input.is_action_pressed()``, which returns ``true`` if it's pressed
@@ -245,7 +247,7 @@ or ``false`` if it isn't.
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
     func _process(delta):
     func _process(delta):
-        var velocity = Vector2()  # The player's movement vector.
+        var velocity = Vector2.ZERO # The player's movement vector.
         if Input.is_action_pressed("ui_right"):
         if Input.is_action_pressed("ui_right"):
             velocity.x += 1
             velocity.x += 1
         if Input.is_action_pressed("ui_left"):
         if Input.is_action_pressed("ui_left"):
@@ -254,6 +256,7 @@ or ``false`` if it isn't.
             velocity.y += 1
             velocity.y += 1
         if Input.is_action_pressed("ui_up"):
         if Input.is_action_pressed("ui_up"):
             velocity.y -= 1
             velocity.y -= 1
+
         if velocity.length() > 0:
         if velocity.length() > 0:
             velocity = velocity.normalized() * speed
             velocity = velocity.normalized() * speed
             $AnimatedSprite.play()
             $AnimatedSprite.play()
@@ -264,7 +267,7 @@ or ``false`` if it isn't.
 
 
     public override void _Process(float delta)
     public override void _Process(float delta)
     {
     {
-        var velocity = new Vector2(); // The player's movement vector.
+        var velocity = Vector2.Zero; // The player's movement vector.
 
 
         if (Input.IsActionPressed("ui_right"))
         if (Input.IsActionPressed("ui_right"))
         {
         {
@@ -290,7 +293,7 @@ or ``false`` if it isn't.
 
 
         if (velocity.Length() > 0)
         if (velocity.Length() > 0)
         {
         {
-            velocity = velocity.Normalized() * Speed;
+            velocity = velocity.Normalized() * speed;
             animatedSprite.Play();
             animatedSprite.Play();
         }
         }
         else
         else
@@ -339,8 +342,8 @@ the ``_process`` function (make sure it's not indented under the `else`):
 
 
         Position += velocity * delta;
         Position += velocity * delta;
         Position = new Vector2(
         Position = new Vector2(
-            x: Mathf.Clamp(Position.x, 0, _screenSize.x),
-            y: Mathf.Clamp(Position.y, 0, _screenSize.y)
+            x: Mathf.Clamp(Position.x, 0, screenSize.x),
+            y: Mathf.Clamp(Position.y, 0, screenSize.y)
         );
         );
 
 
 
 
@@ -377,7 +380,7 @@ function:
         if velocity.x != 0:
         if velocity.x != 0:
             $AnimatedSprite.animation = "walk"
             $AnimatedSprite.animation = "walk"
             $AnimatedSprite.flip_v = false
             $AnimatedSprite.flip_v = false
-            # See the note below about boolean assignment
+            # See the note below about boolean assignment.
             $AnimatedSprite.flip_h = velocity.x < 0
             $AnimatedSprite.flip_h = velocity.x < 0
         elif velocity.y != 0:
         elif velocity.y != 0:
             $AnimatedSprite.animation = "up"
             $AnimatedSprite.animation = "up"
@@ -389,7 +392,7 @@ function:
         {
         {
             animatedSprite.Animation = "walk";
             animatedSprite.Animation = "walk";
             animatedSprite.FlipV = false;
             animatedSprite.FlipV = false;
-            // See the note below about boolean assignment
+            // See the note below about boolean assignment.
             animatedSprite.FlipH = velocity.x < 0;
             animatedSprite.FlipH = velocity.x < 0;
         }
         }
         else if (velocity.y != 0)
         else if (velocity.y != 0)
@@ -486,8 +489,9 @@ this code to the function:
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
     func _on_Player_body_entered(body):
     func _on_Player_body_entered(body):
-        hide()  # Player disappears after being hit.
+        hide() # Player disappears after being hit.
         emit_signal("hit")
         emit_signal("hit")
+        # Must be deferred as we can't change physics properties on a physics callback.
         $CollisionShape2D.set_deferred("disabled", true)
         $CollisionShape2D.set_deferred("disabled", true)
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
@@ -495,7 +499,8 @@ this code to the function:
     public void OnPlayerBodyEntered(PhysicsBody2D body)
     public void OnPlayerBodyEntered(PhysicsBody2D body)
     {
     {
         Hide(); // Player disappears after being hit.
         Hide(); // Player disappears after being hit.
-        EmitSignal("Hit");
+        EmitSignal(nameof(Hit));
+        // Must be deferred as we can't change physics properties on a physics callback.
         GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
         GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
     }
     }
 
 
@@ -605,33 +610,31 @@ Add a script to the ``Mob`` and add the following member variables:
 
 
         [Export]
         [Export]
         public int MaxSpeed = 250; // Maximum speed range.
         public int MaxSpeed = 250; // Maximum speed range.
-
     }
     }
 
 
 When we spawn a mob, we'll pick a random value between ``min_speed`` and
 When we spawn a mob, we'll pick a random value between ``min_speed`` and
 ``max_speed`` for how fast each mob will move (it would be boring if they
 ``max_speed`` for how fast each mob will move (it would be boring if they
 were all moving at the same speed).
 were all moving at the same speed).
 
 
-Now let's look at the rest of the script. In ``_ready()`` we randomly
-choose one of the three animation types:
+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:
 
 
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
     func _ready():
     func _ready():
+        $AnimatedSprite.playing = true
         var mob_types = $AnimatedSprite.frames.get_animation_names()
         var mob_types = $AnimatedSprite.frames.get_animation_names()
         $AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
         $AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-    // C# doesn't implement GDScript's random methods, so we use 'System.Random' as an alternative.
-    static private Random _random = new Random();
-
     public override void _Ready()
     public override void _Ready()
     {
     {
         var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
         var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
-        var mobTypes = animSprite.Frames.GetAnimationNames();
-        animSprite.Animation = mobTypes[_random.Next(0, mobTypes.Length)];
+        animSprite.Playing = true;
+        string[] mobTypes = animSprite.Frames.GetAnimationNames();
+        animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
     }
     }
 
 
 First, we get the list of animation names from the AnimatedSprite's ``frames``
 First, we get the list of animation names from the AnimatedSprite's ``frames``
@@ -740,7 +743,7 @@ instance.
 
 
     extends Node
     extends Node
 
 
-    export (PackedScene) var Mob
+    export(PackedScene) var mob_scene
     var score
     var score
 
 
     func _ready():
     func _ready():
@@ -752,22 +755,17 @@ 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 Mob;
+        public PackedScene mobScene;
+    #pragma warning restore 649
 
 
-        private int _score;
-
-        // We use 'System.Random' as an alternative to GDScript's random methods.
-        private Random _random = new Random();
+        public int score;
 
 
         public override void _Ready()
         public override void _Ready()
         {
         {
-        }
-
-        // We'll use this later because C# doesn't support GDScript's randi().
-        private float RandRange(float min, float max)
-        {
-            return (float)_random.NextDouble() * (max - min) + min;
+            GD.Randomize();
         }
         }
     }
     }
 
 
@@ -816,7 +814,7 @@ for a 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<Position2D>("StartPosition");
         var startPosition = GetNode<Position2D>("StartPosition");
@@ -849,7 +847,7 @@ the other two timers. ``ScoreTimer`` will increment the score by 1.
 
 
     public void OnScoreTimerTimeout()
     public void OnScoreTimerTimeout()
     {
     {
-        _score++;
+        score++;
     }
     }
 
 
 In ``_on_MobTimer_timeout()``, we will create a mob instance, pick a
 In ``_on_MobTimer_timeout()``, we will create a mob instance, pick a
@@ -865,49 +863,62 @@ Note that a new instance must be added to the scene using ``add_child()``.
 
 
     func _on_MobTimer_timeout():
     func _on_MobTimer_timeout():
         # Choose a random location on Path2D.
         # Choose a random location on Path2D.
-        $MobPath/MobSpawnLocation.offset = randi()
+        var mob_spawn_location = get_node("MobPath/MobSpawnLocation");
+        mob_spawn_location.offset = randi()
+
         # Create a Mob instance and add it to the scene.
         # Create a Mob instance and add it to the scene.
-        var mob = Mob.instance()
+        var mob = mob_scene.instance()
         add_child(mob)
         add_child(mob)
+
         # Set the mob's direction perpendicular to the path direction.
         # Set the mob's direction perpendicular to the path direction.
-        var direction = $MobPath/MobSpawnLocation.rotation + PI / 2
+        var direction = mob_spawn_location.rotation + PI / 2
+
         # Set the mob's position to a random location.
         # Set the mob's position to a random location.
-        mob.position = $MobPath/MobSpawnLocation.position
+        mob.position = mob_spawn_location.position
+
         # Add some randomness to the direction.
         # Add some randomness to the direction.
         direction += rand_range(-PI / 4, PI / 4)
         direction += rand_range(-PI / 4, PI / 4)
         mob.rotation = direction
         mob.rotation = direction
-        # Set the velocity (speed & direction).
-        mob.linear_velocity = Vector2(rand_range(mob.min_speed, mob.max_speed), 0)
-        mob.linear_velocity = mob.linear_velocity.rotated(direction)
+
+        # Choose the velocity.
+        var velocity = Vector2(rand_range(mob.min_speed, mob.max_speed), 0)
+        mob.linear_velocity = velocity.rotated(direction)
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
     public void OnMobTimerTimeout()
     public void OnMobTimerTimeout()
     {
     {
+        // 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
+        // obviously PathFollow2D and Mob, since they appear later on the line.
+
         // Choose a random location on Path2D.
         // Choose a random location on Path2D.
         var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
         var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
-        mobSpawnLocation.Offset = _random.Next();
+        mobSpawnLocation.Offset = GD.Randi();
 
 
         // Create a Mob instance and add it to the scene.
         // Create a Mob instance and add it to the scene.
-        var mobInstance = (RigidBody2D)Mob.Instance();
-        AddChild(mobInstance);
+        var mob = (Mob)mobScene.Instance();
+        AddChild(mob);
 
 
         // Set the mob's direction perpendicular to the path direction.
         // Set the mob's direction perpendicular to the path direction.
         float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
         float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
 
 
         // Set the mob's position to a random location.
         // Set the mob's position to a random location.
-        mobInstance.Position = mobSpawnLocation.Position;
+        mob.Position = mobSpawnLocation.Position;
 
 
         // Add some randomness to the direction.
         // Add some randomness to the direction.
-        direction += RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
-        mobInstance.Rotation = direction;
+        direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
+        mob.Rotation = direction;
 
 
         // Choose the velocity.
         // Choose the velocity.
-        mobInstance.LinearVelocity = new Vector2(RandRange(150f, 250f), 0).Rotated(direction);
+        var velocity = new Vector2((float)GD.RandRange(mob.minSpeed, mob.maxSpeed), 0);
+        mob.LinearVelocity = velocity.Rotated(direction);
     }
     }
 
 
-.. important:: Why ``PI``? In functions requiring angles, GDScript uses *radians*,
-               not degrees. If you're more comfortable working with
+.. 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``).
+               If you're more comfortable working with
                degrees, you'll need to use the ``deg2rad()`` and
                degrees, you'll need to use the ``deg2rad()`` and
                ``rad2deg()`` functions to convert between the two.
                ``rad2deg()`` functions to convert between the two.
 
 
@@ -925,10 +936,9 @@ Let's test the scene to make sure everything is working. Add this to ``_ready()`
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-        public override void _Ready()
-        {
-            NewGame();
-        }
+    public override void _Ready()
+    {
+        NewGame();
     }
     }
 
 
 Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically
 Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically
@@ -1187,7 +1197,7 @@ 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!");
 
 
 In ``game_over()`` we need to call the corresponding ``HUD`` function:
 In ``game_over()`` we need to call the corresponding ``HUD`` function:
@@ -1211,7 +1221,7 @@ sync with the changing score:
 
 
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
-        GetNode<HUD>("HUD").UpdateScore(_score);
+        GetNode<HUD>("HUD").UpdateScore(score);
 
 
 Now you're ready to play! Click the "Play the Project" button. You will
 Now you're ready to play! Click the "Play the Project" button. You will
 be asked to select a main scene, so choose ``Main.tscn``.
 be asked to select a main scene, so choose ``Main.tscn``.
@@ -1231,16 +1241,17 @@ click "Groups" and you can type a new group name and click "Add".
 .. image:: img/group_tab.png
 .. image:: img/group_tab.png
 
 
 Now all mobs will be in the "mobs" group. We can then add the following line to
 Now all mobs will be in the "mobs" group. We can then add the following line to
-the ``game_over()`` function in ``Main``:
+the ``new_game()`` function in ``Main``:
 
 
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
 
 
         get_tree().call_group("mobs", "queue_free")
         get_tree().call_group("mobs", "queue_free")
 
 
-
  .. code-tab:: csharp
  .. code-tab:: csharp
 
 
+        // Note that for calling Godot-provided methods with strings,
+        // we have to use the original Godot snake_case name.
         GetTree().CallGroup("mobs", "queue_free");
         GetTree().CallGroup("mobs", "queue_free");
 
 
 The ``call_group()`` function calls the named function on every node in a group -
 The ``call_group()`` function calls the named function on every node in a group -