Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Die Hauptszene des Spiels
Jetzt ist es Zeit, alles, was wir zusammen gemacht haben, in eine spielbare Szene zusammenzuführen.
Erstellen Sie eine neue Szene und fügen einen Node namens Main hinzu. (Der Grund, warum wir Node anstelle von Node2D verwenden, ist, dass dieser Node ein Container für die Handhabung der Spiellogik sein wird. Er benötigt selbst keine 2D-Funktionalität.)
Klicken Sie auf den Instanz-Button (dargestellt durch ein Kettenglied-Icon) und wählen Sie Ihre gespeicherte player.tscn aus.
Fügen Sie nun die folgenden Nodes als Child-Elemente von Main hinzu und benenne sie wie abgebildet:
Timer (genannt
MobTimer) - um zu steuern, wie oft Mobs spawnenTimer (genannt
ScoreTimer) - um die Punktzahl jede Sekunde zu erhöhenTimer (genannt
StartTimer) - um eine Verzögerung vor dem Start zu gebenMarker2D (namens
StartPosition) - um die Startposition des Spielers anzugeben
Stellen Sie die Eigenschaft Wartezeit von jedem der Timer Nodes wie folgt ein (Werte sind in Sekunden):
MobTimer:0.5ScoreTimer:1StartTimer:2
Stellen Sie zusätzlich die Eigenschaft Einmalig von StartTimer auf "An" und die Position des StartPosition-Nodes auf (240, 450).
Mobs spawnen
The Main node will be spawning new mobs, and we want them to appear at a random
location on the edge of the screen. Click the Main node in the Scene dock, then
add a child Path2D node named MobPath. When you select
Path2D, you will see some new buttons at the top of the editor:
Wählen Sie den Mittleren ("Punkt hinzufügen") und zeichnen Sie den Pfad durch Klicken, um die Punkte an den angezeigten Ecken hinzuzufügen. Damit die Punkte am Raster einrasten, vergewissern Sie sich, dass die Optionen "am Raster einrasten" und "Intelligentes Einrasten" aktiviert sind. Diese Optionen befinden sich links neben dem "Sperren"-Button, der als Magnet neben einigen Punkten bzw. sich schneidenden Linien angezeigt wird.
Wichtig
Zeichnen Sie den Pfad im Uhrzeigersinn, sonst spawnen Ihre Mobs nach außen statt nach innen!
Nachdem Sie den Punkt 4 im Bild platziert haben, klicken Sie auf den "Kurve schließen"-Button und Ihre Kurve ist vollständig.
Jetzt, nachdem der Pfad definiert ist, fügen Sie einen PathFollow2D Node als Child von MobPath hinzu und nennen ihn MobSpawnLocation. Dieser Node wird sich automatisch drehen udn dem Pfad folgen, während er sich bewegt, so dass wir damit eine zufällige Position und Richtung entlang des Pfades auswählen können.
Die Szene sollte so aussehen:
Main-Skript
Fügen Sie ein Skript zu Main hinzu. Am Anfang des Skripts benutzen wir @export var mob_scene: PackedScene, was uns erlaubt, die Mob-Szene auszuwählen, die wir instanziieren wollen.
extends Node
@export var mob_scene: PackedScene
var score
using Godot;
public partial class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
[Export]
public PackedScene MobScene { get; set; }
private int _score;
}
Klicken Sie auf den Main-Node und Sie werden die Property Mob Scene im Inspektor unter "Main.gd" sehen.
Sie können einen Wert für diese Eigenschaft auf zwei Wege hinzufügen:
Ziehen Sie
mob.tscnaus dem "Dateisystem"-Dock und legen Sie es in der Mob Scene-Eigenschaft ab.Klicken Sie auf den Pfeil nach unten neben "[leer]" und wählen Sie "Laden". Wählen Sie
mob.tscn.
Next, select the instance of the Player scene under Main node in the Scene dock,
and access the Signals dock on the sidebar.
Sie sollten eine Liste der Signale für den Player-Node sehen. Suchen Sie das Signal hit in der Liste und doppelklicken Sie darauf (oder klicken Sie es mit der rechten Maustaste an und wählen Sie "Verbinden..."). Dies öffnet den Dialog für die Signalverbindung. Wir wollen eine neue Funktion namens game_over erstellen, die sich darum kümmert, was passiert, wenn ein Spiel endet. Geben Sie "game_over" in das Feld "Empfänger-Methode" am unteren Ende des Signalverbindungsdialogs ein und klicken Sie auf "Verbinden". Das hit-Signal soll vom Player ausgesendet und im Main-Skript verarbeitet werden. Fügen Sie den folgenden Code zu der neuen Funktion hinzu, sowie eine new_game-Funktion, die alles für ein neues Spiel einrichten wird:
func game_over():
$ScoreTimer.stop()
$MobTimer.stop()
func new_game():
score = 0
$Player.start($StartPosition.position)
$StartTimer.start()
public void GameOver()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Timer>("ScoreTimer").Stop();
}
public void NewGame()
{
_score = 0;
var player = GetNode<Player>("Player");
var startPosition = GetNode<Marker2D>("StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("StartTimer").Start();
}
Now we'll connect the timeout() signal of each Timer node (StartTimer,
ScoreTimer, and MobTimer) to the main script. For each of the three
timers, select the timer in the Scene dock, open the Signals tab of the Node
dock, then double-click the timeout() signal in the list. This will open a new
signal connection dialog. The default settings in this dialog should be fine, so
select Connect to create a new signal connection.
Once all three timers have this set up, you should be able to see each timer
have a Signal connection for their respective timeout() signal, showing in
green, within their respective Signals tabs:
(For MobTimer):
_on_mob_timer_timeout()(For ScoreTimer):
_on_score_timer_timeout()(For StartTimer):
_on_start_timer_timeout()
Now we define how each of these timers operate by adding the code below. Notice
that StartTimer will start the other two timers, and that ScoreTimer
will increment the score by 1.
func _on_score_timer_timeout():
score += 1
func _on_start_timer_timeout():
$MobTimer.start()
$ScoreTimer.start()
// We also specified this function name in PascalCase in the editor's connection window.
private void OnScoreTimerTimeout()
{
_score++;
}
// We also specified this function name in PascalCase in the editor's connection window.
private void OnStartTimerTimeout()
{
GetNode<Timer>("MobTimer").Start();
GetNode<Timer>("ScoreTimer").Start();
}
In _on_mob_timer_timeout() erzeugen wir eine Mob-Instanz, wählen eine zufällige Startposition entlang des Path2D und setzen den Mob in Bewegung. Der PathFollow2D-Node wird sich automatisch drehen, während er dem Pfad folgt, also werden wir ihn benutzen, um die Richtung und die Position des Mobs zu bestimmen. Wenn wir einen Mob spawnen, wählen wir einen Zufallswert zwischen 150.0 und 250.0 für die Geschwindigkeit, mit der sich jeder Mob bewegt (es wäre langweilig, wenn sie sich alle mit der gleichen Geschwindigkeit bewegen würden).
Beachten Sie, dass der Szene mit add_child() eine neue Instanz hinzugefügt werden muss.
func _on_mob_timer_timeout():
# Create a new instance of the Mob scene.
var mob = mob_scene.instantiate()
# Choose a random location on Path2D.
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf()
# Set the mob's position to the random location.
mob.position = mob_spawn_location.position
# Set the mob's direction perpendicular to the path direction.
var direction = mob_spawn_location.rotation + PI / 2
# Add some randomness to the direction.
direction += randf_range(-PI / 4, PI / 4)
mob.rotation = direction
# Choose the velocity for the mob.
var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
mob.linear_velocity = velocity.rotated(direction)
# Spawn the mob by adding it to the Main scene.
add_child(mob)
// We also specified this function name in PascalCase in the editor's connection window.
private void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = MobScene.Instantiate<Mob>();
// Choose a random location on Path2D.
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.ProgressRatio = GD.Randf();
// 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.
mob.Position = mobSpawnLocation.Position;
// Add some randomness to the direction.
direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
mob.Rotation = direction;
// Choose the velocity.
var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
mob.LinearVelocity = velocity.Rotated(direction);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
Wichtig
Warum PI? Bei Funktionen, die Winkel erfordern, verwendet Godot Bogenmaß, nicht Grad. Pi steht für eine halbe Umdrehung im Bogenmaß, etwa 3.1415 (es gibt auch TAU, das gleich 2 * PI ist). Wenn Sie lieber mit Grad arbeiten, müssen Sie die Funktionen deg_to_rad() und rad_to_deg() verwenden, um zwischen den beiden zu konvertieren.
Die Szene testen
Testen wir die Szene, um sicherzustellen, dass alles funktioniert. Fügen Sie diesen new_game-Aufruf zu _ready() hinzu:
func _ready():
new_game()
public override void _Ready()
{
NewGame();
}
Lassen Sie uns auch Main als unsere "Hauptszene" zuweisen - diejenige, die automatisch läuft, wenn das Spiel gestartet wird. Drücken Sie den "Play"-Button und wählen Sie main.tscn, wenn Sie dazu aufgefordert werden.
Tipp
Wenn Sie bereits eine andere Szene als "Hauptszene " festgelegt haben, können Sie mit der rechten Maustaste auf main.tscn im Dateisystem-Dock klicken und "als Hauptszene verweden" wählen.
Sie sollten jetzt den Spieler bewegen können, spawnende Mobs sehen und den Spieler verschwinden sehen, wenn er von einem Mob getroffen wird.
Wenn Sie sicher sind, dass alles läuft, löschen Sie den Funktionsaufruf new_game() von _ready() und ersetzen Sie es mit pass.
Was fehlt noch in unserem Spiel? Eine Benutzeroberfläche. In der nächsten Lektion werden wir ein Titelbild hinzufügen und die Punktzahl anzeigen lassen.