Jump to content
Unity Insider Forum

Tilemaps / Tiles variabler größe erzeugen mit unterschiedlichen Ebenen.


HuntySX

Recommended Posts

Hallo hallo!

Ich hab mir hier angemeldet um eventuell Hilfe von euch bei 1-2 Problemchen zu erhalten.

Im Zuge eines Hobby Projektes arbeiten ein Kollege und ich momentan an einem kleinen RPG.

Bisher konnten wir gute Fortschritte erzielen. Mein Kollege hat es geschafft ein System für Items / Ausrüstung und Charaktereigenschaften zu schaffen, während es mir gelungen ist eine Zufällig generierte Karte aus Tiles zu erzeugen (32x32 Pixel pro tile), mit einem Start und Endpunkt und objekten die den Weg blockieren. Gleichzeitig habe ich einen Pathfinding Algorithmus implementiert mit dem man prüfen kann ob einzelne Tiles zu erreichen sind (bspw ob es überhaupt möglich ist vom Start zum Ziel zu kommen) und ähnliches.

Meine Klassenstruktur sieht kurz umschrieben so aus.

Jedes Tile ist ein Gameobject (Tile), welches in einer eigenen Klasse verwaltet wird (mit infos ob das tile betretbar ist, Zahlenwerten fürs Pathfinding sowie dem Prefab vom Sprite etc.).

Darüberliegend der Tilemanager der die Prefabs aller Sprites besitzt und daraus zufällig ein Gameobject vom Typ Tile erzeugt.

das erzeugte Tile wird vom Tilemanager an den Leveldesigner zurückgegeben der das Grid bereitstellt (ein 2 Array mit x und y koordinate vom Gameobject Tile) und die Tiles dort einsetzt.

zu guter letzt prüft der Designer mit dem Pathfinding algorithmus ob das Level beendet werden kann (Start->Ziel) und verändert die Tiles auf dem kürzesten Weg in einen Steinweg.

 

Bis dato läuft alles auch prima, er erstellt zufällige räume und findet immer start und endpunkt bzw generiert den Weg der dort hinführen soll.

 

Nun wollen wir das ganze System erweitern. Aus Ästhetischen und künstlerischen Gründen (wir sind voll nieten beim malen) würden wir gerne für den Boden kleinere 8x8 Tiles verwenden und diese zufällig durchrotieren, anstatt direkt 32x32 Tiles zu zeichnen die sich dann eventuell schnell wiederholen. Die Objekte (Felsen, Bäume etc.) die instanziert werden sollen, sollen jedoch unterschiedliche Größen unterstützen können.

Hierzu würde ich mehrere Ebenen nutzen. Meine Grundideen:

 

1. Ebene: Der Boden (Graß, Stein, Erde) soll aus 8x8 Sprites erzeugt werden. das Grid muss dementsprechend vergrösert werden damit es von der skalierung bzw den Koordinaten noch passt (also mehr Gameobjects)

2. Ebene: Die Objekte (Bäume, Felsen, Kisten etc.) soll aus variablen Sprites erzeugt werden können (also 32x32, ebenfalls 8x8 oder aus unförmigen sachen (44x20 o.ä.)). Hierfür dachte ich das meine Klasse Leveldesigner, welche das Grid bisher zur verfügung stellt, ein weiteres erzeugt in welchem die Objekte abgelegt werden können. Dieses Grid wird kleiner skaliert.

