mirror of
https://github.com/stan220/godot-docs.git
synced 2026-09-08 18:29:11 +00:00
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:
@@ -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 -
|
||||
|
||||
Reference in New Issue
Block a user