How-To 'Moorhuhn'-Klon in Godot Part 2

Lasst die Schweine fliegen!

Skript hinzufügen

Im letzten Teil haben wir unser Projekt erzeugt und unsere erste Szene angelegt. Unser Moorschwein hat auch schon ein Bild bekommen, kann sich allerdings weder bewegen, noch auf Mausklicks reagieren. Das werden wir in diesem Teil ändern. Sorgen wir zuerst einmal dafür, dass unser Schweinchen sich über die Welt bewegen kann.
Da unser Schwein nicht zu einfach zu treffen sein sollte, lassen wir es stattdessen (würde ich weglassen das Wort, weil statt was denn? :P) in einer Sinuskurve fliegen. Damit sich aber überhaupt etwas tut, brauchen wir zuerst einmal ein Skript. Öffnet, wenn nicht schon geschehen, die Moorhuhn Szene.
Klickt auf die Wurzelnode im Szenenfenster. Daraufhin sollte ein Skriptsymbol neben dem Nodesfilter erscheinen.

Wenn ihr darauf klickt öffnet sich ein Dialog. Setzt den Pfad in den Gameobjects Ordner und belasst die anderen Einstellungen wie sie sind. Sobald ihr auf “Erstellen” drückt, öffnet sich der Skripteditor. Jetzt können wir loslegen.

Unser erstes Skript

Zuerst benötigen wir ein paar Variablen. Löscht den Inhalt des Skripts und fügt folgendes ein:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extends Node2D

var m_start : float = 0
var m_start_position : Vector2
export(int) var POINTS = 25
export(float) var HORIZONTAL_SPEED = 100
export(float) var VERTICAL_SPEED = 2
export(float) var AMPLITUDE = 150

func _ready() -> void:
m_start_position = position

```

In der ersten Zeile sagen wir, welche Node wir erweitern. Da unsere Wurzel eine Node2D ist, geben wir auch genau das an. Damit haben wir auch Zugriff auf alle Eigenschaften der Node2d, wie die Position und der Skalierungsfaktor.

Danach deklarieren wir Variablen. In ihnen können wir Werte speichern. Da GDSkript eigentlich nicht typisiert ist benötigt man zum Erzeugen einer Variable nur das Schlüsselwort "var". Danach könnt ihr darin alles speichern was ihr wollt.

```python
var number = 1
number = "number1" #Übeschreibt die 1 mit dem String number1
number = { #Überschreibt den String mit einem Objekt
"number" : 1
}

Seit Godot 3.1 können wir allerdings angeben, welchen Typ wir erwarten. Das tun wir durch das Setzen des Typs nach einem Doppelpunkt:

1
2
3
4
5
var number : int = 1
number = "number1" #Ein Fehler wird angezeigt, da der neue Wert kein int ist
number = { #Ein Fehler wird angezeigt, da der neue Wert kein int ist
"number" : 1
}

Auf diese Weise deklarieren wir zwei Variablen, mit denen wir unsere Bewegung beginnen: m_start, in der wir die vergangene Zeit speichern und m_start_position, in der wir die Position festhalten.
Danach sehen wir ein weiteres neues Konstrukt, nämlich export(type).
Durch dieses Schlüsselwort können wir den Wert dieser Variable außerhalb des Skriptes im Editor verändern. Diese werden nämlich im Inspektor angezeigt und können sogar zur Laufzeit verändert werden.

Zum Schluss haben wir eine Funktion namens _ready(). Diese wird aufgerufen, sobald die Node in eine Szene eingefügt wird. Das ist der perfekt Ort um unsere Startposition festzulegen.

Als nächstes brauchen wir eine Möglichkeit unsere Node zu bewegen. Dazu brauchen wir eine Methode, die periodisch aufgerufen wird. In Godot gibt es hierzu _process(delta:float) und _physics_process(delta:float). Beide Funktionen werden periodisch für jede Node aufgerufen. Die Variable delta ist die Zeit in ms, die seit dem letzten Aufruf vergangen ist. Der Unterschied zwischen beiden ist, dass _Process in jedem Frame so schnell wie möglich ausgeführt wird. Dadurch sind keine regelmäßigen delta Schritte garantiert. Das kann dazu führen, das Variablen, welche die Physik eines Körpers beschreiben (Zum Beispiel die Position) in diesem Schritt ungenau sind.
Hier kommt der _PhysicsProcess ins Spiel. Diese wird in regelmäßigen Abständen aufgerufen. So können die Zustandsgrößen der physikalischen Körper, die in Abhängigkeit von der Zeit berechnet werden (wieder unsere Position), genau berechnet werden.

Also immer wenn wir etwas machen wollen, die diese Zustandgrößen verändert nutzen wir _PhysicsProcess. Fügt folgenden Code ein:

1
2
3
4
5
6
7
8
func _physics_process(delta:float) -> void:
m_start += delta
position.x += delta * HORIZONTAL_SPEED
position.y = sin((m_start)*VERTICAL_SPEED)*AMPLITUDE + m_start_position.y
if global_position.x > get_viewport_rect().size.x:
global_position.x = 0
if global_position.x < 0:
global_position.x = get_viewport_rect().size.x

Zuerst addieren wir unsere Zeit auf. Dann bewegen wir unsere Node durch das verändern ihrer Positionskoordinaten. Die X-Koordinate verschieben wir um unsere horizontale Geschwindigkeit. Durch das Multiplizieren mit delta garantieren wir eine flüssige Bewegung unabhängig von den FPS mit denen unser Spiel läuft.

Für die Y-Bewegung brauchen wir ein wenig Mathematik. Schauen wir uns einmal eine Sinuskurve an:

Unser X ist die vergangene Zeit. Diese multiplizieren wir mit unserer vertikalen Geschwindigkeit. Die Amplitude bestimmt wie hoch der Ausschlag der Kurve ist. Da wir unsere Node frei im Raum platzieren wollen, verschieben wir den berechneten Sinuswert um unsere Y-Startposition.

Darauf folgen zwei if Anweisungen, die vorerst nur zu Debugzwecken da sind. Durch die Funktion get_viewport_rect() bekommen wir das Rechteck unseres Spielfensters. Wenn unser Schwein links oder rechts das Fenster verlässt, teleportieren wir ihn wieder auf die andere Seite.

Erster Test

Jetzt können wir das Ganze auch schon ausprobieren. Wechselt in die 2D Ansicht und positioniert unser Schwein ein wenig mittiger. Startet die Szene und seht unser Schwein fliegen!

Jetzt könnt ihr im Inspektor an unseren exportierten Variablen herumspielen und ausprobieren, wie sie sich auf die Bewegung auswirken.
Wenn ihr fertig seid, setzt die Position wieder auf 0/0 zurück.

Im nächsten Part kümmern wir uns dann darum, unsere Schweinchen endlich abschießen zu können!