3. Ebene: Fallengelassene Gegenstände, Monster, die Spieler etc. (dies braucht ja kein wirkliches Grid, nur jeweilige Collision mit den anderen Ebenen und freie Positionen.

 

drei ideen hatte ich für dieses neue Grid:

1. Idee das neue Grid besitzt nur 1/4 der größe des 8x8 Boden Grids. Dadurch überdecken Objekte des 2. Grids dann beim erzeugen quasi 16 8x8 Sprites (ein 4x4 feld, also 32x32 pixel). Dieses wird dann mit objekten befüllt und überdeckt damit das Bodengrid.

(haben wir beispielsweise 128x128 Felder im 1.Grid (welche mit 8x8 gefüllt werden) haben wir darüber ein Grid von der größe 32x32 in dem die größeren Objekte geladen werden können.

Problem Hierbei: dies beschränkt die sache erneut nur auf 32x32 Sprites und ermöglicht es nicht relativ einfach unförmige Sprites zu verwenden ohne direkt ein ganzes Feld zu nutzen.

2. Idee: wir spalten alle größeren Sprites ebenfalls in 8x8 große Sprites und erzeugen daraus dann auf einem 2. Grid unsere Objekte (dies halte ich bis dato für sehr umständlich da jedes Tile egal wie unförmig aufgeteilt werden muss).

3. Idee: wir erzeugen aus unseren 8x8 Tiles, zusammenhängende Gameobjects der größe 32x32 und instanzieren diese als Boden. Die Objekte werden dann auf diese Felder drüber gelegt (ähliches Problem dann jedoch wie in Idee 1).

Wie kann ich das alles gescheit skalieren bzw. implementieren? übersehe ich vielleicht eine unity eigene funktion? kann man grid gescheit frei skalieren lassen?

---------------------------------------------------------------------------------------------------------------------------------------------------

das Zweite Problemchen ist die Performance des Projektes: erzeugen wir gebiete mit 8x8 Tiles können schnell tausende von Objekten erzeugt werden. Mein jetziges Grid kann auf eine größe von 100 x 100 wachsen. das sind bereits 10.000 Gameobjects. erzeuge ich nun ein Grid für kleinere Tiles (8x8) und will diese in der selben größe erzeugen brauche ich 400x400 felder (bereits 160.000 Objekte nur für den Boden). vielleicht schrecken mich diese großen Zahlen auch nur ab aber ist die Engine in so einem Rahmen nicht irgendwann überfordert?

 

Ich Danke euch schonmal im Vorfeld und hoffe ihr könnt mir Helfen!

Liebe Grüße,

HuntySX

 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo und willkommen im Forum!

Die übliche Variante bei sowas ist schon, eine uniforme Gridgröße zu haben. Dann besteht ein Baum eben mal aus 2x2 Sprites. Wenn du das anders aber besser findest, kannst du das auch so machen. Vielleicht verstehe ich dein Problem damit noch nicht so ganz :)

Was die Performance angeht: Das Instanziieren von 160.000 GOs kann schon etwas länger als gewünscht dauern, aber wenn die erstmal da sind, ist es gar nicht mehr so schlimm. Bei 2D-Spielen erledigt automatisches Frustum Culling das meiste (Renderer, die nicht im Blickfeld der Kamera sind, rendern gar nicht erst), und solange du jetzt nicht auf jedem der GOs ein Script hast, das ein Update-Event hat, sollte das ganze kein Problem sein.

Prinzipiell kann aber immer etwas unerwartetes sein, daher einfach immer testen. Nutzt den Profiler. Und wenn tatsächlich Probleme auftreten, teilt die Welt in Chunks ein. Simpelste Variante: Gruppen von x*y Feldern sind Children eines GameObjects, das dann deaktiviert ist, solange die Kamera weit genug davon weg ist. Probiert's aber erstmal aus.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin,

Sorry das ich jetzt erst antworte, ich war etwas  beschäftigt wegen umzug und uni in der woche.

Zum Problem: ja, eine uniforme Grid Größe wäre natürlich das einfachste, jedoch weiß ich nicht wie ich die Tiles etc trennen soll ohne fantastillionen an GO´s zu besitzen.

Bleiben wir bei dem Beispiel Fels und Gras:

Mein Gras bleibt in 8x8 TIlegröße. soweit so gut, das habe ich implementiert und das läuft. er pickt sich random aus den ganzen Prefabs (den GrasTiles die er kennt) für das Gras eines raus und setzt es. nun habe ich eine grasfläche die ich mit gegenständen füllen will (z.b. felsen oder Baum)

nutze ich ein Grid der selben größe (also ebenfalls 8x8) muss ich die Tiles des Felsen (der beispielsweise 32x32 ist) auftrennen in jeweils 16 8x8 Tiles (damit diese zum Grid passen) und diese einzeln setzen. ich habe also kein Prefab für ein 32x32 Tile sondern 16 Prefabs die jeweils 8x8 groß sind nur für einen Felsen. und muss je nach form des Felsens (vielleicht mal kein 32x32 sondern mal 64x16) einen eigenen Algorithmus zum setzen schreiben.

Gleichzeitig füllt dieser Felsen mit seinen 16 Tiles auch 16 Positionen im Grid mit diesen 16 Prefabs die ich erzeugt habe. wie führe ich da die Collision durch? wie zeige ich ihm das dieser Felsen zwar zerstört werden kann aber nur ein Gameobject dann das Item fallen lest (bspw. Kieselsteine) anstatt das es alle 16 tun.

Das halte ich für ziemlich umständlich.

Es geht also mehr oder weniger darum wie ich es am elegantesten Lösen kann das die Tiles gesetzt werden, das ich variable Tiles setzen kann und diese nur ein Gameobject haben.

 

vielleicht wird es jetzt klarer :)

