Nachdem man die Grundlagen von C# gelernt hat, stoßen viele Anfänger auf dasselbe Problem: Sie verstehen die Syntax, wissen aber nicht, wie sie diese beim Erstellen eines Spiels konkret anwenden sollen.
Auf den ersten Blick wirken es wie zwei völlig unterschiedliche Welten. Code besteht aus Text und Zahlen, während ein Spiel aus Bildern, Bewegung und Sound besteht. Es scheint, als hätten diese Teile nichts miteinander zu tun.
In dieser Lektion schauen wir uns an, wie diese beiden Welten miteinander verbunden sind, und bauen die Brücke zwischen Code und den Objekten in deiner Szene.
In Unity gibt es keine Magie. Es gibt Verbindungen. Und aus diesen Verbindungen entsteht Handlung.
Jedes Objekt in einer Unity-Szene ist ein GameObject. Dabei spielt es keine Rolle, ob du es sehen kannst oder nicht. Der Spieler, NPCs, Umgebungsobjekte, UI-Elemente, die Kamera — sie alle sind GameObjects.
Sie erfüllen unterschiedliche Aufgaben, doch eines haben sie gemeinsam — sie sind Teil der Szene.
Deshalb besitzt jedes GameObject eine Transform-Komponente. Sie bestimmt die Position des Objekts in der Spielwelt: wo es sich befindet, wie es gedreht ist und welche Größe es hat.
Wichtig ist zu verstehen: Ein GameObject ist nur ein Container. Für sich allein macht es nichts. Sein Verhalten wird vollständig durch die Komponenten bestimmt, die ihm hinzugefügt wurden.
Vorhin hast du das Camera-Objekt gesehen. Neben der obligatorischen Transform-Komponente hatte es noch zwei weitere: Camera und Audio Listener.
Die Camera-Komponente ermöglicht es dem Objekt, die Szene darzustellen, während der Audio Listener Geräusche empfängt, die von anderen Objekten erzeugt werden.
Ein GameObject ist ein Container. Seine „Fähigkeiten“ kommen von seinen Komponenten. Die Regel ist einfach: Zeig mir deine Komponenten — und ich sage dir, was du tust 🙂
Fügen wir der Szene ein neues GameObject hinzu. Zum Beispiel ein einfaches quadratisches Sprite. Unity erstellt automatisch ein GameObject mit der obligatorischen Transform-Komponente und einer Sprite Renderer-Komponente, die für die Darstellung des Sprites verantwortlich ist.
Wähle dieses Objekt in der Hierarchy aus und wirf einen Blick in den Inspector. Dort siehst du alle Komponenten, aus denen dieses Objekt aktuell „zusammengesetzt“ ist.
Wenn ein GameObject der Schauspieler ist, dann ist der Inspector sowohl seine Garderobe als auch sein Kontrollzentrum. Hier wird alles sichtbar, was sein Verhalten bestimmt.
Und das Interessante ist: Du kannst Parameter während das Spiel läuft verändern.
Du schreibst keinen Code. Du veränderst lediglich Werte. Und trotzdem reagiert das Objekt sofort.
Kleines Geheimnis: Wenn du Werte im Inspector änderst, macht Unity im Hintergrund genau das, was ein Programmierer per Code tun würde. Du steuerst die Engine also bereits — nur ohne zu tippen.
Ein Skript, das du in C# schreibst, kann ebenfalls eine Komponente sein. Solange es nur im Assets-Ordner liegt, passiert nichts. Es ist lediglich Text.
Sobald du es jedoch einem GameObject hinzufügst, wird es Teil dieses Objekts. Ab diesem Moment führt Unity die darin beschriebene Logik aus. Nun ja… zumindest die Logik, die du tatsächlich geschrieben hast 🙂
Skripte, die mit Unity arbeiten, werden in der Regel als MonoBehaviour erstellt. Ein neues Skript sieht daher typischerweise so aus:
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Wenn du das Spiel startest, führt Unity zunächst den Code in Start() aus und ruft anschließend Update() in jedem Frame auf.
Füge dieses Skript nun dem Objekt Square hinzu. Im Moment macht es noch nichts — aber das Objekt besitzt jetzt eine neue Komponente: TheBestScript.
Jetzt kommen wir zum wichtigsten Teil. Zuvor haben wir Parameter von Komponenten über den Inspector verändert und sofort das Ergebnis gesehen.
Beim Lernen von C# hast du bereits mit Variablen, Datentypen und Werten gearbeitet. Nun geht es darum, diese beiden Welten zu verbinden: Variablen im Skript sollen Eigenschaften von Komponenten verändern.
Dafür benötigt das Skript Zugriff auf die Komponente, mit der es
interagieren soll. Das geschieht mit der Methode
GetComponent<ComponentName>().
Wiederholen wir unsere Schritte aus dem Inspector — diesmal jedoch per Code.
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Variable zum Speichern einer Referenz auf SpriteRenderer
private SpriteRenderer sr;
void Start()
{
// Referenz auf die SpriteRenderer-Komponente abrufen
sr = GetComponent<SpriteRenderer>();
// Farbe auf Rot setzen
sr.color = Color.red;
}
void Update()
{
// Rotation um die Z-Achse
transform.Rotate(0f, 0f, 90f * Time.deltaTime);
}
}
Jetzt ändert das Objekt beim Start des Spiels seine Farbe und rotiert in jedem Frame. Was du zuvor manuell gemacht hast, übernimmt nun das Skript.
Beachte, dass die Transform-Komponente bei jedem
GameObject standardmäßig vorhanden ist. Deshalb kannst du direkt über
transform darauf zugreifen, ohne
GetComponent aufzurufen.
Außerdem stimmen die Namen vieler Komponenten-Eigenschaften mit der Art überein, wie du sie im Code ansprichst. Anfangs lohnt sich ein Blick in die Dokumentation — oder eine kurze Nachfrage bei einem KI-Assistenten 🙂 — doch mit der Zeit wird das ganz selbstverständlich.
Logikfehler und NullReferenceException gehören zu den häufigsten Problemen, denen du beim Entwickeln eigener Spiele begegnen wirst.
Solange dein Projekt noch klein ist, bist du vielleicht sicher, dass die benötigte Komponente definitiv am Objekt vorhanden ist. Doch je größer das Projekt wird, desto sinnvoller ist es, eine Sicherheitsprüfung einzubauen, um Laufzeitfehler zu vermeiden.
Eine sorgfältigere Version des Codes könnte so aussehen:
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Variable zum Speichern einer Referenz auf SpriteRenderer
private SpriteRenderer sr;
void Start()
{
// Referenz auf die SpriteRenderer-Komponente abrufen
sr = GetComponent<SpriteRenderer>();
// Prüfen, ob die Komponente gefunden wurde
if (sr != null)
{
// Wenn gefunden — Farbe auf Rot setzen
sr.color = Color.red;
}
else
{
// Wenn die Komponente fehlt — Warnung in der Konsole ausgeben
Debug.LogWarning("SpriteRenderer wurde auf diesem Objekt nicht gefunden!");
}
}
void Update()
{
// Rotation um die Z-Achse mit 90 Grad pro Sekunde
transform.Rotate(0f, 0f, 90f * Time.deltaTime);
}
}
Wir prüfen lediglich, ob die Referenz auf die Komponente erfolgreich erhalten wurde. Wenn die Komponente nicht existiert, versucht der Code nicht, darauf zuzugreifen.
Eine NullReferenceException bedeutet nicht, dass dein Spiel „kaputt“ ist — sie zeigt lediglich, dass eine Verbindung fehlt. Das Skript versucht, auf etwas zuzugreifen, das nicht vorhanden ist.
Sollte der Fehler dennoch auftreten, bleib ruhig. Lies die Meldung in der Console — Unity zeigt in der Regel direkt die Zeile an, in der das Problem entstanden ist. Mit einem Doppelklick auf die Fehlermeldung springst du sofort zur entsprechenden Stelle im Code.
Bisher haben wir mit Komponenten gearbeitet, die sich auf demselben Objekt wie das Skript befinden. In einem echten Spiel musst du jedoch häufig andere GameObjects steuern.
Dafür benötigst du zunächst eine Referenz auf das Zielobjekt und
kannst anschließend die bekannte Methode
GetComponent<>() verwenden oder auf dessen
Transform zugreifen.
Gängige Möglichkeiten, eine Referenz auf ein Objekt zu erhalten:
GameObject.FindWithTag() oder
GameObject.Find()
(praktisch, aber langsamer).
Instantiate() sichern.
In diesem Beispiel steuern wir das Objekt Square von einem anderen GameObject aus.
Weise Square den Player-Tag zu. Erstelle anschließend ein leeres Objekt und füge ihm folgendes Skript hinzu:
using UnityEngine;
using UnityEngine.InputSystem;
public class SquareMoving : MonoBehaviour
{
// Öffentliche Referenz auf ein anderes GameObject
public GameObject otherGameObject;
void Start()
{
// Falls die Referenz im Inspector nicht gesetzt wurde
if (otherGameObject == null)
{
// Objekt über den Tag suchen
otherGameObject = GameObject.FindWithTag("Player");
}
// Falls das Objekt gefunden wurde — SpriteRenderer abrufen
if (otherGameObject != null)
{
SpriteRenderer sr = otherGameObject.GetComponent<SpriteRenderer>();
// Wenn ein SpriteRenderer vorhanden ist — Farbe auf Blau setzen
if (sr != null)
{
sr.color = Color.blue;
}
}
}
void Update()
{
// Falls das Objekt weiterhin nicht gefunden wurde — Abbruch
if (otherGameObject == null) return;
float x = 0f;
// Prüfen, ob eine Tastatur vorhanden ist
if (Keyboard.current != null)
{
if (Keyboard.current.aKey.isPressed)
x -= 1f;
if (Keyboard.current.dKey.isPressed)
x += 1f;
}
// Bewegungsrichtung erstellen
Vector3 direction = new Vector3(x, 0f, 0f);
// Anderes Objekt bewegen
otherGameObject.transform.Translate(direction * 5f * Time.deltaTime);
}
}
Du kannst Square entweder manuell in das Feld otherGameObject im Inspector ziehen oder es leer lassen — in diesem Fall findet das Skript das Objekt automatisch über den Tag.
Starte das Spiel — und du wirst sehen, dass sich Square bewegt, obwohl das Bewegungsskript auf einem anderen Objekt liegt.
Beim Start des Spiels kann das Quadrat blau werden. Nun verändern jedoch zwei Skripte dieselbe Eigenschaft — die Farbe des Objekts. Ob es am Ende rot oder blau ist, hängt von der Ausführungsreihenfolge der Skripte ab.
Genau so entstehen logische Fehler: Der Code läuft, es gibt keine Fehlermeldungen in der Console, aber das Ergebnis entspricht nicht den Erwartungen. Dieses Beispiel lassen wir bewusst so stehen.
Ein Skript muss nicht nur „sein eigenes“ Objekt steuern. Entscheidend
ist die Referenz. Jetzt verstehst du, wie
GetComponent<>() dir ermöglicht, mit Komponenten
eines anderen Objekts zu arbeiten.
Zuerst haben wir Parameter manuell über den Inspector verändert. Danach haben wir dieselben Schritte mit einem Skript wiederholt. Und schließlich haben wir gelernt, sogar ein anderes Objekt zu steuern.
Farbe, Bewegung, Rotation — all das sind lediglich Eigenschaften von Komponenten, auf die du über eine Referenz zugreifen kannst.
In Unity basiert alles auf Verbindungen: Referenz erhalten → Komponente abrufen → Zustand verändern.