Jump to content
Unity Insider Forum

2D-Welt mit einer tile-Map erstellt, Einstieg


Terep

Recommended Posts

Hallo

In meinem ersten Posting „Einfache Funktions-Landschaft mit XY-Koordinaten generieren“ war ich von unity noch zu weit weg. Jetzt habe ich mich mit tile-mapping beschäftigt und versucht, mir dies zu erarbeiten. Am besten klappt es, wenn ich es als Tutorial formuliere. Dann bleibe ich besser am Thema. Dies hat auch alles gut geklappt.

 

Was mich interessieren würde ist die Frage, ob ich die Sachen richtig beschreibe oder ob ich wegtriffte. Es folgt meine Ausarbeitung:

 

 

Text-Tutorial

Eine 2D-Welt mit tiles also mit einer Tilemap erstellen

Teil 1 Die Map darstellen

 

Wer mit der Überschrift nichts anfangen kann, bitte nach dem Stichwort Bilder zu einer Tilemap surfen.

Der erste Gedanke ist es, hier Gameobjekte vom Typ Plane als Basis zu verwenden. Diesen kann man mit einer Textur versehen und mit Scale soweit verkleinern, dass es möglich ist, so eine Landschaft zu generieren. Nur sind diese tiles als klein gedacht. Ein Landschaftsausschnitt mit der Fläche 100 * 100 hat schon 10.000 Gameobjekte vom Typ Plane. Jede Plane hat per Definition eine Größe von 10 * 10 Quards. Ein Quard besteht aus 2 Tris (Dreiecken). Das heißt bei 10.000 Gameobjekten * 200 Tries mal 60 Framerate pro Sekunde. Eine gewaltige Zahl. Folglich ist die Performance schon miserabel, obwohl noch nichts passiert ist. Dieser Lösungsweg wird zur Sackgasse.

Hier gibt es natürlich Alternativen. Zum Beispiel werden wir jetzt ein eigenes Mesh generieren. Einfach gesagt, besteht ein Mesh im Wesentlichen aus Dreiecken. Für unsere Landkarte brauchen wir also nur 2 Dreiecke zu einem Rechteck zusammenfügen, unsere vorbereitete Karte als Textur verwenden und fertigt ist es. Ist dies vorbereitet, merkt man nicht, was für ein Aufwand dahinter steht. Man klickt halt zusammen und ist vielleicht irritiert, dass so viele Spiele ganz ähnlich daherkommen. Individuelle Spiele verlangen entsprechenden persönlichen Einsatz. Das sollte Grund genug sein, das Folgende zu Lesen.

Vorweg, inspiriert hat mich die englisch gesprochene Unity 3D Tutorial Serie von quill18creates TileMaps #2 und das Buch „Spiele entwickeln mit Unity“ von Carsten Seifert.

 

Wir beginnen in unity in einer 3D-Umgebung in dem wir ein leeres Gameobject (create empty) erstellen und es in „TileMap“ umbenennen. Danach ziehen wir es aus der Historie in die Assets und erhalten so ein Prefab

Was gehört jetzt eigentlich inhaltlich in das noch leere Gameobjekt? Im ersten Gedankengang hatten wir das Gameobjekt Plane verwendet. Nur aus Performancegründen haben wir dies nicht weiterverfolgt. Die Plane hat all dass, was wir brauchen: Mesh -Filter, -Collider und -Renderer.

 

Wir werden jetzt sehen, wie funktional Scripte sind.

Jetzt erzeugen wir ein c-sharp Script mit der Bezeichnung „TileMap“ und fügen es als Komponente dem Gameobjekt TileMap zu.

Wir rufen das Script auf.

Um Komponenten per Script hinzuzufügen, müssen wir unter den using-Anweisungen die benötigten Komponenten Mesh -Filter, -Collider und -Renderer nur anfordern.

