Das Terrain von Warman wurde von Grund auf für ein Top-Down-Action-RPG gebaut. Unitys eingebautes Terrain-System ist für Open-World-Ego-Spiele konzipiert. Heightmaps, Splatmaps, Baumplatzierung. Nichts davon passt zu einem Spiel, in dem die Welt ein Gitter aus handgefertigten Räumen ist. Also ist das Terrain komplett eigens entwickelt.

Punkte und Zellen

Das Terrain ist ein Gitter. Jede Position im Gitter ist ein TerrainPoint, ein Struct, das einen Höhenwert, eine Wasserhöhe, einen Texturschicht-Index, einen Detailtyp und ein Rampen-Flag enthält. Punkte sind die Ecken. Zwischen vier Punkten liegt eine TerrainCell. Die eigentliche Terraineinheit, auf der der Spieler läuft. Ein 30x30-Raum hat 30x30 Zellen und 31x31 Punkte.

Jeder Punkt speichert seine Daten in Bytes. Höhe ist ein Byte (0-255 Stufen), Wasserhöhe ist ein Byte, und der Texturindex ist ein Byte, das auf eine der Tileset-Schichten des Bioms verweist. Das gesamte Punkt-Struct serialisiert zu etwa 6 Bytes, was Raumdateien klein und schnell ladbar hält.

Cell Cell Cell Cell Cell Cell TerrainPoint height, water, texture (~6 bytes) TerrainCell walkability, cliff state, ramp Texture transition resolved via dynamic atlas
Terrain-Gitter: Punkte an den Ecken, Zellen dazwischen

Der Textur-Atlas

Hier wird es interessant. Jede Zelle hat vier Eckpunkte, jeder mit einer potenziell anderen Textur. Eine Zelle, bei der alle vier Ecken dieselbe Gras-Textur verwenden, ist einfach. rendere einfach ein Gras-Quad. Aber was ist mit einer Zelle, bei der zwei Ecken Gras und zwei Erde sind? Das ist eine Übergangskachel, und sie braucht eine spezifische Textur, die beide Materialien mischt.

Warman löst das mit einem dynamischen Textur-Atlas. Jede einzigartige Kombination von Eck-Texturen bekommt eine Kachel in einem zur Laufzeit generierten Atlas. Der Kacheltyp wird berechnet, indem die vier Eck-Indizes in eine einzelne Ganzzahl gepackt werden (durch Bit-Verschiebung), was einen eindeutigen Schlüssel für jede mögliche Kombination erzeugt. Der Atlas startet bei 1024x1024 und wächst bei Bedarf. Wenn eine neue Kombination auftaucht, die noch nicht gesehen wurde, wird eine Kachel im Atlas reserviert, die korrekte Übergangstextur aus den Tileset-Schichten zusammengesetzt und die UVs der Zelle zeigen auf die neue Atlas-Position.

Klippen

Klippen erscheinen überall dort, wo benachbarte Terrain-Punkte unterschiedliche Höhen haben. Die Klippenschicht scannt jede Zelle, berechnet welche Kanten Höhenübergänge haben und platziert das passende Klippen-Mesh. Es gibt spezifische Tilesets für Klippenflächen, die Innenkurven, Außenkurven und gerade Kanten abdecken. 15 Varianten in einem Standard-Klippen-Tileset, basierend darauf welche der vier Ecken erhöht sind.

Rampen funktionieren ähnlich, sind aber nuancierter. Wenn der Editor Punkte als „Rampen-Punkte" markiert, erzeugt das Klippensystem geneigte Geometrie statt vertikaler Klippenflächen. Jede Zelle mit einer Rampe bekommt pro Vertex berechnete Höhen basierend auf der Rampenrichtung (Norden, Osten, Süden oder Westen), wobei der Höhenübergang in Achtel aufgeteilt wird, damit die Neigung glatt aussieht. Das Pfadfindungssystem kennt Rampen. Es unterteilt jede Zelle in ein dichteres Gitter und weist jedem Knoten interpolierte Höhen zu, damit Einheiten sanft Steigungen hinauflaufen.

Wasser

Wasser ist eine separate Schicht, die auf dem Terrain liegt. Jeder Punkt hat eine Wasserhöhe, und wenn die Wasserhöhe die Terrainhöhe erreicht oder übersteigt, gilt dieser Punkt als „sichtbar nass". Die Wasserschicht erzeugt ihr eigenes Mesh mit animierten UVs, positioniert auf der Wasserhöhe plus einem kleinen Tiefenversatz, damit es knapp über der Terrainoberfläche sitzt.

Das Pfadfindungsgitter markiert Wasserzellen basierend auf den Oberflächentyp-Einstellungen des Bioms. einige Biome behandeln Wasser als begehbar (flache Bäche), andere nicht (tiefes Wasser). Das Schrittgeräusch-System liest ebenfalls den Oberflächentyp, sodass der Spieler Platschen hört, wenn er durch Wasser läuft.

Doodads

Dekorative Objekte (Grasbüschel, Blumen, Pilze, Kerzen) werden auf zwei Arten auf dem Terrain platziert. Die meisten Doodads sind Standard-GameObjects. Sie werden mit einer Position, Rotation und Skalierung instanziiert. Aber volumenstarke Vegetation wie Gras und Blumen nutzt Unitys ECS (Entity Component System). Ein einziger Aufruf erzeugt Hunderte von Entities mit geteiltem Mesh und Material, was in einem Bruchteil der Zeit rendert, die einzelne GameObjects brauchen würden.

Das Terrain-System prüft eine fest kodierte Liste von „ECS-fähigen" Doodad-Typen. Wenn ein Doodad-Typ auf der Liste steht, geht er über den Entity-Pfad. Andernfalls ist es ein reguläres GameObject. Diese Aufteilung existiert, weil ECS-Entities keine benutzerdefinierte Konfiguration haben können (wie Interaktions-Komponenten), sodass nur rein visuelle Doodads qualifiziert sind.

Anbindung an die Pfadfindung

Nach der Terrain-Generierung liest das Pfadfindungssystem die Zelldaten, um sein Navigationsgitter aufzubauen. Jede Terrain-Zelle wird zu einem Cluster von Pfadfindungsknoten (standardmäßig 2x2 Unterteilungen pro Zelle), wobei die Begehbarkeit durch den Terrain-Texturtyp, Klippen-Belegung und Rampenstatus bestimmt wird. Klippen-belegte Zellen sind nicht begehbar. Rampenzellen bekommen aus der Rampenrichtung interpolierte Höhenversätze. Das Ergebnis ist ein 3D-Gitter, in dem Einheiten über flachen Boden, über Rampen und um Klippenkanten herum Pfade finden können, alles aus denselben zugrunde liegenden Terrain-Daten berechnet.