Merge pull request #4208 from aaronfranke/dtc-consistency

Change Dodge the Creeps to be more consistent with the demo projects
This commit is contained in:
Aaron Franke
2020-10-21 01:50:54 -04:00
committed by GitHub
@@ -176,17 +176,17 @@ Start by declaring the member variables this object will need:
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
public class Player : Area2D
{
[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():
screen_size = get_viewport_rect().size
hide() # Start the player hidden.
.. code-tab:: csharp
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.
@@ -233,9 +235,9 @@ need to do the following:
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
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
``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
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"):
velocity.x += 1
if Input.is_action_pressed("ui_left"):
@@ -254,6 +256,7 @@ or ``false`` if it isn't.
velocity.y += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite.play()
@@ -264,7 +267,7 @@ or ``false`` if it isn't.
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"))
{
@@ -290,7 +293,7 @@ or ``false`` if it isn't.
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
velocity = velocity.Normalized() * speed;
animatedSprite.Play();
}
else
@@ -339,8 +342,8 @@ the ``_process`` function (make sure it's not indented under the `else`):
Position += velocity * delta;
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:
$AnimatedSprite.animation = "walk"
$AnimatedSprite.flip_v = false
# See the note below about boolean assignment
# See the note below about boolean assignment.
$AnimatedSprite.flip_h = velocity.x < 0
elif velocity.y != 0:
$AnimatedSprite.animation = "up"
@@ -389,7 +392,7 @@ function:
{
animatedSprite.Animation = "walk";
animatedSprite.FlipV = false;
// See the note below about boolean assignment
// See the note below about boolean assignment.
animatedSprite.FlipH = velocity.x < 0;
}
else if (velocity.y != 0)
@@ -486,8 +489,9 @@ this code to the function:
.. code-tab:: gdscript GDScript
func _on_Player_body_entered(body):
hide() # Player disappears after being hit.
hide() # Player disappears after being hit.
emit_signal("hit")
# Must be deferred as we can't change physics properties on a physics callback.
$CollisionShape2D.set_deferred("disabled", true)
.. code-tab:: csharp
@@ -495,7 +499,8 @@ this code to the function:
public void OnPlayerBodyEntered(PhysicsBody2D body)
{
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);
}
@@ -605,33 +610,31 @@ Add a script to the ``Mob`` and add the following member variables:
[Export]
public int MaxSpeed = 250; // Maximum speed range.
}
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
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::
.. code-tab:: gdscript GDScript
func _ready():
$AnimatedSprite.playing = true
var mob_types = $AnimatedSprite.frames.get_animation_names()
$AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
.. 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()
{
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``
@@ -740,7 +743,7 @@ instance.
extends Node
export (PackedScene) var Mob
export(PackedScene) var mob_scene
var score
func _ready():
@@ -752,22 +755,17 @@ instance.
{
// 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]
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()
{
}
// 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()
{
_score = 0;
score = 0;
var player = GetNode<Player>("Player");
var startPosition = GetNode<Position2D>("StartPosition");
@@ -849,7 +847,7 @@ the other two timers. ``ScoreTimer`` will increment the score by 1.
public void OnScoreTimerTimeout()
{
_score++;
score++;
}
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():
# 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.
var mob = Mob.instance()
var mob = mob_scene.instance()
add_child(mob)
# 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.
mob.position = $MobPath/MobSpawnLocation.position
mob.position = mob_spawn_location.position
# Add some randomness to the direction.
direction += rand_range(-PI / 4, PI / 4)
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
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.
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.Offset = _random.Next();
mobSpawnLocation.Offset = GD.Randi();
// 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.
float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
// Set the mob's position to a random location.
mobInstance.Position = mobSpawnLocation.Position;
mob.Position = mobSpawnLocation.Position;
// 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.
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
``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
public override void _Ready()
{
NewGame();
}
public override void _Ready()
{
NewGame();
}
Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically
@@ -1187,7 +1197,7 @@ message:
.. code-tab:: csharp
var hud = GetNode<HUD>("HUD");
hud.UpdateScore(_score);
hud.UpdateScore(score);
hud.ShowMessage("Get Ready!");
In ``game_over()`` we need to call the corresponding ``HUD`` function:
@@ -1211,7 +1221,7 @@ sync with the changing score:
.. 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
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
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::
.. code-tab:: gdscript GDScript
get_tree().call_group("mobs", "queue_free")
.. 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");
The ``call_group()`` function calls the named function on every node in a group -