Wir löschen die void-Update-Methode und formulieren eine bildeMesh Methode. Da diese Methode nur einmal aufgerufen werden darf, kommt der Methodenaufruf in Start() auf. Das Script sieht jetzt wie folgt aus:

using UnityEngine;

using System.Collections;

 

[RequireComponent(typeof(MeshFilter))]

[RequireComponent(typeof(MeshRenderer))]

[RequireComponent(typeof(MeshCollider))]

 

public class TileMap : MonoBehaviour {

 

void Start ()

{

bildeMesh ();

}

 

 

void bildeMesh ()

{

 

}

}

 

Testweise starten wir und prüfen so, ob Fehler vorliegen. Sehen tuen wir im Game-Schirm noch nichts. Was auffällt, die required-Komponenten sind jetzt im Gameobjekt vorhanden. Wenn nicht, einmal das Script entfernen und erneut als Komponente hinzufügen.

 

Es geht im Script innerhalb von bildeMesh weiter

Wir wollen ein Mesh Objekt erzeugen auf dem wir auf einem Rechteck die Landkarte darstellen. Das Rechteck wird durch zwei Dreiecke gebildet. Die Eckpunkte der Dreiecke sind die vertices. Da wir in 3D arbeiten, werden die vertices mit Vector3 Variablen angegeben.

Wichtig ist die Reihenfolge bei der Nennung der Eckpunkte. Diese müssen im Uhrzeigersinn erfolgen.

 

Konkret: das Rechteck: oben links = 0, oben rechts = 1, unten links = 2, unten rechts = 3

Das erste Dreieck: Oben = 0, unten rechts = 3, unten links = 2

Das zweite Dreieck: Oben links = 0, oben rechts = 1, unten = 3

 

Die erforderlichen Daten müssen wir vor der Mesherzeugung angeben. Dies machen wir in der bildeMesh-Methode mit Arrays.

Vector3[] vertices = new Vector3[4];

int[] triangle = new int[6];

 

Jetzt benötigen wir noch normals für die Ausrichtung.

Vector3[] normals = new Vector3[4];

 

Die einzelnen Arraydaten sind im Script unten gelistet.

 

Jetzt können wir das Objekt erzeugen und die Daten übergeben

Mesh mesh = new Mesh ();

mesh.vertices = vertices;

mesh.triangles = triangles;

mesh.normals = normals;

 

Wir schaffen Zugriffe auf die Komponenten:

MeshFilter mesh_filter = GetComponent<MeshFilter> ();

MeshRenderer mesh_renderer = GetComponent<MeshRenderer> ();

MeshCollider mesh_collider = GetComponent<MeshCollider> ();

mesh_filter.mesh = mesh;

 

Die bisherige bildeMesh () Methode

 

void bildeMesh ()

{

// Das Rechteck benötigt 4 Punkte im 3dimensionalen Raum

Vector3[] vertices = new Vector3[4];

vertices [0] = new Vector3 (0, 0, -1);

vertices [1] = new Vector3 (1, 0, -1);

vertices [2] = new Vector3 (0, 0, 0);

vertices [3] = new Vector3 (1, 0, 0);

// Ein Dreieck benötigt 3 Punkte mal 2 Dreicke = 6

int[] triangles = new int[6];

triangles [0] = 0;

triangles [1] = 3;

triangles [2] = 2;

 

triangles [3] = 0;

triangles [4] = 1;

triangles [5] = 3;

 

// Ausrichtung, hauptsächlich für die Lichtberechnung

Vector3[] normals = new Vector3[4];

 

// die ausführliche Schreibweise: normals [0] = new Vector3(0, 0, 0);

normals [0] = Vector3.up;

normals [1] = Vector3.up;

normals [2] = Vector3.up;

normals [3] = Vector3.up;

 

// Mesh wird erzeugt

Mesh mesh = new Mesh ();

 

// die Basis-Daten werden den Mesh-Membern mitgeteilt

mesh.vertices = vertices;

mesh.triangles = triangles;

mesh.normals = normals;

 

 

// Die Daten unseren Komponenten übergeben

MeshFilter mesh_filter = GetComponent<MeshFilter> ();

MeshRenderer mesh_renderer = GetComponent<MeshRenderer> ();

MeshCollider mesh_collider = GetComponent<MeshCollider> ();

 

mesh_filter.mesh = mesh;

}

 

