Nachdem wir uns im ersten Teil in Unity umgeschaut und eine kleine Szene gebastelt haben, geht es nun an die Programmierung. Unser Ziel ist es, den Würfel in alle drei Richtungen zu drehen.
Wieso das denn?
Wir schauen uns an, wie man Skripte erstellt, Objekten zuweist und Eigenschaften – in diesem Fall die Rotation – manipuliert. Außerdem stellen wir dabei die Eigenschaften dem Objekt so zur Verfügung, dass wir sie im Inspektor verändern können.
Das Drehen und Bewegen von Objekten ist in der Spieleentwicklung ohnehin sehr wichtig. Stellen wir uns einen Deckenventilator vor, oder eine Waffe, die sich in Richtung des Spielers dreht. Alles, was in diesem Teil gezeigt wird, ist für die Entwicklung von Spielen elementar.
Skript erstellen
Das ist denkbar einfach: Klicke mit der linken Maustaste in die Assets (Ressourcen) in einen freien Bereich. Dann wähle im Menü Create → C# Script. Anschließend gibst Du dem Skript einen Namen. Bei mir heißt es Object_Rotation.
Fertig, du hast dein erstes Skript erstellt. Doch wir wollen mehr, also doppelklicke es. Es sollte sich nun ein Editor öffnen, vorzugsweise Visual Studio Code.
Was uns Unity liefert
Wenn das Skript im Editor geladen wurde, wirst du feststellen, dass da bereits einiges drin steht.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Object_Rotation : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } } |
Bevor wir loslegen, schauen wir es uns an und versuchen zu verstehen, was uns Unity zur Verfügung gestellt hat.
using System.Collections;
: Diese Zeile fügt den Namensraum System.Collections hinzu, der grundlegende Typen und Interfaces für Sammlungen von Objekten bereitstellt, wie z. B. Listen und Arrays.
Ein Namensraum (oder englisch „namespace“) ist in der Informatik ein Mechanismus, der dazu dient, den Gültigkeitsbereich von Bezeichnern (wie z. B. Klassen, Funktionen, Variablen) zu organisieren und zu strukturieren. In vielen Programmiersprachen, einschließlich C#, dienen Namensräume dazu, Kollisionen zwischen den Bezeichnern in verschiedenen Teilen des Codes zu vermeiden.
using System.Collections.Generic;
: Hier wird ein weiterer Namensraum, System.Collections.Generic, hinzugefügt. Dieser Namensraum erweitert die Funktionalität von System.Collections und bietet generische Sammlungstypen an.
using UnityEngine;
: Dies fügt den Namensraum UnityEngine hinzu, der die Unity-Engine-API enthält. Die Unity-Engine ist die Kernbibliothek von Unity und enthält Funktionen und Klassen, die für die Spieleentwicklung in Unity benötigt werden. Von den drei Namensräumen werden wir nur diesen brauchen. Die anderen beiden werden wir nachher löschen.
public class Object_Rotation : MonoBehaviour
: Hier wird eine Klasse mit dem Namen Object_Rotation erstellt, die von der Unity-Klasse MonoBehaviour erbt. MonoBehaviour ist die Basisklasse für alle Skripte in Unity und bietet Funktionen wie Start() und Update(), die im Lebenszyklus eines Spielobjekts aufgerufen werden.
void Start() { }
: Dies ist die Start()-Methode. Sie wird einmal aufgerufen, wenn das Spielobjekt erstellt wird. Du kannst hier Code platzieren, der beim Start des Spiels ausgeführt werden soll. Leute, die mit GameMaker arbeiten, kennen das als Create-Event.
void Update() { }
: Dies ist die Update()-Methode. Sie wird einmal pro Frame aufgerufen und wird oft für fortlaufende Aktualisierungen und Animationen verwendet. Code, der in dieser Methode steht, wird also in jeder Frame-Aktualisierung ausgeführt. Im GameMaker kennt man das als Step-Event.
Klassengesellschaft
Bei C# und Unity ist eine Klasse eine grundlegende Bausteineinheit des objektorientierten Programmierparadigmas. In späteren Tutorials werde ich tiefer darauf eingehen, weshalb die Erklärungen hier nur knapp gehalten werden.
In der objektorientierten Programmierung werden Konzepte und Daten in Einheiten namens „Objekte“ organisiert. Eine Klasse ist eine Vorlage (oder ein Bauplan), die beschreibt, welche Eigenschaften (Daten) und welche Aktionen (Methoden) ein Objekt haben kann.
Eine Klasse kann Datenfelder enthalten, die als Eigenschaften oder Attribute bezeichnet werden. Diese repräsentieren den Zustand eines Objekts. Zum Beispiel könnte eine Klasse für einen Gegner Eigenschaften wie „Geschwindigkeit“, „Lebenspunkte“ und „Feuerkraft“ haben.
Eine Klasse kann auch Methoden (Aktionen) enthalten, die Aktionen repräsentieren, die auf den Daten der Klasse ausgeführt werden können. Beispielsweise könnte die Klasse für einen Gegner Methoden wie „Laufen“ und „Schießen“ haben. Methoden sind im Prinzip Funktionen.
Eine Klasse allein ist nur eine Vorlage. Um tatsächliche Daten zu speichern und Aktionen auszuführen, musst du Objekte dieser Klasse erstellen. Dies wird als „Instanziierung“ bezeichnet.
In Unity werden Klassen oft für die Definition von Skripten verwendet, die das Verhalten von Spielobjekten steuern. Zum Beispiel könntest du eine Klasse für ein Skript haben, das die Bewegung eines Charakters steuert oder das Aussehen eines Objekts ändert. Klassen in Unity-Skripten erben oft von der Klasse MonoBehaviour, um Unity-spezifische Funktionalitäten zu nutzen, wie zum Beispiel die Start()– und Update()-Methoden.
Wie gesagt, gehe ich in späteren Tutorials genauer darauf ein. Jetzt wollen wir erst einmal den Würfel drehen.
Object_Rotation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using UnityEngine; public class Object_Rotation : MonoBehaviour { public Vector3 rotationSpeeds = new Vector3(30.0f, 15.0f, 45.0f); // Geschwindigkeiten der X, Y und Z-Achsen void Start() { } void Update() { // Holen dir die aktuelle Rotation des Objekts als Quaternion Quaternion currentRotation = transform.rotation; // Erzeuge Quaternion-Deltas für die Rotation in den X, Y und Z-Achsen Quaternion deltaRotationX = Quaternion.Euler(rotationSpeeds.x * Time.deltaTime, 0, 0); Quaternion deltaRotationY = Quaternion.Euler(0, rotationSpeeds.y * Time.deltaTime, 0); Quaternion deltaRotationZ = Quaternion.Euler(0, 0, rotationSpeeds.z * Time.deltaTime); // Multipliziere die aktuellen Rotationen mit den Delta-Rotationen currentRotation *= deltaRotationX; currentRotation *= deltaRotationY; currentRotation *= deltaRotationZ; // Setze die neue Rotation transform.rotation = currentRotation; } } |
Gehen wir den Code schrittweise durch.
public Vector3 rotationSpeeds = new Vector3(30.0f, 15.0f, 45.0f);
: Hier wird eine öffentliche Variable rotationSpeeds
deklariert und initialisiert. Diese Variable ist vom Typ Vector3
und enthält Geschwindigkeiten für die Rotation um die X-, Y- und Z-Achsen. Das wirst du später auch im Inspektor verändern können.
void Update() { }
: Die Update()
-Methode wird einmal pro Frame aufgerufen. Hier wird die eigentliche Logik für die Rotation implementiert.
Quaternion currentRotation = transform.rotation;
: Hier wird die aktuelle Rotation des Spielobjekts als Quaternion in der Variable currentRotation
gespeichert.
Ein Quaternion ist eine mathematische Struktur, die in der 3D-Computergrafik und der Robotik verwendet wird, um Rotationen zu repräsentieren. Im Kontext von Unity und vielen anderen 3D-Grafik-Engines wird der Begriff häufig verwendet.
Im Vergleich zu den gebräuchlicheren Euler-Winkeln (Roll, Pitch, Yaw) haben Quaternionen einige Vorteile bei der Darstellung von Rotationen, insbesondere wenn es um das sogenannte „Gimbal Lock“ geht, das bei Euler-Winkeln auftreten kann.
Quaternion-Deltas für die Rotation in den X, Y und Z-Achsen erstellen:
Quaternion deltaRotationX = Quaternion.Euler(rotationSpeeds.x * Time.deltaTime, 0, 0);
Quaternion deltaRotationY = Quaternion.Euler(0, rotationSpeeds.y * Time.deltaTime, 0);
Quaternion deltaRotationZ = Quaternion.Euler(0, 0, rotationSpeeds.z * Time.deltaTime);
Aktuelle Rotation mit den Delta-Rotationen multiplizieren:
currentRotation *= deltaRotationX;
currentRotation *= deltaRotationY;
currentRotation *= deltaRotationZ;
transform.rotation = currentRotation;
: Schließlich wird die neue Rotation des Spielobjekts auf Basis der berechneten Änderungen gesetzt.
Somit dreht sich das Spielobjekt pro Frame um die angegebenen Rotationsgeschwindigkeiten um die X, Y und Z-Achsen. Die Rotationsgeschwindigkeiten werden mit Time.deltaTime
multipliziert, um sicherzustellen, dass die Rotation in Abhängigkeit von der vergangenen Zeit seit dem letzten Frame erfolgt, und somit unabhängig von der Framerate des Spiels ist.
Skript zuweisen und starten
Wenn das Skript gespeichert wurde, wechselst du in Unity. Da wird es sofort überprüft. Sollten Fehler auftauchen, stehen diese in der Konsole. Das Spiel kann erst gestartet werden, wenn es keine Fehler mehr im Skript gibt.
Ist das Skript fehlerfrei, kannst du es dem Würfel zuweisen. Klicke dafür den Würfel an. Du siehst seine Eigenschaften im Inspektor. Nun wähle das Skript bei den Ressourcen aus, halte die Maustaste gedrückt und ziehe es in den Inspektor ganz unten hin.
Du siehst, dass Unity die drei Geschwindigkeiten anzeigt. Du kannst die Werte ändern, sogar, wenn die Vorschau gestartet wurde.
Die Vorschau startest du, indem du über der 3D-Szene auf den Play-Button drückst. Wenn du später noch einmal drauf drückst, beendest du die Vorschau.
Nun sollte sich der Würfel in drei Richtungen drehen. Wie gesagt, kannst du die Geschwindigkeiten der drei Achsen jetzt weiter ändern, bis es für dich optimal ist. Du kannst auch mit negativen Zahlen arbeiten.
Dieses Skript kannst du später allen anderen Objekten zuweisen und wird für dich immer wieder von Nutzen sein.
Nächster Teil
Das war es auch schon wieder. Im dritten und letzten Teil dieser kleinen Einstiegsserie werden wir weiter mit Code arbeiten. Unser Ziel wird darin bestehen, per Code aus einem einzigen Würfel viele zu erschaffen, die sich wie ein gewaltiger Würfel drehen.
Weiterführende Links
Einstieg in Unity Teil 1 – 3D und erste Szene
Einstieg in Unity Teil 3 – Die Macht der Programmierung
Warum soll ich programmieren lernen?
Arcade-Spiele – die Mutter aller Genres?
Frustspirale – Designschnitzer in Spieleklassikern
Mehr als nur eine Party
Externe Links
Unity Dokumentation
Instanziierung – Wikipedia
Quaternion – Wikipedia
Eulersche Zahl – Wikipedia
Eulerscher Winkel – Wikipedia
Gimbal Lock – Wikipedia