Liebe Grüße

HuntySX

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich beschränke mich gerade mal auf die Physikfrage.

Bei Tilemaps ist es grundsätzlich keine schlechte Idee, Physik (also Kollisionen) und Grafik voneinander zu trennen. Wenn du z.B. einen Wald von 2*2 Bäumen hast, und jeder Baum ist 2*2 Standardtiles groß (unabhängig davon, wie viele SpriteRenderer da involviert sind), dann braucht man nicht 4*4 Collider da hinzubauen. Je nach dem, wie man grundsätzlich arbeiten will, hat man bei handgemachten Maps verschiedene Möglichkeiten:

  1. Man hat immer genau ein Feld große Collider. Das ist recht praktisch, weil man da stumpf mit dem Tilemap-Editor Collisionen bauen kann. Das macht man i.d.R. so, wenn man auch so einen Editor hat, wie z.B. Tiled in diesem Tutorial.
    tilegame_metatiles_drawn-700x464.png
    Hier wurde einfach ein Meta-Layer eingefügt, auf den mit einer Palette, die durchsichtiges Rot anbietet, Felder gemalt werden. Beim Import im Spiel wird diese Palette nicht angezeigt, sondern es werden Collider gebaut. Was man auf dem Bild nicht so gut sieht: Bei meinem Waldbeispiel lässt man natürlich die Collider in der Mitte weg, da der "Innenraum" von der Wand außenrum "geschützt" wird. Da kämen wir also auf 12 einfach und schnell gesetzte Box Collider.
  2. Du gibst deinen Prefabs entsprechende Collider. Jeder baum hat einen 2*2 großen Box Collider, du packst ihn irgendwo hin, fertig. Ergibt in dem Beispiel 4 Collider, aber wenn du jetzt 4*4 Bäume hättest, sind es schon 16, obwohl nur 12 nötig wären. Es sei denn, du hast lust, die Collider vom Prefab in dem Fall wieder runter zu nehmen.
  3. Du baust alle Collider "frei Hand". Ein 2*2 Wald kriegt einfach einen einzelnen 4*4 großen  Box Collider obendrauf und fertig. Das klingt vielleicht etwas merkwürdig, ist aber bei 3D-Leveldesign gang und gäbe. Ein Editor-Tool, mit dem man BoxCollider auf ein Grid ziehen kann, wäre keine schlechte Idee.

Dann gibt es noch eine Überlegung zu Idee 1. Wenn du freie Bewegung hast (vgl. Legend of Zelda-Spiele), brauchst du Collider. Wenn aber nicht, und der Spieler geht von Gridfeld zu Gridfeld (vgl. Pokémon), dann kannst du sie auch weglassen. Stattdessen baust du ein zweidimensionales Array, und in jedem Feld des Arrays steht die Info für das Feld. Und wenn da steht "ist geblockt" kann man nicht drauf gehen. Diese Methode ist um einiges ressourcenschonender als mit Collidern.

Das wäre für handgemachte Maps. Da ihr zufällig generiert, musst du jetzt schauen, welche dieser Varianten am besten auf deinen Generator zu übertragen ist. Variante 2 wäre natürlich sehr simpel. Deine Tiles sind Prefabs, die haben u.U. Collider, werden gespawnt, fertig. Variante 1 lohnt sich vermutlich nur noch mit dem Zusatz, dass man da ein Array pflegt. Du hast das Array, spawst ein Objekt, weiß, welche Tiles es bedeckt (weil du weißt, wo und wie groß es ist) und änderst enstprechend die Werte im Array.

Variante 3 ist vermutlich schwierig umzusetzen. Wenn du aber freie Bewegung hast, könnte es die Performance verbessern. Du bräuchtest dann einen Algorithmus, der feststellt, wenn mehrere Collider durch einen einzelnen abgedeckt werden können, und das dann tut.

Dann noch mein Gedanke zum allgemeinen Tilegrößen-Problem: Ich sehe keine großen Vor- oder Nachteile bei den verschiedenen Ansätzen, die ihr habt. Macht es so, wie ihr das am besten findet und gebacken kriegt. Wenn ihr euch um draw Calls sorgen macht: Sprite Renderer sind super in Sachen Batching. Und wenn es um die schiere Anzahl von GameObjects geht... schaut erstmal, wie viele davon ihr bauen könnt und macht euch über Einschränkungen erst Gedanken, wenn ihr gegen ein Limit stoßt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...