Wenn wir jetzt starten, kommt ein rosa Rechteck. Es ist nur schräg sichtbar. Stellen wir im Gameobjekt Tilemap Rotation x auf 90 bzw. auf 270, schon ist es besser.

Warum, es fehlt das „material“ auf dem die Textur kommt, unsere persönlich erstellte Karte. Wenn wir keine haben, können wir ein x-beliebiges Bild nehmen und es als Textur wie folgt verwenden.

Wir create material und benennen es MatKarte. Wir klicken auf MatKarte und es erscheint im Inspector. Jetzt können wir unsere Textur darauf ziehen. MatKarte fügen wir als Komponente der TileMap zu. Wir starten und sehen zwar nicht mehr pink aber auch nicht unsere Textur. Das liegt daran, dass wir unity sagen müssen, wo genau die Karte hin muss. Wir sehen zwar eindimensional die Karte, unity arbeitet aber mehrdimensional. Stichwort UV-Map. Wir erzeugen einen Vector2 Array. Wir müssen die 4 Eckpunkte unseres Rechtecks angeben.

Wir ergänzen unser Listung um

 

Vector2[] uv = new Vector2[4];

uv[0] = new Vector2(0, 0);

uv[1] = new Vector2(1, 0);

uv[2] = new Vector2(0, 1);

uv[3] = new Vector2(1, 1);

 

Jetzt sollte es aber klappen. Ist das Bild noch verzerrt, hängt es an der Größe der TileMap. Im Verhältnis der Bildgröße (Breite, Höhe) in Transform Scale verkleinern und die Position anpassen.

 

 

Das komplette Script:

using UnityEngine;

using System.Collections;

 

[ RequireComponent(typeof(MeshFilter) ) ]

[ RequireComponent(typeof(MeshRenderer) ) ]

[ RequireComponent(typeof(MeshCollider) ) ]

 

public class TileMap : MonoBehaviour {

 

// Use this for initialization

void Start ()

{

bildeMesh ();

}

 

// Update is called once per frame

void bildeMesh ()

{

Vector3[] vertices = new Vector3[4];

int[] triangles = new int[6];

Vector3[] normals = new Vector3[4];

Vector2[]uv = new Vector2[4];

 

vertices [0] = new Vector3 (0, 0, 0);

vertices [1] = new Vector3 (100, 0, 0);

vertices [2] = new Vector3 (0, 0, -100);

vertices [3] = new Vector3 (100, 0, -100);

 

triangles [0] = 0;

triangles [1] = 3;

triangles [2] = 2;

triangles [3] = 0;

triangles [4] = 1;

triangles [5] = 3;

 

 

// die ausführliche Schreibweise: normals[0] = Vector3 (0, 0, 0);

normals[0] = Vector3.up;

normals[1] = Vector3.up;

normals[2] = Vector3.up;

normals[3] = Vector3.up;

 

uv [0] = new Vector2 (0, 0);

uv [1] = new Vector2 (1, 0);

uv [2] = new Vector2 (0, 1);

uv [3] = new Vector2 (1, 1);

 

Mesh mesh = new Mesh ();

mesh.vertices = vertices;

mesh.triangles = triangles;

mesh.normals = normals;

mesh.uv = uv;

 

MeshFilter mesh_filter = GetComponent<MeshFilter> ();

MeshRenderer mesh_renderer = GetComponent<MeshRenderer> ();

MeshCollider mesh_collider = GetComponent<MeshCollider> ();

mesh_filter.mesh = mesh;

 

}

}

 

Das ist mein Einstieg ins Tile-Mapping

 

Terep

bearbeitet von Terep
Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...