Dies ist der zweite Teil unseres Unity-Plattformer-Tutorials. Im ersten Teil hast du gelernt, dich zu bewegen. In diesem hier baust du eine Welt, in der es sich zu bewegen lohnt.
Wir fügen keine Gegner mit Verhaltensbäumen hinzu. Wir bauen keine Menüs oder Übergänge. Was wir tun: Wir gestalten einen Raum mit Münzen, Wänden, Abgründen, einem leicht mürrischen Pilz… und etwas, das dich still begleitet – bis ganz zum Schluss.
Du wirst Dinge platzieren. Eine Wand reparieren, an der Ratten kleben bleiben. Auf einer Plattform fahren. Und wenn du schließlich den Ausgang erreichst – wirst du wissen, dass das nicht der wahre Grund war, warum dieses Level entstanden ist.
Also öffne Unity. Atme durch. Und beginne – nicht, weil du musst, sondern weil **hier etwas still darauf wartet**, platziert zu werden.
Wir beginnen mit dem Hinzufügen einer vertikalen Wand zur Szene.
Verwende den gleichen largeGround
-Sprite wie für den
Boden – dreh ihn einfach und stelle ihn senkrecht auf. Unity erstellt
automatisch ein neues GameObject. Benenne es in
Wall um.
Im Inspector der neuen Wand:
0
.Drücke jetzt Play und probiere Folgendes: Lauf auf die Wand zu, springe daran hoch und halte die Bewegungstaste gedrückt. Die Ratte klammert sich an die Wand wie ein winziger Superheld. Das ist keine Magie – das ist Reibung.
Das passiert, weil Unitys Standard-Collider Reibung eingebaut haben. Um das zu beheben, weisen wir ein Physics Material 2D mit null Reibung zu.
Im Assets-Ordner:
noFriction
.0
.
Wähle jetzt das Rat-GameObject aus. Im
Capsule Collider 2D-Component setze das
Material auf noFriction
.
Spiele die Szene erneut – die Ratte sollte nicht mehr an Wänden oder Plattformen hängenbleiben.
noFriction
-Material der
Ratte zugewiesen.
Geben wir unserer Ratte etwas zum Einsammeln – Münzen!
Importiere das coin
-Bild in dein Projekt:
Bild hier herunterladen
(wir verwenden die Münze aus diesem Sprite-Sheet). Ziehe sie dann in
die Szene – Unity erstellt ein neues GameObject.
Wähle die Münze in der Hierarchy aus, dann im Inspector:
0
.-10
(damit
sie hinter der Ratte erscheint).
Erstelle ein neues Tag mit dem Namen Coin
und weise es
dem Münzobjekt zu.
Benenne die Münze in CoinPrefab um und ziehe sie dann in den Assets-Ordner, um ein Prefab zu erstellen.
Jetzt erstellen wir ein Skript zum Einsammeln der Münzen. Im Assets-Ordner:
CoinController
.
Füge diesen Code in das Skript ein:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinController : MonoBehaviour {
// Sound, der beim Einsammeln einer Münze abgespielt wird
public AudioClip CoinSound;
// Münzzähler
public int coin;
void OnTriggerEnter2D (Collider2D other) {
// Wenn das Objekt, das wir treffen, das Tag "Coin" hat
if (other.tag == "Coin") {
// Erhöhe den Münzzähler
coin = coin + 1;
// Spiele den Münz-Sound ab
AudioSource.PlayClipAtPoint(CoinSound, transform.position);
// Zerstöre die eingesammelte Münze
Destroy(other.gameObject);
}
}
}
Füge das CoinController
-Skript dem
Rat-GameObject hinzu. Du kannst den Münzsound hier
herunterladen:
CoinSound.wav herunterladen. Importiere den CoinSound
-Clip ins Projekt und weise
ihn im Inspector dem Feld Coin Sound
im Skript zu.
Tipp: Da wir PlayClipAtPoint
verwenden, wird der Sound im
Welt-Raum abgespielt. Wenn er leise klingt, liegt das daran, dass der
Audio Listener auf der Kamera ist – das beheben wir später.
Zeigen wir an, wie viele Münzen die Ratte gesammelt hat – mit einem UI-Textfeld.
Gehe in der Hierarchy zu:
Canvas
,
CoinCounterText
und EventSystem
.
Wähle das Canvas-GameObject aus. Im Inspector:
Scale With Screen Size
.
Expand
.
Doppelklicke auf das Canvas in der Hierarchy, um die UI-Ansicht einzurahmen. Du siehst ein weißes Rechteck, das den sichtbaren Bereich des Bildschirms darstellt.
Wähle CoinCounterText und nimm folgende Änderungen vor:
CoinCounterText
um.0
.
Jetzt aktualisieren wir das CoinController
-Skript, um
diesen Text zu verbinden.
using UnityEngine;
using TMPro;
public class CoinController : MonoBehaviour
{
// Sound beim Einsammeln von Münzen
public AudioClip CoinSound;
// Münzzähler
public int coin;
// Verknüpfung zum TextMeshPro-Zähler
public TMP_Text CoinCounterText;
void OnTriggerEnter2D(Collider2D other)
{
// Prüfen, ob das Objekt das Tag "Coin" hat
if (other.tag == "Coin")
{
coin = coin + 1;
// Abspielgeräusch
AudioSource.PlayClipAtPoint(CoinSound, transform.position);
// Text aktualisieren
CoinCounterText.text = coin.ToString();
// Münze aus Szene entfernen
Destroy(other.gameObject);
}
}
}
Wähle die Rat in der Hierarchy. Im
CoinController-Komponentenfeld ziehe das
CoinCounterText
-GameObject in das Feld
Coin Counter Text.
Drücke nun Play – beim Einsammeln der Münzen wird die UI in Echtzeit aktualisiert.
Lass uns eine kleine Herausforderung hinzufügen: ein Loch, in das der Spieler fallen kann.
Verschiebe eine der Plattformen, um eine Lücke im Boden zu schaffen – das wird zur Fallzone.
Klicke mit der rechten Maustaste in der Hierarchy und
wähle: Create Empty. Benenne das neue Objekt in
DeadEnd
um.
Im Inspector, führe Folgendes aus:
0
.
Erstelle jetzt ein neues C#-Skript mit dem Namen
DeadEndController
. Füge folgenden Code ein:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class DeadEndController : MonoBehaviour {
void OnTriggerEnter2D(Collider2D other) {
if (other.tag == "Player") {
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
Wähle das DeadEnd-Objekt und füge das
DeadEndController
-Skript hinzu. Erstelle dann ein Prefab,
indem du DeadEnd
aus der Hierarchy in
den Assets-Ordner ziehst.
Tipp: Wenn die Ratte in den Trigger-Bereich fällt, wird die Szene neu geladen – das ist eine einfache Möglichkeit, das Level ohne zusätzlichen Aufwand zurückzusetzen.
Zeit für ein bewegliches Hindernis: ein Pilz-Gegner, der hin und her läuft. Wenn die Ratte auf seinen Kopf springt – verschwindet er. Wenn sie ihn seitlich berührt – wird das Level zurückgesetzt.
Platziere ein Ground
-Prefab und ein
DeadEnd
darunter. Ziehe dann den Pilz-Sprite aus den
Assets in die Szene, auf die letzte Plattform. Benenne das neue Objekt
in Enemy um.
Im Inspector für Enemy:
0
.Interpolate
.
Erstelle nun ein neues Skript mit dem Namen
MushroomController
. Füge diesen Code ein:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MushroomController : MonoBehaviour
{
// Wie weit sich der Pilz von seinem Startpunkt entfernt (in Einheiten)
public float distanceToRun = 2f;
// Bewegungsgeschwindigkeit
public float speed = 2f;
// Blickt der Pilz gerade nach links?
public bool isLookingLeft = true;
// Startposition
Vector3 startPos;
// Referenz zu Rigidbody2D
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
startPos = transform.position;
// Distanzprüfung optimieren durch quadrierte Werte
distanceToRun = distanceToRun * distanceToRun;
}
void FixedUpdate()
{
// Distanz vom Startpunkt berechnen
Vector2 dist = transform.position - startPos;
if (isLookingLeft)
{
// Wenn zu weit nach links gelaufen, umdrehen
if (transform.position.x < startPos.x && dist.sqrMagnitude > distanceToRun)
{
TurnTheMushroom();
rb.linearVelocity = new Vector2(speed, rb.linearVelocity.y);
}
else
{
rb.linearVelocity = new Vector2(-speed, rb.linearVelocity.y);
}
}
else
{
// Wenn zu weit nach rechts gelaufen, umdrehen
if (transform.position.x > startPos.x && dist.sqrMagnitude > distanceToRun)
{
TurnTheMushroom();
rb.linearVelocity = new Vector2(-speed, rb.linearVelocity.y);
}
else
{
rb.linearVelocity = new Vector2(speed, rb.linearVelocity.y);
}
}
}
// Dreht die Blickrichtung des Pilzes (visuell und logisch)
void TurnTheMushroom()
{
isLookingLeft = !isLookingLeft;
transform.localScale = new Vector3(
transform.localScale.x * -1,
transform.localScale.y,
transform.localScale.z
);
}
// Wenn die Ratte mit dem Pilz kollidiert (nicht von oben)
void OnCollisionEnter2D(Collision2D other)
{
if (other.collider.gameObject.tag == "Player")
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
// Wenn die Ratte den Trigger oben berührt – Pilz zerstören
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
Destroy(gameObject);
}
}
}
Füge das MushroomController
-Skript dem
Enemy-Objekt hinzu. Passe Speed und
Distance To Run im Inspector an (z. B. beides auf
2
).
Ziehe abschließend Enemy in den Assets-Ordner, um ein wiederverwendbares Prefab zu erstellen.
Fügen wir eine vertikal bewegliche Plattform hinzu, auf der die Ratte fahren kann.
Ziehe das largeGround
-Sprite in die Szene und benenne das
neue Objekt in Platform um.
Im Inspector für Platform:
0
.Ground
.
Kinematic
.
Interpolate
.
Jetzt geben wir der Plattform ein Skript, das sie zwischen zwei Punkten auf- und abbewegt.
Erstelle ein neues Skript mit dem Namen
PlatformMoving
und füge diesen Code ein:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformMoving : MonoBehaviour
{
// Geschwindigkeit der Plattformbewegung
public float speed;
// Oberer Punkt, zu dem sich die Plattform bewegt
public Transform endPoint;
// Startpunkt (anfängliche Position)
Vector3 startPoint;
// Referenz zu Rigidbody2D
Rigidbody2D rb;
// Aktueller Geschwindigkeitswert
float currentSpeed;
void Start()
{
// Anfangsposition speichern
startPoint = transform.position;
// Referenz zu Rigidbody2D holen
rb = GetComponent<Rigidbody2D>();
// Anfangsrichtung setzen
currentSpeed = speed;
}
void FixedUpdate()
{
// Wenn Plattform den Endpunkt erreicht hat, Richtung umkehren
if (transform.position.y > endPoint.position.y)
currentSpeed = -speed;
// Wenn Plattform zum Start zurückkehrt, Richtung erneut umkehren
if (transform.position.y < startPoint.y)
currentSpeed = speed;
// Geschwindigkeit auf Rigidbody anwenden
rb.linearVelocity = new Vector3(0, currentSpeed, 0);
}
}
Erstelle in der Hierarchy ein leeres GameObject und nenne es endPoint. (Optional kannst du ein Icon zuweisen, um es sichtbar zu machen.) Platziere es über der Plattform mit dem Move-Tool.
Wähle erneut Platform aus. Füge das
PlatformMoving
-Skript hinzu. Setze
Speed auf 1
und weise das Objekt
endPoint dem entsprechenden Feld zu.
Starte die Szene und beobachte, wie sich die Plattform vertikal zwischen zwei Positionen bewegt.
Füge rechts von der Plattform eine Wand hinzu und gib ihr einen Box Collider 2D. Platziere dann ein DeadEnd-Prefab unter der beweglichen Plattform und passe dessen Größe an.
Fügen wir eine Kiste hinzu, die mit der Ratte und anderen Objekten durch echte Physik interagiert.
Ziehe das imageGround
-Sprite in die Szene und benenne das
neue Objekt in Box um.
Im Inspector für Box:
Ground
.
0
.Scale X = 2
, Y = 1.5
).
Interpolate
.
Du kannst die Kiste jetzt durch die Szene schieben — probiere aus, sie auf der beweglichen Plattform zu benutzen oder als Stufe zu verwenden!
Fügen wir dem Level einen Ausgang hinzu. Er lädt noch keine neue Szene, aber er fungiert als Ziel-Trigger – wenn du ihn erreichst.
Füge zuerst ein weiteres Ground-Prefab an der Stelle
hinzu, wo die bewegliche Plattform endet – also nahe dem oberen Ende
ihres Pfades. Platziere dann am Ende des Levels eine Wand mit dem
wall
-Sprite und füge ihr einen
Box Collider 2D hinzu.
Importiere das Exit
-Sprite (oder verwende das rote Schild
aus dem vorhandenen Asset-Sheet) und ziehe es in die Szene nahe der
oberen Wand. Es sollte so hoch platziert sein, dass die Ratte
es nicht einfach durch Springen erreichen kann – du wirst die
Box brauchen.
Im Inspector für das Exit-Objekt:
0
.DeadEndController
hinzu (wir nutzen es
zum Neuladen der Szene).
Jetzt wird beim Berühren des Ausgangs durch die Ratte die Szene neu geladen – aber nur, wenn du es geschafft hast, hinaufzuklettern.
Um ein Gefühl von Tiefe zu erzeugen, lassen wir den Hintergrund leicht mitbewegen, wenn sich der Spieler bewegt. Das nennt man Parallax-Effekt — er simuliert Tiefe, indem weiter entfernte Ebenen langsamer scrollen.
Du kannst das Hintergrundbild hier herunterladen: BackGround.png herunterladen
Importiere das Bild in dein Projekt und ziehe es in die Szene. Im Inspector:
X: 7
,
Y: 5
(je nach Bedarf anpassen).
-100
, damit
es hinter allem anderen dargestellt wird.
Erstelle ein neues C#-Skript mit dem Namen Parallax
. Füge
diesen Code ein:
using UnityEngine;
public class Parallax : MonoBehaviour
{
// Referenz zum Spieler
private GameObject player;
// Position aus dem vorherigen Frame
private Vector3 lastPosition;
// Parallax-Scrollgeschwindigkeit
public float speed = 60f;
void Start()
{
// Spieler über Tag finden
player = GameObject.FindWithTag("Player");
if (player != null)
{
lastPosition = player.transform.position;
}
}
void Update()
{
if (player == null) return;
// Versatz berechnen und anwenden
Vector3 offset = player.transform.position - lastPosition;
transform.Translate(offset * speed * Time.deltaTime);
// Aktuelle Position für nächsten Frame speichern
lastPosition = player.transform.position;
}
}
Hänge das Skript an das Hintergrundobjekt. Setze den
Speed-Wert — 60
ist ein guter
Startpunkt, aber du kannst gern experimentieren.
Das ist der Schritt, auf den alles leise hingeführt hat. Nicht die Münzen, nicht der Pilz, nicht einmal der Ausgang. Das hier — ist der Grund.
In den Assets gibt es eine lächelnde Wolke. Sie greift nicht an, bringt keine Punkte, bewegt sich nicht einmal von selbst. Und doch — ist sie das Einzige, das bei dir bleibt. Immer.
Ziehe das Wolken-Sprite in die Szene und benenne es
Cloud. Setze seine Z-Position auf
0
und Order in Layer auf
-50
, damit es hinter der Ratte bleibt.
Platziere sie irgendwo über und hinter der Ratte — z. B. bei
(x - 2, y + 3)
. Präzision ist nicht nötig. Nur Präsenz.
Erstelle ein neues C#-Skript mit dem Namen
CloudFollower
und hänge es an die Wolke. Verwende diesen
Code:
using UnityEngine;
public class CloudFollower : MonoBehaviour
{
public Vector3 offset = new Vector3(-2f, 3f, 0f);
public float followSpeed = 1.5f;
private Transform target;
void Start()
{
// Spieler per Tag finden
GameObject playerObj = GameObject.FindWithTag("Player");
if (playerObj != null)
{
target = playerObj.transform;
}
}
void Update()
{
if (target == null) return;
// Sanft dem Spieler mit Offset folgen
Vector3 desiredPos = target.position + offset;
transform.position = Vector3.Lerp(transform.position, desiredPos, followSpeed * Time.deltaTime);
}
}
Die Wolke folgt der Ratte jetzt — nicht perfekt, nicht exakt, sondern mit sanfter Verzögerung. Sie hilft nicht. Sie spricht nicht. Sie bleibt einfach.
Wenn du langsamer wirst oder fällst — sie wartet. Wenn du neu startest — sie ist wieder da. Du bist in diesem Level nie allein. Nicht wirklich.