Unity 2D Platformer - Level Generator

Amazon

Seit einiger Zeit experimentiere ich hier und da mit der 2D- und 3D Engine Unity. Insbesondere die Programmierung eines 2D-Platformers hat es mir aktuell angetan.

Allerdings ist das programmieren eines 2D-Games (sofern man Programmierer ist) nicht das größte Problem, sondern meiner Meinung nach die Optik, da ich kein Grafiker bin. :D

Amazon So kommt man nicht drum herum, sich einige Tools zu bauen, wie z.B. einen Texture-Generator in Python Python oder wie in diesem Artikel beschrieben einen kleinen Level Generator in CSharp.

Wer seine Prefabs in Unity bereits angelegt hat und nun die leere Scene vor sich hat weiß wie mühsam es sein kann ein Level zu designen und in der Szene alles korrekt zu platzieren. Ebenen, Böden, Wände, Respawn-Point für den Spieler, Gegner, Items die man einsammeln kann um z.B. Punkte zu bekommen, und vieles vieles mehr.

Wie kann man also das erstellen eines Levels beschleunigen und alle Game-Objekte korrekt platzieren?!

Die Idee ist simpel und leicht in extrem kurzer Zeit umgesetzt. Man erstellt für jedes Level ein Bild bzw. eine zunächst transparente PNG-Datei in Photoshop oder GIMP und färbt die Pixel ein, an denen ein GameObject erstellt werden soll.

Zunächst legt man also die Farbwerte für seine Prefabs fest. Beispiel: Der Pixel für den Startpunkt des Spielers wird grün (RGBA: #00ff00ff). Böden werden blau (RGBA: #0000ffff) markiert, Gegner rot (RGBA: #ff0000ff) und Items zum Einsammeln Gelb (RGBA: #ffff00ff).

Man kann die Liste noch beliebig erweitern, je nachdem wieviele unterschiedliche Objekte ihr habt. Um nicht zuviele Farben nutzen zu müssen kann man z.B. Items sehr gut gruppieren indem man wie gehabt alle Items gelb setzt, beim laden später aber immer ein anderes per Zufall an die Position in das Level lädt. Nur ein Ansatz von vielen...

Ein solches Level PNG-File (GIMP-Screenshot - Breite 32px, Höhe 8px) sieht dann in etwa so aus:

2d platformer level generator

Man speichert also die Grafik im PNG-Format und zieht diese in seinem 2d Platformer Projekt in Unity in das Assets-Verzeichnis.

unity 2d platformer - Level GeneratorDie Level-Grafiken sollten später alle unter "Assets/Resources/Level/Level1.png" liegen.

(Level1.png, Level2.png, usw...)

WICHTIG: Damit das Ganze auch funktioniert wie es soll muss man zunächst die Texture-Einstellungen für jedes Level-PNG setzen.

+ Filter Mode: Point (no filter)
+ Compression: None!
+ Read/Write Enabled: true

Danach unten rechts auf Apply klicken. Soweit so gut.

Programmieren - Unity 2D Platformer - Level Generator

Jetzt müssen wir nurnoch etwas Code schreiben damit unser Spiel auch weiß was es mit diesen tollen Bildchen tun soll. Zunächst erstellt man also ein neues C#-Script und nennt die Klasse LevelGenerator. In der LevelGenerator.cs fügt man zunächst eine Struktur ein, welche die notwendigen Informationen (Prefab und Farbe) zu jedem Objekt speichern kann und erstellt eine ArrayList in die man die einzelnen Objekte legen kann.

private struct pixObject{
 public Color pixColor;
 public GameObject prefab;
}
private ArrayList pixToObject = new ArrayList();

Als nächstes benötigen wir eine Funktion um die Prefabs und Farbwerte in die ArrayList zu schreiben, damit wir daraus später per Instantiate die Clones der Objekte erstellen können. Ich habe die Funktion mal initPrefabs genannt:

public void initPrefabs(){
if (pixToObject.Count > 0) //skip if already initialized
return;

pixToObject = new ArrayList();

pixObject o = new pixObject();
o.pixColor = Color.blue; //ground blue
o.prefab = Resources.Load<GameObject>("Prefabs/GROUND_1x1"); //Fullpath: Assets/Resources/Prefabs/GROUND_1x1.prefab
pixToObject.Add(o);

o = new pixObject();
o.pixColor = Color.green; //Player green
o.prefab = Resources.Load<GameObject>("Prefabs/Player");
pixToObject.Add(o);

o = new pixObject();
o.pixColor = Color.red; //Enemy red
o.prefab = Resources.Load<GameObject>("Prefabs/Enemy");
pixToObject.Add(o);

o = new pixObject();
o.pixColor = new Color(1, 1, 0, 1); //Item/Collect yellow
o.prefab = Resources.Load<GameObject>("Prefabs/Collect");
pixToObject.Add(o);
}

In dieser Funktion befüllt man also seine ArrayList. Der Code sollte soweit selbsterklärend sein. Man erstellt ein pixObject, stellt die gewünschte Farbe ein, gibt ihm den passenden Prefab mit und schreibt das Objekt in die ArrayList.

Im nächsten Schritt erstellt man eine Funktion addObject welche einen gewünschten Pixel (xy Koordinate) aus der Level-PNG einließt, und je nachdem an die jeweilige Stelle im Spiel das passende Objekt setzt.

private void addObject(int x, int y, ref Texture2D tex){
Color pixColor = tex.GetPixel(x, y);
if (pixColor.a == 0) //ignore transparent pixels
   return;

 foreach (pixObject o in pixToObject){
   if (o.pixColor.Equals(pixColor)){
      Vector2 pos = new Vector2(x, y);
      Instantiate(o.prefab, pos, Quaternion.identity);
    }
 }
}

Jetzt habe ich eine Funktion namens loadLevel geschrieben, welche als Parameter die Nummer des gewünschten Levels entgegen nimmt. An der Stelle wird der Pfad zu der gewünschten Level-PNG gebildet. Der Inhalt der Datei wird in ein Byte-Array gelesen und im Anschluss per LoadImage in ein Texture2D Objekt geladen, welches wie oben gesehen das auslesen der Pixel sehr einfach macht.

Nachdem das Texture2D-Objekt mapTex nun startklar ist werden die einzelnen Pixel im Bild per For-Schleife durchlaufen und addObject (siehe oben) aufgerufen, um den jeweiligen Pixel auszuwerten.

UPDATE: Auf Mobilgeräten mit Android oder auch iOS schlägt das Laden per ReadAllBytes fehl, da der Relative Pfad nicht korrekt war. Lädt man das Level PNG-Bild direkt über die Unity-Engine per Resources.Load as Texture2D funktioniert alles wie gewünscht und das Plattformunabhängig. Der Code wurde entsprechend angepasst.

public bool loadLevel(int levelNum){
string sFile = "Level/Level" + levelNum.ToString();
Texture2D mapTex = Resources.Load(sFile) as Texture2D;

for (int x = 0; x < mapTex.width; x++){
for (int y = 0; y < mapTex.height; y++){
addObject(x, y, ref mapTex);
}}
return true;
}

Das war es dann auch schon fast. Nicht vergessen in die void Start() noch die beiden Funktionen zum laden aufzurufen.

private void Start(){
initPrefabs();
loadLevel(GameControl.control.currentLevel);
}

Den LevelGenerator kann man dann als Komponente in ein leeres GameObject in seine Scene einfügen. Fertig! Das Ergebnis sieht bei mir dann so aus:

Unity Level Generator - 2D Platformer

Amazon Den Code habe ich hier zum Download hinterlegt: LevelGenerator.cs

Ich hoffe die herangehensweise, der Code und die Erklärungen sind einigermaßen verständlich. :-) Fragen, Lob, Kritik und sonstiges Feedback sind willkommen >> zum Kontaktformular.

