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

AmazonSo 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.

Inhaltsverzeichnis:

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 - Pfadangaben auf Mobilgeräten (iOS, Android)

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

AmazonDen 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: 23. Mai, 2019
Tags: , , , , , , , , ,

Weitere interessante Beiträge

App des Tages: Pokemon GO

Offensichtlich kann niemand dem Pokemon GO hype entfliehen, daher wird das heute zur App des Tages ausgerufen. :-) Viel Spaß beim Suchen und Fangen! Die Pokémon sind da draußen und du musst sie finden. Läufst du in deiner Nachbarschaft herum, vibriert dein Smartphone, wenn sich ein Pokémon in der Nähe befindet. Ziele und wirf einen [...] Weiterlesen »


Projekt: pi_robot - Raspberry Pi, iPad und etwas Code

Der Raspberry Pi ist nach wie vor einer der beliebtesten Mini-Computer, nicht nur unter Bastlern. Kostengünstig (je nach Modell um die 30 bis 40,- EUR), flexibel im Anwendungsbereich und spätestens seit dem Raspberry Pi 3 auch mit ausreichend CPU-Leistung und Arbeitsspeicher ausgestattet, um auch etwas komplexere Aufgaben zu lösen. Mit dem 1,2 GHz quad-core ARM [...] Weiterlesen »


DIYS Smart-Home V1 - Irgendwo muss man ja anfangen!

Schon seit einiger Zeit plane ich mein Smart-Home. Neben dem Kostenfaktor für all die tollen Dinge die ich mir so vorstelle fehlt es oft an der notwendigen Zeit zur detailierten Planung und Umsetzung. Daher habe ich beschlossen einfach mal irgendwo anzufangen und dieses DIY-Projekt parallel niederzuschreiben...mal sehen wohin das führt...Ich bitte um Nachsicht wenn ich [...] Weiterlesen »


iTunes-Connect - Sales-Reports

Wer Apple und iTunes-Connect (ITC) kennt, weiß von den Problemchen welche ITC teilweise mit sich bringt. Mich persönlich hat immer gestört, das die Sales-Reports / Verkaufszahlen des Vortages manchmal erst am späten Abend des Folgetages verfügbar waren. Das liegt wohl daran, das die Apple-Server die Reports für jeden Entwickler aufbereiten müssen und es hier öfter [...] Weiterlesen »


Telegram Messenger und die Bot API

Telegram ist im Prinzip ein Messenger ähnlich Whatsapp oder Threema , kostenlos und leicht zu bedienen. Ich möchte nicht sagen das dieser besser oder schlechter ist als andere Messenger, allerdings gefällt mir die Telegram Bot API sogut, dass ich an dieser Stelle ein kleines Tutorial dazu schreiben möchte. Inhaltsverzeichnis:Telegram Messenger Bot API programmierenSchritt 1 - [...] Weiterlesen »