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

kraken.jpgSo 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

amz music.pngDen 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: , , , , , , , , ,

Dies könnte dich auch interessieren

App-Suchmaschine für iOS und Android

In den letzten Wochen habe ich mich viel mit dem Lesen, Auswerten und Visualisieren von Daten aus den verschiedenen Appstores auseinander gesetzt. Dabei entstanden einige nette kleine Tools die zum Teil hier veröffentlicht wurden, wie z.B. der Apfel-Appstore-Reader und der ITC-Umsatz-Rechner. Irgendwann bin ich dann über diverse App-Suchmaschinen wie AppTicker oder AppGefahren gestolpert. Schön finde [...] Weiterlesen »


Experiment: Hacking Ethereum Wallets - Bruteforce

Vor einiger Zeit bin ich durch Zufall auf ein Youtube-Video gestoßen mit dem Titel: "Eth Wallet Bruteforce Hack". Ein Programmierer zeigte da ein einfaches Python Script welches einen zufälligen Private-Key (64 Zeichen / 32 Byte / 256 Bit) generieren konnte. Daraus wurde Public-Key und Adresse errechnet und an einen Blockchain-Explorer-Webseite wie Etherscan.io übergeben ob diese [...] Weiterlesen »


3 Gründe warum es App-Entwickler nicht einfach haben

Aus gegebenem Anlass heute mal ein kleiner Beitrag aus der Sektion "Nicht ganz so ernst gemeinte Beiträge". Hier nun einige Überlegungen warum es App-Entwickler heute schwieriger haben, als noch vor einigen Jahren. Eines vorweg: Wer in diesem Beitrag einen Hauch von Ironie und Sarkasmus findet, darf ihn behalten! ;-) Inhaltsverzeichnis:Problem 1: Die Masse an Apps [...] Weiterlesen »


iOS-Android-Converter

Lange Zeit habe ich bei neuen App-Projekten zunächst die Sprachdateien / Language-Files für iOS geschrieben, und diese dann für die Android-Version händisch umgeschrieben. Das war vom Aufwand her immer schnell gemacht solange die Anzahl der implementierten Sprachen und der Umfang der Textbausteine niedrig war. Abgesehen vom Zeitaufwand ist das ganz schön langweilige und stumpfe Arbeit, [...] 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 »