Zuletzt bearbeitet: 21. März, 2019
Tags: , , , , , , , , ,

Weitere interessante Beiträge

Kleine Helfer für den erfolgreichen Börsenhandel

BÖRSE ONLINE ist das etablierteste unabhängige Anlegermagazin in Deutschland. Seit mehr als 25 Jahren hilft es Anlegern Woche für Woche bei ihren Anlageentscheidungen. BÖRSE ONLINE richtet sich gleichermaßen an institutionelle Leser in Banken, Versicherungen, Vermögensverwaltungen und Kapitalanlagegesellschaften sowie an selbstentscheidende, kapitalmarktaffine Privatanleger. • Informiert schwerpunktmäßig über deutsche und internationale Aktien. • Fokussiert die Berichterstattung auf [...] Weiterlesen »


DynDNS-Alternative im Eigenbau

Wer ohnehin über einen eigenen Webserver/Webspace im Internet und etwas Programmierkenntnisse verfügt, kann sich ohne großen Aufwand einen Service wie DynDNS.org & Co. sparen, will man auf Teile seines Heimnetzwerkes von Unterwegs aus zugreifen. Solche Services stellen einen Hostnamen/URL bereit (z.B. mein-heimnetzwerk.dyndns.org) welche dann auf die IP-Adresse des Heimnetzwerkes weitergeleitet wird. Neben einem oft kostenpflichtigem [...] Weiterlesen »


ESP8266-WiFi-Modul flashen

Da ich mich seit kurzem wieder intensiver mit den Themen Smart Home und "Home automation" beschäftige habe ich beschlossen einige meiner Baustellen, Stolpersteine und Lösungen hier zu veröffentlichen. Wie im Beitrag zu meinem Smart-Home-Project V1 bereits erwähnt setze ich auf das WLAN-Modul ESP8266-ESP01 , da dieses unter anderem klein, zuverlässig und günstig ist. In diesem [...] Weiterlesen »


Safari Abenteuer: Wir entdecken Afrika - Brettspiel für Kinder

Safari Abenteuer: Wir entdecken Afrika - Brettspiel für Kinder, Schüler, Familie - von Kindermatica Ltd. Bring Familie und Freunde mit dem Safari-Rätselspiel zusammen! In diesem kinderfreundlichen Brettspiel für mehrere Spieler wirst du die Bewohner der heißen Savanne kennen lernen und erstaunliche Abenteuer in Afrika erleben! Du könntest einen Löwen treffen, dich Regenschauern schützen, eine Brücke [...] Weiterlesen »


Fitnessboard v1 - DIYS

Am Wochenende hatte ich glücklicherweise mal wieder etwas Zeit zum Basteln und dazu noch eine ganz nette Idee für ein Fitness-Board für das Training zuhause, welches aus der Liegestütz-Position bedient wird. Ein Video dazu gibt es weiter unten in diesem Artikel und im Youtube-Channel von tedokai.de. Jeder der Zuhause dann und wann Sport betreibt kennt [...] Weiterlesen »