RTS Terrain Studio 1.0.0
Handbuch · Version 1.0.0

RTS Terrain Studio

Baue RTS-Karten im klassischen Stil mit Plateaus, scharfkantigen Klippen, Rampen, texturiertem Boden, Wäldern, Wasser und Dekor – im Unity Editor und direkt in deinem Spiel. Nebel des Krieges, Wegfindung und Terrain-Abfragen sind bereits eingebaut.

Unity 6.3 LTSURP 17.3Im Editor und im SpielMirror, A* Pathfinding Project, Unity NavMesh

Überblick

RTS Terrain Studio ist ein Karteneditor für Echtzeitstrategiespiele im klassischen Stil, entwickelt für Unity 6.3 LTS und die Universal Render Pipeline (URP) 17.3. Damit modellierst du Plateaus mit scharfkantigen, senkrechten Klippen, texturierst den Boden, pflanzt Wälder, flutest Seen und Flüsse, platzierst Dekorobjekte, markierst die Startpositionen der Spieler und prüfst, wo Einheiten laufen und Gebäude stehen können. Wenn du fertig bist, ist dieselbe Karte direkt spielbereit: Nebel des Krieges, Wegfindung und Terrain-Abfragen sind vorhanden, und du brauchst dafür nie einen Collider.

Den Editor gibt es an zwei Stellen. Beide verwenden dieselben Werkzeuge und dasselbe Kartendateiformat (.rtsmap):

  • Im Unity Editor über das Fenster Tools ▸ RTS Terrain Studio ▸ Editor Window und die Scene View. Hier baust du die Karten, die du mit deinem Spiel auslieferst.
  • Im Spiel über die Komponente RTSRuntimeEditor. Das ist ein vollständiger Editor mit einer Oberfläche auf Basis von UI Toolkit. Deine Spieler und Modder können damit in deinem fertigen Spiel Karten erstellen, speichern, laden und per Spieltest ausprobieren.

Das Paket enthält keine Grafiken von Drittanbietern. Stattdessen erzeugt ein einziger Menübefehl einen vollständigen Satz an Demo-Inhalten: Texturen, Low-Poly-Bäume und -Props, zwei Tilesets, drei Karten und drei spielbereite Szenen. Schon eine Minute nach dem Import hast du etwas zum Ausprobieren.

Die Szene Gameplay Demo: eine Plateaukarte mit Klippen, Flüssen, Wäldern und Beispieleinheiten unter dem Nebel des Krieges
Die Szene RTS Terrain Studio - Gameplay Demo auf der Demokarte Two Rivers (1v1).

Dieses Handbuch richtet sich an alle – von deiner ersten Karte bis zur Erweiterung des Quellcodes. Wenn du neu dabei bist, folge unten dem Abschnitt Deine ersten fünf Minuten und lies danach die Kapitel der Reihe nach. Wenn du programmierst: Der Quellcode ist vollständig kommentiert, jeder öffentliche Typ hat eine XML-Dokumentation, und der Abschnitt Ordnerstruktur zeigt, wo alles liegt.

Wie eine Karte aufgebaut ist

Wenn du schon mit dem Karteneditor eines klassischen RTS-Spiels gearbeitet hast, werden dir die meisten dieser Konzepte vertraut vorkommen. Eine Karte ist ein Raster aus quadratischen Zellen (standardmäßig 2 m breit). Die Ecken der Zellen sind die Vertices, und die meisten Daten werden pro Vertex oder in hochauflösenden Texturen gespeichert, die die ganze Karte abdecken.

KonzeptBedeutung
KlippenstufeEine ganzzahlige Höhenstufe von 0 bis 15 für jeden Vertex. Benachbarte Vertices auf unterschiedlichen Stufen bilden eine senkrechte Klippenwand. Die Standard-Stufenhöhe beträgt 2 m, und neue Karten beginnen auf Stufe 2, damit du für Seen und Flüsse nach unten graben kannst.
FeinhöheEin stufenloser Höhenversatz über der Klippenstufe für Hügel und Täler. Die Bodenhöhe eines Vertex ergibt sich aus CliffLevel × CliffLevelHeight + Height.
RampeZellen, deren Ecken mit dem Rampen-Flag markiert sind, gehen weich von einer Stufe in die nächste über, sodass Einheiten hinauflaufen können.
KlippentypDas Material der Klippenwände (zum Beispiel Fels oder grasbewachsene Klippen). Ein Tileset enthält bis zu 4.
BodenschichtEine von bis zu 16 aufgemalten Bodentexturen (Gras, Erde, Fels …), gespeichert in Splatmaps.
WasserEine Höhe der Wasseroberfläche pro Vertex. Die Tiefe entscheidet, ob es sich um Flachwasser (begehbar) oder Tiefwasser (nicht begehbar) handelt.
Details und BäumeAufgemaltes Gras und Blumen (Dichte pro Texel) sowie einzeln platzierte Bäume.
DekorJedes Prefab, das du auf der Karte platzierst: Felsen, Ruinen, Kisten, Zäune, Gebäude.
PathingFür jede Pathing-Zelle: begehbar, bebaubar, befliegbar, Flach- oder Tiefwasser, Klippe, blockiert oder steil. Das Pathing wird automatisch aus der Karte abgeleitet, und du kannst Overrides darübermalen.
MarkerBenannte Regionen (Rechtecke mit Schlüssel-Wert-Eigenschaften für deine Spiellogik), Startpositionen der Spieler und benannte Punkte wie Kamerapositionen.
TilesetEin RTSTileset-Asset, das das Aussehen festlegt: Bodenschichten, Klippentypen und den Wasserstil – wie die Tilesets „Summer“ oder „Winter“ klassischer RTS-Karteneditoren.
BibliothekenEine RTSVegetationLibrary (Details und Bäume) und eine RTSDecorLibrary (Dekor-Prefabs). Sie sind die Paletten, aus denen du malst und platzierst.

In einer Szene liegt eine Karte auf einem GameObject mit der Komponente RTSTerrain. Der Menübefehl GameObject ▸ RTS Terrain Studio ▸ RTS Terrain fügt ihm außerdem die Komponenten der Subsysteme hinzu (RTSVegetationRenderer, RTSWaterRenderer und RTSDecorManager). Das Terrain liest die Karte aus einer .rtsmap-Datei in deinem Projekt (einem RTSMapAsset). Die Chunk-Meshes, das Gras und das Wasser, die es anzeigt, werden aus dieser Datei erzeugt. Sie werden nie in der Szene gespeichert, deshalb bleiben deine Szenen klein.

Deine ersten fünf Minuten

So siehst du am schnellsten, wie alles zusammenspielt. Außer dem Import des Pakets ist keine Einrichtung nötig.

  1. Öffne Tools ▸ RTS Terrain Studio ▸ Validate Project und klicke auf Fix All. Dadurch werden URP, das Renderer Feature für den Nebel des Krieges, die vom Wasser genutzten Kameratexturen und der Farbraum eingerichtet. Ein Bestätigungsdialog erklärt die Änderungen, bevor etwas angepasst wird.
  2. Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus. Während Texturen, Modelle, Tilesets, Karten und Szenen erzeugt werden, erscheint ein Fortschrittsbalken. Danach ist die Szene RTS Terrain Studio - Gameplay Demo im Project-Fenster ausgewählt.
  3. Doppelklicke auf diese Szene, um sie zu öffnen, und drücke dann auf Play.
  4. Wähle deine Einheiten mit der linken Maustaste aus (anklicken oder ein Auswahlrechteck aufziehen) und klicke mit der rechten Maustaste auf den Boden, um sie zu bewegen. Bewege die Kamera mit WASD oder den Pfeiltasten, zoome mit dem Mausrad und drücke F1, um die gesamte Steuerung anzuzeigen.
  5. Beende den Play Mode und öffne die Szene RTS Terrain Studio - Editor Playground. Öffne Tools ▸ RTS Terrain Studio ▸ Editor Window, wähle ein Werkzeug und male in der Scene View. Halte Shift gedrückt, um das Gegenteil zu bewirken (zum Beispiel absenken statt anheben). Drücke Ctrl+Z, um einen Schritt rückgängig zu machen.
  6. Öffne die Szene RTS Terrain Studio - In-Game Editor Demo und drücke auf Play. Das ist der Editor, den deine Spieler bekommen. Drücke F5, um die Karte mit Einheiten im Spiel zu testen, und F2, um den Editor ein- oder auszublenden.

Das Fenster Tools ▸ RTS Terrain Studio ▸ Welcome öffnet sich einmal pro Projekt und zeigt dieselben Schritte als Schaltflächen. Über das Menü kannst du es jederzeit wieder öffnen.

Wo du anfängst

Ich möchte …Hier anfangen
Ein Projekt richtig einrichtenEinrichtung und Tools ▸ RTS Terrain Studio ▸ Validate Project
Schritt für Schritt meine erste Karte erstellenSchnellstart und Tools ▸ RTS Terrain Studio ▸ New Map Wizard...
Das Editorfenster und alle Werkzeuge kennenlernenEditorfenster und Scene View
Spieler in meinem Spiel Karten bauen lassenIn-Game-Editor
Mein Spiel oder den Karteneditor im Webbrowser veröffentlichenWeb-Builds (WebGL)
Die Karte verbergen, bis Einheiten sie erkundenNebel des Krieges
Mirror, das A* Pathfinding Project oder Unity NavMesh verwendenIntegrationen
Das Terrain abfragen oder eigene Werkzeuge in C# schreibenScripting
Etwas beheben, das nicht richtig aussieht oder funktioniertFehlerbehebung

Funktionen

Jedes der folgenden Bearbeitungswerkzeuge gibt es sowohl im Unity Editor als auch im In-Game-Editor, denn beide verwenden dieselben Werkzeugklassen (Unterklassen von RTSTool). Die Namen in dieser Liste entsprechen denen, die du in der Benutzeroberfläche siehst.

Das Editorfenster von RTS Terrain Studio mit geöffnetem Tab Decor
Das Fenster von RTS Terrain Studio: Werkzeugkategorien, Werkzeugschaltflächen, Symmetrieeinstellungen und die Dekorpalette mit generierten Vorschaubildern.

Terrain und Klippen

  • Karten mit 16 bis 1024 Zellen pro Seite, in Schritten von 16. Der New Map Wizard bietet die Presets Tiny (64), Small (96), Medium (128), Large (192), Huge (256), Epic (384) und Epic XL (512) oder eine beliebige eigene Größe. Die Zellgröße und die Stufenhöhe der Klippen lassen sich frei einstellen.
  • 16 Klippenstufen (0–15) mit prozedural erzeugten, abgerundeten Klippenwänden. In jeder Klippenzelle wird auf einem Unterraster eine Kontur nachgezogen, und Rauschen sowie Schrägstellung lassen die Wände wie natürlichen Fels aussehen (all das wird in RTSTerrainSettings eingestellt).
  • Feinhöhe über den Stufen, sodass Plateaus Hügel haben können. Glatter Boden kann 1 bis 4 Quads pro Zellseite verwenden: 1 ergibt den klassischen RTS-Look, 2 bis 4 ergeben weichere Hügel.
  • Werkzeuge der Kategorie Height: Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode.
  • Werkzeuge der Kategorie Cliffs: Raise / Lower Level, Set Level, Ramp, Cliff Type, Clean Cliffs.
  • Bis zu 4 Klippentypen pro Tileset, pro Vertex gemalt, mit Triplanar-Texturierung an den Wänden.
  • Ein optionaler MeshCollider pro Chunk auf einem Layer deiner Wahl. Abfragen ohne Physik funktionieren auch ohne ihn: GetHeight, GetNormal, Raycast, GetCliffLevel, IsWater, GetPathing, IsWalkable und IsBuildable.
  • Beliebig viele Terrains in einer Szene. Jedes Terrain hat seine eigene Position und Y-Rotation. RTSTerrain.FindAt(worldPosition) und RTSTerrain.RaycastAll(ray, out hit) finden das richtige.
  • Ändere die Größe einer Karte in beide Richtungen (vergrößern oder verkleinern), ausgehend von einem Anker. Importiere und exportiere Heightmaps als 16-Bit-PNG oder 16-Bit-RAW, und exportiere die Splatmaps als PNG.

Malen

  • 16 Bodenschichten pro Tileset, gemalt in vier RGBA-Splatmaps. Standardmäßig gibt es 4 Splat-Texel pro Zelle (wählbar von 1 bis 8), deshalb sind gemalte Grenzen viel feiner als das Zellraster.
  • Malwerkzeuge: Paint Layer (weicher Pinsel), Tile Paint (hartes, an Zellen ausgerichtetes Malen im klassischen RTS-Stil), Fill / Replace, Smooth Paint und Auto Paint.
  • Auto Paint malt nach Regeln, die in einem RTSAutoPaintProfile-Asset gespeichert sind. Eine Regel kann Höhe, Neigung, Klippenstufe, Abstand zu Klippen, Abstand zu Wasser und Rauschen prüfen. Verwende das Werkzeug Auto Paint als Pinsel, oder lasse den Kartengenerator ein Profil auf die ganze Karte anwenden.
  • Höhenbasierte Überblendung zwischen Schichten, Anti-Tiling pro Schicht (verhindert sichtbare Wiederholungen gekachelter Texturen), eine Makrovariation für das ganze Tileset sowie Tönung, Glätte, Metallic-Wert und Normalenstärke pro Schicht.
  • Eine Schicht kann das Pathing dort ändern, wo sie dominiert. Die Demo-Tilesets machen zum Beispiel Rock und Blight nicht bebaubar, wie die nicht bebaubaren Kacheln klassischer RTS-Spiele.
  • Pinsel: Circle, Square, Diamond oder Custom mit einer beliebigen Textur als Stempel (RTSBrushShape-Assets). Du kannst Radius, Stärke, Härte, eine optionale Abnahmekurve, Rotation, zufällige Rotation, Abstand und Streuung einstellen.
  • Symmetrisches Malen für faire Wettkampfkarten: Mirror X, Mirror Z, Mirror XZ, Rotate 2 (180°-Punktsymmetrie), Rotate 4 (90°, nur quadratische Karten) und Mirror Diagonal (nur quadratische Karten). Die Symmetrie funktioniert mit jedem Werkzeug, auch beim Platzieren von Objekten.

Vegetation

  • Details wie Gras, Blumen und kleine Pflanzen, als Dichte pro Schicht gemalt und auf der GPU als Meshes oder gekreuzte Quads gezeichnet, mit Windbewegung.
  • Bäume, einzeln platziert oder mit einem Pinsel verstreut. Ein Baum kann per GPU-Instancing gezeichnet (mit optionalem Collider-Proxy) oder als GameObject gespawnt werden, etwa für Gameplay wie das Abbauen von Holz.
  • Bäume blockieren das Pathing automatisch über ihre Grundfläche.
  • Vegetationswerkzeuge: Paint Details, Erase Details, Place Trees, Erase Trees.
  • Der Shader RTS Terrain Studio/Vegetation sorgt für Wind, Transluzenz und eine Tönung pro Instanz. Jedes andere Material mit GPU-Instancing funktioniert ebenfalls.
  • Qualitätseinstellungen in RTSVegetationRenderer: Multiplikatoren für Detaildichte, Detaildistanz und Baumdistanz.

Wasser

  • Eine Wasserhöhe pro Vertex, sodass Seen auf unterschiedlichen Höhen liegen können. Wasser wird als in Chunks aufgeteilte, transparente Meshes gezeichnet, die von Burst-Jobs gebaut werden.
  • Flach- und Tiefwasser ergeben sich aus der Tiefe (WaterProfile.shallowDepthLimit). Flachwasser ist begehbar, Tiefwasser nicht – wie in klassischen RTS-Spielen.
  • Der Shader RTS Terrain Studio/Water bietet tiefenabhängige Färbung, bewegte Normalen, Wellen, Lichtbrechung, Uferschaum und ein weiches Ausblenden am Ufer. Der Stil kommt aus dem WaterProfile des Tilesets.
  • Wasserwerkzeuge: Paint Water, Erase Water, Flood Fill.

Dekor (Doodads)

  • Jedes Prefab kann Dekor sein. Prototypen liegen in einer RTSDecorLibrary mit automatisch erzeugten Vorschaubildern.
  • Jeder Prototyp wird entweder als GameObject gespawnt (für Objekte, mit denen das Gameplay interagiert) oder per GPU-Instancing gezeichnet (für viele statische Props).
  • Zufällige Rotation, Skalierung und Tönung, Ausrichtung am Hang, Einrasten am Raster und eine Pathing-Grundfläche (Kreis oder Rechteck).
  • Dekorwerkzeuge: Place Decor (einzeln oder verstreut), Erase Decor, Select Decor (verschieben, drehen, skalieren, duplizieren, löschen).

Pathing und Wegfindung

  • Eine Pathing-Karte, die automatisch abgeleitet und während der Bearbeitung aktuell gehalten wird. Sie berücksichtigt Klippen, Neigung, Wassertiefe, die Grundflächen von Bäumen und Dekor sowie Regeln der Bodenschichten.
  • Pathing-Werkzeuge: Paint Pathing (Overrides wie nicht begehbar, nicht bebaubar oder erzwungen begehbar) und Clear Pathing. Pathing-Overlays in den Editoren zeigen das Ergebnis.
  • Ein integrierter Pathfinder (RTSPathfinder): Burst-kompilierte A*-Suche auf dem Pathing-Raster mit 8 Nachbarn, ohne Abschneiden von Ecken, mit Berücksichtigung des Einheitenradius (Freiraum) und mit Pfadglättung. Er braucht kein Paket von Drittanbietern.
  • Asynchrone Pfadanfragen für viele Einheiten über RTSPathfindingService.

Nebel des Krieges

  • Klassische RTS-Regeln: Jedes Team hat seine eigene Sicht, und erkundete Gebiete bleiben bekannt. Standardmäßig sieht eine Einheit nur ihre eigene Klippenstufe und tiefere Stufen. Auch Bäume und Dekor können die Sicht blockieren (LineOfSightMode und die Option blocksVision eines Prototyps).
  • Bis zu 32 Teams, geteilte Sicht zwischen Verbündeten (RTSFogOfWarManager.SetAlliance), eine glatte, weichgezeichnete Nebeltextur und Unterstützung für die Minikarte.
  • Ein Renderer Feature für URP (RTSFogOfWarRendererFeature) verdunkelt alles auf dem Bildschirm, unabhängig vom verwendeten Material. Fehlt das Feature, wenden die Shader des Pakets den Nebel selbst an.
  • Einheiten mit RTSFogOfWarRevealer decken die Karte auf, und Objekte mit RTSFogOfWarHideable werden ausgeblendet, solange sie nicht sichtbar sind. Gebäude und Props können nach ihrer Entdeckung als „Geisterbilder“ sichtbar bleiben.
  • Die Sichtbarkeit wird von Burst-Jobs berechnet.

In-Game-Editor

  • Füge ihn über GameObject ▸ RTS Terrain Studio ▸ In-Game Editor hinzu. Er bietet eine Oberfläche auf Basis von UI Toolkit mit einer Menüleiste, Kategorie- und Werkzeugleisten, einem Werkzeug-Panel mit Paletten und Vorschaubildern, einer Statusleiste, Tooltips und Benachrichtigungen.
  • New, Open, Save und Save As mit einem Kartenbrowser. Karten werden standardmäßig unter Application.persistentDataPath gespeichert, in Web-Builds im Browserspeicher. Mit Download und Import überträgst du sie auf den Computer und wieder zurück. Außerdem kannst du eine Karte generieren, die Karteninfo bearbeiten und die Kartengröße ändern.
  • Rückgängig und Wiederherstellen mit Speicherbudget, Tastenkürzel, Symmetrie sowie Overlays für Raster, Pathing und Marker.
  • Ein Spieltest-Schalter (F5), der das Beispiel-Gameplay auf der gerade bearbeiteten Karte startet und danach zum Editor zurückkehrt.
  • Eine eigene Editorkamera (RTSRuntimeEditorCamera) und eigene Gizmos, dazu keine Abhängigkeit von der UnityEditor-API, deshalb funktioniert er auch in Player-Builds.

Kartengenerator

  • Ein prozeduraler Generator (RTSMapGenerator) mit den Stil-Presets Plains, Hills, Highlands, Islands, Canyon und Arena. Er arbeitet mit einem Seed, also einem Startwert für den Zufallsgenerator.
  • Er erzeugt Plateaus und Klippen, Rampen (mit Prüfung der Erreichbarkeit), Seen und Meere, einen Kartenrand, automatische Texturierung, Details, Wälder, Dekor und Startpositionen – in jedem Symmetriemodus.
  • Derselbe Seed ergibt immer dieselbe Karte. Du kannst Einstellungen als RTSMapGeneratorPreset-Assets speichern.
  • Du kannst ihn aus dem New Map Wizard, dem Editorfenster, dem In-Game-Editor oder deinem eigenen Code starten.

Beispiel-Gameplay

  • Eine RTS-Kamera (Schwenken, Rand-Scrollen, Zoom, Rotation, Kartengrenzen), Einheitenauswahl (Klick, Rechteckauswahl, Shift, Doppelklick, Kontrollgruppen) und Befehle (Bewegen in Formation, Befehlswarteschlange, Stopp, Halten, Patrouillieren).
  • Platzhaltereinheiten, die der Terrain-Höhe folgen, den Nebel des Krieges aufdecken und mit jedem Pfad-Provider funktionieren.
  • Eine Minikarte, über die du die Kamera per Klick bewegst, ein HUD, eine Demo zur Gebäudeplatzierung, die das Bebaubarkeits-Flag beachtet, und eine Demo zum Abbauen von Bäumen.
  • RTSDemoGame richtet all das auf jedem Terrain mit Startpositionen ein. Füge es über GameObject ▸ RTS Terrain Studio ▸ Demo Game hinzu.

Integrationen

Alle Integrationen sind optional. Jede einzelne wird nur kompiliert, wenn ihre Abhängigkeit installiert ist. Deshalb führt das Entfernen der Abhängigkeit nie zu Fehlern in deinem Projekt.

IntegrationWas sie ergänzt
Mirror (Multiplayer)Der Host streamt seine Karte an beitretende Clients, sodass Spieler selbst erstellte Karten online spielen können; heruntergeladene Karten werden zwischengespeichert. Einheiten sind serverautoritativ, jeder Client sieht den Nebel des Krieges seines eigenen Teams, und RTSFogOfWarInterestManagement sendet nie Gegner, die vom Nebel verborgen sind (Anti-Maphack).
A* Pathfinding ProjectGrid Graphs, die aus der RTS-Pathing-Karte erzeugt und nach jeder Bearbeitung inkrementell aktualisiert werden, ein Graph pro Einheitengröße, und Einheiten, die von AIPath mit lokaler RVO-Kollisionsvermeidung bewegt werden.
Unity AI Navigation (NavMesh)Ein NavMesh, das aus den Terrain-Chunks und der Pathing-Karte gebaut und nach Bearbeitungen inkrementell aktualisiert wird, sowie ein Pfad-Provider, der NavMesh-Pfade liefert oder NavMeshAgents steuert.

Arbeitsablauf im Editor

  • Ein andockbares Fenster RTS Terrain Studio mit Tabs für die 8 Werkzeugkategorien sowie Map und Settings. Es bietet große Werkzeugschaltflächen mit Icons, kontextabhängige Einstellungen, Paletten mit Vorschaubildern und eine Statusleiste.
  • Integration in die Scene View. Der Pinsel wird vom Terrain-Shader gezeichnet und auf das Terrain projiziert. Dazu kommen Overlays für Raster und Pathing, Symmetrie-Hilfslinien und Tastenkürzel: [ / ] Pinselgröße, - / = Stärke, 1–8 Werkzeugkategorien, G Raster, P Pathing-Overlay und R zufällige Rotation. Du kannst sie unter Edit ▸ Shortcuts... (unter macOS Unity ▸ Shortcuts...) neu belegen.
  • Änderungen an der Karte sind Teil von Unitys Undo-Verlauf, deshalb funktionieren Ctrl+Z und Ctrl+Y wie gewohnt und zeigen die Namen der Schritte an. Das Speicherbudget für den Undo-Verlauf lässt sich einstellen.
  • Eigene Inspectors für Tilesets (Schichten per Drag-and-drop, Vorschauen, automatischer Aufbau der Textur-Arrays), Vegetations- und Dekorbibliotheken, Pinselformen, Auto-Paint-Profile und die Komponente RTSTerrain. Der Importer für .rtsmap-Dateien zeigt eine Vorschau der Minikarte.
  • Ein Welcome-Fenster, ein New Map Wizard und ein Projektvalidator mit Korrekturen per Klick.

Performance

  • Terrain-Meshes werden in Chunks von 16, 32 (Standard) oder 64 Zellen aufgeteilt. Sie werden mit Mesh.MeshDataArray in Burst-Jobs gebaut, und nur die Chunks, die du bearbeitest, werden neu gebaut.
  • Die rechenintensive Arbeit läuft in Burst-kompilierten Jobs (Unity Jobs System): Pinseloperationen, Mesh-Erzeugung, Ableitung des Pathings, Freiraum, A*, Nebel des Krieges, Vegetationsinstanzen, Wasser-Meshes, Kartengenerator, automatische Texturierung und das Backen der Minikarte.
  • Details werden prozedural mit Graphics.RenderMeshPrimitives gezeichnet. Bäume und instanziertes Dekor verwenden Graphics.RenderMeshInstanced. Bei allen erfolgt das Culling pro Patch anhand von Frustum und Entfernung.
  • Die Shader sind mit dem SRP Batcher kompatibel, und alle Chunks teilen sich ein einziges Terrain-Material.
  • Textur-Arrays werden für die aktive Plattform komprimiert (BC7, ASTC 6×6, ETC2 oder DXT5), in einer Auflösung deiner Wahl (256–2048).
  • Keine Garbage-Allokationen pro Frame in den Hot Paths, und der gesamte native Speicher wird freigegeben, wenn Komponenten deaktiviert werden.

Designentscheidungen auf einen Blick

Hier findest du die häufigsten Fragen zu Terrain-Werkzeugen für RTS-Spiele und wie RTS Terrain Studio sie beantwortet.

FrageAntwort
Wie viele Bodentexturen kann eine Karte verwenden? 16 Bodenschichten und 4 Klippentypen pro Tileset, und du kannst beliebig viele Tilesets anlegen. Diese Grenze ist technisch begründet: 16 Schichten passen genau in 4 RGBA-Splatmaps (ein Kanal pro Schicht), und alle 16 Bodenschichten und 4 Klippenschichten werden in zwei Textur-Arrays gepackt (Albedo/Höhe und Normalen, je 20 Slices). Dadurch rendert ein einziges Terrain-Material jeden Chunk.
Hat das Terrain Collider? Kann ich es ohne Physik abfragen? Beides ist möglich. Jeder Chunk kann einen MeshCollider auf einem Layer deiner Wahl erhalten (RTSTerrainSettings.colliderMode). Die Abfrage-API (GetHeight, GetNormal, Raycast, GetCliffLevel, IsWater, TryGetWaterHeight, GetPathing, IsWalkable, IsBuildable) verwendet überhaupt keine Physik. Sie funktioniert im Edit Mode, zur Laufzeit und bei ausgeschalteten Collidern und liefert genau die Oberfläche, die du siehst.
Kann ich große Welten aus mehreren Karten aufbauen? Ja. Eine Szene kann beliebig viele RTSTerrain-Komponenten enthalten, jede mit eigener Position und Y-Rotation. RTSTerrain.All, RTSTerrain.FindAt und RTSTerrain.RaycastAll arbeiten über alle Terrains hinweg. Dank des Chunk-basierten Renderings sind Karten mit 512×512 Zellen praxistauglich.
Landet der Pinsel genau unter dem Cursor, auch mit Bildschirmskalierung? Ja. Der Unity Editor verwendet HandleUtility.GUIPointToWorldRay, das die DPI-Skalierung korrekt berücksichtigt, und der In-Game-Editor verwendet Camera.ScreenPointToRay mit der Zeigerposition aus dem Input System. Den Pinselcursor zeichnet der Terrain-Shader und projiziert ihn dabei auf die Oberfläche. Du malst also genau dort, wo du ihn siehst.
Welche Texturformate und -größen kann ich verwenden? Gewöhnliche Unity-Texturen in beliebiger Größe und beliebigem Format. Der Tileset-Inspector skaliert sie für dich und packt sie in Texture2DArray-Assets (256 bis 2048 Pixel pro Slice, komprimiert oder unkomprimiert). Die Kanalbelegung ist dokumentiert: Albedo in RGB mit optionaler Höhe im Alphakanal oder in einer separaten Höhentextur, dazu eine Normal-Map.
Muss das Paket in einem festen Ordner liegen? Nein. Der Editor findet den Stammordner des Pakets über dessen Runtime-Assembly-Definition. Du kannst RTSTerrainStudio also an eine beliebige Stelle unter Assets verschieben (zum Beispiel nach Assets/Plugins/RTSTerrainStudio).
Kann ich die Kartengröße später ändern? Ja. Wähle im New Map Wizard eine beliebige Größe und vergrößere oder verkleinere die Karte später mit Resize. Dabei wählst du einen Anker, der festlegt, an welchen Seiten Zellen hinzugefügt oder entfernt werden.
Kann ich den Code lesen und ändern? Ja. Der Code ist in Assembly-Definitionen mit jeweils einem Namespace aufgeteilt. Die öffentliche API hat eine XML-Dokumentation, die Interna sind kommentiert, die Burst-Jobs liegen in eigenen *Jobs.cs-Dateien, und es gibt EditMode-Unit-Tests.

Ein erster Blick auf die API

Damit Programmierer einen Eindruck von der API bekommen, folgt hier ein vollständiges Skript. Es fragt beim Terrain ab, was über den Punkt bekannt ist, auf den du klickst, und plant mit dem integrierten Pathfinder einen Pfad dorthin. Es braucht weder Collider noch zusätzliche Komponenten: Hänge es an ein beliebiges GameObject in einer Szene mit einem RTSTerrain.

using System.Collections.Generic;
using RTSTerrainStudio;
using UnityEngine;

/// <summary>Click on the ground: logs what the terrain knows about that point and plans a path to it.</summary>
public class ClickInfoExample : MonoBehaviour
{
    // Reused for every click, so no new list is allocated each time.
    readonly List<Vector3> path = new List<Vector3>();

    void Update()
    {
        // RTSInput works with the Input System package and with the old Input Manager.
        if (!RTSInput.GetMouseButtonDown(0) || Camera.main == null) return;

        Ray ray = Camera.main.ScreenPointToRay(RTSInput.PointerPosition);

        // Analytic ray cast against every RTSTerrain in the scene. Colliders are not needed.
        if (!RTSTerrain.RaycastAll(ray, out RTSTerrainHit hit)) return;
        RTSTerrain terrain = hit.terrain;

        // Gameplay questions, answered from the map data and the pathing map.
        int level = terrain.GetCliffLevel(hit.point);
        bool buildable = terrain.IsBuildable(hit.point);
        bool water = terrain.IsWater(hit.point, out float depth);
        Debug.Log($"Cell {hit.cell}: cliff level {level}, buildable {buildable}, water {water} ({depth:0.00} m)");

        // Built-in Burst A* on the pathing map, for a unit with a radius of 0.6 m.
        PathStatus status = RTSPathfinder.FindPath(terrain, transform.position, hit.point,
                                                   PathRequestOptions.ForUnit(0.6f), path);
        Debug.Log($"Path from {name}: {status}, {path.Count} waypoints");
    }
}

PathStatus ist Success, Partial (der nächstgelegene erreichbare Punkt), NoPath oder InvalidStart. Das Kapitel Scripting-Leitfaden erklärt die gesamte API.

Voraussetzungen

Unity und Render-Pipeline

VoraussetzungVersionHinweise
Unity6.3 LTS (6000.3) oder neuerEntwickelt und getestet mit 6000.3.3f1.
Universal Render Pipeline17.3 (die Version, die mit Unity 6.3 ausgeliefert wird)Ein URP-Asset muss in den Einstellungen Graphics und Quality aktiv sein. Entwickelt mit dem Rendering-Pfad Forward+. Der Terrain-Shader hat außerdem einen GBuffer-Pass für den Deferred-Pfad.
FarbraumLinearGamma funktioniert, aber Farben, Beleuchtung und Wasser wirken ausgewaschen.
GrafikShader Model 3.5 oder höherDie Terrain-, Vegetations-, Detail- und Wasser-Shader sind auf #pragma target 3.5 ausgelegt und laufen daher auch unter WebGL 2.0 und OpenGL ES 3.0. Nur der GBuffer-Pass des Terrains (Rendering-Pfad Deferred) und der Structured-Buffer-Pfad des Detail-Shaders verwenden 4.5, sofern die GPU dies unterstützt.

Die Built-in Render Pipeline und HDRP werden nicht unterstützt. Alle Shader sind für URP geschrieben, und das Renderer Feature für den Nebel des Krieges verwendet die RenderGraph-API von URP.

In ein Projekt ohne URP importiert? Dann bleibt das Paket inaktiv, statt die Console mit Fehlern zu füllen: Seine Skripte werden nicht kompiliert, seine Shader werden übersprungen, und dein Projekt funktioniert weiter wie bisher. Einmal pro Editorsitzung erklärt der Dialog RTS Terrain Studio requires URP, was zu tun ist:

  • Built-in Render Pipeline: Klicke auf Install URP. Sobald Unity das Paket kompiliert hat, öffne Tools ▸ RTS Terrain Studio ▸ Validate Project und klicke auf Use URP. Dadurch wird ein URP-Asset erstellt und in den Einstellungen Graphics und Quality zugewiesen.
  • URP älter als 17.3: Klicke auf Open Package Manager und aktualisiere die Universal Render Pipeline auf die Version, die mit deinem Editor ausgeliefert wird.
  • Unity älter als 6.3: URP 17.3 benötigt Unity 6.3 LTS, daher bietet der Dialog nur Open Manual und Later an. Öffne das Projekt mit Unity 6.3 LTS oder neuer.
  • HDRP oder eine eigene Render-Pipeline: Importiere das Paket stattdessen in ein URP-Projekt, zum Beispiel in ein neues Projekt aus der Projektvorlage Universal 3D.

Tools ▸ RTS Terrain Studio ▸ Set Up Universal Render Pipeline... zeigt den Dialog erneut an. Außerdem kann Unity Warnungen ausgeben, dass die Shader des Pakets nicht unterstützt werden. Sie verschwinden, sobald URP installiert ist.

Benötigte Pakete

Diese Pakete stammen aus der Unity Registry (Window ▸ Package Manager ▸ Unity Registry). Das Fenster Tools ▸ RTS Terrain Studio ▸ Validate Project erkennt fehlende Pakete und installiert sie mit einem Klick (Prüfpunkt Burst, Collections and Mathematics).

PaketNameGetestete VersionVerwendet für
Burstcom.unity.burst1.8.27Kompiliert die Jobs zu schnellem nativem Code.
Collectionscom.unity.collections2.6.2Native Container für Kartendaten und Jobs.
Mathematicscom.unity.mathematics1.3.3Vektormathematik in Jobs und im Datenmodell.
Universal RPcom.unity.render-pipelines.universal17.3.0Rendering.

Eingabeverarbeitung

Alle Eingaben zur Laufzeit laufen über die statische Klasse RTSInput. Sie unterstützt beide Eingabe-Backends:

  • Paket Input System (com.unity.inputsystem, getestet mit 1.17.0). Es wird verwendet, wenn das Paket installiert ist und Active Input Handling auf Input System Package (new) oder Both steht.
  • Input Manager (old). Er dient als Fallback, wenn das Projekt die alte Eingabeverarbeitung verwendet.

Diese Einstellung findest du unter Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Active Input Handling. Der Prüfpunkt Input handling des Projektvalidators zeigt, welcher Modus aktiv ist; siehe Eingabeverarbeitung im Kapitel Projekteinrichtung.

Optionale Pakete für die Integrationen

IntegrationAbhängigkeitVersionErkennung
MirrorMirror (Asset Store oder GitHub)96 oder neuerScripting-Define MIRROR, das Mirror selbst hinzufügt.
A* Pathfinding Projectcom.arongranberg.astarPro 5.4.4 bis (ausschließlich) 6.0, getestet mit 5.4.7Version-Define RTS_ASTAR. Füge RTS_ASTAR_DISABLED hinzu, um die Integration abzuschalten (nötig für die kostenlose Version, die denselben Paketnamen verwendet).
Unity NavMeshAI Navigation, com.unity.ai.navigation2.0 oder neuer, getestet mit 2.0.9Version-Define RTS_AI_NAVIGATION.

Die Integration für das A* Pathfinding Project benötigt dessen Pro-Version, weil sie RVO-basierte lokale Kollisionsvermeidung und TraversalConstraint verwendet. Bevor du Mirror löschst, lies Mirror entfernen: Mirror hinterlässt seine Define-Symbole, und RTS Terrain Studio enthält eine kleine Editor-Assembly, die sie entfernt.

Empfohlene Projekteinstellungen

Der Projektvalidator prüft all diese Punkte und kann sie für dich korrigieren:

EinstellungGrund
URP-Asset aktivDie Shader des Pakets rendern nur in URP.
Depth Texture und Opaque Texture im URP-AssetWasser verwendet die Depth Texture, damit Tiefenfarbe, Schaum und der weiche Übergang am Ufer auch auf Objekte reagieren, die im Wasser stehen. Die Opaque Texture braucht es für die Lichtbrechung. Die eigene Einstellung einer Kamera hat Vorrang vor dem URP-Asset, und das Wasser richtet sich nach jeder Kamera einzeln. Die Demokameras schalten beide selbst ein, daher sehen die Demoszenen in der Game View auch ohne diese Einstellung richtig aus (die Scene View verwendet immer das URP-Asset).
Renderer Feature für den Nebel des Krieges im RendererVerdunkelt alles im Nebel, auch deine eigenen Materialien. Ohne das Renderer Feature zeigen nur die Shader des Pakets den Nebel.
Nebelmodi in Player-Builds beibehaltenUnity entfernt Shader-Varianten für Nebel, die keine Build-Szene verwendet, aber RTSEnvironment schaltet den atmosphärischen Nebel zur Laufzeit ein. Ohne beibehaltene Nebelmodi zeigen Karten mit Nebel in Builds keinen Nebel.
Linearer FarbraumKorrekte Farben und Beleuchtung.
Shader-Referenzen und Textur-Arrays der Tilesets aktuellShader und komprimierte Arrays werden in Player-Builds aufgenommen. Vor jedem Player-Build aktualisiert das Paket automatisch das Shader-Referenz-Asset und erstellt veraltete Textur-Arrays neu.

Plattformen

Der Laufzeitcode hat keine Abhängigkeiten, die nur im Editor existieren. Deshalb lassen sich Karten in Player-Builds auf jeder Plattform laden, rendern und bearbeiten, die URP unterstützt, auch in Web-Builds (WebGL 2.0). Die Shader sind auf Shader Model 3.5 ausgelegt, das niedrigste Shader Model mit Textur-Arrays. Der GBuffer-Pass des Terrains für den Rendering-Pfad Deferred benötigt Shader Model 4.5 und wird auf OpenGL-basierten APIs übersprungen, genau wie der GBuffer-Pass des Lit-Shaders von URP. Detailgras wählt zur Laufzeit, wie es seine Instanzen speichert: in Structured Buffers, wo die GPU sie unterstützt, und in einer Float-Textur unter WebGL 2.0 und OpenGL ES 3 (das Ergebnis sieht gleich aus).

Beim Backen der Textur-Arrays wird die Kompression passend zum aktiven Build-Ziel gewählt: BC7 für Desktop und Konsolen, ASTC 6×6 für Android, iOS, tvOS und visionOS, für Web die im Build eingestellte Texturkompression (DXT5, ASTC 6×6 oder ETC2) und DXT5 für alles andere. Der In-Game-Editor läuft auf allen Plattformen und ist für Maus und Tastatur ausgelegt. Seine Befehle zum Anzeigen des Kartenordners und zum Beenden der Anwendung erscheinen nur dort, wo die Plattform sie unterstützt: Nur Desktop-Player und der Editor können Ordner anzeigen, und WebGL, iOS und Android können die Anwendung nicht beenden. Web-Builds unterscheiden sich in einigen weiteren Punkten; siehe Web-Builds (WebGL).

Ordnerstruktur

Das Paket liegt in einem einzigen Ordner, standardmäßig Assets/RTSTerrainStudio. Du kannst diesen Ordner verschieben oder umbenennen: Der Editor findet den Stammordner des Pakets über den Speicherort von RTSTerrainStudio.Runtime.asmdef, und die Pfade zu Demo-Inhalten und Dokumentation werden relativ dazu bestimmt.

RTSTerrainStudio/
├── Runtime/                 Core, rendering, tools and runtime systems (RTSTerrainStudio.Runtime)
├── Editor/                  Editor window, Scene View, inspectors, setup, demo content (RTSTerrainStudio.Editor)
├── Shaders/                 URP shaders and HLSL includes
│   ├── Hidden/              Internal shaders (fog blur, minimap fog, gizmos, texture array blit)
│   └── Include/             Shared HLSL (fog of war, brush cursor, wind, terrain, vegetation, water)
├── Samples/
│   ├── Scripts/             Sample gameplay: camera, units, selection, orders, minimap, HUD (RTSTerrainStudio.Samples)
│   └── Scenes/              Demo scenes (created by Create Demo Content)
├── Integrations/
│   ├── Mirror/              Multiplayer (compiled only with Mirror)
│   ├── AStarPathfinding/    A* Pathfinding Project (compiled only with A* Pro 5.4.4+)
│   └── UnityNavMesh/        Unity AI Navigation (compiled only with com.unity.ai.navigation 2.0+)
├── Content/                 Generated demo content (empty until you run Create Demo Content)
├── Resources/
│   └── RTSTerrainStudio/    Runtime UI style sheets and theme, shader references, panel settings
├── Documentation/           This manual (index.html) and its images
└── Tests/
    └── Editor/              EditMode unit tests (RTSTerrainStudio.Tests.Editor)

Ordner der obersten Ebene

OrdnerInhaltKann ich ihn löschen?
RuntimeAlles, was im Spiel läuft: die Komponente RTSTerrain, Kartendaten, Rendering, alle Bearbeitungswerkzeuge, Vegetation, Wasser, Dekor, Nebel des Krieges, Pathing, Generierung, der In-Game-Editor, Icons und Eingabe.Nein.
EditorNur Funktionen für den Unity Editor. Nichts davon landet in Player-Builds.Nein.
ShadersRTS Terrain Studio/Terrain, RTS Terrain Studio/Vegetation, RTS Terrain Studio/Water, Hidden/RTS Terrain Studio/Detail, Hidden/RTS Terrain Studio/FogOfWarFullscreen, die internen Shader in Hidden und die HLSL-Includes in Include (RTSCommon.hlsl enthält die globalen Variablen für Nebel des Krieges, Pinselcursor und Wind).Nein.
SamplesGameplay-Beispielskripte und die Demoszenen. Die Editor-Assembly und die Integrationen referenzieren die Beispiel-Assembly (zum Beispiel für RTSUnit und RTSDemoGame).Nur zusammen mit den Integrationen. Die Szenen kannst du jederzeit löschen.
IntegrationsEin Ordner pro Drittanbieter-Integration, jeweils mit eigenen Assemblies, einer README.md und einem Editor-Ordner mit Skripten, die die Demoszenen erstellen. Demoszenen werden in einem Unterordner Demo angelegt.Ja, jeden Integrationsordner einzeln. Lies vorher die Schritte für Mirror.
ContentTexturen, Materialien, Meshes, Prefabs, Tilesets, Bibliotheken, Pinselformen, Karten und Render-Einstellungen für die Demo. Alles darin ist generiert.Ja, du kannst ihn jederzeit neu generieren. Dann dürfen deine eigenen Karten aber nichts daraus verwenden.
Resources/RTSTerrainStudioUI/RTSRuntimeEditor.uss und UI/RTSRuntimeTheme.tss (Stil der In-Game-UI). Außerdem werden hier das Shader-Referenz-Asset (vom Editor automatisch erstellt und aktualisiert) und UI/RTSRuntimePanelSettings.asset (von Create Demo Content erstellt) abgelegt. Sie sorgen dafür, dass die Shader des Pakets und die Laufzeit-Shader von UI Toolkit in Builds aufgenommen werden.Nein.
DocumentationDieses Handbuch. Tools ▸ RTS Terrain Studio ▸ Documentation und jede Schaltfläche ? öffnen es.Ja, aber dann können die Hilfe-Schaltflächen nichts mehr öffnen.
TestsEditMode-Tests für Kartendaten, Geometrie, Pinsel, Symmetrie, Rückgängigmachen, den Serializer, Pathing, den Generator, Terrain-Abfragen, die Werkzeug-Registry und Icons. Du führst sie unter Window ▸ General ▸ Test Runner aus.Ja.

Im Ordner Runtime

OrdnerInhalt
CoreRTSTerrain (Hauptkomponente, Lebenszyklus der Karte, Änderungsverfolgung, Abfragen), MapData, MapTypes, MapGeometryView (die Referenzoberfläche für Meshes, Abfragen und Pathing), PathingMap, RTSTerrainSettings, Shader-Property-IDs, RTSShaderReferences, RTSRuntimeEditorEvents.
DataAsset-Typen: RTSTileset, RTSVegetationLibrary, RTSDecorLibrary, RTSMapAsset.
SerializationRTSMapSerializer: das binäre .rtsmap-Format (Dateiabschnitte, Deflate-Kompression) und Datei-Ein-/Ausgabe.
BrushesBrushSettings, RTSBrushShape, SymmetrySettings, BrushUtility.
ToolsRTSTool (Basisklasse), RTSEditSession, MapUndo und ein Unterordner pro Kategorie: Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers.
MeshErzeugung der Chunk-Geometrie und die zugehörigen Burst-Jobs.
RenderingDer Rendering-Teil von RTSTerrain, RTSTerrainChunk, Splat-Texturen, Schichtdaten und RTSTextureArrayBuilder.
VegetationRTSVegetationRenderer, RTSTreeInstance.
WaterRTSWaterRenderer, RTSWaterChunk, Jobs für das Wasser-Mesh.
DecorRTSDecorManager, RTSDecorInstance.
FogOfWarRTSFogOfWarManager, RTSFogOfWarRevealer, RTSFogOfWarHideable, RTSFogOfWarRendererFeature.
PathingDer Pathing-Teil von RTSTerrain, RTSPathingSettings, PathingMapBuilder, ClearanceMap, RTSPathfinder, RTSPathfindingService, Pathing-Overlays.
GenerationRTSMapGenerator, MapGeneratorSettings, RTSMapGeneratorPreset, RTSAutoPaintProfile, RTSAutoPaint, HeightmapIO und das Werkzeug Auto Paint.
MinimapRTSMinimapBaker.
EnvironmentRTSEnvironment (wendet Sonne, Umgebungslicht und Nebel der Karte an).
ValidationMapValidator (prüft eine Karte auf Probleme wie unerreichbare Startpositionen).
RuntimeEditorDer In-Game-Editor: RTSRuntimeEditor, RTSRuntimeEditorCamera, RuntimeToolGizmos und seine mit UI Toolkit gebaute Oberfläche.
InputRTSInput und RTSKey.
UIRTSIcons: prozedurale Icons, die der Editor und die In-Game-UI gemeinsam nutzen.
Plugins/WebGLJavaScript-Plugins (.jslib), die nur Web-Builds verwenden: Browserspeicher, Downloads, Importe und die Warnung beim Verlassen der Seite (RTSWebFileSystem.jslib), Tastaturlayouts und Browser-Tastenkürzel (RTSWebKeyboard.jslib) sowie die Erkennung, wenn der Mauszeiger die Seite verlässt (RTSPointer.jslib).
link.xmlSorgt dafür, dass die Bearbeitungswerkzeuge in Builds mit Code-Stripping erhalten bleiben (siehe Code-Stripping und eigene Werkzeuge).

Im Ordner Editor

OrdnerInhalt
CoreRTSEditorContext (die gemeinsame Bearbeitungssitzung und die Ansichtsoptionen), RTSEditorPaths, Speicherung der Werkzeugeinstellungen.
WindowDas Fenster von RTS Terrain Studio (RTSTerrainStudioWindow) und seine Seiten.
SceneViewMalen in der Scene View, Zeichnen von Pinsel und Gizmos, Overlays, Tastenkürzel und die Brücke zum Undo-System von Unity.
InspectorsInspectors für Tileset, Bibliotheken, Pinselformen, Auto-Paint-Profile und Komponenten.
ImportersDer .rtsmap-Importer, seine Vorschau und Assets ▸ Create ▸ RTS Terrain Studio ▸ Map (.rtsmap).
BuildingDas Backen der komprimierten Textur-Arrays, die Prüfung, die veraltete Textur-Arrays vor Player-Builds neu erstellt, der Vorschaubild-Generator und die Asset-Factory.
SetupWelcome-Fenster, New Map Wizard, Projektvalidator, das GameObject-Erstellungsmenü, die Aktualisierung der Shader-Referenzen und das Web-Template (WebGLTemplate) mit seinem Installer.
RequirementsDie Hinweismeldung, die erscheint, wenn im Projekt URP 17.3 fehlt (siehe Unity und Render-Pipeline). Abgesehen von der Bereinigung der Mirror-Defines ist das der einzige Teil des Pakets, der ohne URP kompiliert.
RuntimeEditorSupportDas Menü GameObject ▸ RTS Terrain Studio ▸ In-Game Editor.
DemoContentDer prozedurale Generator für Demo-Inhalte (RTSDemoContent).

Assemblies

Der Code ist in Assembly-Definitionen aufgeteilt. So kompiliert er schnell, und die optionalen Teile lassen sich entfernen. Jede Assembly hat genau einen Namespace.

Assembly (asmdef)OrdnerNamespacePlattformenKompiliert, wenn
RTSTerrainStudio.RuntimeRuntimeRTSTerrainStudioAlleURP 17.3 oder neuer ist installiert (RTS_URP). Definiert RTS_INPUT_SYSTEM, wenn das Paket Input System installiert ist.
RTSTerrainStudio.SamplesSamples/ScriptsRTSTerrainStudio.SamplesAlleURP 17.3 oder neuer ist installiert.
RTSTerrainStudio.EditorEditorRTSTerrainStudio.EditorEditorURP 17.3 oder neuer ist installiert.
RTSTerrainStudio.Requirements.EditorEditor/RequirementsRTSTerrainStudio.RequirementsEditorNur solange URP 17.3 oder neuer fehlt. Sie zeigt die Hinweismeldung, dass das Paket URP benötigt.
RTSTerrainStudio.Tests.EditorTests/EditorRTSTerrainStudio.TestsEditorUNITY_INCLUDE_TESTS ist definiert (Unity Test Framework).
RTSTerrainStudio.Integrations.Mirror
RTSTerrainStudio.Integrations.Mirror.Editor
Integrations/MirrorRTSTerrainStudio.Integrations.MirrorAlle / EditorMIRROR ist definiert.
RTSTerrainStudio.Integrations.Mirror.DefineCleanup.EditorIntegrations/Mirror/Editor/DefineCleanupRTSTerrainStudio.Integrations.MirrorEditorImmer. Sie hängt nicht von Mirror ab und entfernt verwaiste Mirror-Defines, nachdem Mirror gelöscht wurde.
RTSTerrainStudio.Integrations.AStar
RTSTerrainStudio.Integrations.AStar.Editor
Integrations/AStarPathfindingRTSTerrainStudio.Integrations.AStarAlle / EditorRTS_ASTAR (A* 5.4.4 bis unter 6.0 installiert) und nicht RTS_ASTAR_DISABLED.
RTSTerrainStudio.Integrations.NavMesh
RTSTerrainStudio.Integrations.NavMesh.Editor
Integrations/UnityNavMeshRTSTerrainStudio.Integrations.NavMeshAlle / EditorRTS_AI_NAVIGATION (AI Navigation 2.0 oder neuer installiert).

Die Test- und Integrations-Assemblies benötigen zusätzlich zur angegebenen Bedingung URP 17.3 oder neuer. Ohne URP werden nur die Bereinigung der Mirror-Defines und die Hinweismeldung zu den Voraussetzungen kompiliert. Das Paket verursacht daher keine Kompilierfehler.

RTSTerrainStudio.Runtime, RTSTerrainStudio.Samples und RTSTerrainStudio.Editor werden automatisch referenziert. Skripte in der Standard-Assembly deines Projekts (Assembly-CSharp) können sie ohne weitere Einrichtung verwenden. Hat dein Code eine eigene Assembly-Definition, füge eine Referenz auf RTSTerrainStudio.Runtime hinzu (und auf RTSTerrainStudio.Samples, wenn du die Gameplay-Beispielklassen verwendest).

Der In-Game-Editor liegt in Runtime/RuntimeEditor. Wenn dein Spiel ihn nicht braucht, kannst du diesen Ordner löschen: Die Werkzeuge im Unity Editor und der Generator für Demo-Inhalte kompilieren weiterhin, und die Szene In-Game Editor Demo wird dann ohne In-Game-Editor erstellt.

Lieferumfang

Das Paket besteht aus Quellcode, Shadern und diesem Handbuch. Alles Visuelle (Texturen, Modelle, Tilesets, Karten und Szenen) erzeugt ein Skript auf deinem Rechner. Das hält den Download klein, und die Inhalte sind genau wie der Code lizenziert.

Demo-Inhaltsgenerator

Tools ▸ RTS Terrain Studio ▸ Create Demo Content führt RTSDemoContent.CreateAll aus. Der Befehl erzeugt alle Demo-Assets prozedural mit festen Seeds, daher liefert jeder Durchlauf dasselbe Ergebnis:

  • Der Generator schreibt nur in seine eigenen Ordner: Content, Samples/Scenes und zwei Assets in Resources/RTSTerrainStudio. Deine eigenen Assets werden nie angetastet.
  • Führst du ihn erneut aus, erzeugt er die Assets an Ort und Stelle neu, sodass Verweise darauf gültig bleiben. Kartendateien, deren Inhalt sich nicht geändert hat, werden nicht neu geschrieben, daher zeigt die Versionsverwaltung keine Änderungen.
  • Er schließt nie deine geöffneten Szenen. Demoszenen werden während ihres Aufbaus additiv geöffnet.
  • Am Ende bietet er an, die Demoszenen zu den Build-Einstellungen hinzuzufügen. Es wird nur dann etwas hinzugefügt, wenn du auf Add Scenes klickst.
ErzeugtOrt (unterhalb des Paketstammordners)Inhalt
TexturenContent/Textures/ (Ground, Cliffs, Water, Details, Props, Brushes)Kachelbare Boden-, Klippen-, Wasser- und Prop-Texturen (512 px), Gras- und Blumen-Sprites sowie Pinselstempel (256 px), jeweils mit passenden Normal-Maps.
Materialien, Meshes, PrefabsContent/Materials, Content/Meshes, Content/PrefabsLow-Poly-Bäume, Felsen, Ruinen und Props mit Materialien.
TilesetsContent/Tilesets/Summer.asset, Winter.assetZwei Tilesets mit je 10 Bodenschichten und 2 Klippentypen. Summer: Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow und Blight, dazu Rock Cliff und Grassy Cliff. Winter: die gefrorenen Varianten, mit Ice Cliff und Snowy Cliff.
Auto-Paint-ProfilContent/Tilesets/Summer Auto Paint.assetRegeln für das Tileset Summer, zum Beispiel Erdflecken auf sanften Hängen und Sand entlang der Ufer.
VegetationsbibliothekContent/Libraries/Demo Vegetation.assetDetails: Grass, Tall Grass, Flowers Yellow, Flowers Purple, Fern. Bäume: Pine, Broadleaf, Dead Tree, Bush.
DekorbibliothekContent/Libraries/Demo Decor.assetRock A, Rock B, Rock C, Boulder, Ruined Pillar, Crate, Barrel, Fence, Tree Stump, Mushrooms. Es sind sowohl instanzierte als auch GameObject-Prototypen dabei, und sie haben Pathing-Grundflächen.
PinselformenContent/BrushesEigene Stempel: Noise Blob, Splatter, Star, Soft Square.
KartenContent/MapsDrei .rtsmap-Dateien (siehe unten).
Render-EinstellungenContent/SettingsEin Post-Processing-Volume-Profil, das die Demoszenen verwenden.
SzenenSamples/ScenesDrei Demoszenen (siehe unten).
ResourcesResources/RTSTerrainStudioDas Shader-Referenz-Asset und UI/RTSRuntimePanelSettings.asset, damit die Shader und die UI in Builds enthalten sind.

Demokarten

Alle drei Karten verwenden 2 m große Zellen, eine Stufenhöhe von 2 m und das Tileset Summer.

KarteGrößeSymmetrieBeschreibung
Two Rivers (1v1).rtsmap128 × 128 ZellenRotate 2Zwei Spieler, zwei Flüsse. Überall führen flache Furten durch die Flüsse, und der See in der Mitte ist tief. Rampen führen hinauf zu bewaldeten Plateaus mit Ruinen und verlassenen Lagern.
Four Kingdoms (4p).rtsmap192 × 192 ZellenRotate 4Vier Königreiche, jedes auf seinem eigenen Hochland. Rampen führen hinab in das gemeinsame Tiefland, und in der Mitte liegt verseuchter Boden mit den Ruinen eines alten Tempels.
Sandbox.rtsmap96 × 96 ZellenNoneEine kleine, überwiegend flache Karte zum Ausprobieren der Werkzeuge.
Draufsicht auf eine Demokarte mit Plateaus, Flüssen, Wäldern und Startpositionen
Draufsicht auf eine Demokarte.

Demoszenen

SzeneKarteWas sie zeigt
RTS Terrain Studio - Gameplay DemoTwo Rivers (1v1)Klicke auf Play, um zu spielen: Einheiten an den Startpositionen (die Gegner streifen umher), Nebel des Krieges, RTS-Kamera, Auswahl, Befehle, Minikarte, HUD, Gebäudeplatzierung (B) und Abbau von Bäumen (T).
RTS Terrain Studio - In-Game Editor DemoSandboxDer In-Game-Editor im Play Mode. Bearbeite die Karte und drücke dann F5, um sie mit Einheiten im Spiel zu testen.
RTS Terrain Studio - Editor PlaygroundFour Kingdoms (4p)Eine Karte ohne Gameplay, um die Werkzeuge im Unity Editor in der Scene View auszuprobieren.

Jede Demoszene enthält eine Sonne, ein Post-Processing-Volume, eine Kamera, die die Depth- und die Opaque-Textur anfordert, und ein Terrain mit RTSTerrain, RTSVegetationRenderer, RTSWaterRenderer, RTSDecorManager und RTSEnvironment.

Jede Integration bringt einen eigenen Befehl zum Erstellen ihrer Demoszene mit. Er setzt die Demo-Inhalte voraus und legt die Szene im Ordner Demo der Integration an:

  • Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene erstellt RTS Mirror Demo.unity.
  • Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene erstellt RTS AStar Demo.unity.
  • Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Unity NavMesh ▸ Create Demo Scene erstellt RTS NavMesh Demo.unity.

Beispielskripte

Der Ordner Samples/Scripts (RTSTerrainStudio.Samples) ist ein kleines, gut lesbares RTS-Framework, von dem du lernen oder auf dem du aufbauen kannst:

BereichKlassen
InitialisierungRTSDemoGame, RTSUnitSpawner
Einheiten und BewegungRTSUnit, RTSUnitFactory, RTSUnitRegistry, RTSFormation, RTSWanderAI
Pfad-ProviderIRTSUnitPathProvider, RTSBuiltinPathProvider, RTSUnitPathProviderComponent, RTSUnitPathProviderRegistry
Kamera, Eingabe, Auswahl, BefehleRTSCameraController, RTSGameInput, RTSSelectionManager, RTSCommandController
UIRTSMinimapUI, RTSDemoHUD, RTSRuntimeUI
Gameplay-DemosRTSBuildingPlacementDemo, RTSDemoBuilding, RTSTreeHarvestDemo, RTSFallingTreeEffect
HilfsklassenRTSMoveMarker, RTSGroundRing, RTSSampleMaterials, RTSSampleMeshes, RTSSampleUtility

Icons

Alle Icons im Editorfenster, in den Inspectors und im In-Game-Editor zeichnet RTSIcons prozedural. Dadurch bleiben sie in jeder Größe scharf und funktionieren in Builds ohne Textur-Assets.

Übersicht aller Icons von RTS Terrain Studio: Werkzeugkategorien, Werkzeuge und Aktionen
Der Icon-Satz, den der Editor und die In-Game-UI verwenden.

Menüs auf einen Blick

MenüeintragFunktion
Tools ▸ RTS Terrain Studio ▸ Editor WindowÖffnet das Hauptfenster zum Bearbeiten.
Tools ▸ RTS Terrain Studio ▸ New Map Wizard...Erstellt Schritt für Schritt eine neue Karte (Größe, Tileset, flach oder generiert).
Tools ▸ RTS Terrain Studio ▸ Create Demo ContentErzeugt die oben beschriebenen Demo-Inhalte.
Tools ▸ RTS Terrain Studio ▸ Validate ProjectPrüft URP, Projekteinstellungen, Pakete, Inhalte und Szene und bietet Korrekturen an.
Tools ▸ RTS Terrain Studio ▸ Install WebGL TemplateInstalliert das Web-Template, das das Browserfenster ausfüllt, und wählt es aus (siehe Das Vollfenster-Template).
Tools ▸ RTS Terrain Studio ▸ WelcomeSchnellstart-Schritte, Links und der Status der Integrationen.
Tools ▸ RTS Terrain Studio ▸ DocumentationÖffnet dieses Handbuch.
Tools ▸ RTS Terrain Studio ▸ Integrations ▸ …Befehle zum Erstellen der Demoszenen und Hilfsfunktionen der installierten Integrationen.
GameObject ▸ RTS Terrain Studio ▸ RTS TerrainErstellt ein GameObject RTS Terrain mit RTSTerrain, Vegetation Renderer, Water Renderer und Decor Manager, dem Standard-Tileset und den Standardbibliotheken des Projekts sowie einer neuen .rtsmap-Datei mit flacher Karte.
GameObject ▸ RTS Terrain Studio ▸ Fog of War ManagerErstellt den Manager für den Nebel des Krieges und bietet an, das Renderer Feature hinzuzufügen.
GameObject ▸ RTS Terrain Studio ▸ EnvironmentErstellt ein RTSEnvironment (und eine Sonne, falls die Szene keine hat).
GameObject ▸ RTS Terrain Studio ▸ In-Game EditorFügt den In-Game-Editor zur Szene hinzu.
GameObject ▸ RTS Terrain Studio ▸ Demo GameFügt RTSDemoGame hinzu, das jedes Terrain mit Startpositionen in eine spielbare Demo verwandelt.
Assets ▸ Create ▸ RTS Terrain Studio ▸ …Map (.rtsmap), Tileset, Vegetation Library, Decor Library, Brush Shape, Auto Paint Profile, Map Generator Preset sowie Advanced ▸ Shader References. Den letzten Eintrag brauchst du selten: Der Validator und Player-Builds erstellen und aktualisieren RTSShaderReferences.asset automatisch.

Die Einträge im Menü GameObject sind im Play Mode deaktiviert, weil sie Assets und Szenenobjekte erstellen, die gespeichert werden müssen.

Schnellstart

Dieses Kapitel führt dich von einem leeren Unity-Projekt zu einer selbst gebauten Karte, auf der du mit Einheiten, Nebel des Krieges und Minikarte gespielt hast. Du brauchst keine Programmierkenntnisse. Arbeite die nummerierten Schritte der Reihe nach ab. Jeder Schritt nennt dir genau das Menü, das Fenster oder die Schaltfläche, die du brauchst.

TeilWas du tustZeit
Installation und EinrichtungImportiere das Paket, lasse den Projektvalidator die URP-Einstellungen korrigieren und erzeuge die Demo-Inhalte.etwa 5 min
DemoszenenSpiele eine fertige Karte, probiere den In-Game-Editor aus und erkunde den Editor Playground.5 min
Deine erste KarteErstelle ein Terrain, male Höhen, Klippen, Texturen, Bäume und Wasser, platziere Startpositionen und speichere.10 min
Dein erster SpieltestSpiele deine Karte mit dem Demospiel, oder bearbeite und teste sie direkt im laufenden Spiel.5 min
10-Minuten-TutorialBaue von Anfang bis Ende eine faire 1v1-Karte mit symmetrischem Malen.10 min

Du kannst dieses Kapitel jederzeit über den Link Quick Start Guide im Welcome-Fenster öffnen (Tools ▸ RTS Terrain Studio ▸ Welcome). Das vollständige Handbuch öffnest du mit Tools ▸ RTS Terrain Studio ▸ Documentation.

Installation und Einrichtung

Voraussetzungen

ElementVersionHinweise
Unity6.3 LTS (6000.3)Verwende die Editor-Version, für die das Paket erstellt wurde.
Universal Render Pipeline17.3Erforderlich. Alle Shader für Terrain, Wasser, Vegetation und Nebel des Krieges sind URP-Shader. Wenn du ein neues Projekt anlegst, beginne mit der Projektvorlage Universal 3D. Ohne URP bleibt das Paket inaktiv und zeigt eine Hinweismeldung an (siehe Voraussetzungen).
Burst, Collections, Mathematicsaus der Unity RegistryErforderlich. Die rechenintensive Arbeit (Mesh-Aufbau, Pinsel, Pathing, Nebel des Krieges, Wegfindung) läuft in Burst-Jobs.
Input Systemaus der Unity RegistryEmpfohlen. Wird vom In-Game-Editor, der RTS-Kamera und den Beispielen verwendet. Der ältere Input Manager funktioniert als Ausweichlösung.
Mirror, A* Pathfinding Project, AI NavigationoptionalNur für die jeweilige Integration nötig. Die Integrationen werden automatisch kompiliert, sobald das zugehörige Paket installiert ist. Siehe Integrationen.

Das Paket importieren

  1. Öffne dein Projekt in Unity 6.3. Stelle sicher, dass es URP verwendet (neue Projekte aus der Vorlage Universal 3D tun das bereits).
  2. Öffne Window ▸ Package Manager, wähle My Assets, suche RTS Terrain Studio und klicke auf Download und dann auf Import.
  3. Lasse im Importdialog alle Häkchen gesetzt und klicke auf Import. Das Paket landet in Assets/RTSTerrainStudio.
  4. Warte, bis Unity mit dem Kompilieren fertig ist. Das Fenster Welcome öffnet sich von selbst (siehe unten).

Du kannst den Ordner RTSTerrainStudio an eine beliebige Stelle innerhalb von Assets verschieben. Das Paket findet seine eigenen Dateien, egal wo der Ordner liegt. Wenn du dein Projekt umorganisierst, geht also nichts kaputt.

Zeigt die Console direkt nach dem Import Kompilierfehler, fehlt höchstwahrscheinlich eines der Pakete Burst, Collections oder Mathematics. Installiere sie über Window ▸ Package Manager ▸ Unity Registry. Sobald sie installiert sind, verschwinden die Fehler.

Erscheint statt des Welcome-Fensters der Dialog RTS Terrain Studio requires URP, hat das Projekt kein URP 17.3. Das Paket bleibt dann inaktiv und verändert nichts. Folge den Schritten im Dialog oder lies Unity und Render-Pipeline.

Das Welcome-Fenster

Das Welcome-Fenster öffnet sich automatisch, wenn das Paket zum ersten Mal in einem Projekt geladen wird. Du kannst es jederzeit über Tools ▸ RTS Terrain Studio ▸ Welcome erneut öffnen. Aktiviere unten Show at startup, wenn es bei jedem Start von Unity erscheinen soll. Diese Einstellung wird pro Benutzer und pro Projekt gespeichert und landet daher nie in der Versionsverwaltung.

Die linke Spalte ist eine Schnellstart-Liste mit einer Schaltfläche pro Schritt:

SchrittSchaltflächeFunktion
1. Validate & fix the projectValidateÖffnet den Project Validator. Unter dem Schritt zeigt eine Statuszeile „Project is ready“ oder die Anzahl der gefundenen Fehler und Warnungen.
2. Create the demo contentCreateFührt Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus.
3. Open a demo sceneSzenenliste + OpenListet die Demoszenen auf, sobald sie existieren, und öffnet die ausgewählte (vorher wirst du gefragt, ob deine geöffneten Szenen gespeichert werden sollen).
4. Create your own mapNew MapÖffnet den New Map Wizard.
5. Start editingEditor WindowÖffnet das Fenster von RTS Terrain Studio.

Die rechte Spalte enthält unter Learn Links in dieses Handbuch (Documentation, Quick Start Guide, Scripting API, Integrations, Troubleshooting) sowie eine Liste Integrations, die zeigt, ob Mirror, das A* Pathfinding Project und Unity AI Navigation im Projekt installiert sind.

Das Projekt validieren

Der Project Validator prüft alles, was das Paket benötigt, und behebt die meisten Probleme mit einem Klick. Führe ihn einmal nach dem Import aus und immer dann erneut, wenn etwas nicht stimmt (pinke Materialien, flaches Wasser, Einheiten, die trotz Nebel des Krieges sichtbar sind).

  1. Öffne Tools ▸ RTS Terrain Studio ▸ Validate Project oder klicke im Welcome-Fenster auf Validate.
  2. Lies die Zusammenfassung oben. Jede Prüfung ist ein Panel mit einer Statusanzeige (OK, Info, Warning oder Error). Öffne auf einem Panel Details, um genau zu sehen, was gefunden wurde.
  3. Klicke auf Fix All. Ein Dialog listet jede Änderung auf, bevor irgendetwas verändert wird. Klicke auf Apply Fixes.
  4. Klicke auf Run Again und vergewissere dich, dass keine Fehler oder Warnungen mehr übrig sind. Info-Panels sind unbedenklich: Sie dienen nur zur Information.

Fix All arbeitet in mehreren Durchgängen. War URP noch nicht aktiv, stellt der erste Durchgang das Projekt auf URP um, und die Korrekturen für Kameratexturen und Nebel des Krieges, die URP benötigen, werden mit demselben Klick angewendet. Jedes Panel hat außerdem eine eigene Korrektur-Schaltfläche, falls du die Probleme lieber einzeln behebst.

PrüfungSchweregradKorrektur-SchaltflächeWas die Korrektur bewirkt und warum sie wichtig ist
Rendering
Universal Render Pipeline is activeErrorUse URPWeist ein URP-Asset des Projekts (oder ein neu erstelltes) als Standard-Render-Pipeline zu, ebenso jeder Qualitätsstufe, die eine andere Pipeline verwendet. Ohne URP werden Terrain, Wasser und Vegetation pink oder gar nicht gerendert.
Depth and Opaque Texture enabledWarningEnable BothSchaltet Depth Texture und Opaque Texture in jedem URP-Asset ein, das eine Qualitätsstufe verwendet. Wasser nutzt die Depth Texture, damit sich Tiefenfarbe, Schaum und Uferlinie an Objekte anpassen, die im Wasser stehen. Die Opaque Texture dient der Lichtbrechung. Ohne diese Texturen wirkt das Wasser schlichter. Kameras können diese Einstellungen in ihrem Abschnitt Rendering weiterhin überschreiben.
Fog of War renderer featureWarningAdd FeatureFügt das Renderer Feature RTS Fog Of War zum Standard-Renderer jedes URP-Assets hinzu, das das Projekt verwendet, und weist ihm seinen Shader zu. Ohne dieses Feature zeigen nur die paketeigenen Shader den Nebel; Einheiten und Props mit anderen Materialien bleiben sichtbar.
Fog kept in player buildsWarningKeep Fog ModesErscheint, wenn ein RTSEnvironment in einer Build-Szene oder einer geöffneten Szene atmosphärischen Nebel anwendet. Setzt Project Settings ▸ Graphics ▸ Fog Modes auf Custom und behält die verwendeten Nebelmodi bei. Andernfalls entfernt Unity die Shader-Varianten für Nebel, und Karten mit Nebel zeigen in Builds keinen Nebel. Siehe Nebel in Player-Builds.
Project
Linear color spaceWarningSwitch to LinearSetzt Player Settings ▸ Other Settings ▸ Color Space auf Linear. Im Gamma-Farbraum wirken Farben, Beleuchtung und Wasser ausgewaschen. Durch die Umstellung werden alle Texturen neu importiert.
Burst, Collections and MathematicsErrorInstallFügt die fehlenden Pakete über den Package Manager hinzu. Ohne sie lassen sich die Skripte des Pakets nicht kompilieren.
Input handlingInfokeineMeldet, ob das Paket Input System installiert ist und welche Eingabeverarbeitung aktiv ist. Der ältere Input Manager funktioniert ebenfalls.
WebGL (nur solange Web die aktive Build-Plattform ist)
Web quality levels have Depth and Opaque TextureWarningEnable BothSchaltet Depth Texture und Opaque Texture in den URP-Assets der Qualitätsstufen ein, die Web-Builds verwenden. Der Editor rendert oft mit einer Desktop-Qualitätsstufe. Ohne diese Prüfung würde sich das schlichtere Wasser daher erst im Browser zeigen.
MSAA is off for Web buildsErrorTurn MSAA OffSchaltet MSAA in den URP-Assets der Web-Qualitätsstufen aus, die zusätzlich die Depth Texture oder die Opaque Texture verwenden. URP kann diese Kombination unter WebGL 2 nicht rendern, sodass das Spiel im Browser schwarz bliebe.
Web template fills the browser windowWarningInstall TemplateInstalliert das Web-Template des Pakets und wählt es aus. Unitys Templates Default und Minimal zeigen das Spiel in einem kleinen Rahmen mit fester Größe, in dem In-Game-Editor, HUD und Minikarte schwer lesbar sind. Siehe Das Vollfenster-Template.
Content
Package shaders compileErrorReimportImportiert Paket-Shader mit Kompilierfehlern neu (das hilft, wenn sie importiert wurden, bevor URP fertig geladen war). Fehlen Shader vollständig, importiere das Paket erneut aus dem Asset Store.
Shaders included in buildsErrorUpdateErstellt oder aktualisiert RTSShaderReferences.asset und das Material DecorPlaceholder im Ordner Resources des Pakets. Shader, die nur im Code erzeugt werden, werden aus Player-Builds entfernt, sofern nichts auf sie verweist, und das Terrain wäre in deinem Build pink. Dieses Asset wird außerdem automatisch aktualisiert: nach dem Neuladen von Skripten, bei Änderungen an Shadern und vor jedem Player-Build.
Tileset texture arrays builtInfoBuild ArraysErstellt die komprimierten Textur-Arrays für Tilesets, die noch keine oder nur veraltete haben. Tilesets funktionieren auch ohne sie, aber dann erzeugt der Editor beim Laden der Karte unkomprimierte Arrays, was Speicher und Ladezeit kostet. Beim Erstellen eines Player-Builds werden veraltete Arrays automatisch neu erstellt; Tilesets, deren Arrays nie erstellt wurden, verwenden auch in Player-Builds unkomprimierte Arrays.
GPU instancing on instanced prototypesWarningEnable InstancingAktiviert Enable GPU Instancing bei Materialien, die von instanzierten Bäumen, Details oder Dekorobjekten verwendet werden.
Scene (nur geöffnete Szenen)
Terrains have a tilesetWarningAssign '<tileset>'Weist jedem Terrain ohne Tileset ein Tileset zu (lässt sich rückgängig machen). Ohne Tileset zeigt ein Terrain eine neutrale Platzhalterdarstellung.
Terrains save to a map fileInfoCreate Map FilesSpeichert die Karte jedes Terrains, das kein .rtsmap-Asset hat, in einer neuen Datei neben seiner Szene und weist sie zu. Ohne Kartendatei gehen Änderungen verloren, wenn die Szene geschlossen wird.

Die Szenenkorrekturen werden im Play Mode nicht angeboten. Beende zuerst den Play Mode.

Die Demo-Inhalte erstellen

Das Paket wird ohne große binäre Grafikdateien ausgeliefert. Stattdessen erzeugt es einen vollständigen Satz Demo-Assets in deinem Projekt. So funktioniert alles ohne weitere Einrichtung, und du kannst sofort mit dem Malen beginnen.

  1. Wähle Tools ▸ RTS Terrain Studio ▸ Create Demo Content oder klicke im Welcome-Fenster auf Create.
  2. Ein Fortschrittsbalken durchläuft die Schritte: Textures, Materials, Meshes and prefabs, Tilesets, libraries and brushes, Shader references and UI settings, Demo maps, Demo scenes. Beim ersten Durchlauf kann das eine Weile dauern.
  3. Am Ende fragt ein Dialog, ob die Demoszenen zu den Build-Einstellungen hinzugefügt werden sollen. Klicke auf Not Now, außer du willst einen Player-Build mit den Demos erstellen (du kannst sie später unter File ▸ Build Profiles hinzufügen). Nur Add Scenes ändert die Build-Einstellungen.
  4. Die Szene der Gameplay-Demo ist im Project-Fenster ausgewählt. Deine geöffneten Szenen bleiben offen; nichts wird ersetzt.

Was erzeugt wird:

Ordner (im Paketordner)Inhalt
Content/TexturesBoden-, Klippen-, Wasser-, Detail-, Prop- und Pinseltexturen.
Content/Materials, Content/Meshes, Content/PrefabsLow-Poly-Bäume, Büsche, Felsen, Props und ihre Materialien.
Content/TilesetsDie Tilesets Summer und Winter sowie das Profil Summer Auto Paint. Summer hat die Bodenschichten Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow und Blight sowie die Klippentypen Rock Cliff und Grassy Cliff.
Content/LibrariesDemo Vegetation (Gras, hohes Gras, Blumen, Farn; die Bäume Pine, Broadleaf, Dead Tree und Bush) und Demo Decor (Felsen, Findlinge, Baumstümpfe und andere Props).
Content/BrushesStempel für Pinselformen.
Content/MapsDrei Karten: Two Rivers (1v1).rtsmap (128 × 128, punktsymmetrisch), Four Kingdoms (4p).rtsmap (192 × 192, vierfache Rotationssymmetrie) und Sandbox.rtsmap (96 × 96, überwiegend flach mit einem Plateau und einem Teich).
Samples/ScenesDie drei unten beschriebenen Demoszenen.
Resources/RTSTerrainStudioShader-Referenzen und die Panel-Einstellungen der Laufzeit-UI.

Führst du den Generator erneut aus, erscheint eine Rückfrage mit Regenerate / Cancel. Nur die erzeugten Demo-Assets und die Demoszenen werden überschrieben; deine eigenen Assets werden nie angetastet. Dateien, deren Inhalt sich nicht geändert hat, werden nicht neu geschrieben.

Der Generator fügt außerdem das Renderer Feature für den Nebel des Krieges zu deinem URP-Renderer hinzu, falls es fehlt. Die Einstellungen für Depth und Opaque Texture deines URP-Assets ändert er nicht: Die Demokameras fordern diese Texturen selbst an. Für Wasser in deinen eigenen Szenen führe Validate Project wie oben beschrieben aus.

Die Demoszenen öffnen

Die Demoszenen liegen in RTSTerrainStudio/Samples/Scenes. Öffne eine per Doppelklick im Project-Fenster, oder wähle sie in Schritt 3 des Welcome-Fensters aus und klicke auf Open.

Die Szene Gameplay Demo: Einheiten auf der Karte Two Rivers mit Nebel des Krieges und Minikarte
Die Szene Gameplay Demo auf der Karte Two Rivers (1v1).
SzeneKarteInhaltZum Ausprobieren
RTS Terrain Studio - Gameplay Demo Two Rivers (1v1) RTS-Kamera, Nebel des Krieges und ein Objekt RTS Demo Game, das das Spiel automatisch startet. Klicke auf Play. Deine Einheiten (Spieler 1) spawnen an deiner Startposition; die gegnerischen Einheiten streifen umher und tauchen im Nebel des Krieges auf und wieder unter.
RTS Terrain Studio - In-Game Editor Demo Sandbox Der In-Game-Karteneditor (Objekt RTS In-Game Editor) sowie dieselben Gameplay-Systeme, die ausgeschaltet bleiben, bis du einen Spieltest startest. Klicke auf Play, male mit dem In-Game-Editor und klicke dann auf Play Test (F5). Drücke Esc, um zum Editor zurückzukehren.
RTS Terrain Studio - Editor Playground Four Kingdoms (4p) Nur Terrain, Licht und Kamera, ohne Gameplay. Bleibe im Edit Mode, öffne Tools ▸ RTS Terrain Studio ▸ Editor Window und probiere die Werkzeuge an einer fertigen Karte aus.

Steuerung in der Gameplay-Demo

EingabeAktion
W A S D / Pfeiltasten, Bildschirmrand, mit gedrückter mittlerer Maustaste ziehenKamera verschieben. Sind Einheiten ausgewählt, stoppt S sie, statt die Kamera zu verschieben; die Pfeiltasten verschieben die Kamera immer.
MausradZoomen
Q / E, Alt + mit gedrückter rechter Maustaste ziehenAnsicht drehen
Linksklick / mit gedrückter linker Maustaste ziehenEine Einheit auswählen / ein Auswahlrechteck aufziehen
Shift + KlickEinheiten hinzufügen oder entfernen
Doppelklick, Ctrl + KlickAlle Einheiten dieses Typs auf dem Bildschirm auswählen
RechtsklickIn Formation bewegen (Shift hängt den Befehl an die Warteschlange an)
S / H / PAnhalten / Position halten / Patrouillieren
Ctrl + 0…9, Shift + 0…9, 0…9Kontrollgruppe speichern / Auswahl zur Gruppe hinzufügen / Gruppe abrufen (zweimal drücken, um die Kamera darauf zu zentrieren)
MinikarteLinksklick bewegt die Kamera, Rechtsklick bewegt die ausgewählten Einheiten
B, DeleteGebäude platzieren / Gebäude unter dem Cursor entfernen
T, Rechtsklick auf einen BaumBaum unter dem Cursor fällen / die ausgewählten Arbeiter den Baum fällen lassen
EscAktuellen Modus abbrechen oder Auswahl aufheben
F1Hilfe-Panel ein- oder ausblenden

Deine erste Karte

Eine Karte befindet sich auf einem GameObject RTS Terrain in deiner Szene und wird in einer .rtsmap-Datei in deinem Projekt gespeichert. Die Szene verweist nur auf die Datei; deine Änderungen werden in der Datei gespeichert. Beginne mit einer neuen Szene, damit du frei experimentieren kannst:

  1. Wähle File ▸ New Scene und eine URP-Szenenvorlage (zum Beispiel Basic (URP)).
  2. Speichere die Szene sofort mit Ctrl+S, zum Beispiel als Assets/MyMaps/FirstMap.unity. Neue Kartendateien werden neben der gespeicherten Szene angelegt. So bleiben Karte und Szene zusammen.

Ein Terrain erstellen

Es gibt zwei Wege. Beide erstellen dasselbe Objekt RTS Terrain, bei dem die Komponenten RTSTerrain, RTSVegetationRenderer, RTSWaterRenderer und RTSDecorManager bereits eingerichtet sind.

WegMenüGeeignet, wenn
Ein KlickGameObject ▸ RTS Terrain Studio ▸ RTS TerrainDu sofort mit dem Malen loslegen willst. Du erhältst eine flache Karte mit 128 × 128 Zellen (Zellgröße 2 m, Klippenstufe 2), die das Standard-Tileset und die Standardbibliotheken des Projekts verwendet. Sie wird am Pivot der Scene View platziert und ausgewählt. Neben der Szene wird eine neue Kartendatei namens <Scene name> Map.rtsmap angelegt (oder Assets/Maps/New Map.rtsmap, wenn die Szene noch nicht gespeichert ist).
GeführtTools ▸ RTS Terrain Studio ▸ New Map Wizard...Du Name, Größe, Tileset und Basistextur selbst wählen oder eine zufällige, spielbare Karte generieren willst.

Wenn das Projekt noch kein Tileset hat, bietet das Ein-Klick-Menü zuerst Create Demo Content an. Mit Continue Without erhält das Terrain ein neutrales Schachbrettmuster, bis du ein Tileset zuweist.

Der New Map Wizard hat fünf Schritte. Verwende Next und Back oder klicke in der Leiste oben auf einen Schritt:

SchrittEinstellungen
1. BasicsMap Name (zugleich der Dateiname), Author (bleibt für die nächste Karte erhalten), Description, Players (1–16; Wettkampfkarten werden meist für 2, 4 oder 8 Spieler gebaut).
2. SizeGrößen-Panels: Tiny 64 (schnelle 1v1-Duelle und Tests), Small 96 (1v1 und 2v2), Medium 128 (2 bis 4 Spieler), Large 192 (4 bis 6 Spieler), Huge 256 (6 bis 8 Spieler), Epic 384 (8+ Spieler, Kampagnen), Epic XL 512 (sehr große Welten) oder Custom (Vielfache von 16). Außerdem Cell Size (m) (im Zweifel bei 2 m belassen), Cliff Level Height (m), Base Cliff Level (Standard 2, damit du wie auf klassischen RTS-Karten für Seen nach unten graben kannst) und ein Ausklappbereich Advanced mit den Auflösungen für Textur, Details und Pathing.
3. LookTileset (klicke auf eine Bodentextur, um sie zur Schicht BASE zu machen, die zu Beginn die gesamte Karte bedeckt), Vegetation Library, Decor Library. Ohne Tileset wird eine Schaltfläche Create Demo Content angezeigt.
4. TerrainFlat (eine leere, flache Karte auf der Grundstufe, ideal zum Kennenlernen der Werkzeuge) oder Generate (Plateaus, Rampen, Wasser, Startpositionen, Vegetation und Dekor aus einem Preset und einem Seed, mit Symmetry, Start Locations, Scatter Vegetation, Scatter Decor und einer Schaltfläche Preview).
5. CreateNew terrain object oder Replace map of selected terrain, der Pfad unter Map File (Browse...), Add Environment (Sonne, Umgebungslicht und Nebel aus der Karte; erstellt ein Sun-Licht, falls die Szene keines hat), Open Editor Window und eine Zusammenfassung. Klicke auf Create Map.

Der Assistent schreibt die Kartendatei, erstellt das Terrain, rückt es in der Scene View ins Bild und öffnet das Editorfenster. Jede Einstellung lässt sich später im Tab Map des Editorfensters ändern.

Das Editorfenster öffnen

Öffne Tools ▸ RTS Terrain Studio ▸ Editor Window (der Assistent und das Welcome-Fenster können es für dich öffnen, und im Inspector von RTS Terrain gibt es eine Schaltfläche Open RTS Terrain Studio). Docke es neben dem Inspector an, damit du gleichzeitig die Scene View siehst.

Das Editorfenster von RTS Terrain Studio mit geöffnetem Tab Cliffs
Das Editorfenster mit geöffnetem Tab Cliffs: Klippenwerkzeuge, Werkzeugbeschreibung, Pinseleinstellungen und Symmetrie.
BereichAnzeige
KopfzeileTerrain-Auswahl, Kartenname (ein Punkt steht für nicht gespeicherte Änderungen), Save, Save As…, Open…, Reload, Undo / Redo mit dem Namen des Schritts, eine Hilfe-Schaltfläche und der Schalter Editing On / Editing Off.
Tab-LeisteAcht Werkzeugkategorien (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers), danach Map und Settings. Ein Punkt markiert die Kategorie des aktiven Werkzeugs.
WerkzeugseiteGroße Werkzeugschaltflächen, ein Panel, das das aktive Werkzeug mit seinem Live-Status beschreibt, die Panels Brush und Symmetry sowie die eigenen Einstellungen und Paletten des Werkzeugs (Schichten, Klippenstufen, Spieler, Bäume, Dekor).
StatusleisteZelle unter dem Cursor, Klippenstufe, Höhe und Pathing unter der Maus sowie der Undo-Speicher.

Die acht Kategorien und ihre Werkzeuge:

TasteKategorieWerkzeuge
1HeightRaise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode
2CliffsRaise / Lower Level, Set Level, Ramp, Cliff Type, Clean Cliffs
3PaintPaint Layer, Tile Paint, Fill / Replace, Smooth Paint, Auto Paint
4VegetationPaint Details, Erase Details, Place Trees, Erase Trees
5DecorPlace Decor, Erase Decor, Select Decor
6WaterPaint Water, Erase Water, Flood Fill
7PathingPaint Pathing, Clear Pathing
8MarkersStart Locations, Regions, Markers

Jedes Werkzeug wird in einem eigenen Kapitel ausführlich beschrieben (zum Beispiel Höhenwerkzeuge und Klippenwerkzeuge); das Fenster selbst wird unter Editorfenster beschrieben.

Grundlagen des Malens

  1. Klicke im Editorfenster auf eine Werkzeugschaltfläche. Dadurch wird die Bearbeitung eingeschaltet (in der Kopfzeile steht Editing On). Ein erneuter Klick auf das aktive Werkzeug schaltet die Bearbeitung wieder aus.
  2. Bewege die Maus in der Scene View über das Terrain. Der Pinselkreis wird direkt auf den Boden gezeichnet, genau dort, wo du malst.
  3. Halte die linke Maustaste gedrückt und ziehe, um zu malen.
Eingabe (Scene View)Aktion
Linke MaustasteEinen Pinselstrich malen (oder klicken, bei Klickwerkzeugen wie Start Locations)
Shift + linke MaustasteUmgekehrte Aktion: absenken, radieren, entfernen
Ctrl + KlickEinen Wert unter dem Cursor aufnehmen (Höhe, Bodenschicht, Wasserhöhe …), bei Werkzeugen, die das unterstützen
[ / ]Pinsel kleiner / größer
- / =Stärke verringern / erhöhen
1 … 8Zu einer Werkzeugkategorie wechseln (erneut drücken, um durch ihre Werkzeuge zu schalten)
G / PRaster ein- und ausschalten / Pathing-Overlay durchschalten (Off, Walkability, Buildability, Combined)
RZufällige Pinseldrehung ein- und ausschalten
Ctrl+Z / Ctrl+YTerrain-Bearbeitungen rückgängig machen / wiederherstellen (das Menü Edit zeigt die Namen der Schritte)
EscDen laufenden Pinselstrich abbrechen oder die Bearbeitung beenden

Die Scene View zeigt außerdem ein Overlay RTS Terrain Studio mit dem Bearbeitungsschalter, Schaltflächen für Kategorien und Werkzeuge, den Schiebereglern Size und Strength und der Einstellung Symmetry. Unten erscheint zusätzlich eine Statuszeile mit der Zelle unter der Maus, der Klippenstufe und der Höhe. Für den Großteil deiner Arbeit kannst du in der Scene View bleiben.

Die Kameranavigation in der Scene View funktioniert wie gewohnt (Ziehen mit der rechten Maustaste zum Fliegen, Alt + Ziehen mit der linken Maustaste zum Umkreisen, Ziehen mit der mittleren Maustaste zum Schwenken). Wenn Unitys Hand-Werkzeug (Q) aktiv ist, schwenkt Ziehen mit der linken Maustaste die Ansicht, statt zu malen; drücke W, um wieder zu malen. Alle Tastenkürzel von RTS Terrain Studio lassen sich unter Edit ▸ Shortcuts... (unter macOS Unity ▸ Shortcuts...) in der Kategorie RTS Terrain Studio neu belegen (nützlich, wenn 2 in deinem Layout mit Unitys Schalter für den 2D-Modus kollidiert).

Höhen malen

  1. Öffne den Tab Height (1) und klicke auf Raise / Lower.
  2. Ziehe über das Terrain, um Hügel aufzubauen. Halte die Maustaste gedrückt, damit der Hügel weiter wächst. Halte Shift gedrückt, um abzusenken.
  3. Wechsle zu Smooth und ziehe über scharfe Stellen, um sie zu glätten.
  4. Probiere Noise für unebenen Boden und Flatten (Ctrl+Klick nimmt die Höhe auf) für Bauflächen aus.

Höhen sind Feinarbeit über den Klippenstufen: Ein Hügel auf einem Plateau bleibt auf diesem Plateau.

Klippen und Rampen malen

Klippen sind das Herzstück einer klassischen RTS-Karte. Die Karte besteht aus den Klippenstufen 0 bis 15 und beginnt auf der Grundstufe (standardmäßig 2). Jede Stufe bildet einen klar abgesetzten, senkrechten Klippenabsatz.

  1. Öffne den Tab Cliffs (2) und klicke auf Raise / Lower Level.
  2. Klicke auf das Terrain: Der Boden hebt sich um eine Klippenstufe. Halte die Maustaste gedrückt und ziehe, um das begonnene Plateau auf derselben Stufe zu erweitern.
  3. Klicke in das Plateau und ziehe erneut, um eine zweite Stufe darauf zu setzen.
  4. Halte Shift gedrückt, um um eine Stufe abzusenken. Wenn du unter die Grundstufe absenkst, gräbst du Becken für Seen und Flüsse.
  5. Klicke auf Ramp und male quer über eine Klippenkante mit einer Stufe Höhenunterschied, um sie in einen begehbaren Hang zu verwandeln. Alle vier Ecken einer Zelle müssen Rampe sein, male also großzügig über die Kante. Shift entfernt eine Rampe.
  6. Male mit Clean Cliffs über unsaubere Kanten, um Säulen und Löcher aus einzelnen Vertices zu entfernen. Cliff Type malt ein anderes Klippenmaterial (zum Beispiel Grassy Cliff statt Rock Cliff).

Texturen malen

  1. Öffne den Tab Paint (3) und klicke auf Paint Layer.
  2. Klicke in der Schichtpalette auf eine Bodenschicht, zum Beispiel Dirt.
  3. Ziehe über das Terrain, um Wege und Lichtungen zu malen. Shift radiert die Schicht weg; Ctrl+Klick nimmt die Schicht unter dem Cursor auf.
  4. Für hart abgegrenzte Kacheln im Stil klassischer RTS-Spiele verwende Tile Paint. Für große Flächen verwende Fill / Replace.
  5. Für ein schnelles, natürliches Ergebnis verwende im Tab Paint das Panel Auto Paint Whole Map und stelle Profile auf Summer Auto Paint: Dabei entsteht Erde in der Nähe von Klippen, Sand an Ufern, Fels an steilen Hängen usw.

Bäume und Gras platzieren

  1. Öffne den Tab Vegetation (4) und klicke auf Place Trees.
  2. Wähle unter Trees eine oder mehrere Baumarten, zum Beispiel Pine und Broadleaf. Mehrere Arten werden zufällig gemischt.
  3. Belasse Mode auf Scatter und ziehe entlang der Kartenränder, um Wälder zu pflanzen. Density bestimmt, wie viele Bäume ein Tupfer hinzufügt. Bäume werden weder auf Hängen platziert, die steiler als Max Slope sind, noch unter Wasser (Avoid Water).
  4. Stelle Mode auf Single, um pro Klick einen Baum zu platzieren. Shift entfernt Bäume.
  5. Klicke auf Paint Details, wähle Grass oder Flowers Yellow unter Details und male Wiesen. Target Density legt fest, wie dicht der Pinsel malt.

Die Vegetationswerkzeuge benötigen eine Vegetationsbibliothek, die dem Terrain zugewiesen ist. Terrains, die nach Create Demo Content erstellt werden, erhalten automatisch Demo Vegetation. Dekor funktioniert genauso, mit Place Decor im Tab Decor (5) und der Bibliothek Demo Decor.

Wasser hinzufügen

Wasser füllt Boden, der unter einer Zielhöhe für die Wasseroberfläche liegt. Am einfachsten gräbst du mit den Klippenwerkzeugen ein Becken und füllst es.

  1. Verwende im Tab Cliffs das Werkzeug Raise / Lower Level mit Shift, um einen See oder Fluss eine Stufe unter der Grundstufe zu graben (Stufe 1 auf einer Standardkarte).
  2. Öffne den Tab Water (6) und klicke auf Flood Fill.
  3. Behalte den Standardmodus von Water Height (Klippenstufe plus Offset) mit Level 1 bei. Der Standard-Offset legt die Wasseroberfläche knapp unter die Grundstufe, sodass sich das Becken füllt und das umliegende Land trocken bleibt.
  4. Klicke in das Becken. Es füllt sich mit einem Klick. Shift+Klick lässt zusammenhängendes Wasser ab.
  5. Verwende Paint Water, um Wasser von Hand zu malen, und Erase Water, um es zu entfernen.

Flach- und Tiefwasser werden automatisch aus der Wassertiefe abgeleitet und beeinflussen das Pathing: Bodeneinheiten können durch Flachwasser laufen (dort lässt sich nichts bauen, weil Gebäude trockenen Boden brauchen), Tiefwasser dagegen blockiert sie.

Wenn das Wasser keine Lichtbrechung zeigt und sein Ufer Objekte ignoriert, die im Wasser stehen, sind in URP die Depth Texture und die Opaque Texture ausgeschaltet. Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und wende Enable Both an.

Startpositionen platzieren

Startpositionen sind die Spawn-Punkte der Spieler. Das Demospiel spawnt die Einheiten jedes Spielers dort.

  1. Öffne den Tab Markers (8) und klicke auf Start Locations.
  2. Wähle in der Spielerpalette P1 (Spieler 1, rot).
  3. Klicke auf die Stelle des Terrains, an der Spieler 1 starten soll. Jeder Spieler hat genau eine Startposition, ein erneuter Klick verschiebt sie also. Mit Yaw legst du die Blickrichtung fest.
  4. Wähle den nächsten Spieler und klicke erneut. Shift+Klick entfernt die nächstgelegene Startposition.

Bei eingeschalteter Symmetrie platziert ein Klick alle Spieler auf einmal: Die symmetrischen Kopien werden den folgenden Spielern (P1, P2, …) zugewiesen, solange Symmetry Assigns Next Players aktiviert ist. Siehe das 10-Minuten-Tutorial.

Platziere Startpositionen auf flachem, trockenem Boden abseits von Klippenkanten. Validate Map im Tab Map (Panel Validation) meldet unter anderem fehlende Startpositionen sowie Startpositionen an Klippenkanten oder in Tiefwasser.

Die Karte speichern

Deine Änderungen liegen im Speicher, bis du sie in die Datei .rtsmap speicherst:

  • Klicke in der Kopfzeile des Editorfensters auf Save. Die Schaltfläche ist hervorgehoben, solange es nicht gespeicherte Änderungen gibt, und neben dem Kartennamen erscheint ein Punkt.
  • Save As… schreibt eine neue Datei .rtsmap an einen beliebigen Ort in Assets und weist sie dem Terrain zu.
  • Beim Speichern der Szene (Ctrl+S) werden auch die Karten ihrer Terrains gespeichert. Du kannst das im Optionsmenü des Overlays in der Scene View ausschalten (Save Maps When Saving the Scene).
  • Auch der Inspector von RTS Terrain und das Overlay-Menü in der Scene View haben die Einträge Save und Save As.

Wenn du mit nicht gespeicherten Kartenänderungen in den Play Mode wechselst, eine Szene schließt oder Unity beendest, fragt ein Dialog, ob du sie speichern willst. Wähle im Project-Fenster eine Datei .rtsmap aus, um ihre Minikarten-Vorschau und eine Schaltfläche Open In Scene zu sehen.

Ein Terrain ohne Kartendatei behält seine Karte nur im Speicher. Wenn du den Dialog „The map of '…' is not saved as a map asset yet“ siehst, wähle Save As…, um die Datei zu erstellen.

Dein erster Spieltest

Du kannst deine Karte auf zwei Arten spielen: mit dem Demo Game im Play Mode oder im laufenden Spiel mit dem In-Game-Editor und seiner Schaltfläche Play Test.

Mit dem Demo Game spielen

  1. Öffne die Szene mit deinem Terrain. Achte darauf, dass sie mindestens eine Startposition hat (sonst spawnen die Einheiten in zwei gegenüberliegenden Ecken).
  2. Wähle GameObject ▸ RTS Terrain Studio ▸ Demo Game. Ein Objekt RTS Demo Game wird erstellt und ausgewählt.
  3. Klicke auf Play. Speichere die Karte, wenn du gefragt wirst.
  4. Das Demospiel richtet alles ein, was es braucht: Nebel des Krieges, RTS-Kamera, Auswahl, Bewegungsbefehle, Minikarte, HUD, Gebäudeplatzierung und das Abbauen von Bäumen. Deine Einheiten (Spieler 1) spawnen an deiner Startposition; die Einheiten der anderen Spieler wandern umher.
  5. Drücke F1 für das Hilfe-Panel. Die Steuerung ist unter Steuerung in der Gameplay-Demo aufgeführt.
  6. Beende den Play Mode. Während des Spiels gefällte Bäume werden wiederhergestellt, sodass die Karte so bleibt, wie du sie erstellt hast.

Die Komponente RTS Demo Game hat Optionen wie Local Team, Units Per Player, Unit Prefabs (wenn leer, werden Platzhaltereinheiten verwendet), Fog Of War, Minimap und Hud. Als Kamera dient die Hauptkamera der Szene.

Wenn Einheiten im Nebel des Krieges sichtbar bleiben, fehlt das Renderer Feature RTS Fog Of War. Führe Validate Project aus und wende Add Feature an.

Im Spiel bearbeiten und testen

Der In-Game-Editor bietet dieselben Werkzeuge in deinem laufenden Spiel, mit einer eigenen UI. Spieler und Modder können damit in einem fertigen Spiel Karten bauen und spielen.

  1. Öffne die Szene mit deinem Terrain und einer Kamera. Wenn sie noch kein RTS Demo Game hat, füge eines wie oben beschrieben hinzu.
  2. Wähle GameObject ▸ RTS Terrain Studio ▸ In-Game Editor.
  3. Klicke auf Play. Der Editor öffnet sich mit einer oberen Leiste (File, Map, Rückgängig und Wiederherstellen, Symmetry, Grid, Pathing, Markers, Play Test), einer Kategorie- und Werkzeugleiste links, dem Werkzeug-Panel rechts und einer Statusleiste. Das Demospiel wartet, bis du einen Spieltest startest.
  4. Male wie im Unity Editor: linke Maustaste, Shift zum Umkehren, Ctrl+Klick zum Aufnehmen, [ ] und - = für den Pinsel, 1…8 für die Kategorien. Bewege die Kamera mit W A S D, durch Ziehen mit der rechten oder mittleren Maustaste, mit Q/E und dem Mausrad.
  5. Klicke auf Play Test oder drücke F5. Einheiten spawnen, der Nebel des Krieges und die RTS-Kamera werden eingeschaltet.
  6. Drücke Esc, um zum Editor zurückzukehren. Die Karte wird auf ihren Zustand vor dem Spieltest zurückgesetzt.

Nützliche Tasten im In-Game-Editor: F1 Hilfe, F2 den gesamten Editor ein- oder ausblenden, H Panels ausblenden, G Raster, P Pathing-Overlay, M Marker, F den Punkt unter dem Cursor fokussieren, Ctrl+Z / Ctrl+Y Rückgängig und Wiederherstellen.

Im In-Game-Editor schreiben File ▸ Save (Ctrl+S) und File ▸ Save As... (Ctrl+Shift+S) Kartendateien in den Kartenordner des Spiels (standardmäßig ein Ordner Maps in Application.persistentDataPath), genau wie ein fertiges Spiel. Wenn du nach dem Bearbeiten im Unity Editor den Play Mode beendest, fragt ein Dialog „Save map changes made in Play Mode?“: Save schreibt die Änderungen in das Asset .rtsmap des Terrains, Discard verwirft sie.

Der In-Game-Editor wird ausführlich unter In-Game-Editor behandelt.

Für Programmierer: auf einen Spieltest reagieren

Dein eigener Gameplay-Code kann dasselbe Event abonnieren, das auch das Demospiel verwendet. Dieses Beispiel protokolliert beim Start eines Spieltests, wo jeder Spieler startet:

using RTSTerrainStudio;
using UnityEngine;

// Add this component to any GameObject in a scene with an RTS Terrain and an in-game editor.
public class PlaytestStartLogger : MonoBehaviour
{
    void OnEnable()
    {
        // Raised by the in-game editor when a play test starts (true) or stops (false).
        RTSRuntimeEditorEvents.PlaytestChanged += OnPlaytestChanged;
    }

    void OnDisable()
    {
        RTSRuntimeEditorEvents.PlaytestChanged -= OnPlaytestChanged;
    }

    void OnPlaytestChanged(bool playing)
    {
        if (!playing) return;

        // The first enabled terrain of the scene.
        RTSTerrain terrain = RTSTerrain.Main;
        if (terrain == null || terrain.Map == null) return;

        foreach (StartLocation start in terrain.Map.StartLocations)
        {
            // Start locations are stored in map-local coordinates: convert to world space
            // and move the point onto the ground.
            var local = new Vector3(start.position.x, start.position.y, start.position.z);
            Vector3 world = terrain.SnapToGround(terrain.LocalToWorld(local));
            Debug.Log($"Player {start.player + 1} starts at {world}, facing {start.yaw:0} degrees");
        }
    }
}

Terrain-Abfragen, das Laden von Karten und die Werkzeug-API findest du unter Scripting.

10-Minuten-Tutorial: deine erste 1v1-Karte

In diesem Tutorial baust du eine kleine, faire Karte für zwei Spieler: Jeder Spieler erhält eine Basis auf einem Plateau mit Rampe, ein Fluss trennt die beiden Hälften, und Wälder rahmen die Karte ein. Weil du mit Symmetrie malst, sind beide Seiten identisch, und du musst nur eine Hälfte bauen.

Draufsicht auf eine symmetrische Karte für zwei Spieler
Eine punktsymmetrische 1v1-Karte von oben: Alles, was du auf einer Seite malst, erscheint um 180° gedreht auf der anderen.

Bevor du beginnst: Das Projekt ist validiert und die Demo-Inhalte sind vorhanden (siehe Installation und Einrichtung).

1. Die Karte erstellen (1 Minute)

  1. Wähle File ▸ New Scene, nimm eine URP-Vorlage und speichere die Szene als Assets/MyMaps/Duel.unity.
  2. Öffne Tools ▸ RTS Terrain Studio ▸ New Map Wizard....
  3. Basics: Map Name Duel, dein Name als Author, Players 2. Klicke auf Next.
  4. Size: Klicke auf Small (96 × 96, 1v1 und 2v2). Behalte Cell Size 2 m und Base Cliff Level 2 bei. Klicke auf Next.
  5. Look: Tileset Summer, klicke auf Grass als Basisschicht, Vegetation Library Demo Vegetation, Decor Library Demo Decor. Klicke auf Next.
  6. Terrain: Wähle Flat. Klicke auf Next.
  7. Create: Behalte New terrain object bei, aktiviere Add Environment und Open Editor Window und klicke dann auf Create Map.

2. Symmetrie einschalten (10 Sekunden)

  1. Öffne im Editorfenster den Tab Cliffs. Das Panel Symmetry findest du unter dem Panel Brush.
  2. Wähle Rotate 180°. Belasse das Symmetriezentrum in der Kartenmitte (0.5, 0.5).
  3. Bewege die Maus über das Terrain: Du siehst jetzt einen zweiten Pinsel auf der gegenüberliegenden Seite der Karte, und die Symmetrie-Hilfslinien werden in der Scene View gezeichnet.

Die Symmetrie gilt für alle Werkzeuge gemeinsam, sie bleibt also eingeschaltet, wenn du das Werkzeug wechselst. Die Modi:

Modus (Fenster)KopienTypische Verwendung
Off1Kampagnen- oder Story-Karten
Mirror X2Links und rechts spiegeln
Mirror Z2Oben und unten spiegeln
Mirror XZ4An beiden Achsen spiegeln (4 Spieler)
Rotate 180°2Punktsymmetrie, das klassische 1v1-Layout (Ecke gegen Ecke)
Rotate 90°4Vierfache Rotationssymmetrie für 4 Spieler; erfordert eine quadratische Karte
Diagonal2An der Diagonalen spiegeln; erfordert eine quadratische Karte

Das Overlay in der Scene View und der In-Game-Editor bieten dieselben Modi (das Overlay zeigt die Enum-Namen, zum Beispiel Rotate 2 für Rotate 180°).

3. Die Basen bauen (2 Minuten)

  1. Klicke auf Raise / Lower Level. Stelle mit [ und ] einen großen Pinsel ein.
  2. Klicke im unteren linken Viertel der Karte und ziehe, um ein Plateau zu malen, das etwa ein Viertel der Kartenbreite einnimmt. Dasselbe Plateau erscheint im oberen rechten Viertel.
  3. Klicke auf Ramp und male quer über die Plateaukante, die zur Kartenmitte zeigt. Mache die Rampe mindestens zwei bis drei Zellen breit, damit Gruppen von Einheiten durchpassen.
  4. Klicke auf Clean Cliffs und male einmal über den Umriss des Plateaus, um übrig gebliebene Buckel aus einzelnen Vertices zu entfernen.

4. Den Fluss graben (2 Minuten)

  1. Bleibe bei Raise / Lower Level, halte Shift gedrückt und ziehe einen Fluss von der oberen linken Ecke in Richtung Kartenmitte. Die Kopie setzt ihn von der unteren rechten Ecke aus fort, sodass der Fluss die gesamte Karte durchquert.
  2. Lasse in der Nähe der Mitte ein oder zwei Landbrücken stehen, damit die Spieler einander erreichen können.
  3. Öffne den Tab Water, klicke auf Flood Fill, behalte Level 1 bei und klicke in das Flussbett. Klicke erneut in jeden Teil, der noch nicht gefüllt ist.

5. Den Boden texturieren (2 Minuten)

  1. Öffne den Tab Paint. Stelle im Panel Auto Paint Whole Map die Einstellung Profile auf Summer Auto Paint (bleibt sie leer, werden eingebaute Regeln verwendet, die auf die Schichtnamen des Tilesets abgestimmt sind) und klicke auf Auto Paint Whole Map. Bestätige den Dialog; das komplette Neutexturieren ist ein einziger Undo-Schritt. Erde erscheint in der Nähe von Klippen, Sand an den Flussufern und Fels an steilen Hängen.
  2. Klicke auf Paint Layer, wähle Cobblestone und male auf jedem Plateau dort ein Quadrat, wo das Hauptgebäude stehen soll (ein Tupfer genügt, die Kopie bemalt die andere Basis).
  3. Wähle Dirt und male einen Weg von der Rampe der Basis zur Landbrücke.
  4. Optional: Verwende im Tab Height das Werkzeug Raise / Lower mit geringer Stärke (-), um ein paar sanfte Hügel zwischen den Basen hinzuzufügen, und Smooth, um sie zu glätten.

6. Wälder, Gras und Felsen (2 Minuten)

  1. Öffne den Tab Vegetation und klicke auf Place Trees. Wähle Pine und Broadleaf und behalte Scatter bei.
  2. Male Wälder entlang des Kartenrands und hinter jeder Basis. Halte Rampen, Wege und Landbrücken frei; Shift entfernt Bäume.
  3. Klicke auf Paint Details, wähle Grass und Flowers Yellow und male ein paar Wiesen.
  4. Öffne den Tab Decor, klicke auf Place Decor, wähle ein paar Felsen und klicke in die Nähe der Flussufer.

7. Die Startpositionen platzieren (30 Sekunden)

  1. Öffne den Tab Markers und klicke auf Start Locations.
  2. Wähle Spieler P1 und achte darauf, dass Symmetry Assigns Next Players aktiviert ist.
  3. Klicke einmal in die Mitte der unteren linken Basis. P1 wird dort platziert und P2 erscheint auf der gegenüberliegenden Basis; beide blicken zur Mitte, wenn du einen Yaw einstellst, der dorthin zeigt.

8. Validieren, speichern und spielen (1 Minute)

  1. Öffne den Tab Map und klicke im Panel Validation auf Validate Map. Behebe alle gemeldeten Fehler (zum Beispiel eine Startposition an einer Klippenkante).
  2. Klicke in der Kopfzeile des Fensters auf Save. Die Karte wird in Assets/MyMaps/Duel.rtsmap gespeichert.
  3. Wähle GameObject ▸ RTS Terrain Studio ▸ Demo Game und klicke auf Play.
  4. Wähle deine Einheiten aus, schicke sie per Rechtsklick über die Landbrücke und beobachte, wie der Gegner aus dem Nebel des Krieges auftaucht.

Du willst vier Spieler? Erstelle eine quadratische Karte (zum Beispiel Medium 128 × 128), wähle Rotate 90° und setze Players auf 4. Ein Klick mit dem Werkzeug Start Locations platziert dann P1 bis P4. Oder überlasse es dem Generator: Wähle im Schritt Terrain des Assistenten Generate, stelle Symmetry und Start Locations ein, klicke auf Preview und verfeinere das Ergebnis anschließend von Hand.

Wie geht es weiter?

  • Editorfenster: alle Tabs, Panels und Einstellungen im Detail, einschließlich des Tabs Map (Größenänderung, Kartengenerator, Import und Export von Heightmaps, Minikarte, Umgebung).
  • Die Werkzeugkapitel, eines pro Kategorie: Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers.
  • In-Game-Editor: den Editor mit deinem Spiel ausliefern, damit Spieler Karten bauen können.
  • Scripting: Höhen, Klippenstufen und Pathing aus Code abfragen, Karten zur Laufzeit laden, auf Bearbeitungen reagieren.
  • Integrationen: Multiplayer mit Mirror, Wegfindung mit dem A* Pathfinding Project oder Unity NavMesh.
  • Fehlerbehebung: pinke Materialien, flach wirkendes Wasser, Versatz beim Malen und andere häufige Probleme.

Projekteinrichtung

RTS Terrain Studio braucht einige Projekteinstellungen: die Universal Render Pipeline mit zwei Kameratexturen, das Renderer Feature für den Nebel des Krieges, den Farbraum Linear und drei Unity-Pakete. Das Fenster Tools ▸ RTS Terrain Studio ▸ Validate Project prüft all diese Punkte und behebt die meisten mit einem Klick. Deshalb brauchst du die manuellen Schritte in diesem Kapitel in der Regel nie. Lies weiter, wenn eine Prüfung rot bleibt, wenn du wissen willst, was eine Korrektur ändert, oder wenn du ein Projekt von Hand einrichtest.

Im Project Validator öffnet die Hilfe-Schaltfläche jedes Panels den passenden Abschnitt dieses Kapitels.

Der Project Validator

Öffne Tools ▸ RTS Terrain Studio ▸ Validate Project oder klicke im Welcome-Fenster auf Validate. Die Prüfungen starten, sobald sich das Fenster öffnet. Sie sind in fünf Kategorien unterteilt:

KategorieWas geprüft wird
RenderingURP ist aktiv, Depth Texture und Opaque Texture sind aktiviert, das Renderer Feature für den Nebel des Krieges ist installiert, Player-Builds behalten die Nebelmodi, die RTS Environment verwendet.
ProjectFarbraum Linear; Burst, Collections und Mathematics sind installiert; welche Eingabeverarbeitung aktiv ist.
WebGLNur solange Web die aktive Build-Plattform ist: Die Qualitätsstufen von Web-Builds haben Depth Texture und Opaque Texture, und das Web-Template füllt das Browserfenster (siehe Web-Prüfungen im Project Validator).
ContentDie Shader des Pakets kompilieren und sind in Builds enthalten, die Textur-Arrays der Tilesets sind gebaut, bei instanzierten Prototypen ist GPU-Instancing aktiviert.
SceneJedes RTS Terrain in den geöffneten Szenen hat ein Tileset und eine Kartendatei.

Jedes Ergebnis hat einen von vier Statuswerten:

StatusBedeutung
OKAlles ist korrekt eingerichtet.
InfoEin Hinweis oder eine Empfehlung. Nichts ist defekt.
WarningEine Funktion arbeitet nur eingeschränkt, zum Beispiel Wasser ohne Lichtbrechung. Eine Korrektur wird empfohlen.
ErrorEine erforderliche Einstellung fehlt. Teile des Pakets funktionieren nicht.

Fix All wendet alle verfügbaren Korrekturen an. Zuvor listet ein Dialog alle Änderungen auf; geändert wird erst, wenn du auf Apply Fixes klickst. Korrekturen, die URP voraussetzen (die Kameratexturen und das Renderer Feature für den Nebel des Krieges), laufen automatisch, sobald die Pipeline zugewiesen ist. So genügt ein einziger Klick, um ein frisches Projekt einzurichten. Run Again wiederholt die Prüfungen. Die vollständige Liste der Prüfungen mit ihren Korrektur-Schaltflächen findest du unter Das Projekt validieren.

Der Project Validator prüft dein Projekt. Um eine einzelne Karte zu prüfen (Startpositionen, Erreichbarkeit, fehlende Prototypen, Performance-Grenzen), verwende den Kartenvalidator im Tab Map; siehe Kartenvalidierung.

Prüfungen per Code ausführen

RTSProjectValidator.RunChecks() liefert die Ergebnisse, ohne etwas zu ändern, und RTSProjectValidator.FixAll() wendet die Korrekturen an. Du kannst beide in einem Build-Skript oder einem Continuous-Integration-Job verwenden. Das Beispiel muss in einem Ordner Editor liegen.

using UnityEditor;
using UnityEngine;
using RTSTerrainStudio.Editor;

// Command line: Unity -batchmode -projectPath <path> -executeMethod ProjectSetupCheck.Run -quit
public static class ProjectSetupCheck
{
    public static void Run()
    {
        // Runs every check. Nothing is modified.
        var results = RTSProjectValidator.RunChecks();

        // Applies every available fix without dialogs, then checks again.
        int applied = RTSProjectValidator.FixAll(results, interactive: false);
        if (applied > 0) results = RTSProjectValidator.RunChecks();

        // Logs warnings and errors ("[Status] Title: Message").
        foreach (var result in results)
            if (result.IsProblem) Debug.LogWarning(result.ToString());

        // Fails the job when a required setting is still missing.
        if (RTSProjectValidator.WorstStatus(results) == RTSValidationStatus.Error)
            EditorApplication.Exit(1);
    }
}

Die Korrektur Install für fehlende Pakete startet nur eine Anfrage an den Package Manager. Bis diese abgeschlossen ist, können die Skripte des Pakets nicht kompilieren. Installiere Burst, Collections und Mathematics, bevor du das Paket im Batch-Modus ausführst.

Universal Render Pipeline

Alle Shader des Pakets sind für URP geschrieben. Ohne URP werden Terrain, Wasser und Vegetation pink oder gar nicht dargestellt. Die Korrektur Use URP verwendet ein vorhandenes URP-Asset des Projekts (oder erstellt Assets/Settings/RTS URP.asset mit einem Renderer) und weist es als Standard-Render-Pipeline sowie jeder Qualitätsstufe zu, die eine andere Pipeline verwendet. So machst du dasselbe von Hand:

  1. Wenn das Projekt noch kein URP-Asset hat, erstelle eines im Project-Fenster über Create ▸ Rendering ▸ URP Asset (with Universal Renderer).
  2. Weise es unter Edit ▸ Project Settings ▸ Graphics als Default Render Pipeline zu.
  3. Öffne Edit ▸ Project Settings ▸ Quality. Prüfe bei jeder Qualitätsstufe mit einem Override für Render Pipeline Asset, dass es sich ebenfalls um ein URP-Asset handelt.

Depth Texture und Opaque Texture

Wasser nutzt die Tiefentextur der Kamera, damit sich Tiefenfarbe, Schaum und der weiche Übergang am Ufer an Objekte anpassen, die im Wasser stehen. Die Opaque-Textur nutzt es für die Lichtbrechung. Ohne sie wirkt das Wasser schlichter. Wähle jedes URP-Asset aus, das deine Qualitätsstufen verwenden, und aktiviere in seinem Abschnitt Rendering die Optionen Depth Texture und Opaque Texture, oder klicke im Validator auf Enable Both.

Eine Kamera kann beide Einstellungen im Abschnitt Rendering ihres eigenen Inspectors überschreiben, und das Wasser richtet sich nach der Einstellung jeder Kamera, die es rendert. Wenn das Wasser in der Game View weiterhin schlichter wirkt als erwartet, prüfe, ob deine Spielkamera sie ausschaltet. Die Scene View verwendet immer das URP-Asset. Siehe auch die URP-Voraussetzungen für Wasser.

Das Renderer Feature für den Nebel des Krieges

Das Renderer Feature RTS Fog Of War (RTSFogOfWarRendererFeature) zeichnet den Nebel über alles auf dem Bildschirm, auch über Einheiten und Props mit deinen eigenen Materialien. Ohne das Feature zeigen nur die Shader des Pakets den Nebel. Add Feature fügt es dem Standard-Renderer jedes verwendeten URP-Assets hinzu. Um es von Hand hinzuzufügen, wähle das Asset Universal Renderer Data aus, klicke auf Add Renderer Feature und wähle RTS Fog Of War. Details findest du unter Rendering: Renderer Feature und Shader-Fallback.

Farbraum

Das Paket ist für den Farbraum Linear ausgelegt, den Standard von URP. Im Farbraum Gamma wirken Farben, Beleuchtung und Wasser im Vergleich zu den Screenshots ausgewaschen. Stelle Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Color Space auf Linear, oder klicke im Validator auf Switch to Linear.

Beim Wechsel des Farbraums werden alle Texturen neu importiert. In einem großen Projekt kann das eine Weile dauern.

Benötigte Pakete

Die Laufzeit-Assembly verwendet Burst (com.unity.burst), Collections (com.unity.collections) und Mathematics (com.unity.mathematics) für ihre Jobs und ihr Datenmodell. Solange eines davon fehlt, kompilieren die Skripte des Pakets nicht. Installiere sie über Window ▸ Package Manager ▸ Unity Registry, oder klicke im Validator auf Install. Der Validator fügt dann alle fehlenden Pakete mit einer einzigen Anfrage an den Package Manager hinzu. Die getesteten Versionen stehen unter Benötigte Pakete.

Wenn der Import Kompilierfehler zeigt und das Menü Tools ▸ RTS Terrain Studio fehlt, kann auch der Validator nicht laufen, weil er Teil des Pakets ist. Installiere die drei Pakete dann von Hand und öffne danach den Validator.

Eingabeverarbeitung

Alle Eingaben zur Laufzeit (für den In-Game-Editor, die Beispielkamera und die Beispieleinheiten) laufen über die statische Klasse RTSInput. Wenn das Paket Input System (com.unity.inputsystem) installiert ist, wird die Laufzeit-Assembly mit dem Define-Symbol RTS_INPUT_SYSTEM kompiliert und nutzt das Input System. Andernfalls oder wenn das Projekt nur den alten Input Manager verwendet, fällt RTSInput auf die alte API UnityEngine.Input zurück.

Active Input HandlingInput System installiertErgebnis
Input System Package (new)jaOK. Das Input System wird verwendet.
BothjaOK. Das Input System wird verwendet.
Input Manager (old)jaInfo. Der Fallback wird verwendet.
beliebigneinInfo. Der Fallback wird verwendet. Er funktioniert, wird für neue Projekte aber nicht empfohlen.

Die Einstellung findest du unter Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Active Input Handling. Wenn du sie änderst, fordert Unity dich auf, den Editor neu zu starten. Die Prüfung Input handling ändert die Einstellung nie von sich aus, weil sie von deinem Spiel abhängt. Siehe auch Eingabeverarbeitung in den Voraussetzungen.

Shader und Inhalte

  • Package shaders compile. Wenn ein Shader des Pakets Kompilierfehler hat (betroffene Objekte werden pink dargestellt), importiert Reimport ihn neu. Das hilft, wenn die Shader importiert wurden, bevor URP fertig geladen war. Fehlen benötigte Shader vollständig, importiere das Paket erneut aus dem Asset Store. Siehe Fehlerbehebung.
  • Shaders included in builds. Siehe Einen Build erstellen.
  • Tileset texture arrays built. Tilesets, deren gebaute Arrays fehlen oder veraltet sind, funktionieren trotzdem. Bis die Arrays neu gebaut sind, baut der Editor beim Laden der Karte aber unkomprimierte Arrays. Das kostet Speicher und Ladezeit. Build Arrays baut sie für jedes veraltete Tileset. Bei Player-Builds werden veraltete Arrays automatisch neu gebaut; Tilesets, die nie gebaut wurden, verwenden auch in Player-Builds unkomprimierte Arrays. Siehe Textur-Arrays und Textur-Arrays in Player-Builds.
  • GPU instancing on instanced prototypes. Bei Materialien von instanzierten Bäumen, Details im Modus Mesh und instanziertem Dekor sollte Enable GPU Instancing aktiviert sein. Andernfalls werden sie über temporäre Kopien gezeichnet, und ihre Instancing-Varianten werden unter Umständen aus Player-Builds entfernt. Enable Instancing aktiviert die Option bei jedem aufgelisteten Material, das sich bearbeiten lässt. Materialien innerhalb eines Modells oder eines Pakets sind schreibgeschützt: Extrahiere sie zuerst. Siehe GPU-Instancing.
  • Terrains have a tileset. Ein Terrain ohne Tileset erscheint in einer neutralen Platzhalterdarstellung. Die Korrektur weist jedem Terrain ohne Tileset ein Tileset des Projekts zu (lässt sich rückgängig machen). Siehe Tilesets.

Die Korrekturen für Szenen werden im Play Mode nicht angeboten, weil Änderungen an der Szene im Play Mode verloren gehen, sobald er endet.

Kartendateien für Terrains in Szenen

Ein RTS Terrain speichert seine Karte in einem .rtsmap-Asset (Feld Map Asset). Ein Terrain ohne ein solches Asset hält seine Karte nur im Arbeitsspeicher, und deine Bearbeitungen gehen verloren, wenn die Szene geschlossen wird. Die Prüfung Terrains save to a map file listet diese Terrains auf. Create Map Files speichert die aktuelle Karte jedes aufgelisteten Terrains in einer neuen .rtsmap-Datei im Ordner seiner Szene (oder in Assets/Maps, wenn die Szene noch nicht gespeichert ist) und weist sie dem Terrain zu.

Terrains, die mit dem New Map Wizard oder den Demo-Inhalten erstellt wurden, haben bereits eine Kartendatei. Wie Karten-Assets, Save und Save As funktionieren, erfährst du unter Karten-Assets und Kartendateien.

Einen Build erstellen

Unity nimmt Shader nur dann in einen Player-Build auf, wenn ein Material im Build sie verwendet. RTS Terrain Studio erzeugt die Materialien für Terrain, Wasser und Nebel des Krieges zur Laufzeit per Code. Deshalb werden die Shader vom Asset Resources/RTSTerrainStudio/RTSShaderReferences.asset im Paketordner referenziert. Das Asset referenziert außerdem das Material DecorPlaceholder, das die Instancing-Varianten von URP Lit im Build hält. Das Asset wird nach jedem Neuladen der Skripte, bei jedem Import von Shadern und vor jedem Player-Build automatisch geprüft und aktualisiert. Wenn die Prüfung Shaders included in builds rot ist, klicke auf Update.

Gehe vor dem Build diese Liste durch:

  1. Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und behebe alle Fehler und Warnungen.
  2. Prüfe, ob jede Qualitätsstufe deiner Zielplattform ein URP-Asset mit Depth Texture und Opaque Texture verwendet und ob dessen Renderer das Feature RTS Fog Of War hat, falls du den Nebel des Krieges nutzt.
  3. Baue die Textur-Arrays deiner Tilesets einmal (Build Arrays im Validator oder Build Texture Arrays im Tileset-Inspector), damit das Terrain sie nicht beim Laden unkomprimiert baut. Veraltete Arrays werden vor jedem Player-Build automatisch neu gebaut (siehe unten).
  4. Wenn deine Karten atmosphärischen Nebel nutzen, stelle sicher, dass die Prüfung Fog kept in player builds OK ist (siehe Nebel in Player-Builds).
  5. Validiere deine Karten im Tab Map (siehe Kartenvalidierung).
  6. Füge deine Szenen der Szenenliste deines Build-Profils hinzu. Karten, die einem RTS Terrain in einer Szene zugewiesen sind, werden wie jedes andere referenzierte Asset in den Build aufgenommen.
  7. Wenn du den In-Game-Editor mit auslieferst, lies Den In-Game-Editor mit dem Spiel ausliefern. Karten, die Spieler zur Laufzeit speichern, landen im Ordner Maps in RTSMapSerializer.StorageRoot (Application.persistentDataPath, in Web-Builds der Browserspeicher); siehe Kartendateien zur Laufzeit.
  8. Wenn du die Einstellung Managed Stripping Level über Minimal anhebst und eigene Bearbeitungswerkzeuge geschrieben hast, lies Code-Stripping und eigene Werkzeuge.
  9. Lies für einen Web-Build außerdem Web-Builds (WebGL).

Du darfst RTSShaderReferences.asset und den Ordner Resources des Pakets weder verschieben noch umbenennen: Das Paket lädt diese Assets zur Laufzeit mit Resources.Load.

Textur-Arrays in Player-Builds

Ein Player-Build verwendet die gebauten Textur-Arrays eines Tilesets so, wie sie sind, denn nur der Editor kann sie mit den Quelltexturen vergleichen. Damit ein Build nie veraltete Bodentexturen ausliefert, wird jedes Tileset, dessen Arrays schon einmal gebaut wurden, direkt vor dem Start eines Player-Builds geprüft:

  • Arrays, die nicht mehr zu den Texturen, der Auflösung oder der Komprimierung passen oder die für eine andere Plattform oder Texturkomprimierung gebaut wurden, werden mit normaler Komprimierungsqualität neu gebaut. Währenddessen wird ein Fortschrittsbalken angezeigt.
  • Tilesets, die nie gebaut wurden, bleiben unverändert. Player-Builds bauen für sie beim Laden einer Karte unkomprimierte Arrays.
  • Wenn ein Neubau abgebrochen wird oder fehlschlägt und sich die Texturen oder Einstellungen geändert haben, bietet ein Dialog Try Again, Cancel Build und Build Anyway an. Arrays, die nur ein anderes Format verwenden, zeigen trotzdem die richtigen Texturen und erzeugen deshalb nur eine Warnung im Log.
  • Ein Build im Batch-Modus bricht in diesem Fall ab. Füge der Befehlszeile -rtsAllowOutdatedTextureArrays hinzu, um trotzdem zu bauen; dann wird pro Tileset eine Warnung ausgegeben.
  • Ein Editor ohne Grafikgerät (-nographics, üblich auf Build-Servern) kann keine Arrays bauen. Der Build läuft dann mit einer Warnung pro veraltetem Tileset weiter, und Player-Builds verwenden die Arrays so, wie sie zuletzt gebaut wurden. Baue die Arrays vor einem solchen Build im Editor.
  • Arrays werden nur für die aktive Build-Plattform neu gebaut. Wenn du für eine andere Plattform baust, schreibt der Build stattdessen eine Warnung ins Log: Wechsle vorher zu dieser Plattform.
  • Asset Bundles und Addressables-Inhalte, die du separat baust, durchlaufen diese Prüfung nicht. Baue die Arrays vorher.

Die Prüfung auf veraltete Arrays nutzt den Importzustand der Quelltexturen, der sich mit einem Unity-Upgrade ändern kann. Der erste Player-Build nach einem Upgrade baut deshalb unter Umständen die Arrays aller gebauten Tilesets neu, auch die der Demo-Tilesets.

Gebaute Arrays werden lesbar im Tileset-Asset gespeichert. Player-Builds geben diese CPU-Kopie frei, sobald die Arrays zum ersten Mal verwendet werden. Das spart etwa 14 MB pro Tileset bei 512 px und etwa 56 MB bei 1024 px.

Nebel in Player-Builds

RTSEnvironment schaltet den in einer Karte gespeicherten atmosphärischen Nebel ein, während das Spiel läuft. Unity entfernt die Shader-Varianten von Nebelmodi, die es für unbenutzt hält. Steht Project Settings ▸ Graphics ▸ Shader Stripping ▸ Fog Modes auf Automatic, behält Unity nur den Nebel von Build-Szenen, die mit eingeschaltetem Nebel gespeichert sind. RTS Environment stellt vor jedem Speichern den eigenen Nebel der Szene wieder her. Sein Nebel zählt deshalb nicht, und Karten mit Nebel würden im Build keinen Nebel zeigen.

Die Prüfung Fog kept in player builds liest die Build-Szenen des aktiven Build-Profils und die geöffneten Szenen und listet die Nebelmodi auf, die RTS Environment dort verwendet. Keep Fog Modes (auch Teil von Fix All) stellt Fog Modes auf Custom und behält genau diese Modi, dazu alle, die bereits behalten wurden. Jeder behaltene Modus fügt dem Build Shader-Varianten hinzu, deshalb werden nicht verwendete Modi nicht hinzugefügt. Wenn das aktive Build-Profil die Graphics-Einstellungen überschreibt, prüfe auch dort die Fog Modes.

Web-Builds (WebGL)

RTS Terrain Studio läuft im Webbrowser: Terrain, Vegetation, Wasser, Dekor, Nebel des Krieges, der In-Game-Editor und die Beispiele. Ein Browser funktioniert allerdings anders als ein Desktop-System: Es gibt keinen Ordner, den der Spieler öffnen kann, manche Tasten gehören dem Browser, der gesamte Code läuft auf einem einzigen Thread, und das ganze Spiel teilt sich einen Speicher-Heap begrenzter Größe. Dieser Abschnitt erklärt, wie das Paket damit umgeht und wie du einen Web-Build erstellst, testest und veröffentlichst.

Kurz zusammengefasst:

  1. Öffne File ▸ Build Profiles, wähle Web und klicke auf Switch Platform (oder mache dein Web-Build-Profil zum aktiven Profil).
  2. Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und klicke auf Fix All. Wenn Web aktiv ist, installiert der Validator außerdem das Vollfenster-Template und prüft die Qualitätsstufen von Web-Builds.
  3. Wenn deine Karten atmosphärischen Nebel nutzen, prüfe, ob Fog kept in player builds OK ist.
  4. Klicke auf Build And Run. Unity baut das Spiel und öffnet es in deinem Browser.

Was im Browser funktioniert

ThemaUnterstützung
Grafik-APIWebGL 2.0, der Standard von Web-Builds (Auto Graphics API). Alle Shader des Pakets laufen dort. Dieser Abschnitt beschreibt WebGL 2.0; WebGPU, das Unity als Option anbietet, wird nicht behandelt.
BrowserAktuelle Desktop-Browser, die der Web-Player von Unity unterstützt, bedient mit Maus und Tastatur.
Smartphones und TabletsDie Seite lädt, aber der In-Game-Editor, die RTS-Kamera und die Beispieleinheiten sind für Maus und Tastatur gemacht. Touch-Eingabe wird nicht unterstützt. Das Template des Pakets zeigt auf Mobilgeräten einen Hinweis.
RenderingTerrain, Vegetation, Wasser, Dekor und der Nebel des Krieges sehen genauso aus wie auf dem Desktop. Gras wird statt in einem Structured Buffer in einer Float-Textur gespeichert (siehe Rendering und Performance).
In-Game-EditorAlle Werkzeuge, der Kartenbrowser und der Spieltest. Karten werden im Browserspeicher abgelegt und lassen sich herunterladen und importieren (siehe Karten im Browser). Für die Kartengröße und den Undo-Speicher gelten niedrigere Grenzen (siehe Der In-Game-Editor im Browser).
ThreadsWeb-Builds haben keine Worker-Threads, daher laufen alle Jobs nacheinander auf dem Hauptthread.

Das Vollfenster-Template

Die Seite rund um einen Web-Build stammt aus einem Web-Template. Unitys eigene Templates Default und Minimal zeigen das Spiel in Desktop-Browsern in einem Rahmen fester Größe (960 × 600 Pixel, sofern du das nicht änderst). Der In-Game-Editor, das HUD und die Minikarte skalieren mit diesem Rahmen, sodass ihr Text schwer lesbar wird. Deshalb bringt das Paket ein eigenes Template mit:

  • Das Spiel füllt das Browserfenster über einer schmalen Fußzeile mit dem Namen des Spiels und der Vollbild-Schaltfläche und passt seine Größe an das Fenster an. Auf Smartphones und Tablets füllt es die ganze Seite.
  • Der Ladebalken (mit Prozentanzeige), die Vollbild-Schaltfläche und die Meldungsbanner von Unity bleiben erhalten.
  • Auf hochauflösenden Bildschirmen ist die Render-Auflösung auf das 1.5-Fache der CSS-Pixelgröße begrenzt, was die Framerate hoch hält. Um das anzupassen, ändere maxDevicePixelRatio in der index.html des installierten Templates (niedrigere Werte bis hinunter zu 1 sind schneller, höhere Werte schärfer).
  • Dateien, die das Spiel speichert, etwa Karten des In-Game-Editors, werden sofort in den Browserspeicher geschrieben.
  • Die Seite stellt keine Anfragen an andere Server; ihre Icons sind Teil der Seite.

So installierst du es:

  1. Wähle Tools ▸ RTS Terrain Studio ▸ Install WebGL Template oder klicke im Project Validator im Panel Web template fills the browser window auf Install Template.
  2. Das Template wird nach Assets/WebGLTemplates/RTSTerrainStudio kopiert (Unity findet Templates nur in Assets/WebGLTemplates) und unter Player Settings ▸ Resolution and Presentation ▸ Web Template ausgewählt.
  3. Ist das Template bereits installiert, fragt das Menü, ob du deine Kopie mit Keep and Select behalten und auswählen oder mit Replace durch die Version des Pakets ersetzen willst. Die Kopie gehört deinem Projekt: Bearbeite sie nach Belieben, das Paket überschreibt sie nie ohne Nachfrage.

Build-Profile: Ein Build-Profil kann die Player Settings überschreiben und behält dann seine eigene Template-Auswahl. Mache dein Web-Build-Profil zum aktiven Profil, bevor du das Template installierst, damit die Einstellung in diesem Profil landet. Wenn du es installiert hast, während eine andere Plattform aktiv war, wähle das Template RTSTerrainStudio von Hand in den Overrides der Player Settings deines Web-Build-Profils aus.

Das Template basiert auf dem Template Default von Unity und verwendet nur Unitys eigene Template-Variablen, deshalb erscheint in den Player Settings nichts Neues. Auf Smartphones und Tablets zeigt es einige Sekunden lang den Hinweis This game is best played on a computer with a mouse and keyboard. an; entferne diese Zeile aus index.html, wenn dein Spiel Touch-Eingabe unterstützt.

Aus einem Editor-Skript (zum Beispiel einem Build-Skript) installiert RTSWebGLTemplateInstaller.InstallAndSelect(false) das Template ohne Dialoge, behält eine vorhandene Kopie und wählt sie aus. IsInstalled und IsSelected melden den aktuellen Zustand.

Web-Prüfungen im Project Validator

Solange Web die aktive Build-Plattform ist, zeigt der Validator eine Gruppe WebGL mit diesen drei Prüfungen.

PrüfungKorrekturWas geprüft wird
Web quality levels have Depth and Opaque TextureEnable BothDie Qualitätsstufen, die in der Spalte Web von Project Settings ▸ Quality aktiviert sind (die Web-Standardstufe steht zuerst), und das URP-Asset, mit dem jede von ihnen rendert. Der Editor rendert meist mit einer Desktop-Qualitätsstufe, daher kann ein Web-Build schlichteres Wasser haben, das du im Editor nie siehst. Jede Textur kostet pro Frame eine Kopie des gesamten Bildschirms. Die allgemeine Prüfung Depth and Opaque Texture enabled meldet unter Umständen dasselbe Asset; Fix All behebt beides.
MSAA is off for Web buildsTurn MSAA OffEin Fehler, wenn eine Web-Qualitätsstufe mit einem URP-Asset rendert, das MSAA mit der Depth Texture oder der Opaque Texture kombiniert. URP kann diese Kombination unter WebGL 2 nicht rendern: Das Canvas-Element im Browser bleibt schwarz, und die Browserkonsole zeigt Render Graph Execution error. Die Korrektur schaltet MSAA in diesen Assets aus. Desktop-Qualitätsstufen, die ein solches Asset mitverwenden, verlieren MSAA ebenfalls. Gib der Web-Plattform daher eine Qualitätsstufe mit eigenem URP-Asset.
Web template fills the browser windowInstall Template / ReinstallEine Warnung bei Unitys Templates Default und Minimal, ein Fehler, wenn das Template des Pakets ausgewählt ist, sein Ordner aber fehlt (der Build würde fehlschlagen), und OK für das Template des Pakets. Das Template PWA füllt das Fenster ebenfalls und ist OK, aber sein Service Worker speichert den Build im Cache: Erhöhe Player Settings ▸ Version bei jedem Upload. Jedes andere eigene Template erhält den Status Info ohne Korrektur.

Die Rendering-Prüfung Fog kept in player builds läuft für jede Plattform und ist auch für Web-Builds wichtig (siehe Nebel in Player-Builds).

Empfohlene Build-Einstellungen

EinstellungOrtEmpfehlung
Texture CompressionDein Web-Build-ProfilUse Player Settings oder DXT für Desktop-Browser. Für einen Build, der auf mobile Browser zielt, wähle ASTC oder ETC2. Die Textur-Arrays der Tilesets folgen dieser Einstellung (DXT5, ASTC 6×6 oder ETC2 RGBA8) und werden vor dem Build neu gebaut. Kann ein Browser das gewählte Format nicht verwenden, dekomprimiert Unity die Texturen für ihn, was etwa den vierfachen Speicher braucht.
Maximum Memory SizePlayer Settings ▸ Publishing SettingsBehalte den Standardwert von 2048 MB bei. Die Grenzen des In-Game-Editors im Browser sind auf diesen Heap ausgelegt.
Compression FormatPlayer Settings ▸ Publishing SettingsBrotli ergibt den kleinsten Download, funktioniert aber nur über HTTPS. Gzip funktioniert auch über HTTP. Beide brauchen die richtigen Server-Header (siehe Auf einer Website veröffentlichen).
Decompression FallbackPlayer Settings ▸ Publishing SettingsSchalte die Option ein, wenn du die Header des Servers nicht festlegen kannst, zum Beispiel auf Spieleportalen. Das Spiel lädt dann überall, startet aber etwas langsamer.
Data CachingPlayer Settings ▸ Publishing SettingsAn. Der Browser behält die heruntergeladenen Spieldaten, sodass der nächste Besuch schneller startet.
Enable ExceptionsPlayer Settings ▸ Publishing SettingsExplicitly Thrown Exceptions Only (der Standard) für Release-Builds. In diesem Modus wird eine Null-Referenz nicht als Exception gemeldet. Verwende daher einen Development-Build mit Full With Stacktrace, während du einen Fehler suchst.
Managed Stripping LevelPlayer Settings ▸ Other Settings ▸ OptimizationFür das Paket funktioniert jede Stufe. Höhere Stufen verkleinern den Download; eigene Bearbeitungswerkzeuge brauchen dann [Preserve] (siehe Code-Stripping und eigene Werkzeuge).
Qualitätsstufen und URP-AssetProject Settings ▸ QualityDeaktiviere die Spalte Web bei Desktop-Qualitätsstufen, die du im Browser nicht verwendest, damit ihre URP-Assets dem Build keine Shader-Varianten hinzufügen. Lass im URP-Asset für Web Light Cookies und Reflection Probe Blending ausgeschaltet, sofern deine Szenen sie nicht brauchen: WebGL 2.0 erlaubt nur 16 Texturen pro Draw Call (siehe Hinweise für Mobilgeräte). Lass Anti Aliasing (MSAA) in diesem Asset ausgeschaltet: Zusammen mit der Depth Texture oder der Opaque Texture verhindert es, dass URP unter WebGL 2 rendert.
Web TemplatePlayer Settings ▸ Resolution and PresentationRTSTerrainStudio (siehe Das Vollfenster-Template).

Einen Web-Build auf deinem Computer testen

  • Build And Run ist der einfachste Weg. Unity startet einen kleinen lokalen Webserver und öffnet das Spiel in deinem Standardbrowser.
  • Ein Doppelklick auf die gebaute index.html funktioniert nicht. Browser führen Web-Builds nicht über file://-Adressen aus, und die Seite zeigt nur eine Fehlermeldung.
  • Um einen fertigen Build-Ordner später erneut zu öffnen, stelle ihn über einen beliebigen lokalen Webserver bereit. Ein einfacher Server, der keine Komprimierungs-Header setzt, kann nur Builds mit eingeschaltetem Decompression Fallback oder mit deaktivierter Komprimierung ausliefern.
  • Teste die Tasten einmal in deinem Zielbrowser: F1, F5, Ctrl+S, Alt+N und Esc im Vollbild (siehe Der In-Game-Editor im Browser).

Auf einer Website veröffentlichen

  • Lade den gesamten Build-Ordner hoch: index.html, Build und TemplateData.
  • Komprimierte Builds brauchen zwei Header für die Dateien in Build: Content-Encoding: br für Brotli oder Content-Encoding: gzip für Gzip sowie den richtigen Content-Type, zum Beispiel application/wasm für die .wasm-Datei. Ohne Content-Encoding zeigt die Seite eine Fehlermeldung, und ein falscher Content-Type verlangsamt den Start. Wenn du den Server nicht konfigurieren kannst, schalte Decompression Fallback ein.
  • Spieleportale wie itch.io zeigen das Spiel in einem iframe. Lade eine ZIP-Datei hoch, in der index.html auf oberster Ebene liegt. Ein iframe mit Sandbox braucht allow-downloads für Download Map und allow-modals, damit der Browser Leave site? fragen kann. Prüfe beides auf der echten Seite.
  • Gespeicherte Karten gehören zur Website. Der Browser speichert sie pro Website-Adresse (Protokoll, Host und Port). Ein neuer Upload auf dieselbe Website behält sie; dasselbe Spiel auf einer anderen Website startet ohne Karten.
  • Safari löscht unter Umständen die gespeicherten Daten einer Website, die sieben Tage lang nicht genutzt wurde, und Safari auf iPhone und iPad zeigt die Rückfrage Leave site? nicht an. Sage deinen Spielern, dass sie Karten, die sie behalten wollen, herunterladen sollen.
  • In einem iframe erhält das Spiel Tastendrücke erst, nachdem der Spieler hineingeklickt hat, und Esc beendet immer den Vollbildmodus des Browsers (siehe unten).

Karten im Browser

Im Browser ist Application.persistentDataPath ein Dateisystem im Arbeitsspeicher. Es wird nur auf Anforderung in die Datenbank des Browsers (IndexedDB) kopiert. Das Template des Pakets stellt diese Anforderung nach jeder Änderung. Bei anderen Templates stellt das Paket sie selbst nach jedem Speichern, Löschen und Importieren, sodass gespeicherte Karten nach einem Neuladen noch vorhanden sind. Dein eigener Code erreicht dasselbe mit RTSWebFileSystem.Flush() (siehe Scripting-API für Web-Builds).

persistentDataPath hängt von der Adresse der Seite ab. Ein neuer Upload unter einer anderen Adresse (Spieleportale verwenden oft pro Upload eine neue) würde also ohne Karten starten. In Web-Builds liegen die Karten deshalb in RTSMapSerializer.StorageRoot, einem Ordner im selben Browserspeicher, der nach dem Firmen- und Produktnamen aus den Player Settings benannt ist. Auf allen anderen Plattformen ist StorageRoot gleich persistentDataPath. Der Kartenordner des In-Game-Editors ist der Unterordner Maps darin.

Spieler können diesen Speicher nicht als Ordner öffnen. Deshalb überträgt der In-Game-Editor Karten selbst zwischen Browser und Computer:

BefehlOrtFunktion
Download MapMenü FileLässt den Browser die aktuelle Karte samt nicht gespeicherter Änderungen als .rtsmap-Datei herunterladen (meist in den Downloads-Ordner).
Import Map...Menü FileÖffnet die Dateiauswahl des Browsers. Die gewählte .rtsmap-Datei wird den in diesem Browser gespeicherten Karten hinzugefügt und nach der üblichen Rückfrage zu nicht gespeicherten Änderungen geöffnet.
Import...Kartenbrowser (Open)Dasselbe wie Import Map, neben der Zeile mit dem Speicherort.
DownloadKartenbrowser (Open)Lädt die ausgewählte Kartendatei herunter.
Ziehen und AblegenSpielflächeEine auf das Spiel gezogene .rtsmap-Datei wird importiert und geöffnet. Andere Dateien werden mit einer Meldung abgelehnt, damit der Browser die Seite nicht verlässt, um sie anzuzeigen.
  • Die ganze Datei wird vor dem Speichern geprüft, und Dateien bis 128 MB werden angenommen.
  • Ein Import ersetzt nie eine andere Karte. Wird dieselbe Datei erneut importiert, wird sie wiederverwendet; eine andere Datei mit demselben Namen wird als Name (2), Name (3) usw. gespeichert. Beim Öffnen der Karte wird ein Vorschaubild geschrieben.
  • Der Kartenbrowser zeigt statt eines Ordnerpfads Saved in this browser. Use Download to keep a copy on your computer. an. Show Maps Folder und Quit gibt es im Browser nicht.
  • Beim Löschen der Browserdaten der Website gehen auch die gespeicherten Karten verloren.

Der In-Game-Editor im Browser

Den Tab mit nicht gespeicherten Änderungen schließen

Solange die Karte nicht gespeicherte Änderungen hat und Confirm Quit With Unsaved Changes eingeschaltet ist, fragt der Browser Leave site?, bevor der Tab geschlossen oder neu geladen wird. Den Text dieser Rückfrage legt der Browser fest. Mit Unitys eigenen Templates, die Dateien nicht sofort speichern, fragt er unabhängig von dieser Option auch, solange ein Speichervorgang noch in den Browserspeicher geschrieben wird.

Den Editor nach dem Schließen zurückholen

In Browsern sind die Funktionstasten oft Medientasten (auf Macs und vielen Laptops), und Smartphones haben gar keine. Wenn der Spieler den Editor mit seiner Schließen-Schaltfläche (×) schließt, erscheint daher unten links eine Schaltfläche Open Map Editor (F2), die ihn zurückholt. Ohne Tastatur oder wenn Toggle Key auf None steht, heißt die Schaltfläche Open Map Editor. Die Schaltfläche erscheint nur, nachdem die Schließen-Schaltfläche benutzt wurde; wenn dein Spielcode den Editor ausblendet, entscheidet dein Spiel, wie er zurückkommt. Im Browser ist die Schließen-Schaltfläche auch dann verfügbar, wenn Toggle Key auf None steht.

Tastatur
  • New Map liegt auf Alt+N. Browser reservieren Ctrl+N für ein neues Fenster und geben es nie an die Seite weiter. Alt+N funktioniert auch auf Desktop-Plattformen. Auch Ctrl+T und Ctrl+W erreichen eine Seite nie.
  • Browser-Tastenkürzel werden zurückgehalten. Solange der Editor aktiviert ist, reagiert der Browser nicht auf F1 bis F10 und auf Ctrl (oder Cmd) + S, O, D und A, sodass sie als Tastenkürzel des Editors funktionieren. F11, F12, Ctrl+F5, Ctrl+R, Kopieren, Einfügen und Tasten, die in Textfelder der Seite eingegeben werden, bleiben beim Browser.
  • F5 startet und beendet den Spieltest, sodass du nicht auf Esc angewiesen bist.
  • Esc im Vollbild: Browser verwenden Esc, um den Vollbildmodus zu beenden. In Chrome und Edge erreicht auf einer sicheren Seite (HTTPS oder localhost), die nicht in einem iframe liegt, ein kurzer Druck auf Esc das Spiel, und Gedrückthalten von Esc beendet den Vollbildmodus. Anderswo beendet bereits das erste Esc den Vollbildmodus. Die Alternativen: F5 oder Back to Editor beendet einen Spieltest, die Schaltfläche × schließt einen Dialog, und Ctrl+Z macht einen Pinselstrich rückgängig. Das Hilfefenster (F1) weist ebenfalls darauf hin.
  • Tastaturlayouts: Die Buchstaben-Tastenkürzel (Ctrl+Z, G, P …) richten sich nach dem Buchstaben, der auf der Taste aufgedruckt ist, auch auf deutschen (QWERTZ) und französischen (AZERTY) Tastaturen. Die Pinseltasten richten sich nach dem eingegebenen Zeichen, sodass [ auch dort funktioniert, wo es auf AltGr+8 liegt. Auf Desktop-Plattformen ist das genauso.
  • Gameplay-Demo: Ctrl+1 bis Ctrl+9 (Kontrollgruppen) wechseln in den meisten Browsern die Browser-Tabs. Wähle für ein Webspiel andere Tasten.
Grenzen

Die Karte, ihre Texturen und der Undo-Verlauf teilen sich einen Speicher-Heap von höchstens 2 GB (der Standardwert von Maximum Memory Size), und dieser Heap schrumpft nie. Der Editor verwendet im Browser deshalb niedrigere Grenzen:

GrenzeBrowserAndere Plattformen
Größte Karte in New Map und Resize2048 ÷ Splat-Auflösung Zellen pro Seite: 512 bei der Standard-Splat-Auflösung 4, 256 bei 8. Größere Presets werden nicht angeboten, und die Dialoge zeigen In the browser, maps can be up to N x N cells. Resize behält die Größe einer größeren Karte bei, die aus einer Datei geöffnet wurde, vergrößert sie aber nicht.1024
Undo-SpeicherHöchstens 192 MB. Ein niedrigerer Wert von Undo Memory MB bleibt erhalten.Undo Memory MB (Standard 512)
Importierte Kartendatei128 MBKein Importbefehl
Weitere Unterschiede
  • Die Benutzeroberfläche wird nie kleiner als 85 %, gemessen in CSS-Pixeln, damit sie auf hochauflösenden Bildschirmen und in kleinen Fenstern lesbar bleibt.
  • Das Rand-Scrollen der Editorkamera und der Beispiel-RTS-Kamera stoppt, wenn die Maus die Seite oder das iframe des Spiels verlässt.

Rendering und Performance

  • Gras: WebGL 2.0 hat keine Structured Buffers. Der Detail-Renderer speichert die Instanzen jedes Patches stattdessen in einer Float-Textur (zwei Texel pro Instanz). Er wählt diesen Weg zur Laufzeit, und das Gras sieht gleich aus.
  • Terrain: Der Forward-Pass verwendet 10 der 16 Texturen, die WebGL 2.0 pro Draw Call erlaubt. Halte das URP-Asset von Web-Builds schlank (siehe Hinweise für Mobilgeräte).
  • Ein Thread: Ohne Worker-Threads dauern Operationen auf der ganzen Karte spürbar länger als auf dem Desktop, und die Seite reagiert währenddessen nicht: Generate, New Map, Resize, das Öffnen einer großen Karte und Apply to Whole Map von Auto Paint. Karten mit 128 bis 256 Zellen pro Seite bleiben angenehm bedienbar.
  • Textur-Arrays: Verwende gebaute Arrays im Format deines Web-Builds (siehe Empfohlene Build-Einstellungen). Player-Builds geben die CPU-Kopie gebauter Arrays frei, was im begrenzten Speicher einer Seite wichtig ist.
  • Render-Auflösung: Das Template begrenzt das Pixelverhältnis auf 1.5; senke es für eine höhere Framerate.

Code-Stripping und eigene Werkzeuge

Bearbeitungswerkzeuge werden per Reflection gefunden und erzeugt (RTSToolRegistry), daher verweist kein Code direkt auf sie. Mit einem Managed Stripping Level über Minimal, wie es für kleinere Web- und IL2CPP-Builds üblich ist, erkennt Unity nicht, dass sie verwendet werden, und würde sie entfernen. Die Datei Runtime/link.xml des Pakets behält jede Klasse im Namespace RTSTerrainStudio, deren Name auf Tool endet, sodass die eingebauten Werkzeuge immer erhalten bleiben.

Markiere deine eigenen Werkzeugklassen mit [UnityEngine.Scripting.Preserve], das die Klasse und ihren parameterlosen Konstruktor behält, oder liste sie in einer link.xml-Datei deines Projekts auf. Findet ein Player-Build überhaupt keine Werkzeuge, schreibt er No editing tools were found in this build. ins Log.

using RTSTerrainStudio;
using UnityEngine.Scripting;

// [Preserve] keeps the tool in builds with a high Managed Stripping Level.
[Preserve]
public sealed class CraterTool : RTSTool
{
    // ...
}

Scripting-API für Web-Builds

MemberBeschreibung
RTSWebFileSystem.IsSupportedTrue in einem WebGL-Player. Auf anderen Plattformen und im Unity Editor tut die Klasse nichts.
RTSWebFileSystem.Flush()Kopiert das Dateisystem der Seite in den Browserspeicher, sodass mit System.IO geschriebene oder gelöschte Dateien ein Neuladen überstehen. Alle Aufrufe eines Frames werden zu einem Schreibvorgang zusammengefasst. RTSMapSerializer.SaveToFile ruft die Methode für dich auf.
RTSWebFileSystem.DownloadFile(fileName, data, mimeType)Lässt den Browser Bytes als Datei herunterladen. Auf anderen Plattformen oder wenn der Browser den Download abgelehnt hat, gibt die Methode false zurück.
RTSMapSerializer.StorageRootOrdner für Dateien, die zwischen Sitzungen erhalten bleiben: Application.persistentDataPath, in Web-Builds ein Ordner im Browserspeicher, der nicht von der Adresse der Seite abhängt.
RTSMapSerializer.DefaultMapsFolderMaps in StorageRoot.
RTSRuntimeEditor.RunsInBrowserTrue in Web-Builds, in denen der Editor die Browser-Grenzen und Alt+N für New Map verwendet.
RTSRuntimeEditor.StoresMapsInBrowserTrue, wenn Karten im Browserspeicher abgelegt werden (WebGL-Player).
RTSRuntimeEditor.HasReliableFunctionKeysFalse in Webbrowsern und auf mobilen Plattformen, wo die Schließen-Schaltfläche die Schaltfläche zum erneuten Öffnen einblendet.
editor.DownloadMap(), editor.ImportMap()Die Befehle des Menüs File. Browser zeigen die Dateiauswahl nur als Reaktion auf einen Klick oder Tastendruck. Rufe ImportMap daher aus einem Klick-Handler auf.
RTSInput.SetBrowserShortcutGuard(bool)Unterdrückt die browsereigene Aktion für F1 bis F10 und Ctrl+S/O/D/A und lässt im Vollbild ein kurzes Esc zum Spiel durch (Chrome, Edge). Jeder Aufruf mit true braucht einen passenden Aufruf mit false. Der In-Game-Editor und RTSDemoHUD schalten diesen Schutz ein, solange sie aktiviert sind. Tut auf anderen Plattformen nichts.
RTSInput.GetKeyDownByLabel(RTSKey)Wie GetKeyDown, aber für die Taste, die im Tastaturlayout des Spielers diesen Buchstaben trägt. Verwende es für Buchstaben-Tastenkürzel wie Ctrl+Z.
RTSInput.GetCharacterDown(char)True, wenn das Zeichen in diesem Frame eingegeben wurde, unabhängig vom Tastaturlayout. Verwende es für Symboltasten wie [ ] - +. Zeichen, die in ein fokussiertes Textfeld eingegeben werden, kommen hier ebenfalls an. Prüfe daher zuerst den UI-Fokus.
RTSInput.PointerInsideScreenTrue, solange sich der Zeiger im Spielfenster befindet. In Web-Builds muss die Maus außerdem noch über der Seite sein.
using System.IO;
using RTSTerrainStudio;

public static class MyGameFiles
{
    // Kept between sessions on every platform; in Web builds in the storage of the browser.
    static string SettingsPath => Path.Combine(RTSMapSerializer.StorageRoot, "settings.json");

    public static void SaveSettings(string json)
    {
        Directory.CreateDirectory(RTSMapSerializer.StorageRoot);
        File.WriteAllText(SettingsPath, json);

        // Web builds: write the change to the browser storage, so it survives a reload.
        // Does nothing on other platforms.
        RTSWebFileSystem.Flush();
    }

    /// <summary>Lets the player keep a copy of the current map on the computer (Web builds only).</summary>
    public static bool DownloadMap(RTSTerrain terrain)
    {
        if (!RTSWebFileSystem.IsSupported || terrain.Map == null) return false;
        byte[] bytes = RTSMapSerializer.Serialize(terrain.Map);
        return RTSWebFileSystem.DownloadFile("My Map" + RTSMapSerializer.FileExtension, bytes);
    }
}

Grundkonzepte

Dieses Kapitel erklärt, wie RTS Terrain Studio eine Karte versteht: was gespeichert wird, in welchen Einheiten, wie die sichtbare Oberfläche aus diesen Daten entsteht und welche Assets (Tilesets, Bibliotheken, Kartendateien) dazugehören. Du musst es vor deiner ersten Karte nicht lesen. Kehre aber hierher zurück, wenn du wissen willst, warum sich etwas so verhält, oder wenn du anfängst, Code zu schreiben, der mit den Kartendaten arbeitet.

Kurz gesagt:

  • Eine Karte ist ein Raster aus quadratischen Zellen. Höhen, Klippenstufen und Wasser liegen auf den Ecken (Vertices) der Zellen.
  • Klippenstufen sind ganze Zahlen von 0 bis 15. Wo benachbarte Ecken unterschiedliche Stufen haben, entsteht eine senkrechte Klippenwand, es sei denn, alle vier Ecken der Zelle sind als Rampe markiert.
  • Bodentexturen, Grasdichte und Pathing-Overrides sind Rasterschichten mit mehreren Texeln pro Zelle.
  • Bäume, Dekor, Regionen, Startpositionen und Marker sind Objektlisten in kartenlokalen Koordinaten.
  • Das Aussehen stammt aus einem Tileset (Boden-, Klippen- und Wassertexturen); die Objekte stammen aus einer Vegetationsbibliothek und einer Dekorbibliothek. Karten speichern nur Indizes und IDs, daher kannst du das Aussehen austauschen, ohne die Karte anzufassen.
  • Alles wird in einer einzigen kompakten Binärdatei .rtsmap gespeichert, die sowohl der Unity Editor als auch der In-Game-Editor verwenden.

Das Kartenmodell

Alle Kartendaten liegen in einem einzigen Objekt der Klasse MapData (Namespace RTSTerrainStudio). Eine Komponente RTSTerrain besitzt genau ein MapData-Objekt (terrain.Map), stellt es als Chunk-Meshes dar, hält die daraus abgeleitete Pathing-Karte aktuell und beantwortet Abfragen wie „Wie hoch ist der Boden hier?“.

Zellen und Vertices

Die Karte ist ein Raster aus Width × Height Zellen. Jede Zelle hat an ihren vier Ecken je einen Vertex, und benachbarte Zellen teilen sich ihre Ecken. Eine Karte mit 4 × 2 Zellen hat deshalb 5 × 3 Vertices:

 z
 2   v10 ---- v11 ---- v12 ---- v13 ---- v14
      |  cell  |  cell  |  cell  |  cell  |
      | (0,1)  | (1,1)  | (2,1)  | (3,1)  |
 1   v5 ----- v6 ----- v7 ----- v8 ----- v9
      |  cell  |  cell  |  cell  |  cell  |
      | (0,0)  | (1,0)  | (2,0)  | (3,0)  |
 0   v0 ----- v1 ----- v2 ----- v3 ----- v4
     x=0      1        2        3        4

 vertex index = z * VertexCountX + x      (VertexCountX = Width + 1)
  • Vertex-Schichten (Feinhöhe, Klippenstufe, Flags, Klippentyp, Wasserhöhe) haben (Width + 1) × (Height + 1) Einträge, Zeile für Zeile gespeichert. Verwende map.VertexIndex(x, z).
  • Die Zellgröße (map.CellSize, standardmäßig 2 m) ist die Weltgröße einer Zelle. Eine Karte mit 128 × 128 Zellen à 2 m deckt 256 × 256 m ab (map.WorldSize).
  • Eine Position gehört zur Zelle floor(localXZ / CellSize); map.LocalToCell(localXZ) berechnet das und begrenzt das Ergebnis auf die Karte.
  • Zum Rendern werden die Zellen zu Chunks zusammengefasst (16, 32 oder 64 Zellen pro Seite, Einstellung Chunk Size der Komponente RTSTerrain, standardmäßig 32). Nur Chunks, die von einer Bearbeitung betroffen sind, werden neu aufgebaut. Die Chunk-Größe ändert die Kartendaten nicht.

Kartenabmessungen

Die Abmessungen werden beim Anlegen einer Karte festgelegt (im New Map Wizard, im In-Game-Dialog New Map oder über MapCreationSettings im Code). Werte außerhalb des gültigen Bereichs korrigiert MapCreationSettings.Validate().

EinstellungStandardGültige WerteBeschreibung
width, height12816–1024, auf ein Vielfaches von 16 gerundetAnzahl der Zellen entlang X und Z. Der New Map Wizard bietet Presets von Tiny (64) bis Epic XL (512).
cellSize2≥ 0.1Weltgröße einer Zelle in Metern.
cliffLevelHeight2≥ 0.05Welthöhe einer Klippenstufe in Metern.
baseCliffLevel20–15Anfängliche Klippenstufe jedes Vertex. Stufe 2 lässt nach unten Platz, um Seen und Flüsse auszuheben.
baseGroundLayer00–15Bodenschicht, die anfangs die gesamte Karte bedeckt.
splatResolution41, 2, 4, 8Texel der Bodentextur (Splat) pro Zellseite.
detailResolution21, 2, 4Texel der Detaildichte (Gras) pro Zellseite.
pathingResolution21, 2, 4Pathing-Zellen pro Seite einer Kartenzelle.
name"New Map"beliebiger TextKartenname (wird in der Karteninfo gespeichert).

Auflösungen, die keine Zweierpotenz sind, werden aufgerundet (aus 3 wird 4). Zellgröße, Stufenhöhe und die drei Rasterauflösungen stehen fest, sobald die Karte existiert. Die Funktion Resize (RTSTerrain.ResizeMap) ändert nur die Anzahl der Zellen: Sie behält den Inhalt, verschiebt ihn um einen Versatz und füllt neue Bereiche mit einer gewählten Klippenstufe und Bodenschicht.

MapData.Resize(newWidth, newHeight, offsetCells, fillCliffLevel, fillGroundLayer) gibt eine neue Karte zurück (das Original bleibt unverändert). Bäume, Dekorobjekte und Startpositionen außerhalb der neuen Grenzen werden entfernt. Marker und Regionen werden verschoben, bleiben aber erhalten, auch wenn sie danach außerhalb liegen. Die Größen werden auf 16–1024 begrenzt (MapData.MinSize–MaxSize), beim Aufruf aus dem Code aber nicht auf ein Vielfaches von 16 gerundet. RTSTerrain.ResizeMap ersetzt die Karte (MapLoaded wird ausgelöst), markiert sie als nicht gespeichert und leert den Undo-Verlauf der Bearbeitungssitzungen.

Was eine Karte enthält

Für jede Datenart gibt es ein passendes Flag im Enum MapLayers. Die Flags werden für Änderungsbenachrichtigungen, die Undo-Aufzeichnung und die Serialisierung verwendet.

DatenGespeichert proMember in MapDataTypFlag in MapLayers
FeinhöheVertexHeightsNativeArray<float>, MeterHeights
KlippenstufeVertexCliffLevelsNativeArray<byte>, 0–15CliffLevels
Vertex-Flags (Rampe, Wasser)VertexVertexFlagsNativeArray<byte> aus VertexFlagsVertexFlags
KlippentypVertexCliffTypesNativeArray<byte>, 0–3CliffTypes
Höhe der WasseroberflächeVertexWaterHeightsNativeArray<float>, kartenlokales YWater
Gewichtungen der BodenschichtenSplat-TexelSplatMaps[0..3]NativeArray<Color32> × 4Splat
Gras- und PflanzendichteDetail-TexelDetailLayersein DetailLayer (ein Byte pro Texel) pro PrototypDetails
BäumeInstanzTrees, TreePrototypeIdsList<TreeInstanceData>Trees
Dekor (Doodads)InstanzDecor, DecorPrototypeIdsList<DecorInstanceData>Decor
Manuelle Pathing-OverridesPathing-ZellePathingOverridesNativeArray<byte> aus PathingOverridePathingOverrides
RegionenListeRegionsList<MapRegion>Regions
StartpositionenListeStartLocationsList<StartLocation>StartLocations
MarkerListeMarkersList<MapMarker>Markers
KarteninfoKarteInfoMapInfoInfo
Umgebung (Beleuchtung)KarteEnvironmentEnvironmentSettingsEnvironment

Zwei kombinierte Flags sind praktisch: MapLayers.Geometry (Höhen, Stufen, Vertex-Flags, Klippentypen) und MapLayers.PathingInputs (alles, was die Pathing-Karte beeinflusst). MapLayers.All umfasst alles.

Die Pathing-Karte (terrain.Pathing, Klasse PathingMap) besteht aus abgeleiteten Daten: Sie wird aus den obigen Schichten berechnet und nie gespeichert. Siehe Pathing-Karte.

VertexFlags.User0 und VertexFlags.User1 sind für deinen eigenen Code reserviert. Das Paket verwendet sie nie, und sie werden mit der Karte gespeichert. So kannst du besondere Vertices markieren (zum Beispiel „Creep-Spawnbereich“), ohne einen eigenen Dateiabschnitt anzulegen.

MapData belegt nativen Speicher. Eine Karte, die du selbst erstellst oder lädst (MapData.Create, RTSMapSerializer.LoadFromFile, RTSMapAsset.LoadMap, map.Clone()), musst du mit Dispose() freigeben – es sei denn, du übergibst sie mit terrain.SetMap(map) an ein Terrain. Dann gehört sie dem Terrain, und es gibt sie frei, sobald die Karte ersetzt oder die Komponente deaktiviert wird.

Klippenstufen, Feinhöhen und Klippentypen

Klassisches RTS-Terrain kennt zwei Arten von Höhe: große, diskrete Stufen (Klippen) und kleine, sanfte Variationen (Hügel). RTS Terrain Studio speichert beide getrennt für jeden Vertex.

Klippenstufen

  • Jeder Vertex hat eine Klippenstufe von 0 bis 15 (MapData.MaxCliffLevel = 15).
  • Eine Stufe ist CliffLevelHeight Meter hoch (standardmäßig 2 m).
  • Neue Karten beginnen auf Stufe 2. So bleiben zwei Stufen unter dem Boden für Seen, Flüsse und Schluchten und dreizehn darüber für Plateaus.
  • Bearbeite die Stufen mit den Klippenwerkzeugen: Raise / Lower Level, Set Level, Ramp, Cliff Type und Clean Cliffs.
  • Der Nebel des Krieges nutzt die Klippenstufen für die Sichtlinie: Einheiten sehen ihre eigene Stufe und alles darunter; höher gelegener Boden versperrt ihnen die Sicht.

Feinhöhen

Zusätzlich zur Stufe hat jeder Vertex eine Feinhöhe in Metern (Heights), positiv oder negativ. Die Höhenwerkzeuge (Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode) ändern nur die Feinhöhe. So kannst du Hügel auf einem Plateau bauen, ohne dass daraus Klippen werden. Die Bodenhöhe eines Vertex ergibt sich so:

vertex height = CliffLevels[i] × CliffLevelHeight + Heights[i]      (map-local Y, metres)

Beispiel: Stufe 3 bei einer Stufenhöhe von 2 m und einer Feinhöhe von 0.4 m ergibt einen Vertex, der 6.4 m über dem Kartenursprung liegt. map.GetVertexHeight(x, z) und map.GetVertexPosition(x, z) berechnen das für dich.

Klippentypen

Ein Tileset kann bis zu 4 Klippenschichten haben (zum Beispiel „Rock“ und „Grassy Cliff“). Jeder Vertex speichert, welche davon verwendet wird (CliffTypes, 0–3, gemalt mit dem Werkzeug Cliff Type). Eine Klippenwand verwendet den Klippentyp der höchsten Ecke ihrer Zelle. Hat ein Klippentyp-Index keine Schicht im Tileset, greift die Wand auf Klippenschicht 0 zurück.

Wie die Oberfläche entsteht (die Referenzoberfläche)

Das Struct MapGeometryView definiert die einzig gültige Oberfläche des Terrains. Die Chunk-Meshes, die Gameplay-Abfragen (GetHeight, Raycast, …), die Pathing-Karte und der Nebel des Krieges folgen alle denselben Regeln. Was du siehst, ist also genau das, worauf die Einheiten laufen. Jede Zelle wird anhand ihrer vier Ecken eingeordnet:

ZellartBedingungOberfläche
Glatte ZelleAlle vier Ecken haben dieselbe Klippenstufe.Bilineare Fläche aus den vier Eckhöhen.
RampenzelleAlle vier Ecken tragen das Flag VertexFlags.Ramp (die Stufen dürfen sich unterscheiden).Bilineare Fläche: Die Stufen gehen weich in einen Hang über. Siehe Rampen.
KlippenzelleDie Ecken haben unterschiedliche Stufen, und die Zelle ist keine Rampenzelle.Flache Terrassen, getrennt durch eine senkrechte Wand entlang einer abgerundeten Kontur.

Abgerundete Konturen. Innerhalb einer Klippenzelle wird für jede Stufenschwelle t der Indikator „Eckstufe ≥ t“ (1 oder 0) bilinear über die Zelle interpoliert. Ein Punkt gehört zur höchsten Stufe, deren interpolierter Indikator mindestens 0.5 beträgt (MapGeometryView.LevelAt). Das ist eine Marching-Squares-Regel: Eine einzelne angehobene Ecke ergibt einen Viertelkreis statt einer Diagonale, und lange Klippenlinien bleiben gerade. Die Wand selbst verläuft an der Kontur senkrecht. Die Feinhöhen werden immer bilinear interpoliert und obendrauf addiert, auch innerhalb von Klippenzellen.

Das Aussehen der gerenderten Wände stellst du an der Komponente RTSTerrain ein (Settings):

EinstellungStandardBeschreibung
Surface Subdivisions2Quads pro Zellseite auf glattem Boden (1 = klassischer RTS-Look, 2–4 = weichere Hügel).
Cliff Subdivisions4Auflösung des Unterrasters, mit dem die abgerundete Kontur innerhalb einer Klippenzelle nachgezeichnet wird.
Cliff Noise0.18Zufällige horizontale Verschiebung der Wand-Vertices (Bruchteil einer Zelle) für natürlich wirkenden Fels.
Cliff Noise Scale1.6Skalierung des Klippenrauschens in Welteinheiten.
Cliff Slant0.12Wie weit die Oberkante einer Wand zurückgesetzt wird, sodass die Wände leicht geneigt sind (Bruchteil einer Zelle).
Cliff Wall Segments3Vertikale Segmente der Wandgeometrie pro Klippenstufe.

Die Geometrieansicht erhältst du mit map.GetGeometryView(). Sie ist ein Struct aus nativen Arrays, daher kannst du sie auch an Burst-Jobs übergeben. Alle Member erwarten kartenlokale Positionen:

MemberBeschreibung
SampleHeight(localXZ)Bodenhöhe (kartenlokales Y).
SampleHeightInCell(cell, uv), SampleLevelHeightInCell(cell, uv)Dasselbe innerhalb einer bekannten Zelle (uv 0–1), mit oder ohne Feinhöhe.
SampleNormal(localXZ), SampleSlope(localXZ)Oberflächennormale; Neigung in Grad.
SampleCliffLevel(localXZ)Klippenstufe an einer Position.
SampleWater(localXZ, out waterHeight), SampleWaterDepth(localXZ)Ob dort Wasser ist und wie hoch seine Oberfläche liegt; Wassertiefe oder 0 bei trockenem Boden.
IsCliffCell(cell), IsRampCell(cell)Ordnet eine Zelle ein (siehe Tabelle oben).
LocateCell(localXZ, out cell, out uv)Die Zelle einer Position und die Position innerhalb dieser Zelle (0–1).
Raycast(origin, direction, maxDistance, out hitPoint, out hitDistance)Analytischer Raycast gegen die Referenzoberfläche, ganz ohne Collider.
static LevelAt(l00, l10, l01, l11, uv), static Bilerp(v00, v10, v01, v11, uv)Die Stufenregel innerhalb einer Klippenzelle und bilineare Interpolation.
width, height, cellSize, cliffLevelHeight, WorldSizeKartengröße in Zellen, Zellgröße, Stufenhöhe und Weltgröße.

Cliff Noise und Cliff Slant verschieben nur die gerenderten Wand-Vertices um einen Bruchteil einer Zelle. Abfragen verwenden dagegen die exakte Kontur. Direkt an einer Klippenkante können der sichtbare Fels und die Referenzoberfläche daher leicht voneinander abweichen. Die Pathing-Karte berücksichtigt das mit ihrer Einstellung Cliff Margin, die entlang jeder Wand einen schmalen, nicht begehbaren Streifen freihält.

Rampen

Über eine Rampe gelangen Einheiten von einer Klippenstufe zur nächsten. Die Regel ist einfach:

Eine Zelle ist eine Rampe, wenn alle vier Ecken als Rampe markiert sind. Fehlt das Flag auch nur an einer Ecke, bleibt die Zelle eine Klippenzelle mit senkrechter Wand.

  • Das Flag heißt VertexFlags.Ramp und wird pro Vertex gesetzt. Das Werkzeug Ramp (ID cliff.ramp) malt es; halte Shift gedrückt, um es zu entfernen.
  • Eine Rampenzelle ist ein weicher, bilinearer Hang zwischen ihren Eckhöhen (Stufe plus Feinhöhe).
  • Rampen funktionieren am besten über genau eine Stufe. Steilere Rampen sind erlaubt, aber die Statusleiste warnt vor Rampenzellen, die mehr als eine Stufe überspannen.
  • Pathing: Bei einer gültigen Rampe über eine Stufe wird die Begehbarkeit nur anhand der Neigung ihrer Feinhöhen geprüft (der Stufenanstieg, den sie überbrückt, macht sie nicht „zu steil“). Rampen sind nie bebaubar.
  • GetCliffLevel liefert auf einer Rampe die interpolierte Stufe, gerundet: auf der unteren Hälfte die untere Stufe, auf der oberen Hälfte die obere.
  1. Hebe mit Raise / Lower Level ein Plateau um eine Stufe an.
  2. Wähle das Werkzeug Ramp und male dort über die Klippenkante, wo die Rampe entstehen soll. Achte darauf, dass der Pinsel mindestens die Vertices auf beiden Seiten der Wand abdeckt, damit jede Ecke der dazwischenliegenden Zellen markiert wird.
  3. Die Wand verschwindet, und ein Hang entsteht. Verbreitere die Rampe, indem du weitere Vertices entlang der Kante bemalst.

Bodenschichten und Splatmaps

Bodentexturen werden in Splatmaps gemalt: vier RGBA-Texturen (MapData.SplatMapCount = 4) mit SplatWidth × SplatHeight Texeln. Jeder der 16 Kanäle enthält die Gewichtung einer Bodenschicht des Tilesets:

SplatmapRGBA
SplatMaps[0]Schicht 0Schicht 1Schicht 2Schicht 3
SplatMaps[1]Schicht 4Schicht 5Schicht 6Schicht 7
SplatMaps[2]Schicht 8Schicht 9Schicht 10Schicht 11
SplatMaps[3]Schicht 12Schicht 13Schicht 14Schicht 15
  • Schicht i liegt in Splatmap i / 4, Kanal i % 4. Deshalb hat ein Tileset genau 16 Bodenschichten: 4 Splatmaps × 4 Kanäle.
  • Die Gewichtungen eines Texels ergeben über alle 16 Kanäle zusammen immer 255. Alle Malwerkzeuge halten diese Invariante ein; Code, der Splat-Daten schreibt, muss sie ebenfalls einhalten.
  • Die Splat-Auflösung ist die Anzahl der Texel pro Zellseite (standardmäßig 4). Bei Zellen von 2 m ergibt das alle 0.5 m ein Texel.
  • Die Mitte des Texels (x, z) liegt in kartenlokalen XZ-Koordinaten bei ((x + 0.5), (z + 0.5)) × CellSize / SplatResolution. Dieselbe Formel gilt auch für Detail- und Pathing-Texel, jeweils mit ihrer eigenen Auflösung.
  • Hilfsmethoden: map.SplatIndex(x, z), map.GetSplatWeight(texelIndex, layer), map.GetDominantLayer(texelIndex), MapData.GetChannel / SetChannel und map.FillSplat(layer).

Karten speichern Bodenschichten über ihren Index (0–15), nicht über ihren Namen. Wenn du Bodenschichten im Tileset umsortierst oder entfernst, ändert sich, was bereits bemalte Karten anzeigen. Füge neue Schichten am Ende hinzu, wenn bereits Karten das Tileset verwenden.

using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

public static class SplatExample
{
    /// <summary>Paints one map cell 100 % with a ground layer (like the Tile Paint tool, without undo).</summary>
    public static void PaintCell(RTSTerrain terrain, int2 cell, int layer)
    {
        MapData map = terrain.Map;
        if (map == null || !map.IsValidCell(cell.x, cell.y)) return;
        layer = math.clamp(layer, 0, MapData.MaxGroundLayers - 1);

        int res = map.SplatResolution;
        for (int tz = cell.y * res; tz < (cell.y + 1) * res; tz++)
        {
            for (int tx = cell.x * res; tx < (cell.x + 1) * res; tx++)
            {
                int texel = map.SplatIndex(tx, tz);
                for (int m = 0; m < MapData.SplatMapCount; m++)
                {
                    // All channels 0, except the one of our layer: the sum stays exactly 255.
                    var c = new Color32(0, 0, 0, 0);
                    if (layer / 4 == m) MapData.SetChannel(ref c, layer % 4, 255);
                    map.SplatMaps[m][texel] = c;
                }
            }
        }

        // Tell the terrain which data changed and where (in cells). Textures and pathing update in LateUpdate.
        terrain.NotifyChanged(MapLayers.Splat, new RectInt(cell.x, cell.y, 1, 1));
    }
}

Wasser

  • Ein Vertex liegt unter Wasser, wenn er das Flag VertexFlags.Water trägt. Die Höhe seiner Wasseroberfläche (kartenlokales Y, Meter) wird in WaterHeights gespeichert; der Wert ist nur dort aussagekräftig, wo das Flag gesetzt ist.
  • Ein Punkt ist nass, wenn die bilinear interpolierte Abdeckung aus den Wasser-Flags der vier Ecken seiner Zelle mindestens 0.5 beträgt. Ecken ohne Wasser in einer nassen Zelle verwenden die höchste Wasserhöhe der Zelle, damit Uferlinien waagerecht bleiben.
  • Wassertiefe = Wasseroberfläche − Bodenhöhe. Die Tiefe bestimmt das Pathing: Wasser, das tiefer ist als der Wert Shallow Depth Limit des Tilesets (standardmäßig 0.9 m), ist Tiefwasser (nicht begehbar); flacheres Wasser ist Flachwasser (begehbar, standardmäßig nicht bebaubar).
  • Die Wasserwerkzeuge sind Paint Water, Erase Water und Flood Fill. Wasseränderungen melden nur MapLayers.Water, deshalb werden beim Malen von Wasser keine Terrain-Chunks neu aufgebaut.
  • Abfragen: terrain.IsWater(world, out depth) und terrain.TryGetWaterHeight(world, out waterWorldY).

Das Aussehen des Wassers (Farben, Wellen, Schaum) stammt aus dem Wasserprofil des Tilesets; siehe Wasserprofil.

Detaildichte (Gras und Kleinpflanzen)

  • Details sind keine einzelnen Objekte. Jeder Detail-Prototyp, der auf der Karte verwendet wird, hat genau einen DetailLayer in map.DetailLayers: ein Byte pro Detail-Texel (DetailWidth × DetailHeight, standardmäßig 2 Texel pro Zellseite).
  • Eine Dichte von 0 bedeutet leer, 255 bedeutet Max Density Instanzen pro Texel (ein Feld des Detail-Prototyps, standardmäßig 6). Jedes Texel erzeugt density / 255 × maxDensity Instanzen. Der Renderer verteilt sie deterministisch, sodass dieselbe Karte immer gleich aussieht.
  • Schichten werden der Vegetationsbibliothek über die prototypeId zugeordnet. map.GetOrAddDetailLayer(id) legt bei Bedarf eine Schicht an; map.FindDetailLayer(id) gibt null zurück, wenn der Prototyp nicht verwendet wird.
  • Male Details mit Paint Details und entferne sie mit Erase Details.

Baum- und Dekorinstanzen

Bäume (TreeInstanceData) und Dekorobjekte (DecorInstanceData) werden als einfache Listen gespeichert. Statt eines Prototypnamens speichert jede Instanz einen kleinen Palettenindex, der auf eine Liste von Bibliotheks-IDs in der Karte verweist:

map.TreePrototypeIds = [ "a3f0…", "77c1…" ]        <- ids of TreePrototype entries in the vegetation library
map.Trees[n].prototype = 1                          <- this tree uses "77c1…"
  • Mit map.GetOrAddTreePrototype(id) / map.GetOrAddDecorPrototype(id) erhältst du den Palettenindex einer Bibliotheks-ID (fehlt sie, wird sie hinzugefügt).
  • Da Karten Prototypen über ihre ID referenzieren, kannst du eine Bibliothek gefahrlos umbenennen, umsortieren oder erweitern. Fehlt ein Prototyp in der Bibliothek, überspringt ihn der Vegetation Renderer, und der Decor Manager zeigt ihn als magentafarbenen Platzhalterwürfel an; fügst du einen Prototyp mit derselben ID hinzu, ist er wieder da. Pathing: Bäume mit fehlendem Prototyp werden nicht gezeichnet, blockieren aber weiterhin einen Kreis mit dem Radius PathingMapBuilder.DefaultTreeFootprintRadius (0.8 m) × Skalierung. Dekorplatzhalter tragen erst dann eine Grundfläche in die Pathing-Karte ein, wenn der Prototyp wieder vorhanden ist.
  • Jede Instanz hat innerhalb der Karte eine eindeutige, stabile id. Neue IDs erhältst du mit map.AllocateInstanceId().
  • Positionen sind kartenlokal. Für Drehungen gilt überall dieselbe Konvention: Winkel in Grad, von oben gesehen im Uhrzeigersinn, 0 = +Z, 90 = +X (wie Unitys Y-Rotation).

Die Konvention für position.y

position.y ist ein zusätzlicher Versatz über dem Boden, normalerweise 0. Der Wert ist nicht die absolute Höhe. Die Renderer tasten den Boden bei jeder Aktualisierung neu ab. So bleiben Bäume und Dekor auf dem Boden, wenn du das Terrain darunter bearbeitest.

ObjektDarstellungshöhe (kartenlokales Y)
BaumBodenhöhe + position.y
DekorBodenhöhe + position.y + Y Offset des Prototyps
Dekor mit DecorFlags.KeepHeightposition.y als absolute kartenlokale Höhe (folgt Änderungen am Terrain nicht)

Kopiere den yOffset des Prototyps niemals in position.y; er wird beim Rendern addiert. DecorMath.ComputeLocalPosition und DecorMath.ToStoredHeight rechnen zwischen gespeicherten und dargestellten Positionen um.

FeldTreeInstanceDataDecorInstanceData
iduint, eindeutig in der Karteuint, eindeutig in der Karte
prototypeushort-Index in TreePrototypeIdsushort-Index in DecorPrototypeIds
variant–byte, 0 = Haupt-Prefab, 1… = Einträge aus variants
flags–DecorFlags: IgnorePathing, Locked (Werkzeuge können das Objekt weder auswählen noch löschen), KeepHeight
positionfloat3, kartenlokal, y = Versatzfloat3, kartenlokal, y = Versatz (oder absolut bei KeepHeight)
Drehungyaw in Gradrotation, eine kartenlokale quaternion
Skalierungscale, ein float (uniforme Skalierung)scale, float3
tintColor32, wird mit der Materialfarbe multipliziertColor32, wird mit der Materialfarbe multipliziert
using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

public static class InstanceExample
{
    /// <summary>Adds a tree of the first tree prototype of the terrain's vegetation library at a world position.</summary>
    public static uint AddTree(RTSTerrain terrain, Vector3 worldPosition, float yawDegrees)
    {
        MapData map = terrain.Map;
        RTSVegetationLibrary library = terrain.VegetationLibrary;
        if (map == null || library == null || library.trees.Count == 0) return 0;

        Vector3 local = terrain.WorldToLocal(worldPosition);
        var tree = new TreeInstanceData
        {
            id = map.AllocateInstanceId(),
            prototype = map.GetOrAddTreePrototype(library.trees[0].id), // palette index of the library id
            position = new float3(local.x, 0f, local.z),                 // y = offset above the ground, normally 0
            yaw = yawDegrees,                                             // clockwise from above, 0 = +Z
            scale = 1f,
            tint = new Color32(255, 255, 255, 255)
        };
        map.Trees.Add(tree);

        // Notify the cell that changed: vegetation, pathing (tree footprint) and fog blockers update.
        int2 cell = map.LocalToCell(new float2(local.x, local.z));
        terrain.NotifyChanged(MapLayers.Trees, new RectInt(cell.x, cell.y, 1, 1));
        return tree.id;
    }

    /// <summary>Adds a decor object (by library id) standing upright on the ground.</summary>
    public static uint AddDecor(RTSTerrain terrain, string decorPrototypeId, Vector3 worldPosition, float yawDegrees)
    {
        MapData map = terrain.Map;
        if (map == null) return 0;

        Vector3 local = terrain.WorldToLocal(worldPosition);
        var decor = new DecorInstanceData
        {
            id = map.AllocateInstanceId(),
            prototype = map.GetOrAddDecorPrototype(decorPrototypeId),
            variant = 0,                                  // main prefab
            flags = DecorFlags.None,
            position = new float3(local.x, 0f, local.z),  // the prototype's yOffset is added at render time
            rotation = DecorMath.Yaw(yawDegrees),
            scale = new float3(1f, 1f, 1f),
            tint = new Color32(255, 255, 255, 255)
        };
        map.Decor.Add(decor);

        int2 cell = map.LocalToCell(new float2(local.x, local.z));
        terrain.NotifyChanged(MapLayers.Decor, new RectInt(cell.x, cell.y, 1, 1));
        return decor.id;
    }
}

Wenn Code MapData direkt ändert, lassen sich diese Änderungen nicht rückgängig machen. Damit Änderungen aus Editor-Skripten rückgängig gemacht werden können, führe sie über die Bearbeitungssitzung mit RTSEditSession.RunOperation(name, layers, operation) aus. Diese Methode zeichnet die angegebenen Schichten als einen einzigen Undo-Schritt auf.

Pathing-Karte

Die Pathing-Karte beantwortet die Frage: „Kann eine Einheit hier laufen, kann hier ein Gebäude stehen?“ Sie ist ein Raster aus Pathing-Zellen (PathingResolution pro Seite einer Kartenzelle, standardmäßig 2, also 1 m große Pathing-Zellen bei einer Karte mit 2 m großen Zellen) mit je einem PathingFlags-Byte. Sie wird automatisch abgeleitet und inkrementell neu aufgebaut, sobald sich eine Eingabe ändert (Geometrie, Wasser, Bodenschichten, Bäume, Dekor, Overrides). Sie selbst wird nie gespeichert, sondern nur die manuellen Overrides.

PathingFlagsBedeutung
WalkableBodeneinheiten können hier laufen.
BuildableHier können Gebäude platziert werden.
FlyableLufteinheiten können hier fliegen. Ist überall gesetzt, es sei denn, ein Unflyable-Effekt entfernt es: ein gemalter Pathing-Override, die Bodenschichtregel des Tilesets oder der Footprint Effect eines Dekor-Prototyps.
ShallowWaterFlachwasser (begehbar, standardmäßig nicht bebaubar).
DeepWaterTiefwasser (nur für See-Einheiten).
CliffDie Zelle berührt eine Klippenwand.
BlockedDurch die Grundfläche eines Baums oder Dekorobjekts blockiert.
SteepZu steil für Bodeneinheiten.

Wie die Flags abgeleitet werden

  1. Terrain: Klippenwände (samt dem Klippenabstand Cliff Margin) setzen Cliff; Neigungen über Max Walkable Slope (standardmäßig 42°) setzen Steep; Wasser setzt je nach Tiefe ShallowWater oder DeepWater; Flyable wird überall gesetzt; Buildable = begehbar, Neigung ≤ Max Buildable Slope (standardmäßig 18°) und trocken (oder Flachwasser, wenn Shallow Water Buildable aktiviert ist). Danach wird der Pathing Effect der dominanten Bodenschicht angewendet (zum Beispiel „Unbuildable“ auf rauem Fels).
  2. Grundflächen: Bäume blockieren einen Kreis (Footprint Radius × Baumskalierung × Tree Footprint Scale); Dekor wendet seinen Footprint Effect innerhalb eines Kreises oder eines gedrehten Rechtecks an, es sei denn, die Instanz hat DecorFlags.IgnorePathing.
  3. Manuelle Overrides, die mit Paint Pathing / Clear Pathing gemalt wurden, werden zuletzt angewendet.

Die Regeleinstellungen befinden sich in der Komponente RTSTerrain (RTSPathingSettings, Eigenschaft terrain.PathingSettings).

PathingOverrideWirkung
UnwalkableEntfernt Walkable (und Buildable), setzt Blocked.
UnbuildableEntfernt Buildable.
UnflyableEntfernt Flyable.
ForceWalkableSetzt Walkable, auch wenn die abgeleiteten Regeln etwas anderes ergeben.
ForceBuildableSetzt Buildable, auch wenn die abgeleiteten Regeln etwas anderes ergeben.

Dieselben PathingOverride-Flags werden für die Pathing-Effekte von Bodenschichten, die Grundflächeneffekte von Dekor und gemalte Overrides verwendet. Frage das Ergebnis mit terrain.GetPathing(world), terrain.IsWalkable(world) und terrain.IsBuildable(world) ab, oder lies terrain.Pathing.Flags direkt aus.

Regionen, Startpositionen und Marker

Es handelt sich um schlanke Objekte für deine Spiellogik. Im Spiel werden sie nicht gerendert; die Editoren zeichnen sie als Gizmos.

TypFelderTypische Verwendung
MapRegion (Werkzeug Regions)name, rect (kartenlokales XZ-Rechteck in Metern: rect.x = X, rect.y = Z), color, properties (Schlüssel-Wert-Liste)Trigger, Spawn-Zonen, Creep-Camps. Contains(localXZ), GetProperty(key, fallback).
StartLocation (Werkzeug Start Locations)player (nullbasiert), position, yawSpawn-Punkte der Spieler. Der Einheiten-Spawner aus den Beispielen verwendet sie.
MapMarker (Werkzeug Markers)name, tag (Freitext wie „camera“ oder „goldmine“), position, rotation, propertiesKameras für Zwischensequenzen, Points of Interest, geskriptete Spawn-Punkte.

Die Werkzeuge speichern für Startpositionen und Marker position.y = 0 und behandeln den Wert wie bei Bäumen als Versatz über dem Boden.

using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

public static class RegionExample
{
    /// <summary>Returns the "gold" property of the first region that contains the world position, or 0.</summary>
    public static int GetGoldAt(RTSTerrain terrain, Vector3 worldPosition)
    {
        if (terrain.Map == null) return 0;
        Vector3 local = terrain.WorldToLocal(worldPosition);
        foreach (MapRegion region in terrain.Map.Regions)
        {
            // Regions are rectangles in map-local XZ.
            if (region.Contains(new float2(local.x, local.z)) &&
                int.TryParse(region.GetProperty("gold", "0"), out int gold))
                return gold;
        }
        return 0;
    }
}

Karteninfo und Umgebung

Feld in MapInfoStandardBeschreibung
name"New Map"Anzeigename (wird auch im unkomprimierten Dateikopf gespeichert).
author""Name des Autors.
description""Freitext, der in Kartenbrowsern angezeigt wird.
maxPlayers2Empfohlene Spielerzahl.
tilesetId""ID des Tilesets, für das die Karte erstellt wurde (RTSTileset.tilesetId). Wird automatisch ausgefüllt, wenn das Terrain ein Tileset hat. Der Inspector von RTSTerrain warnt, wenn das zugewiesene Tileset nicht passt.
createdUtcTicks, modifiedUtcTicksautomatisch gesetztErstellungszeit und Zeitpunkt der letzten Speicherung (UTC-Ticks). Die Änderungszeit setzt RTSMapSerializer.Write bei jeder Serialisierung der Karte: SaveToFile, Save im Editor, Serialize(map) und RTSMapAsset.CreateFromMap.

EnvironmentSettings speichert die Beleuchtung einer Karte. Eine RTSEnvironment-Komponente (GameObject ▸ RTS Terrain Studio ▸ Environment) wendet sie auf die Szene an; ohne diese Komponente werden die Werte zwar gespeichert, aber nicht verwendet.

Feld in EnvironmentSettingsStandardBeschreibung
sunColorwarmes Weiß (1, 0.956, 0.84)Farbe des Sonnenlichts.
sunIntensity1.2Intensität des Sonnenlichts.
sunRotation(50, −30)Drehung der Sonne in Grad (x = Neigung, y = Drehwinkel).
ambientColor(0.45, 0.5, 0.58)Farbe des Umgebungslichts.
fogausAktiviert den Distanznebel.
fogColor(0.6, 0.68, 0.75)Nebelfarbe.
fogDensity0.004Nebeldichte.

Änderungsbenachrichtigungen

Wenn du MapData per Code änderst, teile dem Terrain mit, was sich wo geändert hat – angegeben in Zellen:

  • terrain.NotifyChanged(MapLayers layers, RectInt cellRect) löst MapChanged sofort aus und stellt den Neuaufbau in die Warteschlange.
  • terrain.NotifyChangedAll(layers) markiert die ganze Karte (nach Generatoren oder Importen).
  • Ausstehende Änderungen werden in LateUpdate verarbeitet (außerhalb des Play Mode auch bei jedem Editor-Tick) oder sofort durch einen Aufruf von terrain.ProcessPendingChanges(). Danach wird ChangesProcessed ausgelöst: Meshes, Texturen und Pathing sind dann auf dem neuesten Stand.
  • Ein Vertex berührt bis zu vier Zellen. Ein Vertex-Rechteck rechnest du mit BrushUtility.VertexRectToCellRect(map, vertexRect) in Zellen um, ein Texel-Rechteck mit BrushUtility.TexelRectToCellRect(map, texelRect, resolutionPerCell).
using RTSTerrainStudio;
using UnityEngine;

public static class CliffExample
{
    /// <summary>Raises a square plateau by one cliff level around a world position.</summary>
    public static void RaisePlateau(RTSTerrain terrain, Vector3 worldCenter, int radiusInVertices)
    {
        MapData map = terrain.Map;
        if (map == null) return;

        // Nearest vertex to the centre (vertices sit on multiples of the cell size).
        Vector3 local = terrain.WorldToLocal(worldCenter);
        int cx = Mathf.RoundToInt(local.x / map.CellSize);
        int cz = Mathf.RoundToInt(local.z / map.CellSize);

        int x0 = Mathf.Max(0, cx - radiusInVertices), x1 = Mathf.Min(map.Width, cx + radiusInVertices);
        int z0 = Mathf.Max(0, cz - radiusInVertices), z1 = Mathf.Min(map.Height, cz + radiusInVertices);
        for (int z = z0; z <= z1; z++)
        {
            for (int x = x0; x <= x1; x++)
            {
                int i = map.VertexIndex(x, z);
                // Levels are bytes from 0 to 15: always clamp.
                map.CliffLevels[i] = (byte)Mathf.Min(map.CliffLevels[i] + 1, MapData.MaxCliffLevel);
            }
        }

        // Convert the changed vertices to the cells around them and notify the terrain.
        var vertexRect = new RectInt(x0, z0, x1 - x0 + 1, z1 - z0 + 1);
        RectInt cells = BrushUtility.VertexRectToCellRect(map, vertexRect);
        terrain.NotifyChanged(MapLayers.CliffLevels, cells);
    }
}

Koordinaten und mehrere Terrains

Alle Kartendaten werden in kartenlokalen Koordinaten gespeichert. Der Transform des RTSTerrain-GameObjects legt fest, wo dieses Koordinatensystem in der Welt liegt.

Kartenlokale Koordinaten

  • Der Ursprung ist Vertex (0, 0), die Ecke von Zelle (0, 0). X und Z laufen von 0 bis map.WorldSize in Metern, Y zeigt nach oben.
  • Vertex (x, z) liegt lokal bei (x × CellSize, height, z × CellSize); der Mittelpunkt von Zelle (x, z) liegt bei ((x + 0.5) × CellSize, …, (z + 0.5) × CellSize).
  • Lokales Y = 0 entspricht Stufe 0 mit einer Feinhöhe von 0. Bei einer neuen Karte mit Grundstufe 2 und 2 m hohen Stufen liegt der Boden bei lokalem Y = 4 m.
  • Die Chunk-GameObjects liegen unter einem verborgenen Kindobjekt RTS Chunks des Terrains (RTSTerrain.ChunkRoot). Dieses Wurzelobjekt hat keinen lokalen Versatz und keine lokale Drehung und gleicht die Skalierung des Terrains aus; jeder Chunk hat einen lokalen Identitäts-Transform. Deshalb entspricht der Objektraum der Chunk-Meshes den kartenlokalen Koordinaten. Das ist wichtig, wenn du Shader für das Terrain schreibst.

Umrechnung zwischen Welt- und Kartenkoordinaten

Methode (RTSTerrain)Beschreibung
WorldToLocal(Vector3) / LocalToWorld(Vector3)Positionen zwischen Weltkoordinaten und kartenlokalen Koordinaten.
WorldToLocalDirection / LocalToWorldDirectionRichtungen (Strahlen, Normalen).
WorldToCell(Vector3)Zelle, die eine Weltposition enthält (auf die Karte begrenzt).
CellCenterToWorld(int2)Weltposition einer Zellmitte auf dem Boden.
VertexToWorld(int2)Weltposition eines Vertex.
Contains(Vector3)True, wenn die Weltposition innerhalb der XZ-Fläche der Karte liegt.
WorldBoundsUngefähre Begrenzung der gesamten Karte in Weltkoordinaten, einschließlich der höchstmöglichen Klippenstufe.
GetHeight, SnapToGround, GetNormal, GetCliffLevel, IsWater, TryGetWaterHeight, GetPathing, RaycastAbfragen, die Werte in Weltkoordinaten entgegennehmen und zurückgeben; die Umrechnung erfolgt intern.
GetGeometry()Eine MapGeometryView für schnelle Abfragen in kartenlokalen Koordinaten (auch in Burst-Jobs).
using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

/// <summary>Add to any GameObject and use the context menu to see the map data under it.</summary>
public class TerrainProbe : MonoBehaviour
{
    [ContextMenu("Probe Terrain")]
    void Probe()
    {
        // Several terrains can exist: find the one below this object.
        RTSTerrain terrain = RTSTerrain.FindAt(transform.position);
        if (terrain == null || terrain.Map == null) { Debug.Log("Not above an RTS Terrain."); return; }
        MapData map = terrain.Map;

        // World -> map-local (origin at vertex 0,0; position and Y rotation only).
        Vector3 local = terrain.WorldToLocal(transform.position);
        int2 cell = terrain.WorldToCell(transform.position);

        // Nearest vertex.
        int vx = Mathf.Clamp(Mathf.RoundToInt(local.x / map.CellSize), 0, map.Width);
        int vz = Mathf.Clamp(Mathf.RoundToInt(local.z / map.CellSize), 0, map.Height);
        int v = map.VertexIndex(vx, vz);

        // Splat texel below the object (SplatResolution texels per cell side).
        int tx = Mathf.Clamp((int)(local.x / map.CellSize * map.SplatResolution), 0, map.SplatWidth - 1);
        int tz = Mathf.Clamp((int)(local.z / map.CellSize * map.SplatResolution), 0, map.SplatHeight - 1);
        int dominantLayer = map.GetDominantLayer(map.SplatIndex(tx, tz));

        Debug.Log($"Cell {cell}, vertex ({vx},{vz}): level {map.CliffLevels[v]}, fine height {map.Heights[v]:0.00} m, " +
                  $"vertex height {map.GetVertexHeight(vx, vz):0.00} m. Ground at world Y {terrain.GetHeight(transform.position):0.00}, " +
                  $"cliff level {terrain.GetCliffLevel(transform.position)}, dominant ground layer {dominantLayer}, " +
                  $"pathing {terrain.GetPathing(transform.position)}.");
    }
}

Regeln für Position, Drehung und Skalierung

Teil des TransformsUnterstütztHinweise
PositionjaVerschiebt die gesamte Karte. Damit platzierst du mehrere Terrains nebeneinander oder auf unterschiedlichen Höhen.
Y-RotationjaDreht die Karte um ihren Ursprung (Vertex 0, 0). Abfragen, Werkzeuge, Pinselcursor und die Drehungen von Instanzen folgen ihr.
X-/Z-RotationneinVorgesehen ist nur die Y-Rotation. Der Inspector von RTSTerrain warnt und bietet Fix an: Y bleibt erhalten, X und Z werden zurückgesetzt.
SkalierungignoriertDie Chunks gleichen jede Skalierung des Terrains oder seiner Elternobjekte aus. Der Inspector warnt und bietet Reset Scale an. Für eine größere Karte verwendest du mehr Zellen oder eine größere Zellgröße.

Mehrere Terrains

Eine Szene kann beliebig viele RTSTerrain-Komponenten enthalten. Jede davon hat eine eigene Karte, ein eigenes Tileset, eigene Bibliotheken und Chunks sowie eine eigene Pathing-Karte.

  • RTSTerrain.All listet alle aktivierten Terrains auf; RTSTerrain.Main gibt das erste zurück (praktisch für Spiele mit nur einer Karte; temporäre Vorschau-Terrains werden übersprungen).
  • RTSTerrain.FindAt(worldPosition) gibt das Terrain zurück, dessen XZ-Fläche eine Position enthält.
  • RTSTerrain.RaycastAll(ray, out hit) führt einen Raycast gegen alle Terrains aus und gibt den nächstgelegenen Treffer zurück (hit.terrain verrät dir, welches Terrain getroffen wurde).
  • RTSTerrain.TerrainEnabled / TerrainDisabled sind statische Events für Systeme, die Terrains verfolgen.
  • Terrains sind unabhängig voneinander: Karten werden an ihren Rändern nicht zusammengefügt, und die Wegfindung (RTSPathfinder.FindPath(terrain, …)) arbeitet immer nur auf einem Terrain. Damit zwei Karten nahtlos aneinanderstoßen, gib ihren Randvertices dieselben Höhen.
using RTSTerrainStudio;
using UnityEngine;

public static class TerrainLayout
{
    /// <summary>Places "east" directly next to "west" (along west's local +X), with the same rotation.</summary>
    public static void PlaceNextTo(RTSTerrain west, RTSTerrain east)
    {
        if (west.Map == null) return;
        float widthMetres = west.Map.WorldSize.x;   // map-local X extent in metres
        east.transform.SetPositionAndRotation(
            west.transform.position + west.transform.rotation * new Vector3(widthMetres, 0f, 0f),
            west.transform.rotation);
    }
}

Karten-Assets und Kartendateien

Eine Karte wird als .rtsmap-Datei gespeichert. Der Unity Editor (als Asset in deinem Projekt) und der In-Game-Editor (als Datei auf dem Datenträger) verwenden dasselbe Binärformat. Deshalb kannst du von Spielern erstellte Karten in Unity öffnen und umgekehrt.

.rtsmap-Dateien im Projekt

Findet Unity eine .rtsmap-Datei im Ordner Assets, wandelt der Importer RTSMapImporter sie in ein RTSMapAsset um. Das Asset speichert die komprimierten Dateibytes, einen kleinen Header (Name, Autor, Größe, Spieler, Tileset-ID) und eine Minikarten-Vorschau, die auch als Vorschaubild im Project-Fenster dient.

Ein Karten-Asset erstellen

  • Tools ▸ RTS Terrain Studio ▸ New Map Wizard...: geführte Einrichtung (Grundlagen, Größe, Aussehen, Terrain); schreibt die Datei und erstellt dafür ein Terrain oder weist sie dem ausgewählten Terrain zu.
  • Assets ▸ Create ▸ RTS Terrain Studio ▸ Map (.rtsmap): erstellt im ausgewählten Ordner eine flache Karte der Größe 128 × 128 mit Standardeinstellungen.
  • Save As… im Fenster von RTS Terrain Studio: schreibt die aktuelle Karte eines Terrains in eine neue Datei.
  • Kopiere eine mit dem In-Game-Editor erstellte .rtsmap-Datei in den Ordner Assets; sie wird wie jede andere Datei importiert.

Der Inspector des Karten-Assets

Wenn du ein Karten-Asset auswählst, siehst du Vorschau, Name, Autor, Beschreibung, Größe, Spieler, Tileset und Formatversion sowie Statistiken (Zellen, Weltgröße, Zellgröße, Stufenhöhe, Startpositionen, Bäume, Dekor, Regionen, Marker, Detailschichten, Dateigröße, Speicher, letzte Änderung).

BedienelementBeschreibung
Open In SceneWeist die Karte dem ausgewählten Terrain zu (oder dem einzigen Terrain; bei mehreren wird nachgefragt). Erstellt ein Terrain, wenn die Szene keines hat. Im Play Mode deaktiviert.
Menü ▾Open In New Terrain oder Open On für ein bestimmtes Terrain der Szene.
Show In Explorer (Reveal In Finder unter macOS)Zeigt die Datei im Dateibrowser des Betriebssystems an.
Tileset Id (Copy, Select)Die in der Karte gespeicherte Tileset-ID. Select wählt das passende Tileset-Asset aus.
Preview SizeLängste Seite der gebackenen Vorschau: 128, 256 (Standard) oder 512 Pixel.
Preview TilesetTileset für die Farben der Vorschau. Leer = das Tileset, dessen ID in der Karte gespeichert ist (wird automatisch gefunden).
Re-bake Preview, Apply / RevertErstellt die Vorschau neu / übernimmt geänderte Importeinstellungen.

Die Vorschau wird automatisch neu gebacken, wenn das Tileset mit der ID der Karte hinzugefügt oder entfernt wird oder seine Minikartenfarben ändert. Das Neuerstellen der Textur-Arrays importiert Karten nicht neu. Eine beschädigte Datei wird als leeres Asset mit Fehler-Icon importiert, und in der Console erscheint ein Importfehler.

Bearbeiten, Save und Save As

Weise ein Karten-Asset dem Feld Map Asset eines RTSTerrain zu. Sobald das Terrain aktiviert ist, lädt es die Karte in den Speicher, und alle Änderungen erfolgen an dieser Kopie im Speicher. Die Szenendatei speichert nur den Verweis auf das Asset, nie die Kartendaten selbst.

  • Save (in der Kopfzeile des Fensters von RTS Terrain Studio oder Save Map im Optionsmenü des Overlays RTS Terrain Studio in der Scene View) schreibt die Karte zurück in ihre .rtsmap-Datei und importiert sie neu. Save ist hervorgehoben, solange nicht gespeicherte Änderungen vorliegen.
  • Save As… schreibt eine neue Datei und weist sie dem Terrain zu, ohne neu zu laden. Dein Undo-Verlauf bleibt dabei erhalten.
  • Open… lädt ein anderes Karten-Asset; Reload lädt die Karte neu aus ihrem Asset und verwirft nicht gespeicherte Änderungen.
  • Ein Terrain ohne Karten-Asset erstellt eine Karte aus seinen Default Map Settings. Diese Karte existiert nur im Speicher, bis du Save As verwendest.
  • Beim Speichern der Szene (Ctrl+S) werden auch die bearbeiteten Karten der Terrains dieser Szene gespeichert. Für neue Karten ohne Datei wird Save As angeboten. Beide Verhaltensweisen kannst du im Optionsmenü des Overlays in der Scene View ausschalten (Save Maps When Saving the Scene, Offer Save As for New Maps).
  • Wenn du mit nicht gespeicherten Kartenänderungen den Play Mode startest, eine Szene schließt oder Unity beendest, wirst du gefragt, ob du sie speichern möchtest. Auch Änderungen, die du im Play Mode vornimmst (mit den Werkzeugen in der Scene View oder im In-Game-Editor), werden dir beim Verlassen des Play Mode zum Speichern angeboten.

Kartenänderungen sind nicht Teil der Szene. Unitys eigener Dialog „Save scene?“ erfasst sie nicht, die Abfragen von RTS Terrain Studio dagegen schon. Ist eine .rtsmap-Datei schreibgeschützt oder durch die Versionsverwaltung gesperrt, zeigt Save einen Dialog an, statt zu schreiben.

Kartendateien zur Laufzeit

Im fertigen Spiel sind Karten gewöhnliche Dateien auf dem Datenträger:

  • RTSMapSerializer.DefaultMapsFolder ist der Ordner Maps in RTSMapSerializer.StorageRoot, also in Application.persistentDataPath. Unter Windows ist der Ordner daher typischerweise %USERPROFILE%\AppData\LocalLow\<Company>\<Product>\Maps. In Web-Builds ist StorageRoot ein Ordner im Browserspeicher (siehe Karten im Browser).
  • Der In-Game-Editor (RTSRuntimeEditor) speichert und öffnet Karten in seinem Maps Folder (leer = der oben genannte Standardordner; relative Pfade beziehen sich auf RTSMapSerializer.StorageRoot). Ist Write Preview Images aktiviert, schreibt er neben jede gespeicherte Karte ein kleines Minikartenbild Name.preview.png für seinen Kartenbrowser.
  • Um eine Karte weiterzugeben, kopiere die .rtsmap-Datei in den Kartenordner eines anderen Spielers oder in ein Unity-Projekt. In einem Webbrowser verwende stattdessen die Befehle Download Map und Import Map des In-Game-Editors.
  • Karten, die zum Lieferumfang deines Spiels gehören, können ganz normale Karten-Assets sein, auf die ein Terrain oder ein Skript verweist. Sie werden wie jedes andere Asset in den Build aufgenommen.
APIBeschreibung
terrain.LoadFromFile(path) / terrain.SaveToFile(path)Lädt eine Datei in ein Terrain / speichert die Karte des Terrains. Beide setzen terrain.CurrentFilePath.
terrain.MapAsset = assetWeist ein Karten-Asset zu und lädt es sofort, falls das Terrain aktiviert ist.
terrain.SetMap(mapData)Zeigt ein von dir erstelltes oder geladenes MapData-Objekt an; es geht in den Besitz des Terrains über.
terrain.HasUnsavedChangesTrue, wenn es seit dem letzten Laden oder Speichern Änderungen gab.
RTSMapAsset.LoadMap()Deserialisiert das Asset in ein neues MapData-Objekt (es geht in deinen Besitz über).
RTSMapAsset.CreateFromMap(map, preview)Erstellt ein Karten-Asset im Speicher, zum Beispiel um eine Karte zwischen Szenen weiterzugeben.
RTSMapSerializer.Serialize(map) / Deserialize(bytes)Bytes, zum Beispiel um eine Karte über das Netzwerk zu senden.
RTSMapSerializer.TryReadHeaderFromFile(path, out header)Liest nur den unkomprimierten Header (schnell, für Kartenlisten).
RTSMapSerializer.TryReadHeader(bytes, out header)Dasselbe für Kartenbytes im Speicher (zum Beispiel über das Netzwerk empfangene).
terrain.CreateNewMap(MapCreationSettings)Erstellt eine neue flache Karte und zeigt sie an – das Gegenstück im Code zum Dialog New Map. Übernimmt Info.tilesetId aus dem Tileset des Terrains, leert CurrentFilePath und markiert die Karte als nicht gespeichert.
terrain.LoadFromAsset()Lädt die Karte neu aus MapAsset und verwirft nicht gespeicherte Änderungen. Kann das Asset nicht gelesen werden, protokolliert die Methode einen Fehler und greift auf eine Karte zurück, die aus DefaultMapSettings erstellt wird.
terrain.SaveToMapAsset()Nur im Editor: schreibt die Karte zurück in ihr .rtsmap-Asset. Gibt false zurück, wenn das Terrain kein Karten-Asset hat.
terrain.PendingLayersSchichten mit gemeldeten, aber noch nicht verarbeiteten Änderungen.
RTSMapAsset.Header / Preview / DataDer Karten-Header (gelesen, ohne die Karte zu dekomprimieren), die Minikarten-Vorschau (kann null sein) und die rohen Dateibytes.
RTSMapAsset.SetData(bytes, preview)Ersetzt den Inhalt eines Karten-Assets (der Header wird aus den Bytes gelesen).
using System.IO;
using RTSTerrainStudio;
using UnityEngine;

public class SimpleMapList : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;

    /// <summary>Logs all maps in the default folder without loading them, then opens the first one.</summary>
    [ContextMenu("List And Open Maps")]
    void ListAndOpen()
    {
        string folder = RTSMapSerializer.DefaultMapsFolder;
        if (!Directory.Exists(folder)) { Debug.Log("No maps folder yet: " + folder); return; }

        string[] files = Directory.GetFiles(folder, "*" + RTSMapSerializer.FileExtension);
        foreach (string path in files)
        {
            // The header is stored uncompressed, so this is fast even for big maps.
            if (RTSMapSerializer.TryReadHeaderFromFile(path, out RTSMapHeader h))
                Debug.Log($"{h.name} by {h.author}: {h.width} x {h.height} cells, {h.maxPlayers} players, saved {h.Modified}");
        }

        if (files.Length > 0 && terrain != null)
        {
            try
            {
                terrain.LoadFromFile(files[0]);   // the terrain owns and disposes the loaded map
            }
            catch (InvalidDataException e)
            {
                Debug.LogError("The map file is damaged: " + e.Message);
            }
        }
    }
}

Tilesets

Ein Tileset (RTSTileset) legt das Aussehen einer Karte fest: bis zu 16 Bodenschichten, bis zu 4 Klippenschichten und den Stil des Wassers – vergleichbar mit den Sommer-, Winter- oder Wüsten-Tilesets klassischer RTS-Karteneditoren. Du kannst beliebig viele Tilesets anlegen und das Tileset eines Terrains jederzeit wechseln: Texturierte Karten speichern nur Schichtindizes, deshalb kann dieselbe Karte als Sommer- und als Winterkarte erscheinen.

  1. Erstelle ein Tileset über Assets ▸ Create ▸ RTS Terrain Studio ▸ Tileset.
  2. Ziehe deine Bodentexturen auf den Abschnitt Ground Layers im Inspector. Pro Albedo-Textur wird eine Schicht angelegt. Passende Normal-Maps und Höhentexturen werden am Suffix im Dateinamen erkannt (zum Beispiel _n, _normal, _nrm, _normalgl, _h, _height, _disp). Roughness-, AO- und Metallic-Maps werden ignoriert, sodass du einen ganzen heruntergeladenen Texturordner ablegen kannst.
  3. Füge auf dieselbe Weise Klippenschichten unter Cliff Layers hinzu.
  4. Weise das Tileset dem Feld Tileset deines RTSTerrain zu.
  5. Klicke im Abschnitt Texture Arrays einmal auf Build Texture Arrays (siehe Textur-Arrays).

Tileset-Felder

EinstellungStandardBeschreibung
Tileset Id (tilesetId)generiertStabile Kennung, die in Kartendateien gespeichert wird. So wissen Karten, für welches Tileset sie erstellt wurden. Wird schreibgeschützt mit der Schaltfläche Copy angezeigt.
Display Name (displayName)"New Tileset"Name, der in den Editoren angezeigt wird.
Ground Layers (groundLayers)leerBis zu 16 GroundLayer-Einträge. Überzählige Einträge werden entfernt.
Cliff Layers (cliffLayers)leerBis zu 4 CliffLayer-Einträge.
Water (water)blaugrünDas WaterProfile, siehe unten.
Resolution (arrayResolution)1024Größe der Slices in den Textur-Arrays: 256, 512, 1024 oder 2048. Quelltexturen werden automatisch skaliert.
Compress (compressArrays)anKomprimiert die erstellten Arrays (BC7 / ASTC). Schalte die Option für möglichst schnelles Iterieren aus.
Material Override (materialOverride)keineOptionales Material anstelle des Standard-Terrain-Materials. Es muss den Shader RTS Terrain Studio/Terrain oder einen kompatiblen Shader verwenden.
Macro Variation (macroVariation)0.25Variation im großen Maßstab, die Texturwiederholungen auf großen Flächen aufbricht.
Triplanar Sharpness (triplanarSharpness)6Schärfe der Triplanar-Überblendung an Klippenwänden (1–16).

Felder der Bodenschichten

EinstellungStandardBeschreibung
Name"Layer"Wird in den Paletten angezeigt.
AlbedokeineGrundfarbe (RGB). Beliebige Größe und beliebiges Format.
NormalkeineNormal-Map im Tangentenraum (importiere sie als Normal Map).
Height MapkeineOptionale Höhe (R-Kanal) für die höhenbasierte Überblendung. Hat Vorrang vor dem Albedo-Alpha.
Height From Albedo AlphaausVerwendet das Albedo-Alpha als Höhe, wenn keine Höhentextur zugewiesen ist.
TintweißFarbe, die mit dem Albedo multipliziert wird.
Tile Size6Weltgröße einer Texturwiederholung in Metern (≥ 0.1).
Normal Strength10–2.
Smoothness0.10–1.
Metallic00–1.
Height Blend0.5Stärke der höhenbasierten Überblendung mit benachbarten Schichten (0 = lineare Überblendung). Benötigt eine Höhenquelle.
Anti Tiling0.3Verhindert sichtbare Wiederholungen gekachelter Texturen durch ein zweites, variiertes Sample (0–1).
Pathing EffectNonePathing-Regeln, die dort gelten, wo diese Schicht dominiert, zum Beispiel Unbuildable für zerklüfteten Fels oder Unwalkable für Lava.
Minimap ColorschwarzFarbe auf der Minikarte und in Kartenvorschauen. Schwarz = aus dem Albedo berechnet.

Felder der Klippenschichten

EinstellungStandardBeschreibung
Name"Cliff"Wird in der Palette Cliff Type angezeigt.
AlbedokeineWandfarbe. Ohne sie zeigen Klippen dieses Typs einen dunkelgrauen Platzhalter.
NormalkeineNormal-Map im Tangentenraum.
TintweißFarbe, die mit dem Albedo multipliziert wird.
Tile Size4Weltgröße einer Texturwiederholung in Metern.
Normal Strength10–2.
Smoothness0.150–1.
Top Ground Layer−1 (keine)Bodenschicht, mit der die Oberseite von Klippen dieses Typs automatisch texturiert wird. Wird von Auto Paint verwendet.
Edge Ground Layer−1 (keine)Bodenschicht, mit der die Klippenwerkzeuge die Kanten neuer Klippen dieses Typs texturieren, zum Beispiel für einen steinigen Rand (siehe Automatische Klippenkanten). Erscheint im Klippen-Panel als Edge Layer. −1 = keine automatische Kante.
Minimap ColorschwarzMinikartenfarbe. Schwarz = aus dem Albedo berechnet.

Klippenwände werden per Triplanar-Mapping in kartenlokalen Koordinaten texturiert. Klippentexturen brauchen daher kein spezielles UV-Layout.

Wasserprofil

Das Wasserprofil ist Teil des Tilesets. Es wird vom Wasser-Renderer, von der Pathing-Karte und von der Minikarte verwendet.

EinstellungStandardBeschreibung
Shallow Color(0.22, 0.55, 0.6, 0.55)Farbe von Flachwasser; Alpha = Deckkraft.
Deep Color(0.03, 0.16, 0.27, 0.95)Farbe von Tiefwasser; Alpha = Deckkraft.
Color Depth3Tiefe in Metern, ab der die Farbe für Tiefwasser vollständig erreicht ist.
Shallow Depth Limit0.9Wasser, das tiefer als dieser Wert ist, gilt als Tiefwasser (nicht begehbar). Flacheres Wasser ist begehbar, wie in klassischen RTS-Spielen.
Normal MapkeineNormal-Map für die Wellen.
Normal Strength0.60–2.
Normal Tile Size8Weltgröße einer Wiederholung der Normal-Map in Metern.
Flow Speed(0.03, 0.02)Scrollgeschwindigkeit der Normal-Map.
Wave Height0.050–1.
Smoothness0.920–1.
Refraction0.030–0.2.
Foam Colorweiß, Alpha 0.8Farbe des Uferschaums.
Foam Distance0.6Reichweite des Uferschaums in Metern.
Foam TexturekeineOptionales Schaummuster.
Minimap Color(0.1, 0.3, 0.55)Wasserfarbe auf der Minikarte.

Tilesets per Code bearbeiten

Tilesets sind ScriptableObjects. Rufe nach dem Ändern von Feldern per Code tileset.NotifyChanged() auf, damit Terrains ihre Materialien aktualisieren. tileset.GetGroundLayer(index) und GetCliffLayer(index) geben für unbelegte Slots null zurück.

Textur-Arrays

Der Terrain-Shader zeichnet alle 20 Schichten in einem einzigen Durchgang. Deshalb werden alle Schichttexturen in zwei Textur-Arrays auf der GPU gepackt. Du erstellst diese Arrays nie selbst: Hinein kommen normale Unity-Texturen beliebiger Größe und beliebigen Formats, heraus kommen die Arrays.

Aufbau

ArraySlicesKanäleFarbraum
_RTS_AlbedoArray0–15 Bodenschichten, 16–19 KlippenschichtenRGB = Albedo, A = Höhe für die höhenbasierte ÜberblendungsRGB
_RTS_NormalArray0–15 Bodenschichten, 16–19 KlippenschichtenRGB = Normale im Tangentenraum, auf 0–1 umgerechnet (n × 0.5 + 0.5)linear
  • Das erste Klippen-Slice ist RTSTileset.CliffSliceOffset (16), die Gesamtzahl ist RTSTileset.TotalSlices (20).
  • Die Höhe (Alpha) stammt aus der Height Map der Schicht (R-Kanal), sonst aus dem Albedo-Alpha, wenn Height From Albedo Alpha aktiviert ist, andernfalls ist sie konstant 0.5.
  • Normalen werden beim Erstellen der Arrays entpackt. Deine Normal-Maps können daher wie gewohnt Unitys Normal-Map-Import (DXT5nm / BC5) verwenden. Schichten ohne Normal-Map erhalten eine flache Normale.
  • Schichten ohne Albedo erhalten ein neutralgraues Slice mit dezentem Muster; der Shader wendet die Tönung trotzdem an.
  • Jedes Slice hat eine vollständige Mip-Kette und nutzt trilineare Filterung. Quellen beliebiger Größe werden auf die gewählte Resolution skaliert.

Erstellte Arrays und der Laufzeit-Fallback

Die Arrays können auf zwei Wegen entstehen:

Erstellte Arrays (empfohlen)Laufzeit-Fallback
Erstellt durchSchaltfläche Build Texture Arrays im Inspector des TilesetsDer Renderer, automatisch, wenn keine aktuellen erstellten Arrays vorhanden sind
GespeichertAls Sub-Assets im Tileset-Asset (in Player-Builds enthalten)Nur im GPU-Speicher, wird beim Laden neu erstellt
FormatBC7 (Desktop, Konsolen), ASTC 6×6 (Android, iOS, tvOS, visionOS), für Web die Texturkomprimierung des Builds (DXT5, ASTC 6×6 oder ETC2 RGBA8), DXT5 (andere Plattformen); RGBA32, wenn Compress ausgeschaltet istUnkomprimiert (4 Byte pro Pixel)
Speicher bei 1024 pxetwa 56 MB mit BC7 für beide Arraysetwa 224 MB für beide Arrays

Der Abschnitt Texture Arrays zeigt eine Statusanzeige:

  • Not built: Das Terrain verwendet Laufzeit-Arrays. Klicke einmal auf Build Texture Arrays.
  • Up to date: Format, Größe, Anzahl der Mips und Speicherbedarf werden angezeigt.
  • Outdated: Texturen, Auflösung oder Komprimierung haben sich geändert, oder die Arrays wurden für eine andere Plattform oder Texturkomprimierung erstellt. Erstelle sie neu, oder überlasse das dem nächsten Player-Build.
  • Building: Eine Erstellung läuft oder ist geplant.

Der Abschnitt zeigt außerdem das Zielformat für die aktive Build-Plattform sowie die geschätzte Größe und Erstellungsdauer. Vor Erstellungen über 256 MB fragt er nach einer Bestätigung. Clear entfernt die erstellten Arrays.

Player-Builds

Player-Builds verwenden die erstellten Arrays unverändert, weil nur der Editor sie mit den Quelltexturen vergleichen kann. Veraltete Arrays werden deshalb direkt vor jedem Player-Build automatisch neu erstellt. Tilesets, für die nie Arrays erstellt wurden, verwenden weiterhin Laufzeit-Arrays. Die Details, auch zum Batch-Modus und zu -rtsAllowOutdatedTextureArrays, findest du unter Textur-Arrays in Player-Builds. Player-Builds geben außerdem die CPU-Kopie der erstellten Arrays bei der ersten Verwendung frei. Das spart etwa so viel Speicher, wie die Arrays auf der GPU belegen.

Per Code: tileset.HasValidBuiltArrays gibt an, ob die komprimierten Arrays verwendet werden. Im Editor müssen sie existieren und zu ComputeContentHash() passen. Ein Player-Build vertraut den erstellten Arrays, bis dein Code zur Laufzeit die Texturen der Schichten, die Auflösung oder die Komprimierung ändert. Dann greift er auf Laufzeit-Arrays zurück. Werte von ComputeContentHash() sind nur innerhalb des Editors oder innerhalb einer einzelnen Sitzung eines Player-Builds vergleichbar, weil der Editor den Importzustand der Texturen in den Hash einbezieht. Ist HasValidBuiltArrays false, erstellt RTSTerrain unkomprimierte Laufzeit-Arrays (RTSTextureArrayBuilder.Acquire). tileset.BuiltAlbedoArray / BuiltNormalArray stellen die erstellten Arrays bereit. Verwende in deinem eigenen Laufzeitcode RTSShaderReferences.FindShader(name) (und FindMaterial(name)) statt Shader.Find, damit die Suche auch in Player-Builds funktioniert.

Auto Build

Ist Auto Build aktiviert (Standard; eine Editor-Einstellung für alle Tilesets), werden bereits erstellte Arrays eine Sekunde nach einer Änderung von Texturen, Auflösung oder Komprimierung neu erstellt, und zwar mit schneller Komprimierung. Die erste Erstellung startest du immer von Hand. Eine abgebrochene oder fehlgeschlagene automatische Erstellung wird erst wiederholt, wenn sich der Inhalt oder das Zielformat ändert. Die Schaltfläche Build Texture Arrays komprimiert dagegen mit normaler Qualität, was etwas besser aussieht. Klicke deshalb vor der Veröffentlichung darauf.

Die Größenschätzung lässt sich leicht nachvollziehen: Auflösung² × 20 Slices × 2 Arrays × 4/3 für Mips × Byte pro Pixel (1 bei BC7, etwa 0.44 bei ASTC 6×6, 4 unkomprimiert). Senke die Auflösung für Mobilgeräte auf 512: Das braucht ein Viertel des Speichers.

Für Player-Builds müssen die Shader RTS Terrain Studio/Terrain und Hidden/RTS Terrain Studio/TextureArrayBlit (vom Laufzeit-Fallback verwendet) enthalten sein. Tools ▸ RTS Terrain Studio ▸ Validate Project erstellt das Asset RTSShaderReferences in Resources/RTSTerrainStudio, das dafür sorgt, dass alle Shader des Pakets in Builds enthalten bleiben.

Vegetationsbibliothek

Eine Vegetationsbibliothek (RTSVegetationLibrary) listet das Gras, die Pflanzen und die Bäume auf, die du malen kannst. Erstelle eine über Assets ▸ Create ▸ RTS Terrain Studio ▸ Vegetation Library und weise sie dem Feld Vegetation Library des RTSTerrain zu. Damit Vegetation angezeigt wird, braucht das Terrain außerdem eine Komponente RTSVegetationRenderer (der Inspector von RTSTerrain bietet an, sie hinzuzufügen).

Der Inspector hat zwei Tabs, Details und Trees, mit einem Raster aus Vorschaubildern. Lege Prefabs (Bäume), Texturen (Gras-Quads) oder Meshes (Detail-Meshes) darauf ab, um Einträge hinzuzufügen. Jeder Eintrag erhält eine stabile id, die von Karten verwendet wird; duplizierte Einträge erhalten automatisch neue IDs.

Detail-Prototypen

Details sind kleine Pflanzen in großer Zahl, die du über ihre Dichte malst (siehe Detaildichte). Sie werden in Blöcken von 16 × 16 Zellen erzeugt und mit dem internen prozeduralen Shader Hidden/RTS Terrain Studio/Detail gezeichnet, der Wind, Nebel des Krieges und Farbvariation übernimmt.

EinstellungStandardBeschreibung
IdgeneriertStabile Kennung, die in Kartendateien gespeichert wird.
Name"Grass"Wird in der Palette angezeigt.
Render ModeCrossed QuadsCrossedQuads: gekreuzte Quads, texturiert mit Texture (klassisches Gras). Mesh: verwendet Mesh, texturiert mit der Haupttextur und der Farbe von Material.
MeshkeineMesh für den Modus Mesh.
MaterialkeineMaterial für den Modus Mesh. Seine Haupttextur, seine Farbe und sein Alpha-Cutoff werden vom Detail-Shader verwendet.
TexturekeineTextur mit Alpha für den Modus Crossed Quads.
Healthy Color / Dry Colorhellgrün / strohfarbenInstanzen werden per Rauschen zwischen beiden Farben überblendet.
Min / Max Width0.6 / 1.0Zufälliger Breitenbereich in Metern.
Min / Max Height0.4 / 0.8Zufälliger Höhenbereich in Metern.
Max Density6Instanzen pro Detail-Texel bei voller Dichte (255). Bereich 1–32.
Color Noise Scale12Skalierung des Rauschens, das zwischen gesunder und trockener Farbe überblendet.
Wind Strength0.50–1. Windrichtung und Windgeschwindigkeit stellst du am RTSVegetationRenderer ein.
Render Distance70Instanzen, die weiter als diese Distanz von der Kamera entfernt sind, werden nicht mehr gezeichnet (vorher schrumpfen sie sanft weg).
Cast ShadowsausSchatten von Details sind teuer; lasse die Option bei Gras ausgeschaltet.
Align To GroundanNeigt Instanzen entsprechend der Bodennormale.
Max Slope40Auf Hängen, die steiler als dieser Wert (in Grad) sind, wird nichts gespawnt.
Avoid WateranUnter Wasser wird nichts gespawnt.
IconkeinePaletten-Icon; wird im Editor erzeugt, wenn das Feld leer ist.

Baum-Prototypen

Bäume sind einzelne Instanzen (siehe Bäume und Dekorinstanzen), die du mit dem Werkzeug Place Trees platzierst.

EinstellungStandardBeschreibung
IdgeneriertStabile Kennung, die in Kartendateien gespeichert wird.
Name"Tree"Wird in der Palette angezeigt.
PrefabkeinePrefab mit MeshRenderers und einer optionalen LODGroup (LOD0 und LOD1 werden verwendet).
Spawn ModeInstancedInstanced: per GPU-Instancing gezeichnet, keine GameObjects, am schnellsten. GameObject: eine Prefab-Instanz pro Baum, für Gameplay wie das Abbauen von Bäumen.
Min / Max Scale0.85 / 1.2Bereich für die zufällige gleichmäßige Skalierung.
Random RotationanZufälliger Drehwinkel beim Platzieren.
Tint A / Tint Bweiß / blassgrünZufälliger Tönungsbereich pro Instanz.
Footprint Radius0.8Radius in Metern, der in der Pathing-Karte blockiert wird (skaliert mit dem Baum).
Blocks VisionanBäume blockieren die Sichtlinie für den Nebel des Krieges, wie in klassischen RTS-Spielen.
Min Spacing1.6Mindestabstand zu anderen Bäumen beim Verteilen.
CollidersanModus Instanced: Es werden gepoolte Kapsel-Collider als Stellvertreter erzeugt. Modus GameObject: Die eigenen Collider des Prefabs werden verwendet.
Collider Radius / Height0.4 / 4Größe der Kapsel-Stellvertreter (Modus Instanced).
Render Distance250Bäume, die weiter als diese Distanz von der Kamera entfernt sind, werden nicht mehr gezeichnet.
Cast ShadowsanSchattenwurf.
IconkeinePaletten-Icon; wird im Editor erzeugt, wenn das Feld leer ist.

GPU-Instancing

Instanzierte Bäume werden per GPU-Instancing gezeichnet. Deshalb sollte bei ihren Materialien Enable GPU Instancing eingeschaltet sein. Ist die Option bei einem Material aus, verwendet der Renderer automatisch eine versteckte Instancing-Kopie davon (die beim Bearbeiten mit dem Original synchron bleibt). Der Inspector der Bibliothek zeigt dann eine Infomeldung mit der Schaltfläche Enable, die das Material selbst korrigiert. Bei Details muss Instancing in den Materialien nicht aktiviert sein, weil sie immer mit dem prozeduralen Detail-Shader gezeichnet werden.

Verwende für die Materialien von Baum- und Pflanzen-Prefabs den Shader RTS Terrain Studio/Vegetation, um Wind, Transluzenz, Nebel des Krieges und die Tönung pro Instanz zu erhalten. Andere URP-Materialien mit Instancing-Unterstützung funktionieren ebenfalls, aber ohne die Tönung.

Per Code entfernst du Bauminstanzen mit RTSVegetationRenderer.RemoveTree(id) (zum Beispiel, wenn ein Arbeiter einen Baum abbaut) und findest sie mit TryFindNearestTree. Ihr GameObject oder ihren Collider-Stellvertreter liefert GetTreeObject(id).

Bibliotheken per Code bearbeiten

Karten speichern eine Palette von Prototyp-IDs. Löse einen Palettenindex daher über die Bibliothek auf:

  • Bäume: vegetationLibrary.FindTree(map.TreePrototypeIds[tree.prototype]).
  • Detailschichten: vegetationLibrary.FindDetail(layer.prototypeId).
  • Dekor: decorLibrary.Find(map.DecorPrototypeIds[d.prototype]). Das Prefab einer Variante erhältst du mit prototype.GetPrefab(d.variant): 0 ist das Haupt-Prefab, und ungültige oder leere Varianten fallen auf das Haupt-Prefab zurück. VariantCount zählt das Haupt-Prefab mit.
  • decorLibrary.GetCategories() listet die Kategorien in Anzeigereihenfolge auf (Prototypen ohne Kategorie erscheinen unter Misc).
  • vegetationLibrary.EnsureUniqueIds() vergibt neue IDs an duplizierte Einträge.

Rufe nach dem Ändern einer Bibliothek per Code library.NotifyChanged() auf (die Methode löst das Event Changed der Bibliothek aus). Dann löst RTSTerrain das Event LibrariesChanged aus, und die Renderer aktualisieren sich. Es gilt dieselbe Regel wie für Tilesets.

Dekorbibliothek

Eine Dekorbibliothek (RTSDecorLibrary) listet die Props (Doodads) auf, die du platzieren kannst: Felsen, Ruinen, Zäune, Kisten, Gebäude oder jedes andere Prefab. Erstelle eine über Assets ▸ Create ▸ RTS Terrain Studio ▸ Decor Library und weise sie dem Feld Decor Library von RTSTerrain zu. Eine Komponente RTSDecorManager auf dem Terrain hält die Szene mit den platzierten Instanzen synchron.

Der Inspector zeigt die Prototypen als Raster mit automatisch erzeugten Vorschaubildern und einem Tab pro Kategorie (plus All). Ziehe Prefabs darauf, um sie der aktuellen Kategorie hinzuzufügen; mit einem Rechtsklick auf einen Kategorie-Tab benennst du ihn um. Prototypen ohne Kategorie erscheinen unter Misc.

Felder der Dekor-Prototypen

EinstellungStandardBeschreibung
IdgeneriertStabile Kennung, die in Kartendateien gespeichert wird.
Name"Decor"Wird in der Palette angezeigt.
Category"Props"Paletten-Tab, in dem der Prototyp erscheint.
PrefabkeineDas zu platzierende Objekt.
VariantsleerOptionale alternative Prefabs; beim Platzieren wird eine zufällige Variante gewählt. Die Instanz speichert den Variantenindex (0 = Haupt-Prefab).
Spawn ModeGameObjectGameObject: ein GameObject pro Instanz (Skripte, Collider, Animationen). Instanced: GPU-Instancing, keine GameObjects, nur statische Props, am schnellsten.
Min / Max Scale0.9 / 1.1Bereich für eine zufällige, einheitliche Skalierung.
Random YawanZufällige Drehung um die Hochachse.
Yaw Step0Rastet den Drehwinkel auf Vielfache dieses Winkels ein (0 = frei), zum Beispiel 90 für Mauern und Zäune.
Max Random Tilt0Zufällige Neigung in Grad (0–45).
Align To Normal0Richtet die Hochachse an der Bodennormalen aus (0 = aufrecht, 1 = vollständig ausgerichtet).
Snap To GridausRastet die Position auf Zellmitten ein.
Y Offset0Vertikaler Versatz in Metern, der beim Rendern addiert wird (negative Werte versenken das Objekt im Boden).
Tint A / Tint Bweiß / weißBereich für eine zufällige Einfärbung pro Instanz.
Footprint ShapeCircleNone, Circle oder Box.
Footprint Size(0.75, 0.75)Circle: X ist der Radius. Box: halbe Ausdehnung entlang der X- und Z-Achse des Objekts. In Metern, skaliert mit der Instanz.
Footprint EffectUnwalkable, UnbuildablePathingOverride-Flags, die innerhalb der Grundfläche geschrieben werden.
Blocks VisionausBlockiert die Sichtlinie für den Nebel des Krieges.
Min Spacing1Mindestabstand zu anderen Dekorobjekten beim Verstreuen.
Cast ShadowsanSchattenwurf.
IconkeinePaletten-Icon; wird im Editor erzeugt, wenn das Feld leer ist.

Grundflächen

  • Bei einer kreisförmigen Grundfläche dient der X-Wert von Footprint Size als Radius, multipliziert mit der größten horizontalen Skalierung der Instanz.
  • Eine rechteckige Grundfläche folgt der Drehung der Instanz (ihren auf den Boden projizierten X- und Z-Achsen) und ihrer X/Z-Skalierung. Eine gedrehte Mauer blockiert also genau ihre eigene Fläche.
  • Es wird keine Grundfläche geschrieben, wenn die Form None ist, der Effekt None ist oder die Instanz das Flag DecorFlags.IgnorePathing hat.
  • Verwende für flache Dekorationen, über die Einheiten hinweglaufen dürfen (Blumen, Schutt), nur Unbuildable und für massive Objekte Unwalkable.

Spawn-Modi

GameObjectInstanced
SzenenobjekteEines pro Instanz, unter einem Kindobjekt namens „RTS Decor“ (wird nicht mit der Szene gespeichert)Keine
Skripte, Collider, AnimationJa (jedes Wurzelobjekt erhält eine Komponente RTSDecorInstance)Nein
RenderingDie normalen Renderer des PrefabsGraphics.RenderMeshInstanced mit LOD0, Culling pro Patch
Ideal fürInteraktive Props, Goldminen, zerstörbare ObjekteTausende statische Felsen und Pflanzen

Instanziertes Dekor verhält sich beim GPU-Instancing genauso wie instanzierte Bäume: Materialien ohne Enable GPU Instancing werden über eine verborgene Kopie mit aktiviertem Instancing gezeichnet. Gespawnte Objekte werden in einem Pool wiederverwendet; RTSDecorManager.GetInstanceObject(id) liefert das gespawnte Objekt einer Instanz, und RTSDecorInstance.FromCollider(collider) ordnet einen Physik-Treffer wieder seiner Instanz zu.

Das Dateiformat .rtsmap

Dieser Abschnitt richtet sich an Programmierer, die Karten mit anderen Werkzeugen lesen, Konverter schreiben oder eigene Daten hinzufügen möchten. Um das Paket zu verwenden, brauchst du ihn nicht: RTSMapSerializer erledigt alles.

Binärer Aufbau

Alle Zahlen werden im Little-Endian-Format gespeichert. Strings verwenden das .NET-Format von BinaryWriter: eine 7-Bit-codierte Länge, gefolgt von UTF-8-Bytes.

TeilTypInhalt
Magic-Kennung4 ByteASCII RTSM
Formatversionint32RTSMapSerializer.CurrentVersion (derzeit 1)
Header-Längeint32Anzahl der folgenden Header-Bytes
HeaderBytesstring name, string author, string description, int32 width, int32 height, float cellSize, int32 maxPlayers, string tilesetId, int64 modifiedUtcTicks
Datenteilroher Deflate-Stream (RFC 1951, ohne zlib- oder gzip-Wrapper) bis zum DateiendeEine Folge von Abschnitten, abgeschlossen durch ein int32-Tag mit dem Wert 0

Der Header ist nicht komprimiert, damit Kartenbrowser Name, Autor, Größe und Spielerzahl anzeigen können, ohne die Karte zu entpacken (RTSMapSerializer.TryReadHeader, TryReadHeaderFromFile, Struct RTSMapHeader). Leseroutinen springen anhand der Header-Länge an das Ende des Headers, sodass künftige Versionen weitere Header-Felder anhängen können.

Dateiabschnitte

Jeder Abschnitt hat den Aufbau [int32 tag][int32 length][payload]. Tags sind FourCC-Codes, die als Little-Endian gespeichert werden; das erste Zeichen steht also im niedrigsten Byte (DIMS = 0x534D4944). Arrays mit Rasterdaten werden als int32-Elementanzahl geschrieben, gefolgt von den rohen Elementen.

TagWertNutzdaten
DIMS0x534D4944int32 width, int32 height, float cellSize, float cliffLevelHeight, int32 splatResolution, int32 detailResolution, int32 pathingResolution, uint32 nextInstanceId. Muss der erste Abschnitt sein.
INFO0x4F464E49string name, author, description; int32 maxPlayers; string tilesetId; int64 createdUtcTicks, modifiedUtcTicks
HGHT0x54484748Feinhöhen: float pro Vertex
LEVL0x4C56454CKlippenstufen: byte pro Vertex
VFLG0x474C4656Vertex-Flags: byte pro Vertex
CTYP0x50595443Klippentypen: byte pro Vertex
WATR0x52544157Höhen der Wasseroberfläche: float pro Vertex
SPLT0x544C5053int32 Anzahl der Splatmaps (4), danach pro Splatmap ein Array mit RGBA-Bytes pro Texel
DETL0x4C544544int32 Anzahl der Schichten, danach pro Schicht: string prototypeId und ein Byte-Array mit den Dichten pro Detail-Texel
TREE0x45455254Liste der Prototyp-IDs (int32 Anzahl + Strings), int32 Anzahl der Bäume, danach pro Baum: uint32 id, uint16 prototype, float3 position, float yaw, float scale, 4 Byte tint (30 Byte)
DECO0x4F434544Liste der Prototyp-IDs, int32 Anzahl, danach pro Instanz: uint32 id, uint16 prototype, byte variant, byte flags, float3 position, float4 rotation (x, y, z, w), float3 scale, 4 Byte tint (52 Byte)
PATH0x48544150Manuelle Pathing-Overrides: byte pro Pathing-Zelle
REGN0x4E474552int32 Anzahl, danach pro Region: string name, float x, z, width, depth, float4 colour, Eigenschaften
STRT0x54525453int32 Anzahl, danach pro Startposition: int32 player, float3 position, float yaw
MARK0x4B52414Dint32 Anzahl, danach pro Marker: string name, string tag, float3 position, float4 rotation, Eigenschaften
ENVR0x52564E45float4 sunColor, float sunIntensity, float2 sunRotation, float4 ambientColor, bool fog, float4 fogColor, float fogDensity

„Eigenschaften“ werden als int32-Anzahl geschrieben, gefolgt von Schlüssel/Wert-Paaren aus Strings. Die abgeleitete Pathing-Karte wird nicht gespeichert, sondern nur die manuellen Overrides.

Unbekannte Abschnitte, beschädigte Dateien und Versionierung

  • Unbekannte Abschnitte werden übersprungen, und zwar anhand ihrer Länge. Ältere Versionen des Pakets können deshalb neuere Dateien mit zusätzlichen Abschnitten lesen, und deine eigenen Abschnitte führen bei anderen Leseroutinen nie zu Fehlern.
  • Hat eine Datei eine neuere Formatversion als die Leseroutine, wird eine Warnung protokolliert und die Datei trotzdem gelesen; unbekannte Daten werden übersprungen.
  • Passt die Elementanzahl einer Rasterschicht nicht zur Kartengröße, wird eine Warnung protokolliert; was passt, wird gelesen, der Rest übersprungen.
  • Abgeschnittene oder beschädigte Dateien lösen immer denselben Ausnahmetyp aus: System.IO.InvalidDataException. Bei einer Datei mit falscher Magic-Kennung lautet die Meldung „Not an RTS Terrain Studio map file“.
  • RTSMapSerializer.SaveToFile schreibt zuerst in <path>.tmp und ersetzt dann die Datei. Ein Absturz während des Speicherns hinterlässt deshalb nie eine halb geschriebene Karte.
  • Kompatibilitätsregel für das Format: Neue Daten kommen als neue Abschnitte oder als angehängte Header-Felder hinzu, nie durch Änderungen an bestehenden Abschnitten. Halte es mit deinen eigenen Abschnitten genauso und speichere eine Versionsnummer in deinen Nutzdaten.

Eigene Dateiabschnitte

Du kannst eigene Daten in Kartendateien speichern, etwa Spielregeln, Trigger oder Questdaten. Möglich machen das zwei statische Hooks von RTSMapSerializer:

HookTypAufruf
CustomSectionWriterAction<MapData, Action<int, byte[]>>Einmal pro Speichervorgang, nach den eingebauten Abschnitten. Rufe die übergebene Action für jeden Abschnitt, den du schreiben möchtest, mit deinem Tag und deinen Nutzdaten auf.
CustomSectionReaderAction<MapData, int, byte[]>Für jeden Abschnitt, den die Leseroutine nicht kennt, mit dessen Tag und Nutzdaten. Das Objekt MapData existiert zu diesem Zeitpunkt bereits (DIMS kommt zuerst).
  • Wähle Tags, die nicht mit den eingebauten kollidieren; beginne sie mit X (zum Beispiel XGLD). Verwende nie 0, denn dieser Wert markiert das Ende der Abschnitte.
  • Beide Hooks sind einfache Delegates: Kombiniere Handler mit += und entferne sie mit -=. So können mehrere Systeme Abschnitte beisteuern.
  • Prüfe in deiner Leseroutine das Tag und ignoriere Abschnitte, die nicht von dir stammen.
  • MapData bietet keinen Platz für deine Daten. Verwalte sie neben der Karte (das Beispiel verwendet eine ConditionalWeakTable). So abgelegte Daten kopieren MapData.Clone() und ResizeMap nicht mit, und sie werden auch nicht im Undo-Verlauf erfasst.
  • Registriere die Hooks, bevor eine Karte geladen oder gespeichert wird. Im Unity Editor liest auch der Importer Karten, und Save schreibt sie; registriere die Hooks deshalb auch für den Editor (das Beispiel verwendet InitializeOnLoadMethod).
using System;
using System.IO;
using System.Runtime.CompilerServices;
using RTSTerrainStudio;
using UnityEngine;

/// <summary>Stores a "starting gold" value in every map file, in a custom section "XGLD".</summary>
public static class StartingGoldSection
{
    // FourCC, little-endian like the built-in tags: the first character is in the lowest byte.
    const int Tag = 'X' | ('G' << 8) | ('L' << 16) | ('D' << 24);
    const int PayloadVersion = 1;

    /// <summary>Our data for one map.</summary>
    public sealed class Data
    {
        public int startingGold = 500;
    }

    // One Data object per MapData. Entries disappear automatically together with their map.
    static readonly ConditionalWeakTable<MapData, Data> s_Data = new ConditionalWeakTable<MapData, Data>();

    /// <summary>Returns the data of a map (created with defaults when the map has none).</summary>
    public static Data Get(MapData map) => s_Data.GetOrCreateValue(map);

#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]          // editor: importer and Save
#endif
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]   // player and Play Mode
    static void Register()
    {
        // Remove first so a second registration does not add the handlers twice.
        RTSMapSerializer.CustomSectionWriter -= Write;
        RTSMapSerializer.CustomSectionWriter += Write;
        RTSMapSerializer.CustomSectionReader -= Read;
        RTSMapSerializer.CustomSectionReader += Read;
    }

    static void Write(MapData map, Action<int, byte[]> writeSection)
    {
        if (!s_Data.TryGetValue(map, out Data data)) return;   // nothing to save for this map
        using (var stream = new MemoryStream())
        {
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write(PayloadVersion);   // our own version, so the payload can grow later
                writer.Write(data.startingGold);
            }
            writeSection(Tag, stream.ToArray());
        }
    }

    static void Read(MapData map, int tag, byte[] payload)
    {
        if (tag != Tag) return;   // another system's section: ignore it
        using (var reader = new BinaryReader(new MemoryStream(payload)))
        {
            int version = reader.ReadInt32();
            if (version >= 1) Get(map).startingGold = reader.ReadInt32();
        }
    }
}

Im Spielcode greifst du mit StartingGoldSection.Get(terrain.Map).startingGold darauf zu. Wenn du den Wert im Editor änderst, markiere die Karte anschließend als geändert (zum Beispiel mit terrain.NotifyChanged(MapLayers.Info, terrain.Map.CellBounds) oder indem du terrain.HasUnsavedChanges = true setzt), damit sie gespeichert wird.

Bearbeiten im Unity Editor

Dieses Kapitel behandelt alles, womit du im Unity Editor eine Karte baust: das Fenster von RTS Terrain Studio, das Malen in der Scene View, Tastenkürzel, Pinsel, Symmetrie, Rückgängigmachen, jedes Bearbeitungswerkzeug mit all seinen Einstellungen, Auto Paint und die Komponente RTS Terrain selbst.

Dieselben Werkzeuge sowie die Pinsel- und Symmetrieeinstellungen stecken auch im In-Game-Editor (siehe In-Game-Editor). Was du hier lernst, funktioniert also auch dort.

Deine erste Bearbeitung in fünf Schritten. Wenn du das Paket noch nie verwendet hast, probiere Folgendes:

  1. Erstelle ein Terrain über GameObject ▸ RTS Terrain Studio ▸ RTS Terrain. Hat das Projekt noch kein Tileset, wird dir angeboten, zuerst die Demo-Inhalte zu erstellen. Nimm das Angebot an.
  2. Öffne Tools ▸ RTS Terrain Studio ▸ Editor Window.
  3. Klicke im Tab Height auf Raise / Lower. Die Bearbeitung wird automatisch eingeschaltet.
  4. Bewege die Maus in der Scene View über das Terrain und halte die linke Maustaste gedrückt. Es wächst ein Hügel. Halte zusätzlich Shift gedrückt, um stattdessen zu graben.
  5. Drücke 2 für die Klippenwerkzeuge, klicke auf den Hügel, um ein Plateau anzuheben, und drücke Esc, wenn du fertig bist. Mit Ctrl+Z machst du jeden Schritt rückgängig.

Das Fenster von RTS Terrain Studio

Das Fenster ist die Schaltzentrale des Editors. Es hat keinen eigenen Bearbeitungszustand: Es zeigt und ändert die gemeinsame Bearbeitungssitzung, die auch die Scene View, das Overlay der Scene View und der Inspector von RTS Terrain verwenden. Was du an einer Stelle änderst, erscheint sofort auch an den anderen.

Du kannst es auf folgende Arten öffnen:

  • Tools ▸ RTS Terrain Studio ▸ Editor Window
  • über die Schaltfläche Open RTS Terrain Studio im Inspector von RTS Terrain
  • über die Fenster-Schaltfläche im Overlay von RTS Terrain Studio in der Scene View
Das Fenster von RTS Terrain Studio mit dem Tab Decor und der Dekorpalette
Das Fenster von RTS Terrain Studio (Tab Decor). Oben die Kategorien, darunter die Werkzeuge, dann Beschreibung, Symmetrie und Einstellungen des aktiven Werkzeugs mit der Palette aus Vorschaubildern.

Das Fenster hat vier Bereiche: oben die Kopfzeile, die Tab-Leiste, die scrollbare Seite und unten die Statusleiste.

Kopfzeile

Die Kopfzeile zeigt, welches Terrain und welche Karte du bearbeitest, und enthält die Datei- und Undo-Befehle.

BedienelementFunktion
Terrain-AuswahlLegt fest, welches Terrain das Fenster und die Werkzeuge der Scene View bearbeiten. Sie listet jedes RTS Terrain in den geöffneten Szenen auf und bietet einen Eintrag Create New Terrain. Auch wenn du ein Terrain im Hierarchy-Fenster auswählst oder in der Scene View auf ein anderes Terrain klickst, wechselt das bearbeitete Terrain. Noch offene Texteingaben werden vor dem Wechsel übernommen.
Schaltfläche SelectWählt das GameObject des Terrains im Hierarchy-Fenster aus und hebt es dort kurz hervor.
Kartenname und PunktDer Name der Karte (aus dem Tab Map). Ein Punkt daneben bedeutet, dass die Karte nicht gespeicherte Änderungen hat. Darunter siehst du die Kartengröße und den Namen des .rtsmap-Assets.
SaveSchreibt die Karte in ihr .rtsmap-Asset. Ohne Karten-Asset verhält sich die Schaltfläche wie Save As. Solange es nicht gespeicherte Änderungen gibt, ist sie hervorgehoben. Ist die Datei schreibgeschützt oder gesperrt, erklärt ein Dialog das Problem und bietet Save As an.
Save As…Speichert die Karte als neues .rtsmap-Asset im Projekt und weist es dem Terrain zu.
Open…Öffnet ein anderes .rtsmap-Asset des Projekts auf diesem Terrain. Dateien außerhalb des Ordners Assets musst du zuerst ins Projekt kopieren.
ReloadLädt die Karte neu aus ihrem Asset und verwirft nicht gespeicherte Änderungen. Erfordert ein Karten-Asset.
Undo / RedoMacht die letzte Terrain-Bearbeitung rückgängig bzw. stellt sie wieder her. Die Schaltflächen zeigen den Namen des Schritts, zum Beispiel Undo Raise / Lower. Siehe Rückgängig und Wiederherstellen.
?Öffnet dieses Handbuch im Kapitel über das Editorfenster.
Editing On / Editing OffSchaltet die Terrain-Bearbeitung in der Scene View ein oder aus. Ist noch kein Werkzeug ausgewählt, wird beim Einschalten das erste Werkzeug des gerade angezeigten Tabs gewählt.

Open… und Reload fragen nach, bevor sie eine Karte mit nicht gespeicherten Änderungen oder Undo-Verlauf ersetzen, denn das Laden einer Karte löscht den Undo-Verlauf. Im Play Mode erinnert dich eine Warnung daran, dass Änderungen beim Verlassen des Play Mode verloren gehen, sofern du die Karte nicht speicherst.

Hat die Szene noch kein Terrain, zeigt die Kopfzeile stattdessen die Schaltfläche Create Terrain. Sie erstellt ein GameObject mit RTS Terrain und den Komponenten für Vegetation, Wasser und Dekor; dafür nutzt sie den Menüeintrag GameObject ▸ RTS Terrain Studio ▸ RTS Terrain.

Tab-Leiste

Die Tab-Leiste hat zehn Tabs: die acht Werkzeugkategorien Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing und Markers, gefolgt von Map und Settings. Ein kleiner Punkt markiert die Kategorie des Werkzeugs, mit dem du gerade malst. Das Fenster merkt sich den zuletzt verwendeten Tab.

Solange das Fenster den Tastaturfokus hat, wechselst du mit 1 bis 8 (auch auf dem Ziffernblock) zwischen den Kategorie-Tabs. Während du in ein Textfeld tippst, werden diese Tasten ignoriert.

Werkzeugseiten

Jeder Kategorie-Tab zeigt mehrere untereinander gestapelte Panels. Jedes Panel lässt sich mit einem Klick auf seine Kopfzeile einklappen.

PanelInhalt
Kategorie ToolsDas Werkzeugraster. Klicke auf ein Werkzeug, um damit in der Scene View zu malen. Klicke erneut auf das aktive Werkzeug, um die Bearbeitung zu beenden. Fahre mit der Maus über ein Werkzeug, um seine Beschreibung und seine Zusatztasten zu lesen. Eigene Werkzeuge, die du schreibst, erscheinen hier automatisch.
Werkzeug-PanelName, Beschreibung und Hinweis des ausgewählten Werkzeugs, eine Schaltfläche ?, die den passenden Abschnitt dieses Handbuchs öffnet, und eine Live-Statuszeile. Bewege die Maus über das Terrain, um sie zu sehen, zum Beispiel Ground 4.25 m | Fine +0.25 m | Level 2 bei Höhenwerkzeugen oder Level 2 → 3 bei den Klippenwerkzeugen.
Getting StartedWird angezeigt, wenn kein Werkzeug dieser Kategorie ausgewählt ist. Malst du gerade mit einem Werkzeug einer anderen Kategorie, steht dort Active tool: X, und das Panel bietet Go to Tool an.
BrushForm, Radius, Stärke, Härte, Abnahmekurve, Drehung, zufällige Drehung, Abstand und Streuung. Alle Pinselwerkzeuge teilen sich diese Einstellungen. Bei Werkzeugen, die keinen Pinsel verwenden, ist das Panel ausgeblendet. Siehe Pinsel.
SymmetrySymmetriemodus und Symmetriezentrum (bei Ziehwerkzeugen nicht angezeigt). Siehe Symmetrie.
Werkzeug SettingsDie Einstellungen des ausgewählten Werkzeugs, automatisch aus dem Code des Werkzeugs erzeugt. Sie bleiben über Editorsitzungen hinweg erhalten. Die Tabellen in der Werkzeugreferenz führen jede Einstellung auf.
KategorieaktionenZusätzliche Panels für einige Kategorien: Auto Paint Whole Map (Paint), Selection (Decor, solange Select Decor aktiv ist), Water at Cursor (Water) und Pathing Overlay (Pathing).

Solange die Bearbeitung pausiert ist, zeigt ein Banner Editing is paused – the Scene View uses Unity's tools. Mit der Schaltfläche Resume darin schaltest du die Bearbeitung wieder ein.

Übersicht aller Icons von RTS Terrain Studio: Kategorien, Werkzeuge, Aktionen, Symmetriemodi und Pinselformen
Jede Kategorie, jedes Werkzeug, jede Aktion, jeder Symmetriemodus und jede Pinselform hat ein eigenes Icon. Dieselben Icons werden im Fenster, im Overlay und im In-Game-Editor verwendet.

Werkzeugeinstellungen und Paletten

Das Einstellungs-Panel wählt für jede Einstellung anhand ihres Typs ein Bedienelement:

EinstellungstypBedienelement
Zahl mit WertebereichSchieberegler mit Wertefeld
An/Aus-WertSchalter
Auswahl (Enum)Dropdown-Menü
Farbe / TextFarbfeld / Textfeld
BodenschichtPalette der Bodenschichten des Tilesets mit Textur-Vorschaubildern. Leere Plätze bleiben sichtbar und erklären in ihrem Tooltip, wie du sie füllst.
KlippentypPalette der Klippenschichten des Tilesets.
KlippenstufeSchaltflächen für die Stufen 0 bis 15. Jeder Tooltip zeigt die Bodenhöhe der jeweiligen Stufe, und die Palette zeigt die Stufe unter dem Cursor.
SpielerDie 16 Spielerfarben (Red, Blue, Teal, Purple, Yellow, Orange, Green, Pink, …).
Detail-, Baum- oder Dekor-PrototypenRaster mit den Prototypen der Bibliothek samt Vorschau, einem Suchfeld und bei Dekor zusätzlich Kategoriefiltern. Ein Klick wählt einen Prototyp aus, Ctrl+Klick fügt einen zur Auswahl hinzu oder entfernt ihn daraus, Shift+Klick wählt einen Bereich aus. None hebt die Auswahl auf; Platzierungswerkzeuge verwenden dann den ersten Prototyp der Bibliothek.
Asset-ReferenzObjektfeld (zum Beispiel für das Auto-Paint-Profil). Szenenobjekte werden akzeptiert, wo der Typ es erlaubt.

Braucht eine Palette ein Asset, das dem Terrain fehlt, erklärt das Panel, was fehlt, und bietet eine Schaltfläche an, um es zu erstellen: Create Tileset, Create Vegetation Library oder Create Decor Library. Du kannst auch im Feld daneben ein vorhandenes Asset zuweisen. Jede Palette hat außerdem eine Schaltfläche Edit, die das Tileset oder die Bibliothek auswählt, damit du im Inspector Einträge hinzufügen kannst.

Nimmst du in der Scene View mit Ctrl+Klick einen Wert auf, ändert sich die Einstellung auch im Fenster, zum Beispiel die ausgewählte Bodenschicht oder die Höhe für Flatten.

Statusleiste

Die Statusleiste unten im Fenster zeigt von links nach rechts:

  • den Punkt unter der Maus: Zelle, Weltposition, Klippenstufe, Bodenhöhe, Wassertiefe (an nassen Stellen) und den Namen der dominanten Bodenschicht;
  • die Pathing-Flags an diesem Punkt (begehbar, bebaubar usw.);
  • den Pinsel, zum Beispiel Brush 6.0 m · 50%;
  • den Undo-Verlauf, zum Beispiel Undo 12 (+2) · 48 MB: Schritte, die du rückgängig machen kannst, Schritte, die du wiederherstellen kannst, und den Speicher, den sie belegen.

Tab Map

Der Tab Map enthält kartenweite Einstellungen und Operationen. Jede Änderung lässt sich rückgängig machen, sofern der zugehörige Dialog nichts anderes angibt. Die Schaltfläche ? oben öffnet diesen Abschnitt.

Map

EinstellungBeschreibung
NameName der Karte, der in Kartenbrowsern und Lobbys angezeigt wird. Er erscheint auch in der Kopfzeile des Fensters.
AuthorWer die Karte erstellt hat.
DescriptionKurzbeschreibung, die Spielern angezeigt wird.
Max PlayersEmpfohlene Spielerzahl (1–16).
Map AssetDas .rtsmap-Asset, das dieses Terrain lädt und speichert. Weist du ein anderes Asset zu, wird dessen Karte geöffnet – mit derselben Rückfrage wie bei Open….

Unter den Feldern zeigt das Panel die Kartengröße, die Auflösung der Splat-, Detail- und Pathing-Schichten pro Zelle sowie den Speicherbedarf der Karte. Eingaben in diese Felder erzeugen beim Verlassen des Feldes einen einzigen Undo-Schritt (Edit Map Info).

New Map

Ersetzt die Karte des Terrains durch eine neue, flache Karte.

EinstellungStandardBeschreibung
Größen-Presets–Schaltflächen für quadratische Karten mit 64, 96, 128, 192, 256, 384 und 512 Zellen.
Width / Heightaus der Default Map des Terrains (128)Größe in Zellen. Vielfaches von 16, von 16 bis 1024.
Cell Size2 mGröße einer Zelle in Metern. Klassische RTS-Karten verwenden etwa 2 m.
Cliff Level Height2 mHöhe einer Klippenstufe in der Spielwelt.
Base Cliff Level2Anfängliche Klippenstufe der gesamten Karte. Mit 2 bleibt Platz, um Seen und Täler zu graben.
Base Ground Layer0Bodenschicht, die anfangs die gesamte Karte bedeckt.
Splat4Texel der Bodentexturierung pro Zelle (1, 2, 4 oder 8). 4 ist ein guter Standardwert; 8 ergibt eine sehr feine Texturierung.
Details2Texel für die Grasdichte pro Zelle (1, 2 oder 4).
Pathing2Pathing-Zellen pro Kartenzelle (1, 2 oder 4). Mit höheren Werten lassen sich Einheiten genauer platzieren.
NameNew MapName der neuen Karte.

Eine Übersichtszeile zeigt dir die resultierende Weltgröße, bevor du auf Create New Map klickst.

Das Erstellen einer neuen Karte löscht den Undo-Verlauf und lässt sich nicht rückgängig machen. Das alte Karten-Asset wird vom Terrain getrennt, aber nicht verändert. Speichere die neue Karte mit Save As… in einer eigenen Datei.

Resize

Vergrößert oder verkleinert die Karte und behält dabei ihren Inhalt.

  1. Gib New Width und New Height in Zellen ein (Vielfache von 16).
  2. Klicke auf eine der neun Anker-Schaltflächen. Die Pfeile zeigen, in welche Richtung die Karte wächst; der vorhandene Inhalt bleibt an der verankerten Seite.
  3. Wähle für neue Bereiche Fill Level (Klippenstufe) und Fill Layer (Bodenschicht).
  4. Klicke auf Resize Map. Der Hinweis über der Schaltfläche zeigt die neue Weltgröße und wie weit sich der Inhalt verschiebt.

Beim Verkleinern werden Terrain, Bäume, Dekor und Marker außerhalb des neuen Bereichs entfernt. Eine Größenänderung löscht den Undo-Verlauf.

Generator

Erzeugt aus einem Seed ein komplettes Karten-Layout: Plateaus, Rampen, Wasser, Bodentexturierung, Vegetation, Dekor und Startpositionen. Derselbe Seed mit denselben Einstellungen ergibt immer dieselbe Karte.

BedienelementBeschreibung
Seed, Style, SymmetryDer Seed, der Stil (Plains, Hills, Highlands, Islands, Canyon, Arena) und die Symmetrie des Layouts (standardmäßig Rotate 2, das 180°-Layout; siehe Symmetrie).
Noise, Cliffs, Ramps, Water, Border, Paint, Vegetation, Decor, Start LocationsAusklappbereiche mit den detaillierten Generator-Einstellungen der einzelnen Phasen.
Randomize SeedWählt einen neuen zufälligen Seed.
Apply Style PresetLädt die Werte für Rauschen, Klippen, Rampen und Wasser des ausgewählten Stils.
ResetSetzt alle Generator-Einstellungen auf die Standardwerte des ausgewählten Stils zurück.
GenerateErsetzt Höhen, Klippen, Wasser, Bodentexturierung, Vegetation, Dekor und Startpositionen. Die Kartengröße bleibt erhalten. Das Ganze ist ein einziger Undo-Schritt.

Der Generator arbeitet mit dem Terrain, das in der Kopfzeile des Fensters eingestellt ist. Ist ein anderes Terrain ausgewählt, fordert dich ein Dialog auf, zuerst dieses Terrain in der Kopfzeile auszuwählen. Während er läuft, wird ein Fortschrittsbalken angezeigt, und unter den Schaltflächen erscheint eine Zusammenfassung der erzeugten Inhalte.

Heightmap

Importiert oder exportiert die Terrain-Höhe als 16-Bit-PNG- oder RAW-Datei, zum Beispiel für die Arbeit mit World Machine, Gaea oder Photoshop.

EinstellungBeschreibung
Mode (Import)Fine Heights Only: fügt Hügel über den vorhandenen Klippenstufen hinzu. Cliff Levels And Fine Heights: teilt das Bild in Klippenstufen und Feinhöhen auf, sodass der Boden exakt dem Bild entspricht (ideal für terrassierte Heightmaps; sanfte Hänge, die eine Stufe überqueren, werden zu kleinen Klippen). Absolute Heights Keep Levels: Das Bild gibt die absolute Bodenhöhe an, die Klippen bleiben aber erhalten. Verwende diesen Modus, um einen Export mit der Quelle Ground Height, den du in einem anderen Programm bearbeitet hast, erneut zu importieren.
Height ScaleMeter, die der volle 16-Bit-Bereich abbildet. 0 = automatisch.
Height OffsetImport: Meter, die zu jeder Höhe addiert werden. Export: die Höhe, die als 0 geschrieben wird.
Flip VerticallySpiegelt das Bild entlang Z. Verwende diese Option, wenn die Karte gespiegelt erscheint.
RAW Width / RAW HeightGröße von RAW-Dateien. 0 = aus der Dateigröße ableiten (nur bei quadratischen Dateien).
RAW Little EndianBytereihenfolge von RAW-Dateien. Windows und die meisten Programme verwenden Little Endian.
Source (Export)Ground Height: Klippenstufen plus Feinhöhe. Fine Heights: nur die Feinhöhe über den Stufen.

Zur Verfügung stehen die Schaltflächen Import PNG… (PNG in 8- oder 16-Bit-Graustufen, JPG oder EXR), Import RAW…, Export PNG…, Export RAW… und Export Splat PNG… (ein Bild der Bodentexturierung in den Farben der dominanten Schichten). Bilder mit abweichender Größe werden neu abgetastet. Eine Karte hat in jeder Richtung einen Vertex mehr als Zellen, eine 256er-Karte verwendet also 257 × 257 Samples. Jeder Import ist ein Undo-Schritt.

Minimap

Backt ein Minikartenbild der Karte. Wähle mit Size die Länge der längeren Seite (128, 256, 512 oder 1024 px) und passe die Bake Options an: Bodenfarben (dominante oder überblendete Schichten), Helligkeit der Klippenstufen, Reliefschattierung, Klippen, Wasser, Bäume, Dekor und Startpositionen. Bake Preview zeigt das Ergebnis im Panel; Save PNG… backt das Bild und speichert es als Datei. Wenn du die Karte nach dem Backen bearbeitest, weist dich das Panel darauf hin, erneut zu backen.

Validation

Validate Map prüft die Karte auf Probleme wie fehlende Texturen, unerreichbare Startpositionen oder Performance-Grenzen. Jedes Problem wird als Fehler, Warnung oder Info aufgeführt, zusammen mit einem Hinweis, wie du es behebst. Klicke auf ein Problem mit Ortsangabe, um es in der Scene View anzuzeigen. Wenn du die Karte nach der Validierung bearbeitest, weist das Panel darauf hin, dass die Liste den Stand der Karte vor den letzten Änderungen beschreibt.

Environment

Sonne, Umgebungslicht und Nebel werden in der Karte gespeichert und von der Komponente RTS Environment auf die Szene angewendet.

EinstellungBeschreibung
Sun ColourFarbe des Sonnenlichts.
Sun IntensityIntensität des Sonnenlichts (0–8).
Sun RotationGrad: x = Neigung (Höhe über dem Horizont), y = Drehwinkel.
Ambient ColourFarbe des Umgebungslichts (Himmelslicht).
FogAktiviert den Distanznebel.
Fog Colour / Fog DensityFarbe und Dichte (0–0.05) des exponentiellen Nebels.

Capture From Scene übernimmt die aktuelle Szenenbeleuchtung in die Karte (ein Undo-Schritt). Apply wendet die Umgebung der Karte sofort auf die Szene an, und Select Component wählt die Komponente RTS Environment aus. Hat das Terrain keine Komponente RTS Environment, bietet das Panel Add Environment Component an.

Tab Settings

PanelInhalt
ViewShow Grid (das Zellraster auf dem bearbeiteten Terrain, auch G), Show Markers (Startpositionen, Regionen und Marker, auch wenn kein Marker-Werkzeug aktiv ist), Pathing Overlay (Off, Walkability, Buildability, Flyability, Water, Combined) und Overlay Opacity.
Terrain ComponentDer vollständige Inspector der bearbeiteten Komponente RTS Terrain (siehe Die Komponente RTS Terrain) und eine Schaltfläche Select Terrain.
Undo HistoryMax Steps (10–1000, Standard 100) und Memory Limit (MB) (32–4096, Standard 512), der aktuelle Speicherverbrauch und Clear History. Siehe Grenzen und Speicher.
ShortcutsDie Tastenkürzel der Scene View mit ihren aktuellen Tastenbelegungen, ausgelesen aus Unitys Shortcut Manager. Edit Shortcuts… öffnet den Shortcut Manager; Refresh liest die Belegungen erneut ein, nachdem du sie geändert hast.
Help & MaintenanceOpen Documentation, Validate Project (führt Tools ▸ RTS Terrain Studio ▸ Validate Project aus) und Reset All Tool Settings. Letzteres setzt den Pinsel, die Symmetrie und die Einstellungen aller Werkzeuge auf ihre Standardwerte zurück und löscht den Undo-Verlauf des Terrains.
AboutProduktversion und Unity-Version.

Malen in der Scene View

Die gesamte Bearbeitung findet in der Scene View statt. Wenn die Terrain-Bearbeitung eingeschaltet und ein Werkzeug ausgewählt ist, malt die linke Maustaste mit dem Werkzeug, statt Objekte auszuwählen. Du schaltest die Bearbeitung mit der Schaltfläche Editing im Fenster, der Schaltfläche Start Editing im Overlay, der Schaltfläche Edit Terrain im Inspector oder durch einen Klick auf ein Werkzeug ein. Ausschalten kannst du sie mit Esc.

Maus und Zusatztasten

EingabeAktion
Linke MaustasteMalt mit dem aktiven Werkzeug (drücken, ziehen, loslassen). Ein Pinselstrich ist ein Undo-Schritt. Viele Werkzeuge wirken weiter, während du die Maustaste gedrückt hältst, ohne die Maus zu bewegen. So baut Raise / Lower zum Beispiel den Hügel immer weiter auf.
Shift (gedrückt gehalten)Kehrt das Werkzeug um: absenken statt anheben, radieren statt malen, entfernen statt platzieren. Der Pinselcursor färbt sich rötlich, und das Status-Panel zeigt (inverted).
Ctrl+KlickNimmt einen Wert unter dem Cursor auf, statt zu malen: eine Höhe, eine Bodenschicht, eine Klippenstufe, einen Klippentyp oder eine Wasserhöhe. Werkzeuge ohne Aufnahmefunktion zeigen This tool has nothing to pick und malen nicht.
Alt+ZiehenLässt die Kamera wie gewohnt kreisen. Dabei wird nie gemalt.
Rechte / mittlere MaustasteBehalten ihre Funktion aus Unity (Fliegen, Schwenken).
Hand-Werkzeug (Q)Ist Unitys Hand-Werkzeug aktiv, schwenkst du die Kamera durch Ziehen mit gedrückter linker Maustaste; dabei wird nicht gemalt. Das Status-Panel zeigt Hand tool (Q) active: press W to paint, or drag to pan.
EscFührt die erste zutreffende Aktion aus: bricht das laufende Ziehen eines Dekor-Handles ab, bricht den laufenden Pinselstrich ab (seine Änderungen werden zurückgenommen), hebt die Dekorauswahl auf (Select Decor) oder beendet die Bearbeitung. Esc wirkt in der Scene View, die den Tastaturfokus hat (klicke zuerst hinein); nur mit der Maus darüberzufahren reicht nicht. Hat das Fenster von RTS Terrain Studio den Fokus, bricht Esc den Pinselstrich ab und schaltet die Bearbeitung aus.
Klick auf ein anderes TerrainMacht dieses Terrain zum bearbeiteten Terrain (Benachrichtigung Editing X). Solange du mit der Maus darüberfährst, zeigt das Status-Panel Click to edit 'X'.

Unter macOS funktioniert Cmd überall dort, wo dieses Handbuch Ctrl nennt.

Wo der Mauszeiger das Terrain trifft, wird rein mathematisch berechnet, daher sind keine Collider nötig. Der Terrain-Shader zeichnet den Pinselcursor genau dort, wo du malst. Sind mehrere Scene Views geöffnet, malt nur die Ansicht unter der Maus, und ein Pinselstrich bleibt in der Ansicht, in der er begonnen hat.

Die Bearbeitung funktioniert nur bei einem Terrain, das aktiviert ist, eine geladene Karte hat und zur aktuellen Stage gehört. Bearbeite im Prefab Mode das Terrain innerhalb des Prefabs.

Das Overlay von RTS Terrain Studio

Sobald die Szene ein RTS Terrain enthält, erscheint in der Scene View das kompakte Overlay RTS Terrain Studio. Es bietet dir die meistgenutzten Bedienelemente, ohne dass du das ganze Fenster brauchst:

  • die Schaltfläche Start Editing / Editing (Esc to stop), eine Schaltfläche, die das Fenster öffnet, und ein Optionsmenü;
  • das bearbeitete Terrain (Terrain: X • unsaved, wenn es nicht gespeicherte Änderungen hat);
  • die acht Kategorieschaltflächen (Tasten 1–8) und die Werkzeuge der ausgewählten Kategorie;
  • Size (ein Schieberegler mit Feinsteuerung für kleine Pinsel, dazu ein Feld für den exakten Wert) und Strength;
  • das Dropdown-Menü Symmetry, mit einer Warnung, wenn der Modus eine quadratische Karte erfordert;
  • die Schalter Grid und Markers sowie das Dropdown-Menü Pathing für das Pathing-Overlay.

Das Optionsmenü enthält Show Status Panel, Save Map, Save Map As…, Save Maps When Saving the Scene, Offer Save As for New Maps, Shortcuts… und Documentation. Wie jedes Unity-Overlay lässt es sich verschieben, andocken oder einklappen, und Unity merkt sich seine Position.

Das Tools-Overlay von Unity

Solange ein RTS Terrain ausgewählt ist, enthält Unitys eigenes Tools-Overlay das Werkzeug RTS Terrain Studio. Wenn du es aktivierst, wird das ausgewählte Terrain zum bearbeiteten Terrain, und die Bearbeitung wird eingeschaltet. Wechselst du zu einem anderen Unity-Werkzeug, wird die Bearbeitung wieder ausgeschaltet.

Die Ausnahme ist Select Decor: Solange es aktiv ist, bestimmen Unitys Werkzeuge Move, Rotate, Scale und Transform das Handle für die Dekorauswahl, daher bleibt die Bearbeitung eingeschaltet. Siehe Handles der Dekorauswahl.

Status-Panel und Benachrichtigungen

Ein Status-Panel am unteren Rand der Scene View zeigt zwei Zeilen:

  1. das aktive Werkzeug, (inverted), solange Shift gedrückt ist, Pinselgröße und -stärke (nur bei Pinselwerkzeugen) und den Symmetriemodus, mit einer Warnung, wenn der Modus eine quadratische Karte erfordert;
  2. die Zelle unter der Maus, Klippenstufe und Höhe, den Werkzeugstatus oder -hinweis und was Esc gerade bewirkt (Esc: cancel stroke, Esc: cancel edit, Esc: clear selection oder Esc: stop editing).

Mit Show Status Panel im Optionsmenü des Overlays blendest du es aus oder ein. Tastenkürzel bestätigen ihre Wirkung mit einer kurzen Benachrichtigung in der Scene View, zum Beispiel Brush 8.0 m, Strength 55%, Grid on oder Stroke cancelled. War die Bearbeitung beim Start von Unity aus deiner letzten Sitzung noch eingeschaltet, erscheint einmalig der Hinweis Terrain editing is on (Esc to stop).

Vorschauen und Hilfslinien

  • Pinselcursor: wird in der Farbe des Werkzeugs auf das Terrain projiziert, dazu ein weiterer für jede Symmetriekopie.
  • Platzierungsvorschau: Im Modus Single zeigen Place Trees und Place Decor am Cursor eine halbtransparente Vorschau des nächsten Objekts.
  • Symmetrie-Hilfslinien: magentafarbene Achsen und der Mittelpunkt, solange ein Symmetriemodus aktiv ist.
  • Marker: Startpositionen (in Spielerfarben), Regionen und Marker; sie werden gezeichnet, solange Show Markers eingeschaltet oder ein Marker-Werkzeug aktiv ist.
  • Raster (G) und Pathing-Overlay (P): werden vom Terrain-Shader gezeichnet.

Handles der Dekorauswahl

Ist Select Decor aktiv, klickst du Objekte an, um sie auszuwählen, ziehst ein Rechteck für eine Rechteckauswahl auf und hältst Shift gedrückt, um Objekte zur Auswahl hinzuzufügen oder ihren Auswahlstatus umzuschalten. Die Auswahl erhält Handles im Unity-Stil in der Mitte der ausgewählten Objekte:

Unity-WerkzeugHandle
Move (W)Positions-Handle.
Rotate (E)Drehscheibe: Dekor dreht sich nur um die Hochachse. Halte Ctrl gedrückt, um in Schritten einzurasten.
Scale (R)Handle für gleichmäßige Skalierung. Ist Select Decor aktiv, wählt das Tastenkürzel R dieses Werkzeug aus, statt die zufällige Drehung umzuschalten.
Transform (Y)Positions-Handle und Drehscheibe.
Rect (T)Positions-Handle.
Hand (Q)Kein Handle; Ziehen mit der linken Maustaste schwenkt die Kamera.

Delete entfernt die Auswahl. Ctrl+D dupliziert die Auswahl, um eine Zelle in X und Z versetzt, damit die Kopien sichtbar sind; verschiebe sie dann mit dem Handle. Eine Benachrichtigung wie Duplicated 3 objects bestätigt den Vorgang. Ctrl+A wählt alle nicht gesperrten Dekorobjekte aus, und F rückt die Auswahl ins Bild. Das sind Unitys eigene Befehle Delete, Duplicate, Select All und Frame Selected, daher folgen sie deinen Belegungen im Shortcut Manager. Jedes Ziehen eines Handles ist ein Undo-Schritt. Esc während des Ziehens nimmt es zurück, ohne einen Schritt anzulegen. Weitere Befehle findest du im Panel Selection des Tabs Decor (siehe Dekorwerkzeuge).

Speichern und Schutz vor Datenverlust

Während der Bearbeitung liegt die Karte im Arbeitsspeicher, außerhalb der Szenendatei. Unitys eigene Abfragen Save scene? decken sie daher nicht ab. RTS Terrain Studio ergänzt eigene Schutzmechanismen:

ZeitpunktWas passiert
Speichern einer SzeneDie Karten ihrer Terrains werden automatisch in ihre .rtsmap-Assets gespeichert (Overlay-Option Save Maps When Saving the Scene, standardmäßig an). Für ein Terrain ohne Karten-Asset wird einmal pro Editorsitzung Save As… angeboten, mit den Auswahlmöglichkeiten Not Now und Never Ask Again (Overlay-Option Offer Save As for New Maps).
Wechsel in den Play ModeSave map changes before entering Play Mode? mit Save, Cancel (bleibt im Edit Mode) und Don't Save. Unity lädt die Szene neu und damit auch die Karte aus ihrem Asset. Die Abfrage entfällt, wenn die Enter Play Mode Options sowohl das Neuladen der Szene als auch das der Domain deaktivieren.
Verlassen des Play ModeSave map changes made in Play Mode? mit Save oder Discard. Die Frage erscheint, wenn die Karte während des Play Mode mit den Werkzeugen der Scene View bearbeitet wurde, oder immer dann, wenn die Szene einen In-Game-Editor (RTSRuntimeEditor) enthält und die Karte nicht gespeicherte Änderungen hat. In anderen Szenen lösen Gameplay-Änderungen wie abgebaute Bäume sie nicht aus.
Schließen einer Szene, Beenden von UnityFragt, ob die geänderten Karten gespeichert werden sollen.

Bevor das Paket eine Kartendatei überschreibt, bittet es deine Versionsverwaltung, die Datei auszuchecken. Schlägt das fehl, erklärt ein Dialog, was zu tun ist. Im Batch-Modus und während ein Player-Build erstellt wird, zeigen die Schutzmechanismen nie Dialoge.

Build-Skripte und Editor-Tests können den Schutz vorübergehend ausschalten:

using RTSTerrainStudio.Editor;
using UnityEditor;

static class MyBuildSteps
{
    [MenuItem("Tools/My Project/Run Map Batch")]
    static void RunMapBatch()
    {
        // No save prompts while this scope is open. Scopes can be nested.
        using (RTSMapSaveGuard.Suppress())
        {
            // ... open scenes, edit or generate maps, enter and leave Play Mode ...
        }
    }
}

Tastenkürzel

Die Tastenkürzel der Scene View sind in Unitys Shortcut Manager in der Kategorie RTS Terrain Studio registriert. Sie sind nur aktiv, wenn drei Bedingungen erfüllt sind: Die Terrain-Bearbeitung ist eingeschaltet, eine Scene View oder eines der Editorfenster von RTS Terrain Studio hat den Tastaturfokus (oder die Maus befindet sich über einer Scene View), und du tippst gerade nicht in ein Text- oder Zahlenfeld. Außerhalb der Bearbeitung behalten Tasten wie R oder die Zifferntasten ihre normale Funktion in Unity.

StandardtasteEintrag im Shortcut ManagerAktion
[RTS Terrain Studio/Brush SmallerPinselradius × 0.87.
]RTS Terrain Studio/Brush LargerPinselradius × 1.15.
-RTS Terrain Studio/Strength DownPinselstärke − 5 %.
=RTS Terrain Studio/Strength UpPinselstärke + 5 %.
RRTS Terrain Studio/Toggle Random RotationSchaltet die zufällige Pinseldrehung um. Ist Select Decor aktiv, wählt die Taste stattdessen Unitys Werkzeug Scale aus.
GRTS Terrain Studio/Toggle GridBlendet das Zellraster ein oder aus.
PRTS Terrain Studio/Cycle Pathing OverlayWechselt reihum Off → Walkability → Buildability → Combined → Off.
1RTS Terrain Studio/Height ToolsWählt das erste Werkzeug der Kategorie aus. Drücke die Taste erneut, um reihum durch die Werkzeuge dieser Kategorie zu schalten.
2RTS Terrain Studio/Cliff Tools
3RTS Terrain Studio/Paint Tools
4RTS Terrain Studio/Vegetation Tools
5RTS Terrain Studio/Decor Tools
6RTS Terrain Studio/Water Tools
7RTS Terrain Studio/Pathing Tools
8RTS Terrain Studio/Marker Tools

Diese Tasten und Mausaktionen verarbeitet das Paket selbst; sie lassen sich nicht neu belegen:

TasteOrtAktion
Shift (gedrückt gehalten)Scene ViewWerkzeug umkehren (absenken, radieren, entfernen).
Ctrl+Klick (Cmd+Klick unter macOS)Scene ViewWert aufnehmen (Höhe, Schicht, Stufe, Klippentyp, Wasserhöhe).
EscScene View mit TastaturfokusZiehen des Handles oder Pinselstrich abbrechen, Dekorauswahl aufheben oder Bearbeitung beenden.
1–8 (auch Ziffernblock)Fenster von RTS Terrain StudioKategorie-Tab wechseln.
EscFenster von RTS Terrain StudioPinselstrich abbrechen und Bearbeitung beenden.
Tab, Enter, SpaceFenster von RTS Terrain StudioFokus zwischen Werkzeugen und Tabs verschieben; das fokussierte Element aktivieren.
Ctrl+Klick, Shift+KlickPrototyp-RasterEinen Prototyp hinzufügen oder entfernen; einen Bereich auswählen.

Die folgenden Tasten sind Unitys eigene Tastenkürzel und Befehle. RTS Terrain Studio reagiert auf sie, sie folgen aber dem, was du ihnen in Edit ▸ Shortcuts... unter Unitys Kategorien (Main Menu/Edit, Tools) zuweist:

StandardtasteOrtAktion
Ctrl+Z / Ctrl+YÜberall in UnityTerrain-Bearbeitungen über Unitys Edit-Menü rückgängig machen / wiederherstellen.
W E YScene View, Select DecorUnitys Werkzeuge Move, Rotate und Transform: Sie bestimmen das Handle für die Dekorauswahl.
Delete, Ctrl+D, Ctrl+A, FScene View, Select DecorDelete, Duplicate, Select All (nicht gesperrtes Dekor) und Frame Selected.

R ist bei Select Decor der paketeigene Eintrag RTS Terrain Studio/Toggle Random Rotation: Solange Select Decor aktiv ist, wechselt er zu Unitys Werkzeug Scale, statt die zufällige Drehung umzuschalten.

Tastenkürzel neu belegen

  1. Öffne Edit ▸ Shortcuts... (unter macOS Unity ▸ Shortcuts...) oder klicke im Tab Settings auf Edit Shortcuts….
  2. Wähle die Kategorie RTS Terrain Studio aus.
  3. Weise den oben aufgeführten Einträgen neue Tasten zu.
  4. Klicke im Tab Settings auf Refresh, um die neuen Belegungen im Fenster zu sehen.

Kollidiert eine Taste in deiner Konfiguration mit einem anderen Tastenkürzel, zum Beispiel mit Toggle 2D Mode der Scene View auf 2, fragt Unity einmal, welches verwendet werden soll. Du kannst jedes der beiden im Shortcut Manager neu belegen.

Pinsel

Alle Pinselwerkzeuge teilen sich denselben Pinsel. Du stellst ihn im Panel Brush jeder Werkzeugseite, im Overlay (Size und Strength) oder mit den Tastenkürzeln ein. Der Pinsel bleibt zwischen Editorsitzungen erhalten.

EinstellungBereichStandardBeschreibung
ShapeCircle, Square, Diamond, CustomCircleUmriss des Pinsels. Square und Diamond lassen sich drehen. Custom verwendet ein Asset vom Typ Brush Shape.
Custom Shape–keineDas Asset vom Typ Brush Shape, das die Form Custom verwendet. Klicke auf die Formschaltfläche Custom: Das Panel zeigt dann jedes Asset vom Typ Brush Shape im Projekt als Kachel, dazu ein Feld Custom Shape. Klicke auf eine Kachel (oder weise im Feld ein Asset zu), um es zu verwenden.
Radius0.25–128 m6 mPinselradius in Metern. Der Schieberegler ist logarithmisch, damit sich kleine Pinsel fein einstellen lassen. Tastenkürzel [ und ].
Strength0.01–10.5Wirkung jedes Tupfers. Tastenkürzel - und =.
Hardness0–10.350 = weicher Rand über den gesamten Radius, 1 = harter Rand. Deaktiviert, solange Falloff Curve eingeschaltet ist.
Falloff Curvean/ausausVerwendet statt Hardness eine eigene Kurve.
Curve–Ease-in-out von 1 nach 0Gewichtung von der Pinselmitte (x = 0) bis zum Rand (x = 1).
Rotation0–360°0Drehung quadratischer, rautenförmiger und eigener Formen, im Uhrzeigersinn von oben gesehen. Deaktiviert bei der Form Circle und solange Random Rotation eingeschaltet ist.
Random Rotationan/ausausDreht jeden Tupfer zufällig. Ideal für eigene Stempel. Tastenkürzel R.
Spacing0.05–20.2Abstand zwischen den Tupfern relativ zum Radius. Kleinere Werte ergeben gleichmäßigere Pinselstriche.
Jitter0–10Zufälliger Versatz jedes Tupfers relativ zum Radius.

Wie ein Tupfer gewichtet wird

Jeder Tupfer weist jedem Vertex, Splat-Texel oder Objekt eine Gewichtung zwischen 0 und 1 zu, die das Werkzeug mit Strength multipliziert. Mit Hardness ist die Gewichtung innerhalb von hardness × radius gleich 1 und fällt dann zum Rand hin sanft auf 0 ab. Ist Falloff Curve eingeschaltet, bestimmt stattdessen die Kurve die Gewichtung: x ist der Abstand von der Mitte (0 = Mitte, 1 = Rand), y ist die Gewichtung. Bei quadratischen und rautenförmigen Formen folgt der „Abstand“ dem Umriss, sodass ein quadratischer Pinsel entlang seiner Seiten gleichmäßig stark wirkt.

Manche Werkzeuge verwenden den Pinsel als harte Auswahl: Klippen-, Wasser- und Pathing-Werkzeuge ändern nur, was eine Gewichtung von mindestens 0.5 hat, oder den Vertex unter der Pinselmitte. Weiche Ränder bedeuten dann „etwas kleiner“ statt „schwächer“.

Abstand, Streuung und Wiederholung

  • Spacing: Während du ziehst, wird entlang des Mauswegs alle radius × spacing Meter ein Tupfer gesetzt; der Abstand beträgt aber mindestens ein Zehntel einer Zelle. Werkzeuge, die an Vertices einrasten, setzen Tupfer mit mindestens einer halben Zelle Abstand.
  • Jitter: Jeder Tupfer wird um eine zufällige Distanz von bis zu jitter × radius verschoben. Jitter wirkt nur bei Werkzeugen, die den Pinsel verwenden.
  • Wiederholung: Viele Werkzeuge setzen weiter Tupfer, während du die Maustaste gedrückt hältst, ohne die Maus zu bewegen, zum Beispiel 30 Tupfer pro Sekunde bei Raise / Lower. Die Werkzeugtabellen geben an, welche Werkzeuge das tun.

Konvention für Drehungen

Alle Drehungen in RTS Terrain Studio (Pinseldrehung, Drehwinkel von Bäumen und Dekor, Richtung von Startpositionen und Markern) folgen derselben Konvention: Grad, im Uhrzeigersinn von oben gesehen, wobei 0° entlang +Z (Norden) und 90° entlang +X (Osten) zeigt. Deshalb werden Symmetriekopien eines gedrehten Pinsels oder Objekts genau wie ihre Positionen gedreht und gespiegelt. Ist Random Rotation eingeschaltet, wird pro Tupfer ein Zufallswinkel bestimmt, den alle seine Symmetriekopien teilen. So bleiben symmetrische Pinselstriche symmetrisch.

Eigene Pinselformen

Ein Asset vom Typ Brush Shape macht aus jeder Graustufentextur einen Pinselstempel: Weiße Bereiche wirken voll, schwarze gar nicht.

  1. Importiere eine Graustufentextur, zum Beispiel den Umriss eines Felsens oder einen Spritzer. Sie muss nicht lesbar sein.
  2. Erstelle das Asset mit Assets ▸ Create ▸ RTS Terrain Studio ▸ Brush Shape und weise die Textur zu.
  3. Prüfe die Vorschau im Inspector. Sie zeigt die gebackene Maske genau so, wie die Pinsel sie verwenden, dazu ihre Abdeckung.
  4. Klicke im Panel Brush auf die Formschaltfläche Custom. Das Panel zeigt nun jedes Asset vom Typ Brush Shape als Kachel; klicke auf die Kachel der neuen Form.
EinstellungStandardBeschreibung
TexturekeineGraustufen- oder Alpha-Textur. Weiß = volle Wirkung.
Use Red ChannelanLiest den Rotkanal. Schalte die Option aus, um stattdessen den Alphakanal zu lesen.
InvertausKehrt die Maske um.
Resolution64Auflösung der gebackenen Maske (16–256).

Die Maske wird nach jeder Änderung automatisch gebacken. Bake Mask backt sie sofort. Ein eigener Stempel deckt das Quadrat um die Pinselmitte ab und wird trotzdem noch mit der Härte bzw. der Abnahmekurve multipliziert. Verringere die Härte für weiche Stempelränder, oder setze sie auf 1, um den Stempel unverändert zu verwenden.

Symmetrie

Symmetrie wiederholt jeden Tupfer, jeden Klick und jedes platzierte Objekt an gespiegelten oder gedrehten Positionen. So baust du faire Wettkampfkarten: Jeder Spieler bekommt dieselben Hügel, Klippen, Rampen, Bäume und Goldminen. Wähle den Modus im Panel Symmetry oder im Overlay. Magentafarbene Hilfslinien in der Scene View zeigen die Achsen und das Zentrum.

Symmetrie gilt für Pinsel- und Klickwerkzeuge. Ziehwerkzeuge (Regions und Select Decor) ignorieren sie, und das Fenster blendet das Panel Symmetry aus, solange eines davon aktiv ist. Gespiegelte Regionen legst du von Hand an.

Name im FensterName im Overlay / im CodeKopienFunktionVerwendung
OffNone1Nur der Pinsel selbst malt.Frei gestaltete Karten, Kampagnenkarten, Details, die sich nicht wiederholen sollen.
Mirror XMirror X (MirrorX)2Spiegelt links ↔ rechts an der senkrechten Achse durch das Zentrum.Zwei Spieler oder Teams, die sich West gegen Ost gegenüberstehen.
Mirror ZMirror Z (MirrorZ)2Spiegelt oben ↔ unten an der waagerechten Achse.Zwei Spieler oder Teams, die sich Nord gegen Süd gegenüberstehen.
Mirror XZMirror XZ (MirrorXZ)4Spiegelt an beiden Achsen.Vier Basen in den Ecken oder 2v2-Karten, auf denen jedes Team eine Seite hält.
Rotate 180°Rotate 2 (Rotate2)2Punktsymmetrie um das Zentrum.Das klassische 1v1-Layout mit Basen in gegenüberliegenden Ecken. Anders als eine Spiegelung lässt eine Drehung links und rechts für beide Spieler gleich: Ein Pfad zur Linken des einen Spielers liegt auch zur Linken des anderen.
Rotate 90°Rotate 4 (Rotate4)4Rotationssymmetrie in Vierteldrehungen. Needs a square map.Das klassische Jeder-gegen-jeden mit vier Spielern.
DiagonalMirror Diagonal (MirrorDiagonal)2Spiegelt an der Diagonalen (x ↔ z). Needs a square map.1v1-Karten mit Basen in der oberen linken und der unteren rechten Ecke.

Centre ist das Symmetriezentrum in normierten Kartenkoordinaten: (0.5, 0.5) ist die Kartenmitte und der Standardwert. Reset setzt es dorthin zurück. Wenn du das Zentrum verschiebst, können einige Kopien außerhalb der Karte landen und werden dann übersprungen; das Panel zeigt eine Warnung.

Rotate 90° und Diagonal vertauschen die X- und die Z-Achse. Auf einer nicht quadratischen Karte landen Kopien außerhalb der Karte oder fallen ungleichmäßig aus. Das Panel Symmetry, das Overlay und das Status-Panel warnen dich. Ändere die Kartengröße auf ein quadratisches Format oder wähle einen anderen Modus.

Wie sich Kopien verhalten

  • Kopien, die näher als eine halbe Zelle (oder ein Zehntel des Pinselradius, je nachdem, was größer ist) an einer anderen Kopie liegen, werden zusammengeführt. Malen auf einer Symmetrieachse wendet die Wirkung daher nie doppelt an.
  • Platzierte Bäume, Dekorobjekte, Startpositionen und Marker werden mit ihrer Kopie gedreht. Gespiegelte Kopien erhalten gespiegelte Richtungen.
  • Gedrehte und gespiegelte Pinsel und eigene Stempel drehen sich mit ihrer Kopie (siehe Konvention für Drehungen).
  • Start Locations: Ist Symmetry Assigns Next Players eingeschaltet (Standard), platziert ein Klick die Startposition des ausgewählten Spielers, und seine Kopien platzieren die der folgenden Spieler. Mit Rotate 90° und ausgewähltem Spieler 1 platziert ein Klick die Startpositionen der Spieler 1 bis 4.
  • Raise / Lower Level berechnet seine Zielstufe aus dem ursprünglichen Tupfer, sodass jede Kopie dieselbe Stufe malt.
  • Fill / Replace wirkt in den Modi Replace Dominant Layer und Fill All auf die ganze Karte und läuft nur einmal. Fill Connected füllt von jeder Kopie aus.

Baue die Grundform der Karte (Klippen, Rampen, Wasser, Startpositionen) mit eingeschalteter Symmetrie und schalte sie dann für kleine Dekorationen aus, damit die Karte nicht wie kopiert und eingefügt wirkt. Mit dem Pathing-Overlay (P) prüfst du schnell, ob jede Basis dieselben Wege hat.

Rückgängig und Wiederherstellen

Jede Terrain-Bearbeitung wird im eigenen Undo-Verlauf der Karte aufgezeichnet, der mit Unitys Undo-System verknüpft ist. Ctrl+Z, Ctrl+Y und das Menü Edit ▸ Undo funktionieren für Terrain-Bearbeitungen genau wie für jede andere Änderung, und das Edit-Menü zeigt den Namen des Schritts an, zum Beispiel Undo Raise / Lower. Die Schaltflächen Undo und Redo in der Kopfzeile des Fensters tun dasselbe und halten Unitys Verlauf synchron. Machst du zwischendurch eine Änderung an einem anderen Objekt rückgängig, bringt das den Terrain-Verlauf nicht durcheinander.

AktionUndo-Schritt
Ein Pinselstrich (vom Drücken bis zum Loslassen) oder ein KlickEin Schritt, benannt nach dem Werkzeug, zum Beispiel Paint Layer. Alle Symmetriekopien gehören zum selben Schritt.
Ziehen eines Dekor-HandlesEin Schritt (Move Decor, Rotate Decor, Scale Decor).
Schaltflächen im Panel Selection (Delete, Duplicate, Lock …)Ein Schritt pro Klick.
Generate, Heightmap-Import, Auto Paint Whole Map, Capture From SceneJe ein Schritt (Generate Map, Import Heightmap, Auto Paint Map …).
Felder für Karteninfo und UmgebungEin Schritt, sobald du das Feld verlässt (Edit Map Info, Edit Environment).
Mit Esc abgebrochener PinselstrichKein Schritt. Die Änderungen des Pinselstrichs werden zurückgenommen.
New Map, Resize, Open, Reload, Wechsel zu einem anderen TerrainNicht rückgängig zu machen. Diese Aktionen löschen den Verlauf (ihre Dialoge weisen darauf hin).

Grenzen und Speicher

Jeder Schritt speichert nur die Schichten, die er geändert hat. Rasterschichten (Höhen, Klippenstufen, Wasser, Splatmaps, Details, Pathing-Overrides) werden in Kacheln von 16 × 16 Zellen rund um den bearbeiteten Bereich gespeichert. Objektschichten (Bäume, Dekor, Regionen, Startpositionen, Marker, Karteninfo, Umgebung) werden als Kopie der gesamten Liste gespeichert. Ein einzelner Pinselstrich mit einem Baumwerkzeug auf einer Karte mit sehr vielen Bäumen kostet daher Speicher im Verhältnis zur Gesamtzahl der Bäume auf der Karte. Ein kleiner Pinselstrich mit einem Höhen- oder Malwerkzeug kostet also einige Kilobyte, während das Generieren einer ganzen Karte jede Schicht der Karte speichert. Die Statusleiste und das Panel Undo History zeigen den aktuell belegten Speicher.

Im Tab Settings legst du zwei Grenzen fest. Wird eine davon überschritten, werden zuerst die ältesten Schritte verworfen; der neueste Schritt bleibt immer erhalten.

EinstellungBereichStandardBeschreibung
Max Steps10–1000100Anzahl der Undo-Schritte, die behalten werden.
Memory Limit (MB)32–4096512Speicher, den der Undo-Verlauf belegen darf.

Die Grenzen werden pro Benutzer in den Editor-Einstellungen gespeichert und gelten auch, wenn das Fenster geschlossen ist. Clear History löscht alle Undo- und Redo-Schritte des Terrains, zum Beispiel um nach einer großen Generierung Speicher freizugeben.

Rückgängig machbare Bearbeitungen aus eigenen Editor-Skripten

RTSEditSession.RunOperation zeichnet die angegebenen Schichten der gesamten Karte auf, führt deinen Code aus und legt das Ergebnis als einen einzigen Schritt im Undo-Verlauf ab. Wie bei jedem Werkzeug lässt er sich dann mit Ctrl+Z rückgängig machen.

using RTSTerrainStudio;
using RTSTerrainStudio.Editor;
using UnityEditor;

static class MyTerrainCommands
{
    [MenuItem("Tools/My Project/Raise Whole Map 1 m")]
    static void RaiseWholeMap()
    {
        // The terrain edited by the RTS Terrain Studio window and the Scene View tools.
        RTSTerrain terrain = RTSEditorContext.Terrain;
        if (terrain == null || terrain.Map == null) return;

        RTSEditSession session = RTSEditorContext.Session;

        // Records the Heights layer, runs the code and pushes one undo step named "Raise Whole Map".
        session.RunOperation("Raise Whole Map", MapLayers.Heights, ctx =>
        {
            var heights = ctx.Map.Heights; // fine height per vertex, on top of the cliff level
            for (int i = 0; i < heights.Length; i++)
                heights[i] = TerrainToolUtility.ClampFineHeight(ctx.Map, heights[i] + 1f);

            // Tell the terrain which layers changed so meshes, water and pathing are rebuilt.
            ctx.Terrain.NotifyChangedAll(MapLayers.Heights);
        });
    }
}

Der In-Game-Editor verwendet denselben Typ für den Undo-Verlauf (MapUndoStack) und hat eigene Schaltflächen Undo und Redo. Die Anbindung an Unitys Ctrl+Z gibt es nur im Unity Editor.

Werkzeugreferenz

Die folgenden Abschnitte beschreiben alle Werkzeuge der acht Kategorien. Alle Werkzeuge funktionieren im Unity Editor und im In-Game-Editor gleich. Einige Konventionen gelten für alle:

  • ID ist die eindeutige Kennung des Werkzeugs. Du verwendest sie im Code, zum Beispiel RTSEditorContext.ActivateTool("height.raise").
  • Eingabe: Pinselwerkzeuge malen, während du ziehst. Klickwerkzeuge wirken einmal pro Klick. Ziehwerkzeuge arbeiten mit Drücken, Ziehen und Loslassen (Rechtecke, Rechteckauswahl).
  • Wiederholungen: Tupfer pro Sekunde, während du die Maustaste gedrückt hältst, ohne die Maus zu bewegen.
  • Shift kehrt die Wirkung um und Ctrl+Klick nimmt einen Wert auf, sofern die Tabelle das angibt.
  • Jede Einstellung ist mit Bereich und Standardwert aufgeführt. Die Einstellungen bleiben über Editorsitzungen hinweg erhalten. Reset All Tool Settings im Tab Settings stellt die Standardwerte wieder her.

Klippenstufen und Feinhöhe

Die Bodenhöhe jedes Vertex besteht aus zwei Teilen. Die Klippenstufe (0–15) wird mit den Klippenwerkzeugen gesetzt und erzeugt senkrechte Klippenwände; eine Stufe ist Cliff Level Height Meter hoch (standardmäßig 2 m). Die Feinhöhe wird mit den Höhenwerkzeugen gesetzt und fügt über der Stufe sanfte Hügel und Senken hinzu. Feinhöhen sind auf ±8 Klippenstufen begrenzt (±16 m bei Stufen von 2 m). Gerade weil beides getrennt bleibt, kannst du scharfe Klippen im Stil klassischer RTS-Spiele auf welligen Boden malen.

Eigene Werkzeuge hinzufügen

Jede Klasse, die von RTSTool (oder einer der Kategorie-Basisklassen wie HeightTool) abgeleitet ist und einen öffentlichen, parameterlosen Konstruktor hat, erscheint automatisch im Fenster, im Overlay und im In-Game-Editor. Öffentliche Felder, die mit [ToolSetting] markiert sind, erhalten Bedienelemente. Dieses Beispiel hebt oder senkt den Boden pro Klick um einen festen Wert:

using RTSTerrainStudio;

// Appears on the Height tab after the built-in tools (Order 100).
public sealed class StepTool : HeightTool
{
    public override string Id => "mygame.step";
    public override string DisplayName => "Step";
    public override string Description => "Raises the ground under the brush by a fixed step per click.";
    public override string Hint => "Shift: step down";
    public override int Order => 100;

    // One dab per click; keep the brush panel visible for the radius and falloff.
    public override ToolInputMode InputMode => ToolInputMode.Click;
    public override bool UsesBrush => true;

    [ToolSetting("Step", Min = 0.05f, Max = 4f, Order = 0, Tooltip = "Height change per click in metres at full strength.")]
    public float step = 0.5f;

    // Called once per click and once for every symmetry copy.
    public override void ApplyDab(RTSToolContext ctx, in BrushDab dab)
    {
        var map = ctx.Map;
        // Computes the vertex weights and records the Heights layer for undo.
        bool any = BeginVertexEdit(ctx, dab, out var w, out var cells);
        using (w) // the weights must always be disposed
        {
            if (!any) return;
            float sign = dab.invert ? -1f : 1f; // Shift inverts
            var heights = map.Heights;
            for (int z = w.rect.yMin; z < w.rect.yMax; z++)
            for (int x = w.rect.xMin; x < w.rect.xMax; x++)
            {
                float weight = w[x, z];
                if (weight <= 0f) continue;
                int i = map.VertexIndex(x, z);
                heights[i] = TerrainToolUtility.ClampFineHeight(map, heights[i] + sign * step * dab.strength * weight);
            }
        }
        // Rebuilds the chunks, water and pathing of the changed cells.
        EndVertexEdit(ctx, cells);
    }
}

Die vollständige Werkzeug-API (Ziehwerkzeuge, Aufnehmen von Werten, Gizmos und Statustext) findest du unter Scripting. Bei Builds mit hohem Managed Stripping Level markiere dein Werkzeug mit [UnityEngine.Scripting.Preserve] (siehe Code-Stripping und eigene Werkzeuge).

Höhenwerkzeuge

Höhenwerkzeuge formen die Feinhöhe: Hügel, Täler, Straßen und natürliche Details über den Klippenstufen. Sie ändern nie die Klippenstufen, daher bleiben Klippenwände scharf. Die Statuszeile zeigt Bodenhöhe, Feinhöhe und Klippenstufe unter dem Cursor.

WerkzeugIDFunktionEingabe und Zusatztasten
Raise / Lowerheight.raiseHebt den Boden unter dem Pinsel an; halte die Maustaste gedrückt, um Hügel immer weiter aufzubauen.Pinsel, 30 Wiederholungen/s. Shift: absenken.
Smoothheight.smoothGlättet Unebenheiten und scharfe Kanten des Bodens, ohne Klippenwände zu verwischen.Pinsel, 20 Wiederholungen/s.
Flattenheight.flattenEbnet den Boden auf eine einheitliche Höhe ein, ideal für Bauplätze und Straßen.Pinsel, 20 Wiederholungen/s. Ctrl+Klick: Höhe aufnehmen.
Set Heightheight.setSetzt den Boden auf eine exakte Höhe; bei voller Stärke erreicht die Pinselmitte sie mit einem einzigen Tupfer.Pinsel, 20 Wiederholungen/s. Ctrl+Klick: Höhe aufnehmen.
Noiseheight.noiseFügt mit Seed-gesteuertem prozeduralem Rauschen natürlich wirkende Unebenheiten, Hügel oder Grate hinzu.Pinsel, 10 Wiederholungen/s. Shift: Rauschen abziehen.
Terraceheight.terraceVerwandelt Hänge in flache Terrassen mit abgerundeten Kanten, wie bei Reisfeldern oder geschichtetem Fels.Pinsel, 15 Wiederholungen/s.
Erodeheight.erodeSimuliert Material, das steile Hänge hinabrutscht, und verleiht Hügeln ein natürliches, verwittertes Aussehen.Pinsel, 15 Wiederholungen/s.

Einstellungen von Raise / Lower

EinstellungBereichStandardBeschreibung
Speed0.01–20.15Höhenänderung pro Tupfer in Metern bei voller Pinselstärke.

Einstellungen von Smooth

EinstellungBereichStandardBeschreibung
Filter Radius1–41Größe der Nachbarschaft in Vertices, über die gemittelt wird. Größere Werte entfernen größere Strukturen.
Iterations1–82Glättungsdurchläufe pro Tupfer.

Einstellungen von Flatten

EinstellungBereichStandardBeschreibung
TargetStroke Start, Fixed HeightStroke StartStroke Start verwendet die Höhe unter dem Cursor zu Beginn des Pinselstrichs. Fixed Height verwendet Target Height.
Target Height−32–32 m0Feste Feinhöhe relativ zur Klippenstufe. Ctrl+Klick nimmt sie auf. Werte jenseits der Feinhöhengrenze der Karte (±8 Klippenstufen, ±16 m bei Stufen von 2 m) werden beim Anwenden begrenzt.
ModeFlatten, Raise Only, Lower OnlyFlattenIn beide Richtungen einebnen, nur Boden unterhalb des Zielwerts auffüllen oder nur Boden oberhalb davon abtragen.

Einstellungen von Set Height

EinstellungBereichStandardBeschreibung
Height−32–64 m0Zu setzende Höhe; wie sie interpretiert wird, legt Height Space fest. Ctrl+Klick nimmt sie auf. Werte jenseits der Feinhöhengrenze der Karte (±8 Klippenstufen, ±16 m bei Stufen von 2 m) werden beim Anwenden begrenzt.
Height SpaceFine Height, Ground HeightFine HeightFeinhöhe relativ zur Klippenstufe oder absolute Bodenhöhe.

Einstellungen von Noise

EinstellungBereichStandardBeschreibung
Noise TypeFbm, Ridged, BillowFbmFbm: sanft gewellte Hügel. Ridged: scharfe Grate. Billow: rundliche Buckel.
Scale2–256 m24Größe der größten Strukturen.
Amplitude0.01–4 m0.25Pro Tupfer hinzugefügte Höhe bei voller Stärke.
Octaves1–84Anzahl der Detailebenen. Mehr Oktaven fügen feinere Details hinzu.
Roughness0.2–0.80.5Amplitudenverhältnis zwischen den Oktaven. Höhere Werte ergeben raueren Boden.
Seed0–99991Ändert das Muster. Derselbe Seed erzeugt immer dasselbe Muster.

Einstellungen von Terrace

EinstellungBereichStandardBeschreibung
Step Height0.1–8 m1Höhe einer Terrassenstufe.
Sharpness0–10.70 = weiche, abgerundete Terrassenstufen. 1 = scharfe, steile Absätze.
Offset−4–4 m0Verschiebt alle Terrassenstufen nach oben oder unten.

Einstellungen von Erode

EinstellungBereichStandardBeschreibung
Talus Angle5–70°30Hänge, die steiler als dieser Winkel sind, erodieren.
Erosion Strength0.05–10.5Anteil des überschüssigen Materials, der pro Durchlauf bewegt wird.
Iterations1–508Erosionsdurchläufe pro Tupfer.

Für einen Bauplatz verwendest du Flatten mit Target = Stroke Start: Beginne den Pinselstrich an der Stelle, deren Höhe du übernehmen willst, und male dann über die Fläche. Für eine Straße auf exakter Höhe nimmst du mit Ctrl+Klick die Höhe eines Punkts auf, stellst Target auf Fixed Height um und malst.

Klippenwerkzeuge

Klippenwerkzeuge ändern die Klippenstufe von Vertices (0–15) und das Aussehen der Klippenwände zwischen den Stufen. Der Pinsel rastet an Vertices ein und wirkt als harte Auswahl: Du malst immer ganze Vertices, nie halbe Stufen. Die Statuszeile zeigt die Stufe unter dem Cursor und die Stufe, die du gleich malst, zum Beispiel Level 2 → 3.

WerkzeugIDFunktionEingabe und Zusatztasten
Raise / Lower Levelcliff.raiseHebt den Boden um eine Klippenstufe an; ziehe, um das Plateau zu erweitern, auf dem du begonnen hast.Pinsel. Shift: um eine Stufe absenken.
Set Levelcliff.setMalt eine exakte Klippenstufe, nützlich für große Plateaus, Becken und schnelle Rohentwürfe.Pinsel. Ctrl+Klick: Stufe aufnehmen.
Rampcliff.rampVerwandelt Klippenkanten in begehbare Hänge; male quer über eine Klippe mit einer Stufe Höhenunterschied, sodass alle vier Ecken einer Zelle zur Rampe gehören.Pinsel. Shift: Rampe entfernen.
Cliff Typecliff.typeÄndert das Material der Klippenwände, zum Beispiel Felsklippen neben grasbewachsenen Klippen.Pinsel. Ctrl+Klick: Klippentyp aufnehmen.
Clean Cliffscliff.smoothEdgesEntfernt isolierte Säulen und Gruben aus einzelnen Vertices, damit die Klippenumrisse sauber aussehen.Pinsel.

Einstellungen von Raise / Lower Level

Die Zielstufe ergibt sich aus dem Vertex unter dem Cursor zu Beginn des Pinselstrichs: eine Stufe höher (oder mit Shift eine Stufe tiefer). Beim Ziehen wird genau diese Stufe gemalt, sodass du ein Plateau erweitern kannst, ohne Stufen übereinanderzustapeln. Das Werkzeug hebt Vertices nur an (bzw. senkt sie nur ab).

EinstellungBereichStandardBeschreibung
Break Rampsan/ausanEntfernt das Rampen-Flag von Vertices, deren Stufe sich ändert.
Paint Cliff Edgesan/ausanMalt am Ende des Pinselstrichs die Kantenschicht des Klippentyps entlang neuer Klippenkanten (siehe Automatische Klippenkanten).
Edge Width0.25–4 Zellen1Breite des gemalten Rands, gemessen von der Klippenwand aus.
Edge Strength0.1–10.9Wie stark die Kantenschicht den Boden direkt an der Wand bedeckt.
Edge SidesBoth / Top / BottomBothMalt den Plateaurand, den Klippenfuß oder beides.
Clean Up Old Edgesan/ausanEntfernt die Kantenschicht dort, wo nach dem Pinselstrich keine Kante mehr existiert (verschobene Klippen, neue Rampen).

Einstellungen von Set Level

EinstellungBereichStandardBeschreibung
Level0–15 (Palette)2Zu malende Klippenstufe.
Break Rampsan/ausanEntfernt das Rampen-Flag von Vertices, deren Stufe sich ändert.
Kanteneinstellungenwie obenPaint Cliff Edges, Edge Width, Edge Strength, Edge Sides und Clean Up Old Edges funktionieren wie bei Raise / Lower Level. Ramp und Clean Cliffs haben dieselben fünf Einstellungen.

Ramp

Ramp hat keine Einstellungen. Eine Zelle wird zur Rampe, wenn alle vier Eckvertices mit dem Rampen-Flag markiert sind und die Zelle einen Höhenunterschied von genau einer Stufe überbrückt. Die Statuszeile hilft dabei: Sie zählt die gültigen Rampenzellen unter dem Pinsel, weist auf markierte Zellen ohne Stufenunterschied hin und warnt, wenn eine Rampe mehr als eine Stufe überbrückt (sie würde dann gestreckt).

Einstellungen von Cliff Type

EinstellungBereichStandardBeschreibung
Cliff Type0–3 (Palette)0Zu malende Klippenschicht des Tilesets.

Einstellungen von Clean Cliffs

EinstellungBereichStandardBeschreibung
Strictness5–88Wie viele der 8 Nachbarn die andere Stufe haben müssen, bevor ein Vertex sie übernimmt. Bei 8 werden nur vollständig isolierte Vertices entfernt; niedrigere Werte runden auch gezackte Ecken ab. Vertices am Kartenrand haben weniger Nachbarn und brauchen davon denselben Anteil.
Break Rampsan/ausanEntfernt das Rampen-Flag von Vertices, deren Stufe sich ändert.

Automatische Klippenkanten

In klassischen RTS-Karteneditoren verändert das Anheben einer Klippe auch den Boden daneben. Die Klippenwerkzeuge machen es genauso: Am Ende eines Pinselstrichs wird der Boden entlang jeder neuen Klippenkante mit der Kanten-Bodenschicht des Klippentyps bemalt, zum Beispiel mit einem steinigen Rand aus „Rocky Dirt“. Der Rand folgt der abgerundeten Klippenkontur, läuft über die mit Edge Width eingestellte Breite aus und hat einen leicht unregelmäßigen Umriss. So wirkt er natürlich statt wie aus Zellen zusammengesetzt.

  1. Wähle das Tileset aus und öffne den Bereich Cliff Layers. Wähle auf jedem Klippen-Panel unter Edge Layer eine Schicht aus (die Demo-Tilesets verwenden Rocky Dirt).
  2. Wähle Raise / Lower Level, Set Level, Ramp oder Clean Cliffs und lasse Paint Cliff Edges eingeschaltet.
  3. Male Klippen wie gewohnt. Der Rand erscheint, sobald du die Maustaste loslässt.
  • Ein Undo-Schritt: Stufenänderung und Kantenbemalung werden gemeinsam rückgängig gemacht.
  • Symmetrie: Alle Symmetriekopien erhalten ihre Kanten im selben Durchgang.
  • Rampen: Rampenzellen erhalten nie einen Rand. Wenn du eine Rampe über eine Kante malst, wird der Rand dort entfernt, sofern Clean Up Old Edges eingeschaltet ist.
  • Verschobene Klippen: Ist Clean Up Old Edges eingeschaltet, wird der Rand einer verschwundenen Kante (abgesenktes oder erweitertes Plateau) wieder entfernt. Dabei wird die Kantenschicht auch dann entfernt, wenn du sie dort von Hand gemalt hast.
  • Optional: Schalte Paint Cliff Edges für ein Werkzeug aus oder setze für das ganze Tileset die Einstellung Edge Layer des Klippentyps auf None. Ist die Option eingeschaltet, aber keine Kantenschicht festgelegt, weist die Statuszeile darauf hin.

Wenn du Klippenstufen im Code selbst änderst (zum Beispiel in einem eigenen Generator), rufe danach CliffEdgePainter.PaintEdges(map, tileset, cellRect, CliffEdgeOptions.Default) und anschließend terrain.NotifyChanged(MapLayers.Splat, returnedRect) auf.

Ein typischer Arbeitsablauf: Lege Plateaus grob mit Set Level an, verfeinere ihre Kanten mit Raise / Lower Level, fahre mit Clean Cliffs über die Umrisse und lege dann mit Ramp Rampen an, wo Einheiten hinauflaufen sollen. Prüfe das Ergebnis mit dem Pathing-Overlay (P).

Malwerkzeuge

Malwerkzeuge tragen die Bodenschichten des Tilesets auf (bis zu 16). Jedes Splat-Texel speichert die Gewichtungen aller Schichten, die zusammen immer 100 % ergeben. Fehlt die ausgewählte Schicht im Tileset oder hat sie keine Textur, weist die Statuszeile darauf hin, statt stillschweigend mit einem Ersatz zu malen.

WerkzeugIDFunktionEingabe und Zusatztasten
Paint Layerpaint.layerMalt eine Bodenschicht mit weichen Pinselrändern.Pinsel, 10 Wiederholungen/s. Shift: Schicht entfernen. Ctrl+Klick: Schicht unter dem Cursor aufnehmen.
Tile Paintpaint.tileMalt ganze Zellen mit einer Bodenschicht, wie in klassischen RTS-Karteneditoren. Jede Zelle, deren Mitte innerhalb des Pinselumrisses liegt, wird zu 100 % bemalt.Pinsel, rastet an Zellmitten ein. Shift: Schicht aus Zellen entfernen. Ctrl+Klick: aufnehmen.
Fill / Replacepaint.fillErsetzt mit einem Klick eine Bodenschicht überall, füllt eine zusammenhängende Fläche oder die ganze Karte.Klick. Ctrl+Klick: aufnehmen.
Smooth Paintpaint.smoothZeichnet Übergänge zwischen Bodenschichten unter dem Pinsel weich und sorgt so für sanftere Überblendungen.Pinsel, 15 Wiederholungen/s.
Auto Paintpaint.autoMalt Bodenschichten anhand von Höhe, Neigung, Klippen und Wasser mithilfe eines Auto-Paint-Profils.Pinsel, 8 Wiederholungen/s. Shift: Basisschicht des Profils malen.

Einstellungen von Paint Layer

EinstellungBereichStandardBeschreibung
LayerPalette0Zu malende Bodenschicht des Tilesets.
Opacity0.05–11Höchste Deckung, die die Schicht erreichen kann. Niedrigere Werte überblenden die Schicht mit dem darunterliegenden Boden.

Einstellungen von Tile Paint

EinstellungBereichStandardBeschreibung
LayerPalette0Zu malende Bodenschicht des Tilesets.
Soft Borderan/ausausÜberblendet ein Texel rund um die bemalten Zellen für etwas weichere Kanten.

Einstellungen von Fill / Replace

EinstellungBereichStandardBeschreibung
LayerPalette0Bodenschicht, mit der die Fläche gefüllt wird.
ModeReplace Dominant Layer, Fill Connected, Fill AllFill ConnectedReplace Dominant Layer: jedes Texel der Karte, dessen dominante Schicht die angeklickte Schicht ist. Fill Connected: Flächenfüllung der zusammenhängenden Texel, deren dominante Schicht die angeklickte ist. Fill All: die ganze Karte.
Preserve Blendsan/ausanTauscht nur die angeklickte Schicht gegen die neue aus und erhält Überblendungen mit anderen Schichten (Replace und Flood Fill).

Jeder Klick ist ein Undo-Schritt, der nur den tatsächlich geänderten Bereich aufzeichnet.

Einstellungen von Smooth Paint

EinstellungBereichStandardBeschreibung
Blur Radius1–31Größe des Weichzeichnungskernels in Splat-Texeln. Größere Werte glätten schneller.

Einstellungen von Auto Paint

EinstellungStandardBeschreibung
ProfilekeineDas Asset vom Typ Auto Paint Profile, mit dem der Pinsel malt. Leer = integrierte Regeln, die anhand der Schichtnamen des Tilesets zugeordnet werden. Siehe Auto Paint.

Der Tab Paint zeigt außerdem das Panel Auto Paint Whole Map, das die gesamte Karte in einem Schritt anhand des Profils neu bemalt.

Vegetationswerkzeuge

Vegetationswerkzeuge malen Gras und Kleinpflanzen (Details) und platzieren Bäume aus der Vegetation Library des Terrains. Hat das Terrain keine, bietet die Werkzeugseite die Schaltfläche Create Vegetation Library an. Wähle Prototypen im Raster aus; ist keiner ausgewählt, wird der erste Prototyp der Bibliothek verwendet.

WerkzeugIDFunktionEingabe und Zusatztasten
Paint Detailsveg.detailsMalt Gras, Blumen und andere Kleinpflanzen nach Dichte.Pinsel, 10 Wiederholungen/s. Shift: ausgewählte Details entfernen.
Erase Detailsveg.detailsEraseEntfernt Gras und andere Detailvegetation unter dem Pinsel.Pinsel, 10 Wiederholungen/s.
Place Treesveg.treesVerteilt Bäume mit dem Pinsel oder platziert einzelne Bäume per Klick.Scatter: Pinsel, Shift entfernt Bäume. Single: Klick, Shift+Klick entfernt den nächstgelegenen Baum.
Erase Treesveg.treesEraseEntfernt Bäume unter dem Pinsel. Bei geringerer Stärke werden nur Bäume nahe der Pinselmitte entfernt.Pinsel.

Einstellungen von Paint Details

EinstellungBereichStandardBeschreibung
DetailsPrototyp-RasterkeineZu malende Detail-Prototypen.
Target Density0–10.6Dichte, auf die der Pinsel hinarbeitet (1 = maximale Dichte des Prototyps).
ModeRaise, SetRaiseRaise erhöht nur die Dichte. Set dünnt zusätzlich dichtere Bereiche auf den Zielwert aus.

Einstellungen von Erase Details

EinstellungBereichStandardBeschreibung
Only Selectedan/ausausEntfernt nur die ausgewählten Detail-Prototypen statt aller Details.
DetailsPrototyp-RasterkeineZu entfernende Detail-Prototypen, wenn Only Selected eingeschaltet ist.

Einstellungen von Place Trees

EinstellungBereichStandardBeschreibung
TreesPrototyp-RasterkeineBaum-Prototypen, die zufällig platziert werden.
ModeScatter, SingleScatterScatter malt Bäume mit dem Pinsel. Single platziert einen Baum pro Klick und zeigt eine halbtransparente Vorschau.
Density0.05–50.6Bäume pro 100 m², die jeder Tupfer bei voller Stärke erzeugt. Der Mindestabstand der Prototypen gilt weiterhin.
Max Slope0–90°35Auf steileren Hängen werden keine Bäume verteilt.
Avoid Wateran/ausanVerteilt keine Bäume unter Wasser.

Einstellungen von Erase Trees

EinstellungBereichStandardBeschreibung
Only Selectedan/ausausEntfernt nur Bäume der ausgewählten Prototypen.
TreesPrototyp-RasterkeineZu entfernende Baum-Prototypen, wenn Only Selected eingeschaltet ist.

Dekorwerkzeuge

Mit den Dekorwerkzeugen platzierst, löschst und bearbeitest du Props wie Felsen, Ruinen und Zäune aus der Decor Library des Terrains. Hat das Terrain keine, bietet die Werkzeugseite die Schaltfläche Create Decor Library an. Das Prototyp-Raster hat ein Suchfeld und Kategoriefilter.

WerkzeugIDFunktionEingabe und Zusatztasten
Place Decordecor.placePlatziert Props, Felsen und andere Dekorobjekte einzeln oder mit dem Pinsel verstreut.Single: Klick, Shift+Klick entfernt das nächstgelegene Objekt. Scatter: Pinsel, Shift entfernt Dekor unter dem Pinsel.
Erase Decordecor.eraseEntfernt Dekorobjekte unter dem Pinsel. Gesperrte Objekte bleiben erhalten. Bei geringerer Stärke wird nur nahe der Pinselmitte gelöscht.Pinsel.
Select Decordecor.selectWählt Dekorobjekte per Klick oder durch Aufziehen eines Rechtecks aus, um sie zu verschieben, zu drehen, zu skalieren oder zu löschen.Ziehen. Klick: auswählen. Ziehen: Rechteckauswahl. Shift: hinzufügen / umschalten.

Einstellungen von Place Decor

EinstellungBereichStandardBeschreibung
ModeSingle, ScatterSingleSingle platziert pro Klick ein Objekt mit halbtransparenter Vorschau. Scatter malt Objekte mit dem Pinsel.
DecorPrototyp-RasterkeineDekor-Prototypen, die zufällig platziert werden. Ist keiner ausgewählt, wird der erste der Bibliothek verwendet.
Density0.05–50.4Scatter: Objekte pro 100 m², die jeder Tupfer bei voller Stärke spawnt.
Use Prototype Defaultsan/ausanÜbernimmt zufälligen Drehwinkel, Ausrichtung am Boden und Einrasten am Raster vom Prototyp.
Random Yawan/ausanZufällige Drehung um die Hochachse (wenn die Standardwerte des Prototyps nicht verwendet werden).
Yaw0–360°0Drehung um die Hochachse, wenn Random Yaw ausgeschaltet ist.
Scale0.1–51Faktor, der auf die zufällige Skalierung des Prototyps angewendet wird.
Align To Ground0–100 = aufrecht, 1 = folgt der Neigung des Bodens (wenn die Standardwerte des Prototyps nicht verwendet werden).
Snap To Gridan/ausausPlatziert Objekte in den Zellmitten.
Random Variantan/ausanVerwendet eine zufällige Prefab-Variante des Prototyps.
Max Slope0–90°90Scatter: Lässt Hänge aus, die steiler als dieser Wert sind. 90 = keine Begrenzung.
Avoid Wateran/ausausScatter: Platziert keine Objekte unter Wasser.

Einstellungen von Erase Decor

EinstellungBereichStandardBeschreibung
Only Selectedan/ausausEntfernt nur Objekte der ausgewählten Prototypen.
DecorPrototyp-RasterkeineDekor-Prototypen, die entfernt werden, wenn Only Selected eingeschaltet ist.

Einstellungen von Select Decor

EinstellungBereichStandardBeschreibung
Pick Radius0.25–10 m1Mindestabstand, innerhalb dessen ein Klick ein Objekt noch auswählt (größere Objekte werden innerhalb ihrer Grundfläche ausgewählt).
Select Lockedan/ausausErlaubt das Auswählen gesperrter Objekte, etwa um sie zu entsperren.

Panel Selection

Während Select Decor aktiv ist, zeigt der Tab Decor das Panel Selection mit der Anzahl der ausgewählten Objekte. Jede Bearbeitungsschaltfläche (Delete, Duplicate, Snap to Ground, Rotate 90°, Lock, Unlock) erzeugt genau einen Undo-Schritt. Select All und Clear ändern nur die Auswahl.

SchaltflächeAktion
Select AllWählt alle Dekorobjekte der Karte aus (gesperrte Objekte werden übersprungen).
ClearHebt die Auswahl auf.
DeleteLöscht die ausgewählten Objekte.
DuplicateDupliziert die Auswahl, diagonal um eine Zelle versetzt (+X und +Z). Die Kopien werden zur neuen Auswahl und sind entsperrt.
Snap to GroundSetzt die ausgewählten Objekte wieder auf den Boden.
Rotate 90°Dreht die Auswahl um 90° im Uhrzeigersinn um ihren Mittelpunkt.
Lock / UnlockGesperrte Objekte können von den Werkzeugen nicht mehr ausgewählt oder gelöscht werden. Schalte Select Locked ein, um sie auszuwählen und zu entsperren. Auch wenn sie auf diese Weise ausgewählt sind, bleiben gesperrte Objekte von Delete und den Handles unberührt.

Das Verschieben, Drehen und Skalieren mit Handles ist unter Handles der Dekorauswahl beschrieben.

Wasserwerkzeuge

Wasser wird pro Vertex gespeichert: Ein Vertex ist entweder nass – dann hat er eine Höhe für die Wasseroberfläche – oder trocken. Wie die Klippenwerkzeuge arbeiten die Wasserwerkzeuge mit einer harten Auswahl. Das Panel Water at Cursor zeigt Wasserstand und Wassertiefe unter dem Mauszeiger und ob die Stelle als Flachwasser (begehbar) oder Tiefwasser gilt, gemessen an der Flachwassergrenze des Tilesets.

WerkzeugIDFunktionEingabe und Zusatztasten
Paint Waterwater.paintMalt Seen und Flüsse auf der Zielhöhe; Boden oberhalb der Wasseroberfläche bleibt trocken.Pinsel. Shift: Wasser löschen. Ctrl+Klick: Höhe aufnehmen.
Erase Waterwater.eraseEntfernt Wasser von den Vertices unter dem Pinsel.Pinsel.
Flood Fillwater.floodFüllt das Becken unter dem Cursor mit einem Klick bis zur Zielhöhe mit Wasser.Klick. Shift+Klick: zusammenhängendes Wasser ablassen. Ctrl+Klick: Höhe aufnehmen.

Einstellungen der Wasserhöhe (Paint Water und Flood Fill)

EinstellungBereichStandardBeschreibung
Water HeightCliff Level Plus Offset, Absolute Height, Picked HeightCliff Level Plus OffsetLegt fest, wie die Höhe der Wasseroberfläche bestimmt wird.
Level0–15 (Palette)1Klippenstufe, auf der die Wasseroberfläche basiert (Modus Cliff Level Plus Offset).
Offset−8–16 m0.6 StufenMeter über der Klippenstufe. Solange du den Wert nicht änderst, ergibt er sich automatisch aus 0.6 × Stufenhöhe der Karte (1.2 m bei 2 m hohen Stufen).
Absolute Height−16–64 m3.2Höhe der Wasseroberfläche in kartenlokalen Metern (Modus Absolute Height).
Picked Height−16–64 m3.2Mit Ctrl+Klick aufgenommene Höhe (Modus Picked Height). Beim Aufnehmen wird die Höhe der Wasseroberfläche übernommen, wenn der Punkt nass ist, sonst die Bodenhöhe; außerdem schaltet der Modus auf Picked Height um.

Einstellungen von Paint Water

EinstellungBereichStandardBeschreibung
Forcean/ausausMarkiert auch Vertices, deren Boden über der Wasseroberfläche liegt, etwa für Wasserfälle oder Sümpfe.

Einstellungen von Flood Fill

EinstellungBereichStandardBeschreibung
Max Vertices100–1 000 00020 000Sicherheitsgrenze: Ein Füll- oder Ablassvorgang, der mehr Vertices erfassen würde, wird ohne Änderungen abgebrochen.

Der Standardwert (Stufe 1 + 0.6 Stufen) füllt Seen, die eine Stufe unter die Grundstufe 2 einer neuen Karte gegraben wurden. Grabe mit Raise / Lower Level und Shift ein Becken und klicke dann mit Flood Fill hinein.

Pathing-Werkzeuge

Die Pathing-Karte (begehbar, bebaubar, befliegbar, Wasser) wird automatisch aus Neigung, Klippen, Wasser, Bodenschichten, Bäumen und Dekor abgeleitet. Die Pathing-Werkzeuge malen manuelle Overrides darüber und verwenden dabei eine harte Auswahl in Pathing-Zellen. Schalte das Overlay im Panel Pathing Overlay oder mit P ein, um das Ergebnis zu sehen.

WerkzeugIDFunktionEingabe und Zusatztasten
Paint Pathingpathing.paintMalt unsichtbare Pathing-Regeln wie Blocker, Bauverbotszonen oder erzwungen begehbare Bereiche.Pinsel. Shift: ausgewählten Override entfernen.
Clear Pathingpathing.clearEntfernt alle gemalten Pathing-Overrides, sodass wieder das automatische Pathing gilt.Pinsel.

Einstellungen von Paint Pathing

EinstellungBereichStandardBeschreibung
Overridesiehe untenBlockerWelcher Pathing-Override gemalt wird.
OverrideWirkung
BlockerNicht begehbar und nicht bebaubar: eine unsichtbare Wand, typisch für Kartenränder und dekorative Bereiche.
UnwalkableBodeneinheiten können hier nicht laufen.
UnbuildableHier können keine Gebäude platziert werden, etwa um Wege und Expansionen freizuhalten.
UnflyableLufteinheiten können hier nicht fliegen.
Force WalkableBodeneinheiten können hier laufen, auch wenn Neigung, Wasser oder Grundflächen es blockieren würden.
Force BuildableGebäude können hier platziert werden, auch wenn die abgeleiteten Regeln es verbieten würden.

Farben des Pathing-Overlays

OverlayFarben
WalkabilityGrün = begehbar, Rot = blockiert, Dunkelrot = Klippe, Orange = zu steil, Blau = Tiefwasser, Cyan = Flachwasser.
BuildabilityGrün = bebaubar, Gelb = begehbar, aber nicht bebaubar, Rot / Dunkelrot = nicht begehbar.
FlyabilityHellblau = befliegbar, Magenta = nicht befliegbar.
WaterCyan = Flachwasser, Blau = Tiefwasser.
CombinedAlles auf einmal, wie die Pathing-Ansicht klassischer RTS-Karteneditoren.

Die Regeln für den automatischen Teil (maximale Neigungen, Klippenabstand usw.) stellst du in der Komponente RTS Terrain ein; siehe Pathing Rules.

Marker-Werkzeuge

Marker-Werkzeuge ergänzen die Karte um Gameplay-Informationen: wo die Spieler starten, benannte Regionen für Trigger und benannte Punkte wie Kamerapositionen. Marker werden in der Scene View gezeichnet, solange ein Marker-Werkzeug aktiv oder Show Markers eingeschaltet ist.

WerkzeugIDFunktionEingabe und Zusatztasten
Start Locationsmarkers.startPlatziert den Spawn-Punkt eines Spielers. Klicke erneut, um ihn zu verschieben.Klick. Shift+Klick: nächstgelegene Startposition entfernen.
Regionsmarkers.regionZiehe Rechtecke auf, um benannte Regionen für die Spiellogik anzulegen.Ziehen. Shift+Klick: Region unter dem Cursor löschen.
Markersmarkers.markerPlatziert benannte Punkte mit einer Richtung, etwa für Kameras oder Wegpunkte.Klick. Shift+Klick: nächstgelegenen Marker entfernen.

Einstellungen von Start Locations

EinstellungBereichStandardBeschreibung
PlayerPlayer 1–16 (Palette)Player 1 (Red)Spieler, dessen Startposition platziert wird. Jeder Spieler hat genau eine Startposition.
Yaw0–360°0Blickrichtung der Startposition.
Symmetry Assigns Next Playersan/ausanBei aktiver Symmetrie platzieren die Symmetriekopien die Startpositionen der folgenden Spieler. Ist die Option ausgeschaltet, platziert oder verschiebt nur der ursprüngliche Klick die Startposition.

Einstellungen von Regions

EinstellungBereichStandardBeschreibung
ColorFarbedurchscheinendes BlauFarbe neuer Regionen.
Snap To Cellsan/ausanRichtet die Grenzen von Regionen am Zellraster aus.

Neue Regionen erhalten automatisch einen eindeutigen Namen.

Einstellungen von Markers

EinstellungBereichStandardBeschreibung
NameTextMarkerName neuer Marker. Ist der Name bereits vergeben, wird eine Nummer angehängt.
TagTextleerFreitext, über den dein Spielcode Marker findet, zum Beispiel camera oder waypoint.
Yaw0–360°0Blickrichtung neuer Marker.

Auto Paint

Auto Paint malt die Bodenschichten anhand der Form des Terrains: Gras auf flachem Boden, Fels an steilen Hängen und entlang von Klippen, Sand am Ufer und durch Rauschen aufgelockerte Erdflecken. So lässt du eine frisch modellierte oder generierte Karte am schnellsten fertig aussehen. Anschließend kannst du von Hand nacharbeiten.

Es gibt drei Möglichkeiten, Auto Paint zu nutzen:

  • das Pinselwerkzeug Auto Paint im Tab Paint: Es malt die Regeln innerhalb des Pinsels und überblendet sie entsprechend Pinselgewichtung und Stärke mit der vorhandenen Texturierung. Mit Shift malt es stattdessen die Basisschicht und entfernt damit, was die Regeln gemalt haben;
  • das Panel Auto Paint Whole Map im Tab Paint: Es texturiert jedes Bodentexel der Karte in einem einzigen Undo-Schritt neu. Es hat ein Feld Profile und eine Schaltfläche Create Profile;
  • die Schaltfläche Apply To Map im Inspector des Profils sowie die Paint-Phase des Generators.

Ohne Profil werden eingebaute Standardregeln verwendet. Sie wählen ihre Schichten anhand der Namen der Bodenschichten im Tileset.

Auto-Paint-Profile

  1. Klicke im Tab Paint im Panel Auto Paint Whole Map auf Create Profile. Dadurch wird ein Profil mit den Standardregeln für das Tileset des Terrains erstellt und zugewiesen. Alternativ verwendest du Assets ▸ Create ▸ RTS Terrain Studio ▸ Auto Paint Profile.
  2. Wähle das Profil aus. Weise im Inspector ein Preview Tileset zu, damit die Regel-Panels Schichtnamen und Vorschaubilder anzeigen. Es dient nur der Anzeige; das Profil funktioniert mit jedem Tileset.
  3. Bearbeite die Regeln und klicke dann auf Apply To Map, um das ausgewählte oder bearbeitete Terrain neu zu texturieren. Gibt es mehrere Terrains und ist keines ausgewählt, wählst du eines in einem Menü aus.
  4. Klicke auf Use In Auto Paint Tool, damit das Pinselwerkzeug mit diesem Profil malt.
ProfileinstellungStandardBeschreibung
Base Layer0Schicht, die jedes Texel bedeckt, bevor die Regeln angewendet werden.
Start From ExistingausStartet mit den aktuellen Splat-Gewichtungen statt mit der Basisschicht. So bleibt von Hand Gemaltes erhalten, wo keine Regel zutrifft.
Rules–Werden von oben nach unten angewendet; spätere Regeln übermalen frühere. Jedes Regel-Panel hat einen Schalter zum Aktivieren, einen Namen, die zugehörige Schicht, ▲ / ▼ zum Umsortieren, Schaltflächen zum Duplizieren und Löschen sowie einen Ausklappbereich Conditions. Add Rule und Reset To Defaults befinden sich unter der Liste.

Für jedes Splat-Texel beginnt Auto Paint mit 100 % der Basisschicht (oder den vorhandenen Gewichtungen), wendet die aktivierten Regeln von oben nach unten an und normalisiert das Ergebnis. Jede aktivierte Bedingung einer Regel liefert eine Gewichtung zwischen 0 und 1, mit einem weichen Übergang an den Bereichsgrenzen. Die Gewichtungen werden multipliziert, durch Rauschen aufgelockert und mit der Stärke der Regel skaliert.

Regeleinstellungen

EinstellungStandardBeschreibung
NameRuleAnzeigename der Regel.
Layer0Bodenschicht (Tileset-Index), die diese Regel malt.
EnabledanDeaktivierte Regeln werden übersprungen.
Use Height, Min Height, Max Heightaus, 0, 20Malt nur dort, wo die kartenlokale Bodenhöhe (in Metern) im Bereich liegt.
Height Falloff0.5Weicher Übergang außerhalb des Höhenbereichs in Metern (0 = harte Kante).
Use Slope, Min Slope, Max Slopeaus, 0, 90Malt nur dort, wo die Neigung (in Grad) im Bereich liegt.
Slope Falloff5Weicher Übergang außerhalb des Neigungsbereichs in Grad.
Use Cliff Level, Min Cliff Level, Max Cliff Levelaus, 0, 15Malt nur auf diesen Klippenstufen (harte Kante).
Near Cliff Distance0Malt nur bis zu diesem Abstand (in Zellen) von einer Klippenwand (0 = aus). Läuft über die äußere Hälfte weich aus.
Near Water Distance0Malt nur bis zu diesem Abstand (in Zellen) vom Wasser (0 = aus). Läuft über die äußere Hälfte weich aus.
Noise Scale16Strukturgröße des Rauschens in Metern.
Noise Amount0Wie stark das Rauschen die Regel auflockert (0 = kein Rauschen, 1 = Flecken).
Noise Seed0Regeln mit unterschiedlichen Seeds erzeugen unterschiedliche Flecken.
Strength1Gesamtstärke der Regel (0–1).
Blend ModeReplaceWie die Regel mit den Regeln über ihr kombiniert wird (siehe unten).
Blend ModeWirkung
AddFügt die Schicht der Regel obenauf hinzu. Vorhandene Schichten werden beim Normalisieren des Ergebnisses anteilig verringert.
ReplaceÜberblendet jede Schicht entsprechend der Regelgewichtung in Richtung der Schicht der Regel. Die übliche Wahl.
MultiplyMultipliziert die vorhandene Gewichtung der Schicht der Regel mit der Regelmaske. Dadurch wird die Schicht dort entfernt, wo die Regel nicht zutrifft.

Standardregeln

Die eingebauten Regeln verwenden als Basisschicht die Schicht, deren Name grass, meadow oder green enthält, und suchen die Schichten für dirt, rock und sand über ähnliche Schlüsselwörter (ohne Tileset die Schichten 0–3).

RegelMaltBedingungen
Dirt PatchesErdeRauschflecken (Skalierung 14 m), Stärke 0.55.
Sand ShoreSandBis zu 3 Zellen vom Wasser entfernt, leicht durch Rauschen aufgelockert.
Rock SlopesFelsHänge ab 28° Neigung (Abnahme über 8°).
Rock Near CliffsFelsBis zu 1.6 Zellen von einer Klippenwand entfernt, durch Rauschen aufgelockert, Stärke 0.85.

Auto Paint per Code

using RTSTerrainStudio;
using UnityEngine;

// Repaints the whole map with a profile, for example after your own procedural edits.
public class RepaintWithProfile : MonoBehaviour
{
    public RTSTerrain terrain;
    public RTSAutoPaintProfile profile;

    void Start()
    {
        if (terrain == null || terrain.Map == null || profile == null) return;

        // Evaluates the rules for every splat texel inside the rectangle (here: the whole map).
        RTSAutoPaint.Apply(terrain.Map, profile, terrain.Map.CellBounds);

        // Let the terrain upload the new splat maps and update dependent systems (pathing ground rules).
        terrain.NotifyChangedAll(MapLayers.Splat);
        terrain.ProcessPendingChanges();
    }
}

Die Komponente RTS Terrain

Die Komponente RTS Terrain stellt eine Karte dar. Sie baut das Terrain-Mesh in Chunks auf, rendert Boden und Klippen mit dem Tileset, hält die Pathing-Karte aktuell und ermöglicht Abfragen wie Höhe, Wasser und Pathing an einer Weltposition. Erstelle sie über GameObject ▸ RTS Terrain Studio ▸ RTS Terrain oder füge sie mit Add Component ▸ RTS Terrain Studio ▸ RTS Terrain zu einem beliebigen GameObject hinzu.

Der Menüeintrag erstellt ein sofort bearbeitbares Terrain: ein GameObject RTS Terrain mit den Komponenten RTS Terrain, Vegetation Renderer, Water Renderer und Decor Manager, dem Standard-Tileset und den Standardbibliotheken des Projekts sowie einer neuen, flachen .rtsmap-Datei neben der aktiven Szene (oder in Assets/Maps). Hat das Projekt noch kein Tileset, wird dir angeboten, zuerst die Demo-Inhalte zu erstellen.

Inspector

BereichInhalt
KopfzeileKartenname, Weltgröße, Zellgröße und Karten-Asset, ein Badge Unsaved changes sowie Live-Statistiken: Cells, Chunks, Map Memory, Trees, Decor, Detail Layers, Regions, Start Locations und Markers.
AktionenOpen RTS Terrain Studio, Edit Terrain (schaltet die Bearbeitung in der Scene View für dieses Terrain ein oder aus), Save, Save As… und Reload.
WarnungenProbleme, wo möglich mit Korrektur per Klick: URP nicht aktiv, fehlende Shader, kein Tileset (mit einer Schaltfläche, die das Tileset verwendet, für das die Karte erstellt wurde), Textur-Arrays nicht gebaut oder veraltet (Build), eine Karte, die für ein anderes Tileset erstellt wurde, Vegetation oder Dekor in der Karte ohne Bibliothek, ein um X oder Z gedrehtes (Fix) oder skaliertes Terrain (Reset Scale) sowie fehlende Komponenten von Subsystemen (Add).
AssetsDie Felder Map, Tileset, Vegetation und Decor, jeweils mit einer Schaltfläche New, die ein neues Asset erstellt und zuweist.
Chunks & Geometry, Cliff Shape, Physics, RenderingDie Terrain-Einstellungen (Tabelle unten).
Pathing RulesWie die Pathing-Karte abgeleitet wird (Tabelle unten). Sie wird nach Änderungen automatisch neu aufgebaut.
Default MapGröße und Standardwerte der Karte, die erstellt wird, wenn kein Karten-Asset zugewiesen ist.
Rebuild TerrainZerstört alle Chunks, Texturen und die Pathing-Karte und erstellt sie neu; auch Vegetation, Wasser und Dekor werden neu aufgebaut. Verwende diese Schaltfläche, nachdem du Einstellungen per Code geändert hast oder wenn etwas veraltet aussieht.

Derselbe Inspector ist im Tab Settings des Fensters unter Terrain Component eingebettet. Das Zuweisen eines Tilesets und die Textur-Arrays werden unter Tilesets behandelt, Kartendateien unter Karten-Assets.

Das Terrain verwendet nur die Y-Rotation seines Transforms und ignoriert die Skalierung. Um eine Karte zu vergrößern, füge mit Resize (RTSTerrain.ResizeMap) Zellen hinzu oder erstelle eine neue Karte mit größerer Cell Size (MapCreationSettings.cellSize).

Terrain-Einstellungen

Diese Felder gehören zu RTSTerrainSettings (Eigenschaft RTSTerrain.Settings).

EinstellungStandardBeschreibung
Chunks & Geometry
Chunk Size32Zellen pro Chunk-Seite (16, 32 oder 64). Kleinere Chunks werden beim Bearbeiten schneller neu aufgebaut; größere Chunks bedeuten weniger Draw Calls.
Surface Subdivisions2Quads pro Zellseite auf glattem Boden (1–4). 1 = klassischer RTS-Look, 2–4 = weichere Hügel.
Cliff Subdivisions4Auflösung des Unterrasters, mit dem abgerundete Klippenkonturen innerhalb einer Klippenzelle nachgezeichnet werden (2–8).
Cliff Shape
Cliff Noise0.18Zufällige horizontale Verschiebung der Vertices von Klippenwänden, als Bruchteil einer Zelle (0–0.5). Sorgt für eine natürliche Felsoptik.
Cliff Noise Scale1.6Skalierung des Klippenrauschens in Welteinheiten (mindestens 0.1).
Cliff Slant0.12Wie weit die Oberkante einer Klippe zurückversetzt wird (als Bruchteil einer Zelle, 0–0.4), damit die Wände leicht schräg stehen.
Cliff Wall Segments3Vertikale Segmente pro Klippenstufe für die Wandgeometrie (1–8).
Physics
Collider ModeMesh ColliderNone: keine Collider; verwende für Abfragen den Raycast des Terrains. Mesh Collider: ein MeshCollider pro Chunk, der nach Bearbeitungen neu aufgebaut wird.
Chunk LayerDefault (0)Layer der Chunk-GameObjects, den Collider und Kameras verwenden.
Rendering
Cast ShadowsOnSchattenwurfmodus der Chunks.
Receive ShadowsanOb die Chunks Schatten empfangen.
Material OverridekeineOptionales Material. Ist das Feld leer, wird ein Material mit dem Shader RTS Terrain Studio/Terrain erstellt.
Show GridausZeigt das Zellraster an, auch im Play Mode und in Builds. Der Schalter G des Editors ist davon unabhängig und wirkt sich nur beim Bearbeiten aus.
Grid Opacity0.35Deckkraft des Rasters (0–1).

Pathing Rules

Diese Felder gehören zu RTSPathingSettings (Eigenschaft RTSTerrain.PathingSettings). Sie legen fest, wie aus Neigung, Klippen, Wasser und Bodenschichten begehbare und bebaubare Bereiche werden. Mit den Pathing-Werkzeugen gemalte Overrides werden darüber angewendet.

EinstellungStandardBeschreibung
Max Walkable Slope42°Boden, der steiler als dieser Winkel ist, ist nicht begehbar.
Max Buildable Slope18°Boden, der steiler als dieser Winkel ist, ist nicht bebaubar.
Cliff Margin0.25Wie weit der blockierte Bereich über eine Klippenwand hinausreicht, als Bruchteil einer Pathing-Zelle (0–1). Größere Werte halten Einheiten weiter von Klippenkanten fern.
Shallow Water BuildableausErlaubt Gebäude im Flachwasser.
Tree Footprint Scale1Faktor für den Radius der Pathing-Grundfläche jedes Baums.
Use Ground Layer RulesanWendet den Pathing-Effekt der dominanten Bodenschicht an, zum Beispiel Unbuildable auf rauem Fels.

Default Map

Die Karte, die erstellt wird, wenn kein Karten-Asset zugewiesen ist. Das Panel New Map im Tab Map startet mit diesen Werten.

EinstellungStandardBeschreibung
Width / Height128 / 128Größe in Zellen, ein Vielfaches von 16 (16–1024).
Cell Size2Größe einer Zelle in Welteinheiten.
Cliff Level Height2Höhe einer Klippenstufe in Welteinheiten.
Base Cliff Level2Anfängliche Klippenstufe jedes Vertex (0–15).
Base Ground Layer0Bodenschicht, die die Karte anfangs bedeckt.
Splat Resolution4Splat-Texel pro Zelle (1, 2, 4 oder 8).
Detail Resolution2Texel der Detaildichte pro Zelle (1, 2 oder 4).
Pathing Resolution2Pathing-Zellen pro Kartenzelle (1, 2 oder 4).

Subsystem-Komponenten

Das Terrain zeichnet nur den Boden und die Klippen. Andere Teile der Karte werden von Komponenten dargestellt, die auf demselben GameObject oder an anderer Stelle in der Szene liegen. Der Inspector des Terrains warnt, wenn eine der ersten vier fehlt, und bietet eine Schaltfläche Add an.

KomponenteVom Erstellungsmenü hinzugefügtZweck
Vegetation RendererjaRendert das gemalte Gras, die gemalten Pflanzen und die Bäume, per GPU-Instancing oder als Prefab-Instanzen. Erzeugt nur die Bereiche neu, die sich geändert haben.
Water RendererjaBaut und rendert die Wasseroberfläche pro Chunk, mit dem Erscheinungsbild aus dem Wasserprofil des Tilesets. Für die beste Optik aktivierst du im URP Asset (oder an der Kamera) die Optionen Depth Texture und Opaque Texture.
Decor ManagerjaZeigt die Dekorobjekte der Karte an und hält sie auf dem Boden, wenn sich die Höhen ändern.
Environmentdurch den New Map Wizard (optional) oder über GameObject ▸ RTS Terrain Studio ▸ EnvironmentÜberträgt Sonne, Umgebungslicht und Nebel der Karte auf die Szene. Beim Deaktivieren wird die ursprüngliche Szenenbeleuchtung wiederhergestellt; die Kartenbeleuchtung wird nie in die Szenendatei geschrieben.
Fog of War ManagerGameObject ▸ RTS Terrain Studio ▸ Fog of War ManagerBerechnet den Nebel des Krieges für das lokale Team. Einer pro Szene.
Pathfinding ServiceAdd Component ▸ RTS Terrain Studio ▸ Pathfinding Service oder bei Bedarf automatisch durch RTSPathfindingService.GetOrCreate() erstelltFührt die integrierten Pfadsuchen auf der Pathing-Karte des Terrains aus.

Terrain-Einstellungen per Code ändern

using RTSTerrainStudio;
using UnityEngine;

public class TerrainQualityOptions : MonoBehaviour
{
    public RTSTerrain terrain;

    // Called from a graphics options menu, for example.
    public void SetSmoothHills(bool smooth)
    {
        // Settings fields can be changed directly, but the chunks are only rebuilt on request.
        // Use 1, 2 or 4: an odd value such as 3 rounds Cliff Subdivisions up to a multiple of 6 (expensive).
        terrain.Settings.surfaceSubdivisions = smooth ? 2 : 1;
        terrain.RebuildAll();
    }

    public void UseStricterSlopes()
    {
        // Assigning new pathing settings rebuilds the pathing map automatically.
        RTSPathingSettings rules = terrain.PathingSettings.Clone();
        rules.maxWalkableSlope = 35f;
        rules.maxBuildableSlope = 12f;
        terrain.PathingSettings = rules;
    }
}

Wenn du die Felder der aktuellen Instanz von PathingSettings änderst, statt eine neue zuzuweisen, rufe danach terrain.RebuildPathing() auf.

In-Game-Editor

Der In-Game-Editor bringt den Karteneditor von RTS Terrain Studio in dein fertiges Spiel. Spieler und Modder können Karten ohne Unity erstellen, bemalen, speichern, laden und im Spiel testen – genau wie mit den Karteneditoren, die klassischen RTS-Spielen beilagen. Er basiert auf UI Toolkit, verwendet dieselben Werkzeuge wie das Editorfenster in Unity (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing und Markers) und schreibt dieselben .rtsmap-Dateien. Eine in deinem Spiel erstellte Karte kannst du in ein Unity-Projekt kopieren und dort unverändert öffnen – und umgekehrt.

Der In-Game-Karteneditor in der Game View
Der In-Game-Editor mit der oberen Leiste, Werkzeugkategorien und Werkzeugen links, Pinsel- und Werkzeugeinstellungen rechts und der Statusleiste unten.

Alles steckt in einer einzigen Komponente: RTSRuntimeEditor. Füge sie einer Szene mit einem RTS Terrain und einer Kamera hinzu, drücke Play – und du hast einen funktionierenden Editor. Dieses Kapitel behandelt die Einrichtung, jeden Teil der Oberfläche, die komplette Tastatur- und Maussteuerung, die Schaltfläche Play Test, wie dein Spielcode darauf reagiert, wo Karten gespeichert werden, wie du die UI umgestaltest und was du vor der Veröffentlichung prüfen solltest.

Im Unity Editor öffnet die Schaltfläche Open Manual im Fenster Shortcuts & Help (F1 oder die Schaltfläche ? in der oberen Leiste) dieses Kapitel. In einem Build öffnet die Schaltfläche die Seite, die du unter Manual Url einträgst, zum Beispiel deinen eigenen Modding-Leitfaden. Ist das Feld leer, wird die Schaltfläche in Builds ausgeblendet.

In zwei Minuten ausprobieren

Am schnellsten lernst du den In-Game-Editor mit der Demoszene kennen, die dem Paket beiliegt.

  1. Wähle Tools ▸ RTS Terrain Studio ▸ Create Demo Content. Dadurch werden die Demo-Texturen, -Bibliotheken, -Karten und -Szenen erzeugt. Das musst du nur einmal tun.
  2. Öffne im Project-Fenster Samples/Scenes/RTS Terrain Studio - In-Game Editor Demo. Die Szene enthält die Karte Sandbox, ein Objekt RTS In-Game Editor, ein Objekt RTS Demo Game und einen Manager für den Nebel des Krieges.
  3. Drücke Play. Der Editor öffnet sich mit einem Willkommenshinweis. Wähle links ein Werkzeug (standardmäßig ist das Höhenwerkzeug Raise / Lower aktiv) und male mit der linken Maustaste auf das Terrain.
  4. Halte beim Malen Shift gedrückt, um den Boden abzusenken. Mit [ und ] änderst du die Pinselgröße. Ziehe mit gedrückter rechter Maustaste, um die Kamera zu bewegen, und zoome mit dem Mausrad.
  5. Drücke F5 (oder Play Test in der oberen Leiste). Das Demospiel spawnt Einheiten an den Startpositionen und schaltet den Nebel des Krieges ein. Wähle Einheiten aus und bewege sie per Rechtsklick.
  6. Drücke Esc oder F5, um zum Editor zurückzukehren. Die Karte ist danach wieder genau so wie vor dem Spieltest.
  7. Drücke Ctrl+S, gib einen Namen ein und drücke Enter, um deine Karte zu speichern.

Den In-Game-Editor zu deiner Szene hinzufügen

Voraussetzungen

  • Ein RTS Terrain mit einer Karte in der Szene (GameObject ▸ RTS Terrain Studio ▸ RTS Terrain).
  • Eine Kamera. Standardmäßig verwendet der Editor Camera.main, also die Kamera mit dem Tag MainCamera. Du kannst aber auch jede beliebige Kamera zuweisen.
  • Der Ordner Resources/RTSTerrainStudio/UI aus dem Paket. Er enthält das UI-Theme und das Stylesheet.

Schritte

  1. Öffne deine Szene und wähle GameObject ▸ RTS Terrain Studio ▸ In-Game Editor. Dadurch entsteht ein Objekt namens RTS In-Game Editor mit der Komponente RTSRuntimeEditor. Wenn du im Hierarchy-Fenster mit der rechten Maustaste auf ein Objekt klickst und das Menü dort verwendest, wird das neue Objekt diesem Objekt als Kind untergeordnet.
  2. Warnt die Console, dass kein RTS Terrain vorhanden ist, füge eines hinzu. Warnt sie, dass keine Kamera das Tag MainCamera trägt, versieh deine Kamera mit diesem Tag oder weise sie unter Target Camera zu.
  3. Weise optional Terrain und Kamera explizit zu und passe die übrigen Einstellungen an (siehe Inspector-Referenz).
  4. Drücke Play. Der Editor erscheint. Hast du Start Visible ausgeschaltet, öffnest du ihn mit F2.

Im Play Mode ist der Menüeintrag deaktiviert. Enthält die Szene bereits einen In-Game-Editor, fragt Unity, ob du einen weiteren erstellen (Create Another) oder den vorhandenen auswählen (Select Existing) möchtest. Ein Editor pro Szene genügt. Du kannst die Komponente auch über Add Component ▸ RTS Terrain Studio ▸ In-Game Editor hinzufügen; pro GameObject kann es nur eine davon geben.

Was die Komponente zur Laufzeit erstellt

Du musst weder eine UI noch ein Kamera-Rig selbst bauen. Sobald RTSRuntimeEditor aktiviert ist, erstellt die Komponente:

ObjektZweck
RTSEditSessionDie Bearbeitungssitzung, erreichbar über Session. Sie enthält Werkzeuge, Pinsel, Symmetrie, Auswahl und Undo-Verlauf und ist dieselbe Klasse, die auch das Editorfenster in Unity verwendet.
Kindobjekt RTS In-Game Editor UIEnthält ein UIDocument mit der kompletten Editoroberfläche. Es wird wieder zerstört, wenn die Komponente deaktiviert wird.
RTSRuntimeEditorCameraWird der Zielkamera hinzugefügt, wenn Use Built In Camera eingeschaltet ist. Hat die Kamera bereits eine solche Komponente, wird diese verwendet. Eine Kamerakomponente, die der Editor selbst hinzugefügt hat, entfernt er wieder, wenn er zerstört wird.
RuntimeToolGizmosZeichnet Pinselumrisse, Auswahlrechtecke, Dekorvorschauen, Startpositionen und Symmetrie-Hilfslinien in der Game View. Erreichbar über Gizmos.
PanelSettingsWird erstellt, wenn du Panel Settings leer lässt. Verwendet das RTS-Theme und skaliert mit dem Bildschirm. Weist du Panel Settings zu, die mit der Bildschirmgröße skalieren und nicht in eine Textur rendern, verwendet der Editor stattdessen eine Laufzeitkopie davon, es sei denn, Enforce Minimum UI Scale ist ausgeschaltet (siehe Die Oberfläche anpassen).

Wie Terrain und Kamera gefunden werden

  • Terrain: Ein zugewiesenes Terrain wird verwendet, sobald es aktiviert ist. Ist das Feld leer, verwendet der Editor RTSTerrain.Main, das erste RTS Terrain in der Szene. Solange kein Terrain gefunden wird, sucht der Editor zweimal pro Sekunde erneut und zeigt die Hinweismeldung No RTS Terrain found.
  • Mehrere Terrains: Enthält die Szene mehr als ein RTS Terrain, listet das Menü Map sie unter Edited terrain auf. Der Wechsel zu einem anderen Terrain löscht den Undo-Verlauf.
  • Kamera: Ist Target Camera leer, verwendet der Editor Camera.main und sucht fortlaufend danach. Solange keine Kamera existiert, zeigt er die Hinweismeldung No camera found.

Inspector-Referenz

EinstellungStandardBeschreibung
Scene
TerrainleerDas zu bearbeitende Terrain. Bleibt das Feld leer, wird das erste RTS Terrain in der Szene verwendet (RTSTerrain.Main).
Target CameraleerDie Kamera für das Picking, die Gizmos und die integrierte Editorkamera. Bleibt das Feld leer, wird Camera.main verwendet.
Use Built In CameraanFügt der Zielkamera eine RTSRuntimeEditorCamera hinzu und steuert sie, solange der Editor sichtbar ist. Während Spieltests ist diese Komponente ausgeschaltet. Schalte die Option aus, wenn dein eigenes Kameraskript die Kamera beim Bearbeiten bewegen soll.
Behaviour
Start VisibleanZeigt den Editor beim Start der Szene an. Ist die Option aus, öffnet der Spieler ihn mit der unter Toggle Key festgelegten Taste, oder dein Code setzt EditorVisible.
Toggle KeyF2Blendet den gesamten Editor ein oder aus. Wähle None, um die Taste abzuschalten. Die Schließen-Schaltfläche in der oberen Leiste wird dann ausgeblendet, sodass dein Spiel den Editor selbst öffnen und schließen muss. In Webbrowsern sowie auf Smartphones und Tablets bleibt die Schließen-Schaltfläche sichtbar, weil die Schaltfläche Open Map Editor den Editor dort zurückholt.
Start Toolheight.raiseDie ID des Werkzeugs, das beim Start aktiv ist, zum Beispiel paint.layer, veg.trees, decor.place oder markers.start. Gibt es die ID nicht, wird das erste Werkzeug verwendet.
Show First Time HintanZeigt beim ersten Öffnen des Editors den Willkommenshinweis an, bis der Spieler auf Got it klickt oder ihn schließt. Diese Entscheidung wird in PlayerPrefs gespeichert.
Show Quit CommandanFügt den Eintrag File ▸ Quit hinzu, der die Anwendung beendet. Im Unity Editor beendet er den Play Mode. Unter WebGL, iOS und Android wird er nie angezeigt.
Confirm Quit With Unsaved ChangesanFragt nach, bevor die Anwendung beendet wird, wenn die Karte nicht gespeicherte Änderungen hat – auch wenn die Aufforderung zum Beenden von außerhalb des Editors kommt (Schließen-Schaltfläche des Fensters, Alt+F4, Application.Quit). Dabei wird zuerst der Editor eingeblendet und ein laufender Spieltest beendet. Web-Builds lassen sich nicht beenden; dort fragt der Browser Leave site?, bevor die Seite geschlossen oder neu geladen wird.
Undo Memory MB512Der maximale Speicher, den der Undo-Verlauf belegen darf, in Megabyte (mindestens 16). Ist dieses Limit erreicht, werden die ältesten Schritte verworfen. Außerdem speichert der Verlauf höchstens 100 Schritte. Web-Builds verwenden höchstens 192 MB, weil der Browser dem gesamten Spiel nur einen begrenzten Speicher-Heap zur Verfügung stellt.
Manual UrlleerDie Webseite, die Open Manual im Hilfefenster in einem Build öffnet. Ist das Feld leer, wird die Schaltfläche in Builds ausgeblendet. Im Unity Editor öffnet die Schaltfläche bei leerem Feld dieses Handbuch.
Files
Maps FolderleerDer Ordner, in dem Karten aufgelistet, geladen und gespeichert werden. Bleibt das Feld leer, wird der Ordner Maps im Speicherordner RTSMapSerializer.StorageRoot verwendet. Ein relativer Pfad wie UserMaps bezieht sich auf den Speicherordner. Ein absoluter Pfad wird unverändert übernommen. Der Speicherordner ist Application.persistentDataPath; in Web-Builds ist es ein Ordner im Browserspeicher, der gleich bleibt, wenn sich die Adresse der Seite ändert (siehe Karten im Browser).
Write Preview ImagesanSchreibt neben jede gespeicherte Karte ein kleines Minikartenbild (192 × 192 Pixel). Der Kartenbrowser zeigt es an.
User Interface
Panel SettingsleerOptionale Panel Settings für UI Toolkit. Ist das Feld leer, erstellt der Editor zur Laufzeit Panel Settings mit dem RTS-Theme. Sie skalieren mit der Bildschirmgröße (Referenz 1920 × 1080), aber nie unter 85 %, sodass Text auch bei 1280 × 720 lesbar bleibt.
Enforce Minimum UI ScaleanHält den Editor in kleinen Fenstern und kleinen Browser-Canvas-Elementen lesbar. Zugewiesene Panel Settings, die mit der Bildschirmgröße skalieren, werden zur Laufzeit kopiert (das Asset selbst wird nie verändert), und die Kopie skaliert die UI nie unter 85 %. Schalte die Option aus, um das zugewiesene Asset unverändert zu verwenden, zum Beispiel wenn sich der Editor ein Panel mit dem HUD deines Spiels teilt.
Ui Scale1Ein zusätzlicher Skalierungsfaktor von 0.5 bis 2. Er wirkt auf die Panel Settings, die der Editor zur Laufzeit erstellt, und auf die Laufzeitkopie zugewiesener Panel Settings.
Sorting Order1000Die Sortierreihenfolge der Editor-UI. Durch den hohen Standardwert liegt der Editor über dem HUD deines Spiels.

Ein Rundgang durch die Oberfläche

Der Bildschirm ist in fünf Bereiche aufgeteilt. Jede Schaltfläche, jedes Feld und jedes Statuselement zeigt einen Tooltip, wenn du den Mauszeiger kurz darüber hältst – das gilt auch für deaktivierte Schaltflächen. Bist du dir nicht sicher, was etwas bewirkt, fahre mit der Maus darüber.

BereichPositionInhalt
Obere Leisteoberer RandMenüs File und Map, Rückgängig und Wiederherstellen, Symmetrie, Ansichtsschalter, Play Test, Hilfe, Panels ausblenden, Schließen.
Werkzeugleistelinker RandAcht Kategorieschaltflächen (Tasten 1 bis 8) und die Werkzeuge der ausgewählten Kategorie.
Werkzeug-Panelrechter RandName und Beschreibung des aktiven Werkzeugs, Pinsel, Symmetrie, die Einstellungen des Werkzeugs und zusätzliche Befehle. Das Panel lässt sich einklappen.
Statusleisteunterer RandInformationen zum Punkt unter dem Mauszeiger, Werkzeugstatus, Pinsel, Undo-Speicher und Kartenname.
Overlaysüber der KarteToast-Meldungen, Tooltips, Dialoge, der Willkommenshinweis, die Spieltest-Leiste und Hinweismeldungen.

Obere Leiste

BedienelementFunktion
FileÖffnet das Menü File (siehe unten).
MapÖffnet das Menü Map (siehe unten).
Undo / RedoRückgängig (Ctrl+Z) und Wiederherstellen (Ctrl+Y). Ihre Tooltips zeigen den Namen des Schritts, zum Beispiel Undo "Raise / Lower". Die Schaltflächen sind ausgegraut, wenn es nichts rückgängig zu machen oder wiederherzustellen gibt.
Symmetry: OffÖffnet das Symmetriemenü. Ist die Symmetrie eingeschaltet, wird jeder Pinselstrich gespiegelt oder gedreht – so lassen sich faire Wettkampfkarten leicht erstellen. Die Schaltfläche zeigt den aktuellen Modus an, zum Beispiel Symmetry: 180°.
GridBlendet das Zellraster ein oder aus (G).
PathingÖffnet das Menü des Pathing-Overlays. Ist ein Overlay eingeschaltet, zeigt die Schaltfläche dessen Modus an, zum Beispiel Pathing: Walkability. P schaltet das Overlay mit dem zuletzt verwendeten Modus ein bzw. wieder aus.
MarkersBlendet Startpositionen, Regionen und Marker ein oder aus, während andere Werkzeuge aktiv sind (M). Standardmäßig eingeschaltet.
Play TestStartet einen Spieltest (F5).
Help (?)Öffnet das Fenster Shortcuts & Help (F1).
EyeBlendet die Panels aus, damit du freie Sicht auf die Karte hast (H). Bearbeiten funktioniert weiterhin. Eine kleine Schaltfläche Panels hidden - press H to show them holt sie zurück.
Close (×)Blendet den Editor aus und zeigt eine Toast-Meldung mit der Taste, die ihn wieder öffnet. Die Schaltfläche ist ausgeblendet, wenn Toggle Key auf None steht. In Webbrowsern sowie auf Smartphones und Tablets, wo Funktionstasten unzuverlässig sind, blendet sie zusätzlich unten links eine Schaltfläche Open Map Editor ein. Dort ist sie auch dann verfügbar, wenn unter Toggle Key keine Taste festgelegt ist (siehe Der In-Game-Editor im Browser).

Menü File

ElementTastenkürzelFunktion
New Map...Ctrl+N (Alt+N in Webbrowsern)Öffnet den Dialog New Map. Hat die aktuelle Karte nicht gespeicherte Änderungen, fragt der Editor vorher nach. Alt+N funktioniert auf allen Plattformen.
Open...Ctrl+OÖffnet den Kartenbrowser, in dem du eine Karte auswählst. Hat die aktuelle Karte nicht gespeicherte Änderungen, fragt der Editor vorher nach.
SaveCtrl+SSpeichert in die Datei, aus der die Karte stammt. Beim ersten Mal öffnet sich Save Map As und fragt nach einem Namen.
Save As...Ctrl+Shift+SÖffnet den Kartenbrowser, damit du unter einem neuen Namen speichern kannst.
Download MapNur in Web-Builds. Speichert eine Kopie der Karte einschließlich nicht gespeicherter Änderungen als .rtsmap-Datei auf dem Computer des Spielers (meist im Ordner Downloads). Siehe Karten im Browser.
Import Map...Nur in Web-Builds. Öffnet eine .rtsmap-Datei vom Computer des Spielers. Sie wird zu den in diesem Browser gespeicherten Karten hinzugefügt und geöffnet.
Show Maps FolderÖffnet den Kartenordner im Dateibrowser des Betriebssystems. Diesen Eintrag gibt es nur unter Windows, macOS und Linux, im Unity Editor und in Desktop-Playern.
QuitBeendet die Anwendung und fragt vorher nach nicht gespeicherten Änderungen. Wird nur angezeigt, wenn Show Quit Command eingeschaltet ist, und nie unter WebGL, iOS oder Android.

Menü Map

ElementFunktion
Map Info...Name, Autor, Beschreibung und empfohlene Spielerzahl.
Resize...Vergrößert oder verkleinert die Karte.
Generate...Erzeugt aus einem Seed ein zufälliges Karten-Layout. Du kannst das in einem Schritt rückgängig machen.
Frame Whole MapBewegt die Editorkamera so, dass die ganze Karte sichtbar ist. Nur mit der integrierten Kamera verfügbar.
Edited terrainWird nur angezeigt, wenn die Szene mehr als ein RTS Terrain enthält. Hier wählst du, welches Terrain du bearbeitest. Ein Wechsel löscht den Undo-Verlauf.

Symmetriemenü

ModusKopienTypische Verwendung
Off1Malen ohne Symmetrie.
Mirror Left / Right2Spiegelt an der senkrechten Mittellinie.
Mirror Top / Bottom2Spiegelt an der waagerechten Mittellinie.
Mirror 4-Way4Spiegelt an beiden Achsen.
Rotate 180°2Punktsymmetrie um das Zentrum, typisch für 1v1-Karten.
Rotate 90° (4 players)4Rotationssymmetrie für Karten mit vier Spielern. Erfordert eine quadratische Karte; bei einer nicht quadratischen Karte ist der Eintrag ausgegraut.
Mirror Diagonal2Spiegelt an der Diagonalen. Erfordert eine quadratische Karte; bei einer nicht quadratischen Karte ist der Eintrag ausgegraut.

Reset Centre setzt das Symmetriezentrum zurück in die Mitte der Karte. Im Abschnitt Symmetry des Werkzeug-Panels verschiebst du das Zentrum mit den Schiebereglern Centre X und Centre Z. Violette Hilfslinien auf dem Terrain zeigen die Symmetrieachsen.

Menü des Pathing-Overlays

Das Overlay färbt das Terrain danach ein, was Einheiten dort tun können. Wähle Off, Walkability (grün = begehbar, rot = blockiert), Buildability, Flyability, Water (Flach- und Tiefwasser) oder Combined (begehbare, bebaubare, Wasser- und blockierte Zellen in einer Ansicht). Die Einträge unter Opacity setzen die Deckkraft des Overlays auf 30 %, 55 % (Standard) oder 80 %.

Werkzeugleiste (links)

Die obere Spalte enthält eine Schaltfläche pro Werkzeugkategorie. Die kleine Zahl auf jeder Schaltfläche ist die zugehörige Taste. Die untere Spalte listet die Werkzeuge der ausgewählten Kategorie mit Icon und Namen auf. Fahre mit der Maus über ein Werkzeug, um seine Beschreibung und seinen Hinweis zu sehen.

TasteKategorieBeschreibungWerkzeuge
1HeightBoden anheben, absenken, glätten und formen.Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode
2CliffsKlippenstufen im Stil klassischer RTS-Spiele, Rampen und Klippenmaterialien.Raise / Lower Level, Set Level, Ramp, Cliff Type, Clean Cliffs
3PaintBodentexturen malen.Paint Layer, Tile Paint, Fill / Replace, Smooth Paint, Auto Paint
4VegetationGras, Blumen und Bäume.Paint Details, Erase Details, Place Trees, Erase Trees
5DecorObjekte (Doodads) platzieren, löschen und auswählen.Place Decor, Erase Decor, Select Decor
6WaterSeen, Flüsse und Meere.Paint Water, Erase Water, Flood Fill
7PathingFestlegen, wo Einheiten laufen, bauen und fliegen können.Paint Pathing, Clear Pathing
8MarkersStartpositionen, Regionen und benannte Marker.Start Locations, Regions, Markers

Der Editor merkt sich in jeder Kategorie das zuletzt verwendete Werkzeug. Wählst du eine Kategorie über ihre Schaltfläche oder Zahlentaste, ist dieses Werkzeug wieder aktiv. Drückst du die Zahlentaste der Kategorie, in der du dich bereits befindest, wechselst du zum nächsten Werkzeug darin. Eigene Werkzeuge, die du registrierst, erscheinen hier automatisch – mit einer Einstellungs-UI, die aus ihren [ToolSetting]-Feldern erzeugt wird.

Werkzeug-Panel (rechts)

Die Kopfzeile zeigt Icon und Namen des aktiven Werkzeugs. Fahre mit der Maus über den Namen, um die Beschreibung des Werkzeugs zu lesen. Die Pfeilschaltfläche klappt das Panel ein. Unter der Kopfzeile folgen einklappbare Abschnitte. Abschnitte, die du einklappst, bleiben auch beim Werkzeugwechsel eingeklappt.

AbschnittAngezeigt fürInhalt
(Info)jedes WerkzeugBeschreibung und Bedienhinweis des Werkzeugs, zum Beispiel Click: place / move · Shift+click: remove the nearest.
BrushPinselwerkzeugeShape (Circle, Square, Diamond sowie Stamp, wenn ein eigener Stempel gesetzt ist), Size (ein logarithmischer Schieberegler plus ein Feld in Metern), Strength, Hardness, Rotation, Random Rotation, Spacing, Jitter und eine Schaltfläche Reset in der Kopfzeile des Abschnitts. Alle Werkzeuge teilen sich denselben Pinsel.
Symmetryjedes WerkzeugSymmetriemodus, Centre X und Centre Z sowie eine Warnung, wenn der Modus eine quadratische Karte erfordert.
SettingsWerkzeuge mit OptionenDie Optionen des aktiven Werkzeugs, etwa Zielhöhe, Texturpaletten, Vorschaubilder für Bäume und Dekor, Klippenstufe oder Spieler.
Actionseinige WerkzeugeZusätzliche Befehle: Apply to Whole Map (Auto Paint), New Random Layout (Place Trees, Place Decor) und Ähnliches.
SelectionSelect DecorDelete, Duplicate, Drehen (−15°, +15°, +90°), Verschieben um eine halbe Zelle (−X, +X, −Z, +Z), Smaller / Larger (10 %), auf den Boden setzen (Snap), Lock / Unlock, Select All, Clear.

Hat das Terrain keine Karte, zeigen sowohl das Panel als auch die Statusleiste There is no map to edit. an.

Statusleiste (unten)

ElementBeispielBedeutung
ZelleCell 42, 17Die Zelle unter dem Mauszeiger (x, z).
HöheHeight 3.5 mBodenhöhe unter dem Mauszeiger in Metern.
StufeLevel 2Die Klippenstufe unter dem Mauszeiger (0 bis 15).
PathingWalkable, BuildableWas Einheiten hier tun können, zum Beispiel Shallow water, Cliff, Too steep, Blocked oder No flying.
WasserWater 1.2 m oder DryWassertiefe unter dem Mauszeiger.
WerkzeugstatusInformationen des aktiven Werkzeugs oder sein Hinweis.
PinselBrush 8.0 m 50 %Pinselradius und -stärke.
UndoUndo 12 4.2 MBAnzahl der Undo-Schritte und der Speicher, den sie belegen.
Kartenname● SandboxDer Name der Karte. Ein Punkt bedeutet, dass es nicht gespeicherte Änderungen gibt; Ctrl+S speichert sie.

Toast-Meldungen, Tooltips und Hinweismeldungen

  • Toast-Meldungen sind kurze Nachrichten, die über der Statusleiste erscheinen und von selbst wieder ausgeblendet werden, zum Beispiel Saved "Sandbox" oder Undo: Raise / Lower. Es gibt vier Arten: Info und Success bleiben etwa 3 Sekunden sichtbar, Warning 5 Sekunden und Error 6.5 Sekunden. Klicke auf eine Toast-Meldung, um sie zu schließen. Es werden höchstens vier gleichzeitig angezeigt. Toast-Meldungen bleiben sichtbar, während der Editor ausgeblendet ist, damit die Nachricht Editor closed. Press F2 to open it again. lesbar ist.
  • Open Map Editor: in Webbrowsern sowie auf Smartphones und Tablets eine Schaltfläche unten links, die erscheint, nachdem die Schließen-Schaltfläche verwendet wurde, und den Editor wieder öffnet. Mit Tastatur zeigt sie die unter Toggle Key festgelegte Taste an, zum Beispiel Open Map Editor (F2).
  • Tooltips erscheinen nach etwa einer halben Sekunde über jedem Bedienelement – auch über deaktivierten und während eines Spieltests. Ein Klick blendet den Tooltip aus, bis der Mauszeiger das Bedienelement verlässt.
  • Willkommenshinweis: ein kleines Panel mit den vier wichtigsten Bedienhinweisen und den Schaltflächen All Shortcuts (F1) und Got it. Mit Open Welcome Hint im Hilfefenster holst du ihn zurück.
  • Hinweismeldungen: No RTS Terrain found oder No camera found erscheinen in der Bildschirmmitte, wenn der Editor nicht arbeiten kann, zusammen mit einer kurzen Erklärung, was zu beheben ist.
  • Spieltest-Leiste: Während eines Spieltests zeigt eine kleine Leiste oben in der Mitte Play test running an und bietet die Schaltfläche Back to Editor (Esc or F5).

Dialoge

Dialoge sind modal: Solange einer geöffnet ist, lässt sich die Karte nicht bearbeiten und die Tastenkürzel sind deaktiviert. Enter löst die hervorgehobene (primäre) Schaltfläche aus, Esc schließt den Dialog.

DialogGeöffnet überInhalt
New MapFile ▸ New Map...Name; Size mit den Presets Tiny (64 × 64), Small (96), Medium (128), Large (192), Huge (256), Epic (384), Giant (512); Width und Depth (16 bis 1024 Zellen, auf Vielfache von 16 gerundet; in Webbrowsern ist die Grenze niedriger, siehe Grenzen); Cell Size 1, 2 oder 4 m (2 m entspricht klassischen RTS-Karten); Base Level (Klippenstufe 0 bis 15; bei 2 bleibt Platz, um für Seen und Flüsse nach unten zu graben); Base Layer (die Bodentextur). Eine Zeile darunter zeigt die resultierende Größe in Metern. Das Anlegen der neuen Karte lässt sich nicht rückgängig machen – speichere also vorher.
Generate MapMap ▸ Generate...Style (setzt die Formoptionen zurück), Seed mit einer Schaltfläche Random, Symmetry; Abschnitt Terrain: Cliffs, Plateaus, Ramps, Ramp Count, Water, Lake Coverage, Island, Border; Abschnitt Content: Textures, Vegetation, Forests, Decor, Decor Count, Start Locations, Players. Ersetzt den Inhalt der aktuellen Karte in einem einzigen Undo-Schritt. Die Einstellungen bleiben bis zum nächsten Öffnen des Dialogs erhalten.
Map InfoMap ▸ Map Info...Name, Author, Description (wird im Kartenbrowser angezeigt), Players (1 bis 16) sowie Statistiken: Größe, Startpositionen, Bäume, Objekte, Regionen. Das Übernehmen zählt als ein Undo-Schritt.
Resize MapMap ▸ Resize...Width und Depth (Vielfache von 16, im Browser mit derselben Grenze wie im Dialog New Map), Anchor (ein Raster aus 3 × 3 Feldern: legt fest, welcher Teil der Karte an seiner Stelle bleibt), Fill Level und Fill Layer für neue Bereiche. Die Größenänderung löscht den Undo-Verlauf. Beim Verkleinern wird Inhalt außerhalb des neuen Bereichs entfernt.
Open Map / Save Map AsFile ▸ Open... / Save As...Der Kartenbrowser.
Unsaved ChangesNew, Open, Quit bei nicht gespeicherten ÄnderungenSave (speichert zuerst und fragt nach einem Namen, falls die Karte noch keinen hat), Don't Save, Cancel.
Shortcuts & HelpF1 oder die Schaltfläche ?Die komplette Maus- und Tastatursteuerung (siehe Steuerung), Open Welcome Hint, Open Manual (wenn ein Handbuch verfügbar ist) und Close.

Der Kartenbrowser

File ▸ Open... (Ctrl+O) und File ▸ Save As... (Ctrl+Shift+S) öffnen denselben Browser mit dem Titel Open Map bzw. Save Map As. Er listet alle .rtsmap-Dateien im Kartenordner auf.

TeilBeschreibung
OrdnerzeileDer vollständige Pfad des Kartenordners. Refresh liest den Ordner neu ein, zum Beispiel nachdem du Karten hineinkopiert hast. In Web-Builds steht in der Zeile Saved in this browser. Use Download to keep a copy on your computer. Beim Öffnen von Karten enthält sie außerdem eine Schaltfläche Import... (siehe Karten im Browser).
KartenlisteEine Zeile pro Karte mit Namen, Größe, Spielerzahl und Datum der letzten Änderung; die neueste steht oben. Dateien, die sich nicht lesen lassen, werden als Unreadable or damaged file markiert und lassen sich nicht öffnen. Fahre mit der Maus über eine Zeile, um ihren Dateipfad zu sehen (in Web-Builds nur den Dateinamen).
DetailsDie Minikarten-Vorschau, Name, Größe in Zellen und Metern, Autor, Spieler, Änderungsdatum und Beschreibung der ausgewählten Karte. Die Vorschau gibt es nur, wenn Write Preview Images beim Speichern der Karte eingeschaltet war.
Map NameNur bei Save As. Der Dateiname wird aus diesem Namen gebildet. Ein Klick auf eine Karte in der Liste kopiert ihren Namen in das Feld, sodass du sie überschreiben kannst.
DeleteLöscht nach einer Rückfrage die ausgewählte Kartendatei und ihr Vorschaubild. Das lässt sich nicht rückgängig machen.
DownloadNur in Web-Builds und nur beim Öffnen von Karten. Speichert eine Kopie der ausgewählten Karte als .rtsmap-Datei auf dem Computer des Spielers.
Cancel / Open / SaveOpen lädt die ausgewählte Karte. Ein Doppelklick auf eine Zeile oder ein Druck auf Enter bewirkt dasselbe. Save speichert die Karte unter dem eingegebenen Namen.

Der Browser liest nur den kleinen Kopfbereich jeder Datei. Deshalb öffnet er sich schnell, auch wenn der Ordner viele große Karten enthält.

Unter einem neuen Namen speichern

  1. Drücke Ctrl+Shift+S. Im Namensfeld steht bereits der aktuelle Kartenname.
  2. Gib einen neuen Namen ein, zum Beispiel Glacier Pass (2v2), und drücke Enter.
  3. Gibt es bereits eine andere Datei mit diesem Namen, fragt der Editor mit Replace Map? nach. Da Enter standardmäßig Cancel auslöst, musst du auf Replace klicken, um die Datei zu überschreiben.
  4. Die Karte und ein Vorschaubild werden gespeichert, und die Toast-Meldung Saved "Glacier Pass (2v2)" bestätigt den Vorgang. Danach speichert Ctrl+S ohne Nachfrage in diese Datei.

Wenn du unter einem anderen Namen speicherst, ändert sich auch der Name der Karte selbst (angezeigt in Map Info). Diese Umbenennung ist ein normaler Undo-Schritt namens Rename Map.

Steuerung und Tastenkürzel

Dieselbe Liste rufst du im Editor mit F1 auf. Unter macOS funktioniert Cmd überall dort, wo Ctrl angegeben ist. Mit Zifferntasten ist die Zahlenreihe der Haupttastatur gemeint. Buchstaben-Tastenkürzel richten sich nach dem Buchstaben, der auf der Taste aufgedruckt ist. Ctrl+Z verwendet also auch auf deutschen (QWERTZ) und französischen (AZERTY) Tastaturen die Taste mit der Aufschrift Z. Die Pinseltasten richten sich nach dem Zeichen, das sie eingeben. [ und ] funktionieren deshalb unabhängig davon, wo sie auf deiner Tastatur liegen, zum Beispiel mit AltGr. In Webbrowsern verhalten sich einige Tasten anders (siehe Der In-Game-Editor im Browser).

Maus

EingabeAktion
Linke MaustasteMalen oder das aktive Werkzeug anwenden. Für einen Pinselstrich gedrückt halten und ziehen. Ein ganzer Pinselstrich ist ein einziger Undo-Schritt.
Shift + linke MaustasteDie umgekehrte Aktion: absenken statt anheben, radieren statt malen, entfernen statt platzieren.
Ctrl + LinksklickEinen Wert vom Terrain aufnehmen (Höhe, Bodenschicht, Klippenstufe …), sofern das Werkzeug dies unterstützt.
Mit gedrückter rechter oder mittlerer Maustaste ziehenDie Kamera verschieben („den Boden greifen“).
Alt + mit gedrückter linker oder rechter Maustaste ziehenDie Kamera drehen. Solange Alt gedrückt ist, malt die linke Maustaste nicht.
MausradHinein- und herauszoomen, ausgerichtet auf den Punkt unter dem Mauszeiger.

Kamera (integrierte Editorkamera)

TasteAktion
W A S D oder PfeiltastenVerschieben. Halte Shift gedrückt, um schneller zu verschieben.
Q / ENach links / rechts drehen.
FDie Kamera auf den Punkt unter dem Mauszeiger fokussieren.

Pinsel und Werkzeuge

TasteAktion
[ / ]Kleinerer / größerer Pinsel (etwa 15 % pro Tastendruck).
- / = (oder +)Geringere / höhere Stärke (5 % pro Tastendruck). - und + auf dem Ziffernblock funktionieren ebenfalls.
1 bis 8Eine Werkzeugkategorie wählen (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers). Erneutes Drücken wählt das nächste Werkzeug der Kategorie.
EscDen laufenden Pinselstrich abbrechen. Das Terrain wird auf den Stand zurückgesetzt, den es beim Drücken der Maustaste hatte.

Datei und Verlauf

TasteAktion
Ctrl+N oder Alt+NNeue Karte. In Webbrowsern funktioniert nur Alt+N, weil der Browser Ctrl+N für ein neues Fenster reserviert.
Ctrl+OEine Karte öffnen (Kartenbrowser).
Ctrl+SSpeichern. Fragt beim ersten Mal nach einem Namen.
Ctrl+Shift+SSpeichern unter.
Ctrl+ZRückgängig machen.
Ctrl+Y oder Ctrl+Shift+ZWiederherstellen.

Ansicht und Editor

TasteAktion
GDas Zellraster ein- oder ausblenden.
PDas Pathing-Overlay einschalten (zuletzt verwendeter Modus, anfangs Combined) oder ausschalten.
MStartpositionen, Regionen und Marker ein- oder ausblenden.
HDie Editor-Panels aus- oder einblenden.
F1Shortcuts & Help.
F5Einen Spieltest starten oder den laufenden Spieltest beenden.
Esc (während eines Spieltests)Den Spieltest beenden und zum Editor zurückkehren.
F2 (eingestellt unter Toggle Key)Den gesamten Editor ein- oder ausblenden. Während eines Spieltests bewirkt diese Taste nichts.

Dekorauswahl (nur mit dem Werkzeug Select Decor)

EingabeAktion
Klicken / Rechteck aufziehenObjekte auswählen.
Shift + Klick / RechteckObjekte zur Auswahl hinzufügen oder aus ihr entfernen.
Ein ausgewähltes Objekt ziehenDie Auswahl verschieben. Der gesamte Ziehvorgang ist ein Undo-Schritt. Esc während des Ziehens setzt die Objekte zurück. Gesperrte Objekte lassen sich nicht ziehen.
Auf ein Objekt klicken, das Teil einer Gruppenauswahl istNur dieses Objekt auswählen.
Delete / BackspaceDie ausgewählten Objekte löschen.
Ctrl+DDie Auswahl um eine Zelle diagonal versetzt duplizieren (+X und +Z).
Ctrl+AAlle nicht gesperrten Objekte auswählen.
EscDie Auswahl aufheben.

Was Esc bewirkt

Esc führt jeweils die erste zutreffende Aktion dieser Liste aus:

  1. Schließt ein offenes Popup-Menü.
  2. Nimmt den Tastaturfokus von einem Textfeld oder Schieberegler.
  3. Schließt den obersten Dialog.
  4. Bricht das Ziehen von Dekorobjekten ab (die Objekte kehren an ihren Platz zurück).
  5. Bricht den Pinselstrich ab, den du gerade malst (Toast-Meldung Stroke cancelled).
  6. Hebt die Dekorauswahl auf (Werkzeug Select Decor).
  7. Blendet die Panels wieder ein, wenn sie mit H ausgeblendet wurden.

Tastenkürzel funktionieren nicht? Solange ein Textfeld den Tastaturfokus hat, landen Buchstabentasten im Feld. Solange ein Dialog offen ist, funktionieren nur Enter und Esc. Klicke auf die Karte oder drücke Esc und versuche es dann erneut. Malen und Zoomen mit dem Mausrad sind außerdem blockiert, solange sich der Mauszeiger über einem Panel befindet.

Die integrierte Editorkamera

Wenn Use Built In Camera eingeschaltet ist, fügt der Editor der Zielkamera eine RTSRuntimeEditorCamera hinzu. Diese Orbit-Kamera kreist um einen Fokuspunkt auf dem Boden. Sie bleibt innerhalb der Terrain-Grenzen samt einem zusätzlichen Rand, taucht nie unter den Boden und neigt sich umso stärker nach unten, je weiter du herauszoomst. Sie verwendet ungeskalierte Zeit und funktioniert deshalb auch, während dein Spiel pausiert ist (Time.timeScale = 0). Der Editor schaltet die Kamera ein, solange er sichtbar ist, und schaltet sie aus, solange er ausgeblendet ist oder ein Spieltest läuft. Nach einem Spieltest kehrt sie an ihre vorherige Position zurück.

Um ihr Verhalten zu ändern, füge RTSRuntimeEditorCamera selbst deiner Kamera hinzu (Add Component ▸ RTS Terrain Studio ▸ In-Game Editor Camera) und passe die Felder an. Ist die Komponente bereits vorhanden, verwendet der Editor sie, statt eine neue hinzuzufügen.

EinstellungStandardBeschreibung
Movement
Pan Speed18Verschiebegeschwindigkeit per Tastatur in Metern pro Sekunde bei geringster Höhe. Sie nimmt mit der Höhe zu.
Fast Multiplier2.5Multiplikator für die Verschiebegeschwindigkeit, solange Shift gedrückt ist.
Edge PanausVerschieben, wenn der Mauszeiger den Bildschirmrand berührt.
Edge Pan Margin12Breite des Bildschirmrands für das Rand-Scrollen, in Pixeln.
Rotate Speed100Drehgeschwindigkeit von Q / E in Grad pro Sekunde.
Mouse Rotate Speed0.25Drehung bei Alt + Ziehen mit der Maus in Grad pro Pixel.
Zoom
Min Height8Geringste Kamerahöhe über dem Fokuspunkt, in Metern.
Max Height120Größte Kamerahöhe über dem Fokuspunkt, in Metern.
Zoom Step0.14Höhenänderung pro Raste des Mausrads, als Anteil der aktuellen Höhe.
Zoom Towards CursoranIn Richtung des Punkts unter dem Mauszeiger zoomen statt zur Bildschirmmitte.
Min Pitch40Kameraneigung in Grad unterhalb des Horizonts bei geringster Höhe.
Max Pitch70Kameraneigung bei größter Höhe.
Pitch CurveEase-in-outÜbergang von Min Pitch (0) zu Max Pitch (1) in Abhängigkeit von der normalisierten Höhe.
Limits & Smoothing
Ground Clearance3Kleinster zulässiger Abstand zwischen der Kamera und dem Boden darunter, in Metern.
Bounds Padding8Zusätzlicher Raum um das Terrain, in den sich der Fokuspunkt bewegen darf, in Metern.
Smooth Time0.1Glättungszeit in Sekunden (0 = keine Glättung).

Im Code liefert RTSRuntimeEditor.EditorCamera die Komponente oder null, wenn die integrierte Kamera ausgeschaltet ist:

MemberBeschreibung
void Focus(Vector3 worldPosition, bool instant = false)Verschiebt den Fokus auf eine Position in Weltkoordinaten – geglättet, sofern instant nicht true ist.
void FrameTerrain(bool instant = false)Zentriert die Kamera über dem Terrain und zoomt so weit heraus, dass der Großteil davon zu sehen ist. Neue Karten und Karten mit geänderter Größe werden automatisch so ins Bild gerückt.
void SnapToTargets()Springt ohne Glättung zu Zielposition, Zieldrehung und Zielhöhe.
Vector3 FocusPoint, float Yaw, float HeightDer aktuelle Orbit (schreibgeschützt).
bool IsDragging, bool WantsAltDragTrue, solange die Kamera gezogen wird bzw. solange Alt gedrückt ist.
bool PointerBlocked, bool KeyboardBlockedWerden vom Editor in jedem Frame gesetzt, solange der Mauszeiger über der UI liegt, ein Textfeld den Fokus hat oder ein Dialog offen ist.
RTSTerrain TerrainDas Terrain, das für die Grenzen und die Bodenhöhe verwendet wird.

Eigenes Kameraskript während der Bearbeitung verwenden: Schalte Use Built In Camera aus. Der Editor nutzt die Zielkamera dann nur noch für Picking und Gizmos. Achte darauf, dass deine Kamera Eingaben ignoriert, solange RTSRuntimeEditor.IsPointerOverUI true ist, damit das Scrollen in einem Panel nicht gleichzeitig die Kamera zoomt.

Play Test: die Karte in deinem Spiel ausprobieren

Play Test (F5) verwandelt den Editor für einen Moment in dein Spiel: Die Editor-UI macht Platz, dein Gameplay-Code spawnt Einheiten und schaltet seine Kamera ein, und der Spieler kann die Karte ausprobieren. Esc, F5 oder Back to Editor beendet den Test, und die Karte kehrt exakt in ihren vorherigen Zustand zurück. Einheiten und Gebäude verschwinden, abgebaute Bäume und geändertes Pathing werden zurückgesetzt.

Ein Spieltest im In-Game-Editor mit Einheiten, Minikarte, HUD und Steuerungs-Panel
Ein aus dem In-Game-Editor gestarteter Spieltest: Das Demospiel spawnt Einheiten, die Minikarte zeigt den Nebel des Krieges, und Esc führt zurück zum Editor.

Der Editor weiß nichts von deinem Spiel. Er meldet Beginn und Ende eines Spieltests über die statische Klasse RTSRuntimeEditorEvents, und dein Spiel entscheidet, was zu tun ist. Die Beispielkomponente RTSDemoGame (GameObject ▸ RTS Terrain Studio ▸ Demo Game) reagiert bereits darauf. Füge sie deiner Editorszene hinzu, und du bekommst während Spieltests Einheiten, Nebel des Krieges, Minikarte und HUD, ohne Code schreiben zu müssen.

Was Schritt für Schritt passiert

ZeitpunktDer Editor …
Play Test wird gedrückt
  1. beendet den Pinselstrich, den du gerade malst,
  2. erstellt eine vollständige Kopie (Snapshot) der Karte und merkt sich den Dateipfad und den Speicherstatus,
  3. schließt Dialoge und Menüs und blendet Pinselvorschau, Gizmos, Raster und Pathing-Overlay aus,
  4. schaltet seine integrierte Kamera aus und ersetzt die Panels durch die kleine Spieltest-Leiste,
  5. löst RTSRuntimeEditorEvents.PlaytestChanged(true) aus. Dein Spiel startet.
Während des SpieltestsIgnoriert alle Bearbeitungseingaben, Undo- und Dateibefehle (diese melden dann Stop the play test first.) sowie die Taste zum Ein- und Ausblenden des Editors. Das Gameplay darf die Karte beliebig verändern.
Esc, F5 oder Back to Editor
  1. löst zuerst RTSRuntimeEditorEvents.PlaytestChanged(false) aus, damit dein Spiel aufräumen kann, solange die Spieltest-Karte noch geladen ist,
  2. kopiert den Snapshot zurück in die Karte, wodurch das Terrain neu aufgebaut wird,
  3. stellt Dateipfad und Speicherstatus, die Panels, die Editoransicht und die integrierte Kamera wieder her,
  4. zeigt die Toast-Meldung Play test ended: the map was restored.

Undo-Verlauf: Der Editor kopiert den Snapshot zurück in dieselbe Karteninstanz, sodass dein Undo-Verlauf Spieltests übersteht. Hat das Gameplay die Karte während des Tests ersetzt, neu geladen oder in der Größe geändert, wird der Snapshot stattdessen als neue Karte geladen. Die Toast-Meldung enthält dann (undo history was reset).

Referenz zu RTSRuntimeEditorEvents

RTSRuntimeEditorEvents liegt im Runtime-Kern (Runtime/Core). Dein Gameplay-Code kompiliert und funktioniert also auch dann, wenn du den In-Game-Editor aus einem Projekt entfernst.

MemberBeschreibung
static bool IsPlaytestingTrue, solange ein Spieltest läuft.
static event Action<bool> PlaytestChangedWird ausgelöst, wenn ein Spieltest beginnt (true) oder endet (false).
static event Action<RTSTerrain> MapOpenedWird ausgelöst, nachdem der In-Game-Editor eine Karte aus einer Datei geöffnet oder eine neue Karte erstellt hat.
static void ConsumeEscape()Rufe diese Methode in einem Frame auf, in dem dein Gameplay-Code selbst auf Esc reagiert hat, zum Beispiel um eine Gebäudeplatzierung abzubrechen. Der Editor beendet den Spieltest in diesem Frame dann nicht.
static bool EscapeConsumedThisFrameTrue, wenn ConsumeEscape im aktuellen Frame aufgerufen wurde.
static void SetPlaytesting(bool playtesting)Setzt IsPlaytesting und löst PlaytestChanged aus. Der Editor ruft diese Methode auf. Um einen Test aus deinem eigenen Code zu starten, rufe stattdessen RTSRuntimeEditor.StartPlaytest() auf, denn nur der Editor erstellt den Snapshot der Karte.
static void RaiseMapOpened(RTSTerrain terrain)Löst MapOpened aus. Der Editor ruft diese Methode auf.

Die Events werden beim Start des Play Mode zurückgesetzt. Abonniere sie in OnEnable (oder Awake / Start) eines Szenenobjekts, nicht in einem statischen Konstruktor oder einem Editor-Skript, und melde dich in OnDisable wieder ab.

Beispiel: Einheiten für einen Spieltest spawnen

Diese Komponente spawnt beim Start eines Spieltests eine Einheit pro Startposition und schaltet deine Gameplay-Systeme ein (RTS-Kamera, Nebel des Krieges, HUD). Wenn der Test endet, entfernt sie die Einheiten und schaltet die Systeme wieder aus. Füge sie einem beliebigen Objekt in der Editorszene hinzu und weise ihr ein Einheiten-Prefab zu.

using System.Collections.Generic;
using UnityEngine;
using RTSTerrainStudio;

/// <summary>Starts a tiny "game" whenever the in-game editor runs a play test.</summary>
public class MyPlaytestGame : MonoBehaviour
{
    [Tooltip("Spawned once at every start location of the map.")]
    [SerializeField] GameObject unitPrefab;

    [Tooltip("Gameplay components that should only run during a play test, " +
             "for example your RTS camera script, the fog of war manager or your HUD.")]
    [SerializeField] Behaviour[] gameplaySystems = new Behaviour[0];

    readonly List<GameObject> spawned = new List<GameObject>();

    void OnEnable()
    {
        // Static event: always pair += in OnEnable with -= in OnDisable.
        RTSRuntimeEditorEvents.PlaytestChanged += HandlePlaytestChanged;
        SetSystemsEnabled(RTSRuntimeEditorEvents.IsPlaytesting);
    }

    void OnDisable()
    {
        RTSRuntimeEditorEvents.PlaytestChanged -= HandlePlaytestChanged;
        DespawnAll();
    }

    void HandlePlaytestChanged(bool playtesting)
    {
        if (playtesting) StartGame();
        else StopGame();
    }

    void StartGame()
    {
        var terrain = RTSTerrain.Main;          // the first RTS Terrain in the scene
        if (terrain == null || terrain.Map == null) return;

        foreach (var start in terrain.Map.StartLocations)
        {
            // Start locations are stored in map-local space: convert to world space
            // and put the unit on the ground.
            Vector3 position = terrain.LocalToWorld(start.position);
            position.y = terrain.GetHeight(position);
            Quaternion rotation = Quaternion.Euler(0f, terrain.transform.eulerAngles.y + start.yaw, 0f);

            var unit = Instantiate(unitPrefab, position, rotation);
            unit.name = "Player " + (start.player + 1) + " Unit";
            spawned.Add(unit);
        }
        SetSystemsEnabled(true);
    }

    void StopGame()
    {
        // Called BEFORE the editor restores the map, so everything is still in place.
        DespawnAll();
        SetSystemsEnabled(false);
    }

    void DespawnAll()
    {
        foreach (var unit in spawned)
            if (unit != null) Destroy(unit);
        spawned.Clear();
    }

    void SetSystemsEnabled(bool enabled)
    {
        foreach (var system in gameplaySystems)
            if (system != null) system.enabled = enabled;
    }
}

StartLocation.position ist ein Unity.Mathematics.float3, das automatisch in Vector3 umgewandelt wird. Wenn dein Skript in einer eigenen Assembly Definition liegt, füge Referenzen auf RTSTerrainStudio.Runtime und Unity.Mathematics hinzu.

Beispiel: die Esc-Taste mit deinem Spiel teilen

Viele RTS-Spiele verwenden Esc, um einen Befehl oder eine Gebäudeplatzierung abzubrechen. Rufe ConsumeEscape() in dem Frame auf, in dem du die Taste auswertest, dann beendet der Editor den Spieltest nicht. Der Editor prüft das in LateUpdate, daher spielt die Ausführungsreihenfolge deiner Skripte keine Rolle.

using UnityEngine;
using RTSTerrainStudio;

public class MyBuildingPlacement : MonoBehaviour
{
    bool placing;   // true while a building ghost follows the cursor

    void Update()
    {
        if (placing && RTSInput.GetKeyDown(RTSKey.Escape))
        {
            placing = false;                          // cancel the placement ...
            RTSRuntimeEditorEvents.ConsumeEscape();   // ... and keep the play test running
        }
    }
}

Die Beispielskripte RTSBuildingPlacementDemo, RTSCommandController und RTSSelectionManager machen das bereits. In der Demo hebt der erste Druck auf Esc die Auswahl auf, der nächste kehrt zum Editor zurück.

Spieltest-Checkliste für dein eigenes Spiel

  • Lasse dein RTS-Kameraskript, deinen Manager für den Nebel des Krieges und dein HUD in der Szene deaktiviert und aktiviere sie in PlaytestChanged(true). Die Demoszene macht dasselbe mit RTSCameraController und RTSFogOfWarManager.
  • Die eingebaute Kamera des Editors ist während des Tests ausgeschaltet, sodass dein Kameraskript die volle Kontrolle über die Kamera hat.
  • Entferne in PlaytestChanged(false) alles, was du gespawnt hast. Kartenänderungen musst du nicht rückgängig machen, denn der Editor stellt die Karte wieder her.
  • Im Multiplayer (Mirror-Integration) werden Kartenänderungen, die während eines laufenden Spiels vorgenommen werden, nicht live übertragen. Rufe RTSNetworkMapSync.SendMapToAll() auf, um die Karte erneut zu senden.

Karten in Builds speichern und laden

Wo Karten gespeichert werden

Karten werden in RTSRuntimeEditor.MapsFolder gespeichert. Standardmäßig ist das der Ordner Maps in RTSMapSerializer.StorageRoot; diesen Pfad liefert auch RTSMapSerializer.DefaultMapsFolder. StorageRoot entspricht auf allen Plattformen außer dem Web Application.persistentDataPath. Der Ordner wird beim ersten Speichern angelegt. Unity legt persistentDataPath je nach Plattform fest. Unter Windows liegt er unter %USERPROFILE%\AppData\LocalLow\<Company>\<Product>; Firmen- und Produktname stammen aus den Player Settings. Auf Desktop-Plattformen öffnet File ▸ Show Maps Folder den Ordner, und der Kartenbrowser zeigt den vollständigen Pfad an.

In Web-Builds liegen die Karten im Speicher des Browsers. File ▸ Download Map und File ▸ Import Map... übertragen sie vom Browser auf den Computer des Spielers und zurück. Siehe Karten im Browser.

Zu jeder Karte gibt es bis zu zwei Dateien:

DateiBeispielBeschreibung
KarteGlacier Pass (2v2).rtsmapDie vollständige Karte: Höhen, Klippen, Texturen, Wasser, Vegetation, Dekor, Pathing, Startpositionen, Regionen, Marker, Karteninfo und Umgebung. Sie verwendet dasselbe Format wie Karten im Unity Editor.
VorschauGlacier Pass (2v2).preview.pngEin 192 × 192 Pixel großes Minikartenbild für den Kartenbrowser. Es wird nur geschrieben, wenn Write Preview Images eingeschaltet ist, und zusammen mit der Karte gelöscht.

Der Dateiname wird aus dem Kartennamen gebildet. Zeichen, die in Dateinamen nicht erlaubt sind, werden durch _ ersetzt, Punkte am Ende werden entfernt, und aus einem leeren Namen wird New Map.

Nicht gespeicherte Änderungen

  • Solange es nicht gespeicherte Änderungen gibt, zeigt die Statusleiste einen Punkt vor dem Kartennamen.
  • New Map, Open und Quit fragen zuerst nach: Save, Don't Save oder Cancel.
  • Wenn der Spieler das Spielfenster schließt, während die Karte nicht gespeicherte Änderungen hat, bricht der Editor das Beenden ab und fragt ebenfalls nach, sofern Confirm Quit With Unsaved Changes nicht ausgeschaltet ist. Im Webbrowser fragt stattdessen der Browser selbst Leave site?, bevor der Tab geschlossen oder neu geladen wird; auch das richtet sich nach dieser Option.
  • Ein Spieltest ändert nichts daran, ob die Karte als nicht gespeichert gilt. Der Editor stellt diesen Zustand zusammen mit der Karte wieder her.
  • Die Karte, mit der die Szene startet (das dem Terrain zugewiesene Karten-Asset), hat noch keine Datei im Kartenordner. Deshalb wirst du beim ersten Ctrl+S nach einem Namen gefragt.

Testen im Unity Editor: Wenn du eine Karte im Play Mode mit dem In-Game-Editor bearbeitest und den Play Mode dann beendest, fragt Unity: Save map changes made in Play Mode? Save schreibt die Änderungen in das Karten-Asset des Terrains in deinem Projekt. Mit Ctrl+S gespeicherte Karten landen dagegen im Kartenordner. Du kannst eine solche .rtsmap-Datei in deinen Assets-Ordner kopieren, um sie als Karten-Asset zu verwenden.

Karten mit deinem Spiel ausliefern

Der Kartenbrowser zeigt nur Karten aus dem Kartenordner an. Wenn Spieler deine eigenen Karten als Ausgangspunkt bekommen sollen, kopiere sie beim ersten Start dorthin. Dieses Beispiel kopiert alle Dateien aus StreamingAssets/Maps (Karten und Vorschaubilder) und überschreibt nie eine vom Spieler bearbeitete Version:

using System.IO;
using UnityEngine;
using RTSTerrainStudio;

/// <summary>Copies bundled maps into the in-game editor's maps folder (desktop platforms).</summary>
public class InstallBundledMaps : MonoBehaviour
{
    [SerializeField] RTSRuntimeEditor editor;

    void Awake()
    {
        // Put your .rtsmap files (and optional .preview.png files) into Assets/StreamingAssets/Maps.
        string source = Path.Combine(Application.streamingAssetsPath, "Maps");

        // On Android and WebGL, StreamingAssets is not a normal folder: use UnityWebRequest there instead.
        if (!Directory.Exists(source)) return;

        string target = editor.MapsFolder;   // StorageRoot/Maps unless you changed it
        Directory.CreateDirectory(target);

        foreach (string file in Directory.GetFiles(source))
        {
            if (file.EndsWith(".meta")) continue;   // only matters when running in the Unity editor
            string destination = Path.Combine(target, Path.GetFileName(file));
            if (!File.Exists(destination)) File.Copy(file, destination);
        }
    }
}

Von Spielern erstellte Karten in deinem Spiel spielen

Dein Spiel kann Karten auch ohne den Editor aus dem Kartenordner laden, zum Beispiel in einer eigenen Spiellobby. RTSMapSerializer.TryReadHeaderFromFile liest Name, Größe, Autor und Spielerzahl, ohne die ganze Karte zu laden. Geladen wird sie dann mit RTSTerrain.LoadFromFile.

using System;
using System.IO;
using UnityEngine;
using RTSTerrainStudio;

public static class MapLobby
{
    /// <summary>Logs every map in the default maps folder.</summary>
    public static void ListMaps()
    {
        string folder = RTSMapSerializer.DefaultMapsFolder;
        if (!Directory.Exists(folder)) return;

        foreach (string path in Directory.GetFiles(folder, "*" + RTSMapSerializer.FileExtension))
        {
            // Reads only the small header: fast, even for large maps.
            if (RTSMapSerializer.TryReadHeaderFromFile(path, out RTSMapHeader header))
                Debug.Log(header.name + " by " + header.author + ": " + header.width + " x " + header.height +
                          " cells, " + header.maxPlayers + " players, modified " + header.Modified);
        }
    }

    /// <summary>Loads a map file into the main terrain. Returns false when the file cannot be read.</summary>
    public static bool Play(string path)
    {
        var terrain = RTSTerrain.Main;
        if (terrain == null) return false;
        try
        {
            terrain.LoadFromFile(path);   // throws when the file is missing or damaged
            return true;
        }
        catch (Exception e)
        {
            Debug.LogError("Could not load " + path + ": " + e.Message);
            return false;
        }
    }
}

Eine Karte speichert ihre Inhalte als IDs (Bodenschichten, Vegetations- und Dekor-Prototypen). Von Spielern erstellte Karten sehen nur dann richtig aus, wenn dasselbe Tileset, dieselbe Vegetationsbibliothek und dieselbe Dekorbibliothek verwendet werden wie beim Terrain der Editorszene. Weise dem Terrain in deinen Spielszenen deshalb dieselben Assets zu.

Den Editor per Code steuern

Alles, was die Oberfläche bietet, kannst du auch per Code über RTSRuntimeEditor (Namespace RTSTerrainStudio) erledigen. Die wichtigsten Member:

MemberBeschreibung
Zustand
bool EditorVisible { get; set; }Blendet den gesamten Editor ein oder aus (wie die Taste zum Ein- und Ausblenden). Beim Ausblenden wird ein laufender Pinselstrich beendet, offene Dialoge werden geschlossen und die Terrain-Ansicht geht an das Spiel zurück.
bool PanelsVisible { get; set; }Blendet nur die Panels aus (H). Das Bearbeiten funktioniert weiterhin.
bool IsPlaytesting { get; }True, solange ein von diesem Editor gestarteter Spieltest läuft.
RTSKey ToggleKey { get; set; }Die Taste zum Ein- und Ausblenden. RTSKey.None schaltet sie ab und blendet die Schließen-Schaltfläche aus (außer in Webbrowsern sowie auf Smartphones und Tablets, siehe HasReliableFunctionKeys).
RTSTerrain Terrain { get; set; }Das bearbeitete Terrain. Beim Setzen wird das Terrain gewechselt und der Undo-Verlauf gelöscht.
Camera TargetCamera { get; set; }Die Kamera, über die ermittelt wird, was unter dem Mauszeiger liegt, und mit der die Gizmos gezeichnet werden.
RTSRuntimeEditorCamera EditorCamera { get; }Die integrierte Kamera oder null.
RTSEditSession Session { get; }Die Bearbeitungssitzung: Werkzeuge, Pinsel, Symmetrie, Auswahl, Rückgängigmachen.
ToolCategory ActiveCategory { get; }Die Kategorie des aktiven Werkzeugs.
bool HasHover, Vector3 HoverWorldPositionOb sich der Mauszeiger über dem Terrain befindet, und die Weltposition darunter.
bool IsPointerOverUI { get; }True, solange sich der Mauszeiger über einem Editor-Panel befindet.
bool ShowGrid, bool ShowMarkersDie Schalter für G und M.
PathingOverlayMode? PathingOverlay, float OverlayOpacityDas Pathing-Overlay (null = aus) und seine Deckkraft (0 bis 1).
string MapsFolder { get; set; }Der Kartenordner. Ist der Wert leer, wird RTSMapSerializer.DefaultMapsFolder verwendet. Ein relativer Pfad bezieht sich auf RTSMapSerializer.StorageRoot.
bool ShowQuitCommand, string ManualUrlDer Menüeintrag File ▸ Quit und der Link zum Handbuch (siehe Inspector).
bool HasManual { get; }True, wenn es für Open Manual eine Seite zum Öffnen gibt.
static bool CanShowFolders, static bool CanQuitApplicationPlattformprüfungen: Ein Desktop-Dateibrowser ist verfügbar bzw. das Beenden der Anwendung ist möglich (nicht unter WebGL, iOS oder Android).
static bool RunsInBrowser, static bool StoresMapsInBrowserRunsInBrowser ist in Web-Builds true. Dort gelten die Einschränkungen des Browsers, und der Befehl New Map liegt auf Alt+N. StoresMapsInBrowser ist true, wenn Karten im Browserspeicher abgelegt werden (WebGL-Player). Siehe Web-Builds (WebGL).
static bool HasReliableFunctionKeysFalse in Webbrowsern und auf mobilen Plattformen. Dort blendet die Schließen-Schaltfläche die Schaltfläche Open Map Editor ein.
VisualElement RootElement, UIDocument Document, RuntimeToolGizmos GizmosDas Wurzelelement der Editor-UI, ihr UI-Dokument und der Gizmo-Renderer. Alle sind null, solange die Komponente noch nicht aktiviert wurde.
Befehle
bool SetActiveTool(string toolId)Aktiviert ein Werkzeug anhand seiner ID, zum Beispiel "paint.layer". Gibt false zurück, wenn es die ID nicht gibt.
void SelectCategory(ToolCategory category)Wie die Zahlentasten: Aktiviert das zuletzt verwendete Werkzeug der Kategorie oder, wenn die Kategorie bereits aktiv ist, das nächste Werkzeug.
RTSTool GetRememberedTool(ToolCategory category)Das zuletzt verwendete Werkzeug einer Kategorie oder ihr erstes Werkzeug.
void Undo(), void Redo()Rückgängig machen und wiederherstellen. Während eines Spieltests bewirken beide nichts.
bool OpenMap(string path)Lädt eine .rtsmap-Datei. Nicht gespeicherte Änderungen werden ohne Rückfrage verworfen.
bool SaveMap(string path)Speichert unter dem angegebenen Pfad, ergänzt bei Bedarf die Endung .rtsmap und schreibt das Vorschaubild.
bool SaveCurrent()Wie Ctrl+S. Gibt true zurück, wenn die Karte sofort geschrieben wurde. Hat die Karte noch keine Datei, öffnet die Methode den Dialog Save As und gibt false zurück.
void NewMap(MapCreationSettings settings)Erstellt eine neue flache Karte, ohne wegen nicht gespeicherter Änderungen nachzufragen.
bool DeleteMapFile(string path)Löscht eine Kartendatei und ihr Vorschaubild.
ShowNewMapDialog(), ShowOpenDialog(), ShowSaveAsDialog(), ShowGenerateDialog(), ShowMapInfoDialog(), ShowResizeDialog(), ShowHelp()Öffnen die Dialoge mit demselben Verhalten wie über die Menüs. New und Open fragen bei nicht gespeicherten Änderungen zuerst nach.
void StartPlaytest(), void StopPlaytest()Starten bzw. beenden einen Spieltest.
void TogglePathingOverlay()Wie P.
void DeleteSelectedDecor(), void DuplicateSelectedDecor()Löschen bzw. duplizieren die Dekorauswahl. Jeder Aufruf ist ein Undo-Schritt.
void ShowMapsFolder(), void OpenManual(), void Quit()Die Befehle aus den Menüs File und Help. Quit fragt bei nicht gespeicherten Änderungen nach.
bool DownloadMap(), void ImportMap()Nur in Web-Builds: File ▸ Download Map und File ▸ Import Map.... Browser zeigen die Dateiauswahl nur als Reaktion auf einen Klick oder Tastendruck an. Rufe ImportMap daher aus einem Klick-Handler auf.
void ShowToast(string message, ToastType type = ToastType.Info)Zeigt eine Toast-Meldung an. ToastType ist Info, Success, Warning oder Error.
void ShowToast(string message, ToastType type, string key)Ist noch eine Toast-Meldung mit demselben Schlüssel sichtbar, wird diese aktualisiert, und es kommt keine neue hinzu.
Events
event Action<string> MapSavedNachdem eine Karte gespeichert wurde (vollständiger Dateipfad).
event Action<string> MapLoadedNachdem eine Karte aus einer Datei geöffnet (vollständiger Pfad) oder neu erstellt wurde (null).
event Action<bool> VisibilityChangedWenn sich EditorVisible ändert.

MapLoaded und RTSRuntimeEditorEvents.MapOpened werden bei File ▸ New und Open ausgelöst. Um auf jede Kartenänderung zu reagieren, auch auf Resize und auf Karten, die dein eigener Code lädt, abonniere RTSTerrain.MapLoaded am Terrain.

Beispiel: den Editor aus deinem eigenen Menü öffnen

Viele Spiele öffnen den Karteneditor über das Hauptmenü statt über eine Taste. Schalte die Taste zum Ein- und Ausblenden ab, öffne den Editor über eine Schaltfläche und füge der obersten Ebene des Editors eine Schaltfläche „Back to Menu“ hinzu:

using UnityEngine;
using UnityEngine.UIElements;
using RTSTerrainStudio;

public class MapEditorLauncher : MonoBehaviour
{
    [SerializeField] RTSRuntimeEditor editor;   // with "Start Visible" turned off
    [SerializeField] GameObject mainMenu;       // your own menu UI

    void Start()
    {
        // No F2 key: the game decides when the editor is shown (on desktop this also hides the close button).
        editor.ToggleKey = RTSKey.None;

        // RootElement exists once the editor component is enabled (it is created in its OnEnable).
        // If you disable and enable the editor component later, its UI is rebuilt: add the button again.
        var back = new Button(BackToMenu) { text = "Back to Menu" };
        back.AddToClassList("rts-button");             // use the editor's button style
        back.style.position = Position.Absolute;
        back.style.right = 16;
        back.style.bottom = 56;                        // just above the status bar
        editor.RootElement.Add(back);                  // hidden together with the editor

        editor.VisibilityChanged += visible => mainMenu.SetActive(!visible);
    }

    /// <summary>Hook this up to a "Map Editor" button in your main menu.</summary>
    public void OpenEditor() => editor.EditorVisible = true;

    void BackToMenu()
    {
        if (editor.IsPlaytesting) editor.StopPlaytest();
        editor.EditorVisible = false;
        editor.ShowToast("Map editor closed", ToastType.Info);
    }
}

Beispiel: das HUD deines Spiels beim Bearbeiten ausblenden

using UnityEngine;
using RTSTerrainStudio;

public class HudVisibility : MonoBehaviour
{
    [SerializeField] RTSRuntimeEditor editor;
    [SerializeField] GameObject hud;

    void OnEnable()
    {
        editor.VisibilityChanged += HandleVisibilityChanged;
        RTSRuntimeEditorEvents.PlaytestChanged += HandlePlaytestChanged;
        Refresh();
    }

    void OnDisable()
    {
        editor.VisibilityChanged -= HandleVisibilityChanged;
        RTSRuntimeEditorEvents.PlaytestChanged -= HandlePlaytestChanged;
    }

    void HandleVisibilityChanged(bool visible) => Refresh();
    void HandlePlaytestChanged(bool playtesting) => Refresh();

    // The editor stays "visible" during a play test (only its panels make room),
    // so both states are checked.
    void Refresh() => hud.SetActive(!editor.EditorVisible || RTSRuntimeEditorEvents.IsPlaytesting);
}

Die Oberfläche anpassen

Wo die Styles liegen

Datei (in Resources/RTSTerrainStudio/UI)Geladen alsZweck
RTSRuntimeTheme.tssRTSRuntimeEditor.ThemeResourcePath = "RTSTerrainStudio/UI/RTSRuntimeTheme"Das Theme-Stylesheet. Es importiert Unitys Standard-Laufzeit-Theme (Schieberegler, Schalter, Textfelder, Scroll-Ansichten) und RTSRuntimeEditor.uss.
RTSRuntimeEditor.ussRTSRuntimeEditor.StyleSheetResourcePath = "RTSTerrainStudio/UI/RTSRuntimeEditor"Alle Styles des Editors. Jeder Klassenname beginnt mit rts-, und Typselektoren wie Label gelten nur unterhalb von .rts-root. So verändert der Editor nie das Aussehen deiner Spiel-UI, selbst wenn dein Panel dieses Theme verwendet.

Wenn deine Panel Settings ein anderes Theme verwenden, fügt der Editor RTSRuntimeEditor.uss seinem eigenen Wurzelelement hinzu. So sieht er in beiden Fällen gleich aus.

Farb-Tokens

Alle Farben sind Variablen, die auf .rts-root definiert sind. Ändere sie, um dem gesamten Editor ein neues Aussehen zu geben:

VariableStandardVerwendet für
--rts-panelrgba(20, 24, 30, 0.92)Panel-Hintergründe (obere Leiste, Werkzeugleiste, Werkzeug-Panel, Statusleiste).
--rts-panel-solidrgb(22, 26, 33)Deckende Hintergründe (Dialoge, Popups).
--rts-raised / --rts-hover / --rts-pressedWeiß mit 6 % / 11 % / 3 % DeckkraftZustände von Schaltflächen.
--rts-border / --rts-border-strongWeiß mit 8 % / 16 % DeckkraftRahmen und Trennlinien.
--rts-inputrgba(0, 0, 0, 0.32)Hintergründe von Textfeldern und Schiebereglern.
--rts-text / --rts-text-dim / --rts-text-faintrgb(226, 231, 238) / rgb(150, 160, 174) / rgb(104, 114, 128)Text, Sekundärtext, Hinweise.
--rts-accent / --rts-accent-hover / --rts-accent-dim / --rts-accent-textBlautöne rund um rgb(61, 169, 252)Ausgewählte Werkzeuge, primäre Schaltflächen, Play Test.
--rts-danger / --rts-danger-dimrgb(239, 83, 80)Schaltflächen zum Löschen und Toast-Meldungen für Fehler.
--rts-warning / --rts-successrgb(255, 183, 77) / rgb(102, 187, 106)Toast-Meldungen für Warnungen und Erfolge.
--rts-radius / --rts-radius-small6px / 4pxEckenradius.

Neues Aussehen, ohne Paketdateien anzufassen

Bearbeite die Dateien im Ordner Resources des Pakets nicht direkt. Ein Paket-Update überschreibt sie. Lege deine Änderungen stattdessen in einem eigenen Stylesheet ab.

  1. Erstelle ein Stylesheet in deinem Projekt, zum Beispiel Assets/UI/MyEditorSkin.uss, und überschreibe die Tokens. Der Selektor :root .rts-root ist spezifischer als .rts-root aus dem Paket, deshalb setzen sich deine Werte durch:
    /* MyEditorSkin.uss: an orange accent and warmer panels */
    :root .rts-root {
        --rts-accent: rgb(255, 160, 50);
        --rts-accent-hover: rgb(255, 190, 110);
        --rts-accent-dim: rgba(255, 160, 50, 0.22);
        --rts-accent-text: rgb(255, 210, 160);
        --rts-panel: rgba(30, 24, 20, 0.94);
    }
  2. Füge das Stylesheet beim Start dem UI-Dokument des Editors hinzu:
    using UnityEngine;
    using UnityEngine.UIElements;
    using RTSTerrainStudio;
    
    public class EditorSkin : MonoBehaviour
    {
        [SerializeField] RTSRuntimeEditor editor;
        [SerializeField] StyleSheet skin;   // drag MyEditorSkin.uss here
    
        void Start()
        {
            // The document root holds the editor and its toast layer, so both get the new colours.
            editor.Document.rootVisualElement.styleSheets.Add(skin);
        }
    }

Auf dieselbe Weise kannst du auch einzelne Teile umgestalten. Die wichtigsten Klassen sind .rts-topbar, .rts-leftbar, .rts-toolpanel, .rts-statusbar, .rts-dialog, .rts-popup, .rts-toast (mit den Varianten --success, --warning und --error), .rts-hint, .rts-playtest-bar, .rts-button und .rts-selected. Der UI Toolkit Debugger (Window ▸ UI Toolkit ▸ Debugger) zeigt die Klassen jedes Elements an, während das Spiel läuft.

Panel Settings, Skalierung und Sortierung

  • Panel Settings leer (Standard): Der Editor erstellt eigene Panel Settings mit dem RTS-Theme. Die UI skaliert mit der Bildschirmhöhe (Referenz 1920 × 1080), aber nie unter 85 %. Ui Scale multipliziert das Ergebnis. Das ist nützlich für 4K-Bildschirme oder für Spieler, die größeren Text möchten.
  • Eigene Panel Settings: Weise ein Asset zu, um Theme und Skalierung selbst zu steuern. Tools ▸ RTS Terrain Studio ▸ Create Demo Content erstellt ein fertiges Asset unter Resources/RTSTerrainStudio/UI/RTSRuntimePanelSettings.asset (RTS-Theme, Skalierung mit der Bildschirmgröße, 1920 × 1080). Die Szene In-Game Editor Demo verwendet es.
  • Enforce Minimum UI Scale (standardmäßig an): Wenn dein Asset mit der Bildschirmgröße skaliert und nicht in eine Textur rendert, arbeitet der Editor mit einer Laufzeitkopie davon. Das Asset selbst wird nie verändert. Die Kopie skaliert auf großen Bildschirmen genau wie das Asset, wird in kleinen Fenstern nie kleiner als 85 %, und Ui Scale multipliziert sie. Ihre Sortierreihenfolge liegt einen halben Schritt über der des Assets. So bleibt der Editor über einem Spiel-HUD, das dasselbe Asset verwendet (das HUD erhält dann ein eigenes Panel). Schalte die Option aus, um das Asset unverändert zu verwenden, zum Beispiel um ein Panel mit deiner Spiel-UI zu teilen. Ui Scale und das Minimum von 85 % werden dann nicht angewendet.
  • Web-Builds: Das Minimum von 85 % wird in CSS-Pixeln gemessen. So behält die UI auf hochauflösenden Bildschirmen dieselbe Größe.
  • Sorting Order: Standardmäßig 1000, damit der Editor dein HUD überdeckt. Senke den Wert, wenn Teile deiner UI über dem Editor erscheinen müssen.
  • Eigene Bedienelemente hinzufügen: Füge Elemente zu RootElement hinzu (siehe das Launcher-Beispiel oben). Sie werden zusammen mit dem Editor ausgeblendet. Befindet sich der Mauszeiger über ihnen, blockiert das wie bei jedem anderen Panel das Malen.

Den In-Game-Editor mit dem Spiel ausliefern

Arbeite diese Liste ab, bevor du einen Build deines Spiels mit dem In-Game-Editor erstellst.

  1. Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und behebe die gemeldeten Probleme. Für Builds ist die Prüfung Shader references am wichtigsten (siehe nächsten Schritt).
  2. Shader werden über RTSShaderReferences eingebunden. Unity lässt jeden Shader aus dem Build weg, den kein Material im Build verwendet. Das Paket erstellt die meisten seiner Materialien zur Laufzeit (Terrain, Wasser, Nebel des Krieges und die Gizmo-Shader des Editors Hidden/RTS Terrain Studio/GizmoLines und Hidden/RTS Terrain Studio/GizmoGhost). Deshalb hinterlegt es die Shader im Asset Resources/RTSTerrainStudio/RTSShaderReferences. Dieses Asset wird automatisch aktualisiert, nachdem Skripte kompiliert wurden, wenn Shader importiert werden und vor jedem Player-Build. Fehlt es oder ist es veraltet, verwende die Korrektur Update des Validators. Ohne diese Shader erscheinen im Build weder die Pinselumrisse noch die Dekorvorschauen.
  3. Behalte den Ordner Resources/RTSTerrainStudio mit UI/RTSRuntimeTheme.tss, UI/RTSRuntimeEditor.uss und RTSShaderReferences.asset. Der Editor lädt diese Dateien über ihren Resources-Pfad. Fehlt das Theme, meldet die Console Controls will be unstyled.
  4. Panel Settings: Wenn du ein Asset vom Typ Panel Settings zugewiesen hast, referenziert die Szene es, und es landet automatisch im Build. Hast du das Feld leer gelassen, laden die zur Laufzeit erstellten Panel Settings das Theme aus Resources (Schritt 3).
  5. Eingabe: Der Editor funktioniert mit dem Paket Input System und mit dem alten Input Manager. Siehe Eingabeverarbeitung.
  6. Lege die Optionen für die Spieler fest: Manual Url (Link zu deinem Modding-Leitfaden; leer lassen, um die Schaltfläche auszublenden), Show Quit Command (ausschalten, wenn dein Spiel ein eigenes Menü hat), Toggle Key und Start Visible.
  7. Kartenordner: Der Standardordner Maps in RTSMapSerializer.StorageRoot (persistentDataPath) ist auf allen Desktop-Plattformen beschreibbar. Wenn du Spielern Beispielkarten mitgeben willst, kopiere sie beim ersten Start dorthin (siehe Karten mit deinem Spiel ausliefern).
  8. Web-Builds: Installiere das Vollfenster-Template und lies den Abschnitt Web-Builds (WebGL). Er behandelt Browserspeicher, Downloads, Tasten und Hosting.
  9. Eigene Werkzeuge und Code-Stripping: Steht die Einstellung Managed Stripping Level über Minimal, markiere deine eigenen Werkzeuge mit [Preserve] (siehe Code-Stripping und eigene Werkzeuge).
  10. Inhalte: Spieler können nur das Tileset, die Vegetationsbibliothek und die Dekorbibliothek verwenden, die dem Terrain in der Editorszene zugewiesen sind. Sie landen im Build, weil die Szene sie referenziert.
  11. Teste einen Development Build: Probiere darin je einmal Malen, Speichern, Öffnen, einen Spieltest und das Beenden mit nicht gespeicherten Änderungen aus.

Du lieferst den In-Game-Editor nicht mit aus? Sein Code liegt in Runtime/RuntimeEditor, die Menüunterstützung in Editor/RuntimeEditorSupport. Ein Projekt, das ihn nicht braucht, kann den Ordner Runtime/RuntimeEditor löschen. RTSRuntimeEditorEvents bleibt in Runtime/Core, sodass Gameplay-Code, der auf Spieltests reagiert, weiterhin kompiliert.

Fehlerbehebung

ProblemUrsache und Lösung
Die Hinweismeldung No RTS Terrain found erscheint.Die Szene enthält kein aktiviertes RTS Terrain, oder das zugewiesene Terrain ist deaktiviert. Füge über GameObject ▸ RTS Terrain Studio ▸ RTS Terrain eines hinzu oder aktiviere es.
Die Hinweismeldung No camera found erscheint.Keine Kamera hat das Tag MainCamera, und Target Camera ist leer. Gib deiner Kamera dieses Tag oder weise sie zu.
Klicks auf die Karte bewirken nichts.Der Mauszeiger befindet sich über einem Panel, Alt ist gedrückt (Kameradrehung), ein Dialog ist geöffnet, oder kein Werkzeug ist aktiv. Ist kein Werkzeug aktiv, zeigt die Statusleiste Pick a tool on the left an.
Tastenkürzel bewirken nichts.Ein Textfeld hat den Tastaturfokus, oder ein Dialog ist geöffnet. Drücke Esc oder klicke auf die Karte.
F5 oder F2 bewirkt nichts.F5 funktioniert nur, solange der Editor sichtbar ist und kein Dialog offen ist. Die Taste zum Ein- und Ausblenden wird während eines Spieltests ignoriert (beende ihn zuerst mit Esc oder F5), ebenso während du in ein Feld tippst. Auf Macs und vielen Laptops sind die Funktionstasten Medientasten: Halte Fn gedrückt, oder verwende die Schaltfläche Play Test und in Web-Builds die Schaltfläche Open Map Editor.
Esc beendet den Spieltest, obwohl mein Spiel die Taste verwendet hat.Rufe in diesem Frame RTSRuntimeEditorEvents.ConsumeEscape() auf (siehe das Beispiel).
Während eines Spieltests passiert nichts.Kein Skript reagiert auf RTSRuntimeEditorEvents.PlaytestChanged. Füge RTSDemoGame oder eine eigene Komponente hinzu (siehe Play Test).
Nach einem Spieltest erscheint die Toast-Meldung undo history was reset.Dein Gameplay-Code hat die Karte während des Tests ersetzt, neu geladen oder in der Größe geändert. Die Karte selbst wird korrekt wiederhergestellt.
Die UI ist zu klein oder zu groß.Ändere Ui Scale. Wenn du eigene Panel Settings zugewiesen hast, die nicht mit der Bildschirmgröße skalieren, oder Enforce Minimum UI Scale ausgeschaltet hast, ändere stattdessen die Skalierung in diesem Asset. Verwendet ein Web-Build ein Template mit fester Größe, installiere das Vollfenster-Template (siehe Das Vollfenster-Template).
Mein HUD verdeckt den Editor, oder der Editor verdeckt ein Menü.Passe Sorting Order an (Standard 1000).
Bedienelemente erscheinen ohne Styling (weißes Standardaussehen von Unity).Das Theme wurde in keinem Resources-Ordner gefunden. Stelle Resources/RTSTerrainStudio/UI wieder her.
Pinselumrisse und Vorschauen fehlen im Build.Die Gizmo-Shader wurden nicht in den Build aufgenommen. Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und aktualisiere die Shader-Referenzen.
Der Willkommenshinweis erscheint nie wieder.Der Spieler hat ihn geschlossen. Verwende Open Welcome Hint im F1-Fenster oder lösche den PlayerPrefs-Schlüssel RTSTerrainStudio.InGameEditor.HintDismissed.
Meine Karten erscheinen nicht im Kartenbrowser.Der Browser listet nur .rtsmap-Dateien auf, die direkt in dem Kartenordner liegen, der oben im Browser angezeigt wird. Kopiere die Dateien dorthin und klicke auf Refresh.
Eine Karte zeigt Unreadable or damaged file an.Die Datei ist keine gültige .rtsmap-Datei, oder sie ist unvollständig (zum Beispiel durch einen abgebrochenen Kopiervorgang).
In einem Web-Build gespeicherte Karten sind verschwunden.Der Browser speichert sie pro Website. Unter einer anderen Adresse oder nach dem Löschen der Browserdaten sind daher keine Karten vorhanden. Außerdem kann Safari die Daten von Websites löschen, die sieben Tage lang nicht genutzt wurden. Verwende File ▸ Download Map, um Kopien aufzubewahren (siehe Karten im Browser).
Der Editor hat in einem Build keine Werkzeuge.Code-Stripping hat sie entfernt. Behalte die Datei Runtime/link.xml des Pakets und markiere deine eigenen Werkzeuge mit [Preserve] (siehe Code-Stripping und eigene Werkzeuge).

Rendering und Weltsysteme

Dieses Kapitel erklärt, wie aus einer Karte Pixel werden: wie das Terrain-Mesh aufgebaut und schattiert wird und wie Vegetation, Wasser, Dekor und Szenenbeleuchtung gezeichnet werden. Jeder Abschnitt beginnt mit dem, was du siehst, und mit den Einstellungen, die du ändern kannst. Weiter unten folgen die Scripting-API, der Shader-Vertrag und die Performance-Zahlen, die Programmierer brauchen.

Die Rendering-Komponenten

Jedes System liest dieselben MapData eines RTSTerrain und baut nur neu auf, was eine Bearbeitung verändert hat. Die Systeme sind eigene Komponenten am GameObject des Terrains. GameObject ▸ RTS Terrain Studio ▸ RTS Terrain fügt sie alle für dich hinzu.

KomponenteZeichnetErzeugte ObjekteAbschnitt
RTSTerrainBoden, Klippen, Rampen (Chunk-Meshes, ein gemeinsames Material), das Zellraster, Overlays und den PinselcursorWurzelobjekt „RTS Chunks“ mit einem Kindobjekt pro ChunkTerrain-Rendering, Terrain-Shader
RTSVegetationRendererGemaltes Gras und Pflanzen (Details), BäumeVerborgenes Objekt „RTS Vegetation Objects“ (Collider-Stellvertreter, Bäume als GameObjects)Vegetation
RTSWaterRendererTransparente Wasseroberfläche mit Tiefenfarbe, Wellen, Lichtbrechung und SchaumVerborgenes Wurzelobjekt „RTS Water“ mit einem Kindobjekt pro Chunk, der Wasser enthältWasser
RTSDecorManagerProps und Doodads, als GameObjects oder per GPU-InstancingContainer „RTS Decor“ für gespawnte ObjekteDekor
RTSEnvironment (optional)Selbst nichts. Die Komponente überträgt Sonne, Umgebungslicht und Nebel der Karte auf die SzeneKeineUmgebung

Erzeugte Objekte sind mit DontSave markiert. Sie werden nie in deine Szenendatei geschrieben: Die .rtsmap-Datei ist die einzige maßgebliche Quelle, und beim Öffnen der Szene wird alles daraus neu aufgebaut. Klickst du in der Scene View auf einen erzeugten Chunk, einen Baum oder eine Wasseroberfläche, wird stattdessen das zugehörige Terrain ausgewählt.

Fehlt eine der Renderer-Komponenten, zeigt der Inspector von RTSTerrain eine Warnung (zum Beispiel „No Water Renderer: painted water is not shown.“) mit einer Schaltfläche Add an.

Terrain-Rendering

Chunks

Die Karte ist in quadratische Chunks mit 16, 32 (Standard) oder 64 Zellen Seitenlänge aufgeteilt. Jeder Chunk ist ein GameObject mit einem MeshFilter, einem MeshRenderer, einem optionalen MeshCollider und einer Komponente RTSTerrainChunk. Alle Chunks teilen sich eine Materialinstanz (RTSTerrain.TerrainMaterial). Dadurch bleibt das Terrain mit dem SRP Batcher kompatibel.

  • Chunks werden zeilenweise gespeichert: Index = z * ChunkCount.x + x. Die letzte Zeile und die letzte Spalte können kleiner sein, wenn die Kartengröße kein Vielfaches der Chunk-Größe ist.
  • Eine Bearbeitung baut nur die Chunks neu auf, die sie berührt. Jedes geänderte Rechteck wird um eine Zelle erweitert, weil eine Vertex-Änderung auch die Normalen und Klippenformen der Nachbarzellen beeinflusst. Wenn du nahe an einer Chunk-Grenze malst, wird deshalb auch der benachbarte Chunk neu aufgebaut.
  • Das Malen von Bodentexturen baut keine Meshes neu auf. Nur die Splat-Texturen werden aktualisiert (siehe Splat-Auflösung).
  • Auch das Malen von Wasser baut keine Terrain-Chunks neu auf. Nur der Wasser-Renderer reagiert darauf.

Das Chunk-Mesh liegt in kartenlokalen Koordinaten: Chunk-Objekte haben unter dem verborgenen Wurzelobjekt RTS Chunks des Terrains eine lokale Identitätstransformation. Dieses Wurzelobjekt gleicht die Skalierung des Terrains aus, sodass der Objektraum den kartenlokalen Koordinaten entspricht (X/Z von 0 bis zur Weltgröße der Karte, Y nach oben). Der Transform des Terrains darf verschoben und um Y gedreht werden. Eine Skalierung wird ignoriert.

Mesh-Erzeugung

RTSChunkMeshBuilder erzeugt die Chunk-Meshes mit Burst-Jobs und schreibt sie über Mesh.MeshDataArray:

  1. Die angeforderten Chunks werden in Batches von bis zu RTSChunkMeshBuilder.MaxChunksPerBatch (256) Chunks gesammelt. Das begrenzt den Spitzenspeicherbedarf auf sehr großen Karten.
  2. Müssen nur wenige Chunks neu aufgebaut werden (typisch beim Malen), wird jeder Chunk in Zeilenstreifen aufgeteilt. Die Streifen werden auf verschiedenen Worker-Threads erzeugt und anschließend zusammengeführt. So nutzt selbst die Bearbeitung eines einzelnen Chunks alle CPU-Kerne.
  3. BuildChunkGeometryJob (Burst, parallel) erzeugt die Dreiecke. Glatte Zellen und Rampenzellen werden zu einem regelmäßigen Raster aus S × S Quads (S = Surface Subdivisions). Klippenzellen werden auf einem feineren Unterraster (Cliff Subdivisions) mit Marching Squares nachgezeichnet. Das ergibt abgerundete Konturen, einen flachen Boden für jede Stufe und senkrechte Wände dazwischen.
  4. Collider-Daten werden parallel mit Physics.BakeMesh gebacken und den MeshCollider-Komponenten der Chunks zugewiesen.
  5. Build schließt alle Arbeit ab, bevor die Methode zurückkehrt. So ist eine Bearbeitung noch im selben Frame sichtbar.

Benachbarte Zellen und Chunks erzeugen immer bitidentische Randvertices, weil jede Vertex-Position nur von ihrer Kartenposition abhängt. Deshalb gibt es keine Risse zwischen Chunks. Der Generator folgt der Referenzoberfläche MapGeometryView, die auch von Höhenabfragen, dem Pathing und dem Wasser-Renderer verwendet wird. Der gerenderte Boden ist daher exakt der Boden, den das Gameplay sieht.

Das Vertex-Format ist RTSTerrainVertex (28 Byte: Position, Normale und eine gepackte Farbe). Die Farbe transportiert Daten für den Shader: r = Klippenwandmaske, g = Klippentyp, b = gebackene Ambient Occlusion. Es gibt weder UVs noch Tangenten: Der Shader bildet die Texturen anhand der Vertex-Position ab.

Formgebung der Klippenwände

Klippen werden prozedural erzeugt und brauchen daher keine von Artists modellierten Klippenteile. Vier Einstellungen steuern das Aussehen der Wände:

EinstellungStandardBereichBeschreibung
Cliff Noise0.180–0.5Zufällige horizontale Verschiebung der Vertices von Klippenwänden, als Anteil einer Zelle. Gibt den Wänden ein natürliches Felsaussehen. 0 = vollkommen glatte Wände.
Cliff Noise Scale1.6≥ 0.1Strukturgröße des Klippenrauschens in Welteinheiten. Kleine Werte ergeben feinen, zerklüfteten Fels, große Werte breite Ausbuchtungen.
Cliff Slant0.120–0.4Wie weit die Oberkante einer Klippe zurückgezogen wird, als Anteil einer Zelle. Macht die Wände leicht schräg statt perfekt senkrecht.
Cliff Wall Segments31–8Vertikale Segmente pro Klippenstufe. Mehr Segmente geben dem Rauschen mehr Vertices zum Arbeiten (mehr Relief), kosten aber auch mehr Vertices.

Rauschen und Schrägung zusammen können einen Vertex nie weiter als 45 % einer Zelle verschieben (RTSChunkMeshParams.MaxDisplacementFraction). Ergeben beide Einstellungen in Summe mehr, werden sie gemeinsam herunterskaliert, sodass sich Bodendreiecke neben einer Wand nie überlappen. In der Nähe von Rampen und am Kartenrand läuft die Verschiebung aus, damit diese Kanten gerade bleiben.

Außerdem wird die Wandgeometrie an ihrem Fuß durch die gebackene Ambient Occlusion (Vertex-Farbe b) abgedunkelt. Wo zwei Wände an einer Zellkante aufeinandertreffen, verwenden beide dieselbe Helligkeit. So wirken gestapelte und verschmolzene Klippen nahtlos.

Collider und Abfragen

Mit Collider Mode = MeshCollider (Standard) erhält jeder Chunk einen MeshCollider auf dem Chunk Layer. Er wird nach jeder Bearbeitung zusammen mit dem Mesh neu aufgebaut. Verwende diese Einstellung, wenn dein Spiel auf Unity-Physik angewiesen ist (Physik-Raycasts, Rigidbodies, Backen des NavMesh).

Mit ColliderMode.None werden keine Collider erzeugt. Das Terrain lässt sich trotzdem ohne Physik abfragen: RTSTerrain.Raycast, GetHeight, GetNormal, GetCliffLevel, IsWater und GetPathing arbeiten mit der analytischen Referenzoberfläche, im Edit Mode und zur Laufzeit. Das spart den Speicherbedarf und die Cooking-Zeit der Collider.

using RTSTerrainStudio;
using UnityEngine;

/// Finds the terrain point under the mouse, with or without chunk colliders.
public class TerrainPointer : MonoBehaviour
{
    [SerializeField] Camera viewCamera;

    void Update()
    {
        if (viewCamera == null) return;
        Ray ray = viewCamera.ScreenPointToRay(RTSInput.PointerPosition);

        // 1. Physics-free: works even when Collider Mode is None.
        //    RaycastAll tests every RTSTerrain in the scene and returns the nearest hit.
        if (RTSTerrain.RaycastAll(ray, out RTSTerrainHit hit))
        {
            Debug.Log($"Cell {hit.cell} at {hit.point}, cliff level {hit.terrain.GetCliffLevel(hit.point)}");
        }

        // 2. Unity physics: needs Collider Mode = MeshCollider.
        //    RTSTerrainChunk.GetTerrain maps the collider of a chunk back to its terrain.
        if (Physics.Raycast(ray, out RaycastHit physicsHit, 1000f))
        {
            RTSTerrain terrain = RTSTerrainChunk.GetTerrain(physicsHit.collider);
            if (terrain != null) Debug.Log($"Physics hit on terrain '{terrain.name}'");
        }
    }
}

Rendering-Einstellungen des Terrains

Diese Einstellungen findest du im Inspector von RTSTerrain, gruppiert in Chunks & Geometry, Cliff Shape, Physics und Rendering. Wenn du eine davon im Inspector änderst, wird das Terrain automatisch neu aufgebaut. Per Code änderst du terrain.Settings und rufst danach terrain.RebuildAll() auf.

EinstellungStandardBeschreibung
Chunk Size32Zellen pro Chunk-Seite (16, 32 oder 64). Kleinere Chunks werden beim Bearbeiten schneller neu aufgebaut, größere Chunks bedeuten weniger Draw Calls. Siehe Die Chunk-Größe wählen.
Surface Subdivisions2Quads pro Zellseite auf glattem Boden und Rampen (1–4). 1 = klassischer RTS-Look, 2–4 = weichere Hügel.
Cliff Subdivisions4Auflösung des Unterrasters, mit dem abgerundete Klippenkonturen innerhalb einer Klippenzelle nachgezeichnet werden (2–8). Der Wert wird auf eine gerade Zahl aufgerundet, die zugleich ein Vielfaches von Surface Subdivisions ist (siehe Unterteilungen).
Cliff Noise, Cliff Noise Scale, Cliff Slant, Cliff Wall Segmentssiehe Formgebung der KlippenwändeAussehen der Klippenwände.
Collider ModeMeshColliderNone oder MeshCollider (einer pro Chunk, nach Bearbeitungen neu aufgebaut).
Chunk Layer0 (Default)GameObject-Layer der Chunks. Er wird von den Collidern und den Culling-Masken der Kameras verwendet. Gemalte Details werden ebenfalls auf diesem Layer gezeichnet.
Cast ShadowsOnSchattenwurfmodus der Chunk-Renderer.
Receive ShadowsanOb die Chunks Schatten empfangen.
Material OverrideleerOptionales Material, das anstelle des Standardmaterials verwendet wird (siehe Materialien).
Show GridausZeichnet das Zellraster dauerhaft (vor allem zum Bearbeiten).
Grid Opacity0.35Deckkraft des Rasters, wenn Show Grid eingeschaltet ist.

Die Schaltfläche Rebuild Terrain unten im Inspector (RTSTerrain.RebuildAll()) zerstört alle Chunks, Texturen und die Pathing-Karte und erzeugt sie neu. Vegetation, Wasser und Dekor werden dabei ebenfalls neu aufgebaut.

Materialien, Overlays und das Raster

Standardmäßig erzeugt das Terrain ein eigenes Material aus dem Shader RTS Terrain Studio/Terrain (RTSTerrain.TerrainShaderName). Die Quelle des Materials wählt das Terrain in dieser Reihenfolge:

  1. Material Override in den Terrain-Einstellungen (RTSTerrainSettings.materialOverride).
  2. Material Override des Tilesets (RTSTileset.materialOverride), sodass alle Terrains, die dieses Tileset verwenden, gleich aussehen.
  3. Ein neues Material mit RTS Terrain Studio/Terrain.

Das Terrain arbeitet immer mit einer Kopie des Quellmaterials und füllt sie mit den Splatmaps, Textur-Arrays und Kartenparametern. Ein Override-Material muss den Terrain-Shader oder einen Shader verwenden, der dieselben Eigenschaften deklariert (siehe Schnittstelle der Shader-Eigenschaften).

Da das Terrain eine Kopie rendert, werden spätere Änderungen am Asset des Override-Materials nicht automatisch übernommen. Klicke nach einer Änderung auf Rebuild Terrain (oder rufe RebuildAll() auf). Um einen Wert zur Laufzeit ohne Neuaufbau zu ändern, setze ihn direkt auf terrain.TerrainMaterial.

Wird der Terrain-Shader nicht gefunden, etwa in einem Player-Build, der ihn nicht enthält, wird das Terrain mit einem schlichten grauen URP-Lit-Material gezeichnet. Dabei wird folgende Warnung ausgegeben: „Shader 'RTS Terrain Studio/Terrain' was not found.“ Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus. Die dortige Prüfung „Shaders included in builds“ erzeugt das Asset RTSShaderReferences, das dafür sorgt, dass alle Shader des Pakets in Builds enthalten bleiben (siehe Einen Build erstellen).

Overlay. SetOverlayTexture(texture, opacity) legt eine beliebige Textur über die ganze Karte, vom ersten Vertex bis MapData.WorldSize. Die Terrain-Farbe wird mit Textur-Alpha × Deckkraft in Richtung des RGB-Werts der Textur überblendet. Der Editor nutzt das für die Pathing-Ansicht. Du kannst es für Heatmaps, Territorien oder Vorschauen der Gebäudeplatzierung verwenden. Das Overlay ist Teil der Oberflächenfarbe und wird daher wie das Terrain beleuchtet und beschattet.

Raster. SetGridVisible(visible, opacity) erzwingt die Anzeige des kantengeglätteten Zellrasters per Code. Jede 4. Linie wird dicker gezeichnet. In der Scene View schaltet der Editor das Raster während der Bearbeitung mit G um. IsGridVisible ist true, wenn das Raster per Code oder durch Show Grid angezeigt wird.

using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

/// Shows unwalkable cells in red on top of the terrain (one overlay pixel per cell).
public class WalkabilityOverlay : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;
    Texture2D overlay;

    public void Show()
    {
        MapData map = terrain != null ? terrain.Map : null;
        if (map == null) return;

        // One texel per cell. Point filtering keeps hard cell borders.
        overlay = new Texture2D(map.Width, map.Height, TextureFormat.RGBA32, false)
        {
            filterMode = FilterMode.Point,
            wrapMode = TextureWrapMode.Clamp
        };

        var pixels = new Color32[map.Width * map.Height];
        for (int z = 0; z < map.Height; z++)
        {
            for (int x = 0; x < map.Width; x++)
            {
                // Texture row 0 is the map edge at z = 0, like the map itself.
                Vector3 world = terrain.CellCenterToWorld(new int2(x, z));
                bool walkable = terrain.IsWalkable(world);
                // Alpha 0 leaves the terrain untouched, alpha 160 tints it red.
                pixels[z * map.Width + x] = walkable ? new Color32(0, 0, 0, 0) : new Color32(255, 40, 40, 160);
            }
        }
        overlay.SetPixels32(pixels);
        overlay.Apply(false, false);

        terrain.SetOverlayTexture(overlay, 0.8f);
    }

    public void Hide()
    {
        if (terrain != null) terrain.SetOverlayTexture(null, 0f);   // null hides the overlay
        if (overlay != null) Destroy(overlay);
        overlay = null;
    }

    void OnDisable() => Hide();
}

Die Rendering-API von RTSTerrain auf einen Blick:

MemberBeschreibung
Material TerrainMaterialDie Materialinstanz, die alle Chunks gemeinsam nutzen (null, bevor das Terrain aktiviert ist).
Transform ChunkRootDas Objekt „RTS Chunks“, das die Chunk-GameObjects enthält.
IReadOnlyList<RTSTerrainChunk> Chunks, int2 ChunkCount, int ChunkSizeInCellsDas Chunk-Raster.
RTSTerrainChunk GetChunk(int2 chunkCoord), GetChunkAtCell(int2 cell)Sucht einen Chunk. Gibt außerhalb der Karte null zurück.
bool IsRenderingReadyTrue, sobald Chunks, Texturen und das Material existieren.
Texture GetSplatTexture(int index)Splat-Textur 0–3 (Schicht i liegt in Splatmap i / 4, Kanal i % 4).
void SetOverlayTexture(Texture tex, float opacity), OverlayTexture, OverlayOpacityKartenweites Overlay. Übergib null oder 0, um es auszublenden.
void SetGridVisible(bool visible, float opacity), IsGridVisibleZellraster per Code.
void RebuildChunkMeshes(RectInt cellRect)Baut Meshes und Collider der betroffenen Chunks sofort neu auf. Normalerweise nicht nötig: NotifyChanged erledigt das.
void RefreshMaterial()Überträgt Tileset, Textur-Arrays und Kartenparameter sofort erneut auf das Material.
RTSTerrainChunk: Terrain, ChunkCoord, CellRect, Mesh, MeshRenderer, MeshCollider, LocalBounds, static GetTerrain(Collider)Daten pro Chunk. GetTerrain ordnet einen Physik-Treffer seinem Terrain zu.

Der Terrain-Shader

RTS Terrain Studio/Terrain (Shaders/RTSTerrain.shader) ist ein handgeschriebener Lit-Shader für URP 17.3. Er unterstützt Forward-, Forward+- und Deferred-Rendering, den SRP Batcher, Schatten, SSAO, Decals, Light Layers, Light Cookies und Lightmaps. Sein Material bearbeitest du nie von Hand: Alle Eigenschaften füllt das Terrain aus der Karte und ihrem Tileset.

Funktionen

FunktionWirkungGesteuert über
16 BodenschichtenBis zu 16 per Splatmap überblendete Schichten, abgetastet mit planarem XZ-Mapping. An einem Pixel werden nur Schichten mit sichtbarer Gewichtung abgetastet (dynamische Verzweigungen). Mehr gemalte Schichten auf der Karte machen also nicht jeden Pixel teurer.Bodenschichten des Tilesets; Tile Size, Tint, Normal Strength, Smoothness, Metallic
Höhenbasierte ÜberblendungIn einem Übergang gewinnt die Schicht mit der höheren Texturhöhe. So wächst Gras zwischen Steinen, statt sie per Überblendung zu überdecken. Kontrast = Height Blend² × 16; 0 ergibt eine einfache lineare Überblendung.Pro Schicht Height Blend (0–1) plus eine Höhenquelle: eine Höhentextur (R-Kanal) oder das Albedo-Alpha (Height From Albedo Alpha)
Anti-TilingIn Bereichen, die ein niederfrequentes Rauschen auswählt, blendet die Textur zu einem zweiten Sample mit gedrehten und skalierten UVs über. Das bricht die sichtbare Wiederholung auf. Bei voller Stärke nutzt etwa die Hälfte des Bodens das zweite Sample. In diesen Bereichen kostet das ein zusätzliches Textur-Sample.Pro Schicht Anti Tiling (0–1)
MakrovariationEine großflächige Helligkeitsvariation und eine dezente Farbtonvariation zwischen warm und kühl über die ganze Karte. Sie kaschiert Wiederholungen aus der Entfernung. Klippen erhalten die halbe Stärke.Macro Variation des Tilesets (0–1, Standard 0.25)
Triplanare KlippenBis zu 4 Klippentypen werden entlang aller drei Achsen projiziert, sodass senkrechte Wände nie gestreckt aussehen. Die Normalen werden per Whiteout-Blend gemischt. Wo zwei Klippentypen aufeinandertreffen, blenden sie über einen schmalen Streifen ineinander über.Klippenschichten des Tilesets; Triplanar Sharpness (1–16, Standard 6)
Fels an HängenBoden, der steiler als etwa 50° ist, geht allmählich in das Klippenmaterial über (ab etwa 63° vollständig Fels), sodass steile Hügel nie gestrecktes Gras zeigen. Der Fels bricht dort durch den Boden, wo seine Höhentextur hohe Werte hat.Automatisch
OkklusionGebackene Vertex-AO am Fuß von Klippen, multipliziert mit einem Kavitätsterm aus der Höhentextur der Klippe.Automatisch
ZellrasterKantengeglättete Linien (alle 4 Zellen dicker). Sie blenden in der Ferne aus, bevor sie Moiré-Muster erzeugen könnten.Show Grid / SetGridVisible / G in der Scene View
OverlayKartenweites RGBA-Overlay (Pathing-Ansicht, Heatmaps).SetOverlayTexture; P schaltet in der Scene View reihum durch die Modi des Pathing-Overlays
PinselcursorUmriss und Abnahme des Pinsels werden im Shader an der exakten Oberflächenposition projiziert. Du malst also genau dort, wo du den Cursor siehst (unabhängig von der Anzeigeskalierung). Bis zu 4 Positionen für symmetrisches Malen. Der Cursor wird zuletzt gezeichnet und bleibt deshalb im Nebel des Krieges sichtbar.RTSBrushPreview (von den Editorwerkzeugen gesetzt)
Nebel des Krieges (Fallback)Wenn das Renderer Feature für den Nebel des Krieges nicht installiert ist, verdunkelt der Shader unerkundete und nicht sichtbare Bereiche selbst.RTSFogOfWarManager (siehe Nebel des Krieges)
Fallback ohne TilesetOhne Tileset rendert das Terrain neutralgrauen Boden und dunkler graue Klippen, statt mit einem Fehler abzubrechen.Automatisch
BeleuchtungsoptionenSchalte Glanzlichter und Umgebungsreflexionen ab, um das Shading günstiger zu machen.Material-Schalter Specular Highlights / Environment Reflections (auf einem Override-Material)

Die meisten dieser Werte sind Tileset-Einstellungen. Wie du die Schichten anlegst, erfährst du unter Tilesets.

Passes: ForwardLit (UniversalForward), GBuffer (UniversalGBuffer, nicht unter GLES/GLCore), ShadowCaster, DepthOnly, DepthNormals und Meta. Alle Passes außer GBuffer sind für Shader Model 3.5 ausgelegt, daher wird das Terrain auch unter WebGL 2.0 und OpenGL ES 3.0 gerendert; der GBuffer-Pass benötigt 4.5. Die Render-Queue ist Geometry-100, sodass das große Terrain den Tiefenpuffer füllt, bevor andere opake Objekte gezeichnet werden. LOD-Cross-Fade wird nicht verwendet.

Glanzlichter und Reflexionen abschalten

Für leistungsschwache Geräte oder für einen flacheren, gemalten Look verwendest du ein Override-Material mit ausgeschalteten Beleuchtungsschaltern:

  1. Wähle im Project-Fenster Create ▸ Material und benenne das Material, zum Beispiel Terrain Low End.
  2. Stelle im Inspector des Materials den Shader auf RTS Terrain Studio ▸ Terrain.
  3. Deaktiviere unten im Inspector des Materials Specular Highlights und/oder Environment Reflections. Lasse die übrigen Eigenschaften unverändert; das Terrain überschreibt sie.
  4. Weise das Material dem Feld Material Override des Tilesets zu (gilt für alle Terrains, die es verwenden) oder dem eines einzelnen Terrains (Abschnitt Rendering im Inspector von RTSTerrain).
  5. Wenn sich das Terrain nicht sofort ändert, klicke auf Rebuild Terrain.

Verwende dafür ein Material-Asset, kein per Code erzeugtes Material. Unity behält nur die Shader-Varianten von Material-Keywords, die ein Material-Asset im Build verwendet. Ein Keyword, das nur per Code gesetzt wird, kann daher aus Player-Builds entfernt werden.

Schnittstelle der Shader-Eigenschaften

Alle Eigenschaften setzt der Rendering-Code von RTSTerrain über die IDs in RTSShaderIDs. Ein eigener Shader muss die Eigenschaften, die er verwendet, unter denselben Namen deklarieren. Skalare und Vektoren liegen im Konstantenpuffer UnityPerMaterial (SRP Batcher).

EigenschaftTypInhalt
_RTS_SplatMap0 … _RTS_SplatMap3Texture2D, lineares RGBASplat-Gewichtungen. Schicht i liegt in Splatmap i / 4, Kanal i % 4. Alle Gewichtungen ergeben zusammen 1. UV = positionOS.xz * _RTS_MapSize.zw. Bilinear abgetastet, geklemmt.
_RTS_AlbedoArrayTexture2DArray, sRGBRGB = Albedo, A = Höhe (0.5, wenn eine Schicht keine Höhe hat). Slices 0–15 = Bodenschichten, 16–19 = Klippenschichten (RTSTileset.CliffSliceOffset).
_RTS_NormalArrayTexture2DArray, linearRGB = Normale im Tangentenraum × 0.5 + 0.5 (beim Erstellen des Arrays bereits aus DXT5nm/BC5 entpackt).
_RTS_LayerDataTexture2D 20 × 4, float, gelesen mit LoadEine Spalte pro Array-Slice, siehe Tabelle unten.
_RTS_LayerCountfloatAnzahl der Bodenschichten des Tilesets, mindestens 1 (auf 1–16 begrenzt). Ohne Tileset übergibt RTSTerrain den Wert 1, neutrale Schichtdaten und Platzhalter-Textur-Arrays (RTSTextureArrayBuilder.FallbackResolution = 128), sodass das Terrain grauen Boden und dunkler graue Klippen zeigt. Der Shader behandelt außerdem 0 als „keine Daten“ und zeichnet dann RTS_FALLBACK_GROUND_ALBEDO / RTS_FALLBACK_CLIFF_ALBEDO, ohne _RTS_LayerData zu lesen. Das passiert nur bei einem eigenen Material, das außerhalb von RTSTerrain verwendet wird.
_RTS_MapSizefloat4(Weltgröße X, Weltgröße Z, 1 / X, 1 / Z).
_RTS_MapParamsfloat4(Zellgröße, Stufenhöhe, Makrovariation, Triplanar-Schärfe). Der mitgelieferte Shader verwendet die Stufenhöhe nicht. Sie ist für Anpassungen gedacht, etwa eine höhenabhängige Einfärbung.
_RTS_OverlayTex, _RTS_OverlayOpacityTexture2D RGBA, floatKartenweites Overlay. Deckkraft 0 = aus.
_RTS_GridParamsfloat4(aktiviert 0/1, Deckkraft, ungenutzt, ungenutzt).
_SpecularHighlights, _EnvironmentReflectionsfloat-SchalterKeywords _SPECULARHIGHLIGHTS_OFF, _ENVIRONMENTREFLECTIONS_OFF.
_RTS_LayerParams[20], _RTS_LayerTints[20], _RTS_LayerParams2[20]float4-ArraysDieselben Werte pro Schicht als float4-Arrays, für eigene Shader, die mit Arrays arbeiten. Der mitgelieferte Shader liest nur _RTS_LayerData.

RTSShaderIDs.WorldToMap (_RTS_WorldToMap) ist reserviert und wird von RTSTerrain nicht gesetzt. Verwende den Objektraum (positionOS), der bei Chunk-Meshes den kartenlokalen Koordinaten entspricht.

Aufbau von _RTS_LayerData (Spalte = Array-Slice):

Zeilexyzw
01 / KachelgrößeNormalenstärkeGlätteHöhenüberblendung
1Tönung (RGB) als lineare Farbe (Color.linear)Metallic-Wert
2Anti-Tilinghat Höhe (0/1)Schicht verwendet (0/1)0
3Minikartenfarbe (wird vom Shader nicht gelesen)

Ein Klippen-Slot (16–19) mit „Schicht verwendet“ = 0 wird wie Klippen-Slot 16 gerendert. Klippentypen, die über die Anzahl der Klippenschichten des Tilesets hinaus gemalt werden, zeigen trotzdem Fels.

Mesh-Eingabe: POSITION (kartenlokal), NORMAL (geglättet, Objektraum), COLOR (r = Klippenwandmaske, g = Klippentyp × 85 / 255, sodass die Typen 0–3 auf 0, 1/3, 2/3 und 1 abgebildet werden, b = Ambient Occlusion, a = ungenutzt). Keine UVs, keine Tangenten.

Globale Variablen (aus Shaders/Include/RTSCommon.hlsl, von allen Shadern des Pakets gemeinsam genutzt):

Globale VariableGesetzt vonInhalt
_RTS_FogOfWarTex, _RTS_FogOfWarParams, _RTS_FogOfWarUnexplored, _RTS_FogOfWarExplored, _RTS_FogOfWarEnabledRTSFogOfWarManagerSichtbarkeitstextur (R = sichtbar, G = erkundet), Koordinatenabbildung und Tönungen. HLSL: RTS_SampleFogOfWar(positionWS), RTS_ApplyFogOfWar(color, positionWS).
_RTS_BrushPositions[4], _RTS_BrushRotations[4], _RTS_BrushCount, _RTS_BrushParams, _RTS_BrushColor, _RTS_BrushMaskRTSBrushPreviewPinselcursor. HLSL: RTS_BrushCursor(positionWS) liefert Farbe (rgb) und Deckung (a).
_RTS_WindRTSVegetationRendererxy = Richtung, z = Stärke, w = Geschwindigkeit.

Einen eigenen Terrain-Shader schreiben

Die gesamte Oberflächenlogik des Terrains steckt in einer einzigen Include-Datei, Shaders/Include/RTSTerrainInput.hlsl. Die Pass-Dateien setzen die resultierende Oberfläche nur noch in Beleuchtung um. Um die Optik zu ändern, beginnst du mit einer Kopie des Paket-Shaders:

  1. Kopiere Shaders/RTSTerrain.shader und den gesamten Ordner Shaders/Include in einen Ordner deines eigenen Projekts (zum Beispiel Assets/MyGame/Shaders) und behalte dabei die relative Ordnerstruktur bei. Der Shader bindet seine Pass-Dateien als Include/… ein, und die Includes verweisen über relative Pfade aufeinander.
  2. Benenne den Shader in der ersten Zeile deiner Kopie um, zum Beispiel in Shader "MyGame/Terrain", damit er nicht mit dem Original kollidiert.
  3. Nimm deine Änderungen vor, meist in RTS_EvaluateTerrainSurface deiner Kopie von RTSTerrainInput.hlsl.
  4. Erstelle ein Material mit deinem Shader und weise es als Material Override zu (im Tileset oder im Terrain). Das Material-Asset sorgt außerdem dafür, dass der Shader in Player-Builds landet.
  5. Klicke auf Rebuild Terrain.

Da du mit Kopien arbeitest, überschreibt ein Paket-Update nie deine Änderungen. Wenn du die neuesten Korrekturen haben möchtest, vergleiche deine Kopie nach einem Update mit den neuen Paketdateien.

Beispiel: Schnee auf hohen Plateaus. Füge Folgendes am Ende von RTS_EvaluateTerrainSurface in deiner Kopie ein, direkt vor return surface;:

// Snow on everything above cliff level 6. positionOS.y is the map-local ground height, and
// _RTS_MapParams.y the height of one cliff level, so their ratio is the (fractional) cliff level.
half level = (half)(positionOS.y / max(_RTS_MapParams.y, 1e-3));
// Soft border between level 5.5 and 6.5; cliff walls (cliffBlend = 1) stay rocky.
half snow = smoothstep(5.5h, 6.5h, level) * (1.0h - surface.cliffBlend);
surface.albedo = lerp(surface.albedo, half3(0.92h, 0.94h, 0.97h), snow);
surface.smoothness = lerp(surface.smoothness, 0.35h, snow);

Die wichtigsten Bausteine von RTSTerrainInput.hlsl:

Funktion / TypZweck
struct RTSTerrainSurfaceErgebnis der Oberflächenauswertung: Albedo, Metallic-Wert, Glätte, Okklusion, Pixel- und Geometrienormale (Objektraum), Tangentenbasis, cliffBlend (0 = Boden, 1 = Klippe).
RTS_EvaluateTerrainSurface(positionOS, normalOS, vertexColor, dPdx, dPdy)Die vollständige Oberfläche: Klippenabdeckung, Boden, Klippen, höhenbasierter Übergang, Makrovariation, Okklusion.
RTS_EvaluateGround(positionXZ, dPdx, dPdy, detileNoise), RTS_AccumulateGroundLayer(…)Per Splatmap überblendete Bodenschichten mit höhenbasierter Überblendung und Anti-Tiling (float-Akkumulator RTSGroundAccumulator).
RTS_EvaluateCliff(cliffTypeValue, positionOS, dPdx, dPdy, normalOS), RTS_SampleCliffLayer(…), RTS_TriplanarWeights(normalOS, sharpness)Triplanares Klippenmaterial und Überblendung zwischen Klippentypen.
RTS_LoadLayerData(slice) → RTSLayerDataLiest eine Spalte von _RTS_LayerData.
RTS_DecodeArrayNormal(encoded, strength)Dekodiert ein Texel des Normalen-Arrays.
RTS_EvaluateNoise(positionXZ), RTS_ValueNoise, RTS_Hash21Prozedurales Rauschen (ohne Textur), stabil auf mobilen GPUs.
RTS_GridMask(…), RTS_ApplyEditorOverlays(…)Rasterlinien und Overlay-Textur.
RTS_CreateSurfaceData(surface)Wandelt die Oberfläche in die SurfaceData von URP um.
KonstantenRTS_MAX_GROUND_LAYERS (16), RTS_CLIFF_SLICE_OFFSET (16), RTS_MAX_CLIFF_TYPES (4), RTS_MIN_LAYER_WEIGHT (1/255), RTS_FALLBACK_GROUND_ALBEDO, RTS_FALLBACK_CLIFF_ALBEDO

Texturen werden mit expliziten Gradienten abgetastet (SampleGrad). Die Gradienten dPdx/dPdy werden einmal am Anfang des Fragment-Shaders berechnet, außerhalb jeder Verzweigung. Behalte das bei, wenn du eigene Samples innerhalb von Verzweigungen hinzufügst, sonst entstehen an den Grenzen der Verzweigungen Mipmap-Nähte.

Der Forward-Pass arbeitet in dieser Reihenfolge: Oberflächenauswertung, Editor-Overlays, URP-Beleuchtung (UniversalFragmentPBR), atmosphärischer Nebel von Unity, Nebel des Krieges und Pinselcursor.

Vegetation

RTSVegetationRenderer zeichnet anhand der Kartendaten zwei Arten von Vegetation:

  • Details: Gras, Blumen und Kleinpflanzen, die als Dichte gemalt werden. Burst-Jobs erzeugen sie pro Patch, gezeichnet werden sie mit prozeduralem GPU-Instancing. Sie existieren nie als GameObjects.
  • Bäume: einzeln platzierte Instanzen, entweder per GPU-Instancing gezeichnet (am schnellsten) oder als Prefab-GameObjects gespawnt (für Gameplay wie das Abbauen).

Beide sind in Patches aus RTSVegetationRenderer.PatchCells (16) × 16 Zellen organisiert. Patches sind die Einheit für Culling und inkrementelle Neuaufbauten: Eine Bearbeitung erzeugt nur die Patches neu, die sie berührt hat. Der Renderer arbeitet im Edit Mode und im Play Mode, für Kameras der Game View und der Scene View.

Einrichtung

  1. Wähle das Terrain-GameObject aus. Wenn es keinen Vegetation Renderer hat, klicke im Inspector von RTSTerrain neben der Warnung auf Add (oder verwende Add Component ▸ RTS Terrain Studio ▸ Vegetation Renderer).
  2. Weise im Abschnitt Assets des Inspectors von RTSTerrain eine Vegetation Library zu. Ohne Bibliothek warnt der Inspector des Vegetation Renderer: „The terrain has no vegetation library, so nothing can be drawn.“
  3. Male Details und platziere Bäume mit den Vegetationswerkzeugen (Taste 4 in der Scene View während der Bearbeitung).
  4. Aktiviere für instanziert gezeichnete Bäume Enable GPU Instancing in ihren Materialien. Tools ▸ RTS Terrain Studio ▸ Validate Project listet Materialien auf, bei denen die Option ausgeschaltet ist, und kann sie mit einem Klick korrigieren.

Die Bibliothek definiert die Prototypen (Meshes, Texturen, Farben, Größen, Distanzen). Alle Felder der Prototypen findest du unter Vegetationsbibliothek.

Der Inspector des Vegetation Renderer zeigt Live-Statistiken (Visible Details, Total Details, Visible Trees, Tree Objects, Colliders) und hat eine Schaltfläche Rebuild (RebuildAll()) sowie eine Schaltfläche Select Library.

Renderer-Einstellungen

EinstellungStandardBeschreibung
Quality
Detail Density Multiplier1Multipliziert die Anzahl der Detailinstanzen pro Texel (0–2, 0 = keine Details). Eine Änderung erzeugt alle Details neu.
Detail Distance Multiplier1Multipliziert die Render-Distanz jedes Detail-Prototyps (0.1–3).
Tree Distance Multiplier1Multipliziert die Render-Distanz jedes instanzierten Baum-Prototyps (0.1–3).
Wind
Wind Direction(1, 0.35)Horizontale Windrichtung in Weltkoordinaten (x = Welt-X, y = Welt-Z).
Wind Strength0.6Globale Windstärke (0–3). Jeder Prototyp und jedes Material skaliert sie zusätzlich.
Wind Speed1.6Geschwindigkeit der Windanimation (0–10).
Rendering
Draw DetailsanZeichnet per Dichte gemalte Details.
Draw TreesanZeichnet die per GPU-Instancing gerenderten Bäume.
Draw In Scene ViewanZeichnet die Vegetation auch in Kameras der Scene View.
Crossed Quad Count3Anzahl der Quads des generierten Gras-Meshes für Details im Modus Crossed Quads (2–3). Eine Änderung baut die Vegetation neu auf.
Collider Layer0Layer der Collider-Proxys, die für instanzierte Bäume erzeugt werden.

Details

Für jede gemalte Detailschicht und jeden Patch verteilt DetailScatterJob (Burst) Instanzen anhand der Dichtekarte. Eine Dichte von 255 ergibt pro Detail-Texel so viele Instanzen, wie Max Density des Prototyps angibt, multipliziert mit dem Detail Density Multiplier. Instanzen überspringen Hänge, die steiler als Max Slope sind, und mit Avoid Water auch überfluteten Boden. Sie können sich mit dem Boden neigen (Align To Ground) und erhalten über Rauschen eine Farbe zwischen Healthy Color und Dry Color.

Die Instanzen eines Patches werden in einen strukturierten GraphicsBuffer hochgeladen und mit Graphics.RenderMeshPrimitives über den internen Shader Hidden/RTS Terrain Studio/Detail gezeichnet. WebGL 2.0 unterstützt keine Structured Buffers, und OpenGL ES 3 garantiert sie in Vertex-Shadern nicht. Auf diesen Geräten landen die Instanzen daher stattdessen in einer Float-Textur (zwei Texel pro Instanz). Der Renderer wählt den Weg zur Laufzeit, und das Gras sieht in beiden Fällen gleich aus. Der Shader liest jede Instanz aus dem Buffer oder der Textur und übernimmt Wind, ein weiches Herausschrumpfen über die letzten 25 % der Render-Distanz, das Abdunkeln an der Wurzel, Transluzenz und den Fallback für den Nebel des Krieges. Jeder Patch durchläuft pro Kamera ein Frustum- und Distanz-Culling.

Render-ModusMeshMaterial
CrossedQuadsGeneriertes Gras-Mesh mit 2 oder 3 gekreuzten Quads (Crossed Quad Count)Aus der Texture des Prototyps erzeugt (Alpha-Clipping bei 0.5)
MeshDas Mesh des Prototyps (alle Sub-Meshes)Wenn das Material bereits Hidden/RTS Terrain Studio/Detail verwendet, wird es unverändert genutzt. Andernfalls wird aus seiner Haupttextur und Farbe ein Detail-Material erzeugt. Den Alpha-Cutoff des Quellmaterials übernimmt das Detail-Material nur, wenn das Quellmaterial Alpha-Clipping nutzt.

Andere Shader können die Instanzdaten nicht lesen. Details im Modus Mesh werden daher immer mit dem Detail-Shader gerendert. Eigene Shader-Funktionen des Quellmaterials (Vertex-Animation, spezielle Beleuchtung) werden für Details nicht verwendet. Wenn du sie brauchst, verwende stattdessen einen Baum-Prototyp.

Details werfen nur dann Schatten, wenn Cast Shadows des Prototyps eingeschaltet ist (standardmäßig aus). Sie werden auf dem Chunk Layer des Terrains gezeichnet, ohne Light Probes oder Reflection Probes.

Bäume: instanziert oder als GameObject

Für jeden Baum-Prototyp wählst du einen Spawn Mode:

Instanced (Standard)GameObject
RenderingGraphics.RenderMeshInstanced, bis zu 1023 Instanzen pro Draw Call, Culling pro Patch nach Frustum und Render Distance × Tree Distance MultiplierEine Prefab-Instanz pro Baum, von Unity wie jedes GameObject gerendert
LODLOD0 und LOD1 einer LODGroup, pro Patch-Gruppe nach der LODGroup-Regel von Unity gewählt (berücksichtigt QualitySettings.lodBias). Prefabs ohne LODGroup verwenden alle ihre aktiven MeshRenderer-Komponenten.Die eigene LODGroup des Prefabs
Tönung pro InstanzJa, für Materialien mit RTS Terrain Studio/Vegetation (zufällig zwischen Tint A und Tint B)Nein
ColliderGepoolte Kapsel-Proxys (siehe unten)Die Collider des Prefabs
Skripte auf dem PrefabWerden nicht ausgeführt (nichts wird instanziiert)Laufen normal
Am besten fürWälder, Kulissen, sehr viele BäumeBäume mit Gameplay-Funktion: Abbauen, Animation, eigene Skripte

In beiden Modi werden die Bäume auf Bodenhöhe + position.y platziert, um ihren Drehwinkel gedreht und gleichmäßig skaliert. Wenn das Terrain bearbeitet wird, setzen sie sich automatisch wieder auf den Boden. Gespawnte Bäume und Collider-Proxys liegen unter einem versteckten Kindobjekt „RTS Vegetation Objects“ des Terrains und folgen dessen Transform.

Instanzierte Bäume brauchen Enable GPU Instancing in ihren Materialien. Ist die Option ausgeschaltet, zeichnet der Renderer über eine versteckte Instancing-Kopie des Materials (das Projekt-Asset wird nie verändert). Im Edit Mode gleicht sich die Kopie etwa 4-mal pro Sekunde mit der Quelle ab. In Player-Builds können die Instancing-Varianten solcher Materialien entfernt werden. Aktiviere deshalb das Kontrollkästchen (der Projektvalidator kann das für dich erledigen).

Der Renderer übernimmt für jedes Teil den Schattenwurfmodus und die Rendering Layer Mask. Teile, die auf Shadows Only stehen (Schatten-Proxys), funktionieren wie erwartet. Ein Prototyp mit ausgeschaltetem Cast Shadows schaltet die Schatten für alle seine Teile ab.

Baum-Collider

Wenn bei einem Baum-Prototyp Colliders aktiviert ist:

  • Modus Instanced: Jeder Baum erhält ein unsichtbares GameObject „Tree Collider“ mit einem CapsuleCollider (Collider Radius, Collider Height, mit dem Baum skaliert) und einer Komponente RTSTreeInstance (IsColliderProxy = true). Die Proxys werden gepoolt und wiederverwendet und nutzen den Collider Layer des Renderers.
  • Modus GameObject: Die Collider des Prefabs werden verwendet. Der Renderer fügt jedem gespawnten Baum eine RTSTreeInstance hinzu.

In beiden Fällen verrät dir hit.collider.GetComponentInParent<RTSTreeInstance>(), welchen Baum eine Physik-Abfrage getroffen hat. Pathing und Nebel des Krieges brauchen keine Collider: Sie verwenden Footprint Radius und Blocks Vision des Prototyps.

Wind

Der Renderer übergibt in jedem Frame den globalen Vektor _RTS_Wind (Richtung, Stärke, Geschwindigkeit) an alle Shader. Wie stark eine Pflanze insgesamt schwankt, ergibt sich so:

  • Details: globale Wind Strength × Wind Strength des Prototyps (0–1).
  • Bäume und Pflanzen mit RTS Terrain Studio/Vegetation: globale Wind Strength × Wind Strength des Materials, gewichtet über Wind Mode. Mit Object Height biegt sich die Pflanze nach oben hin stärker, bezogen auf Wind Reference Height. Vertex Color R nutzt die rote Vertex-Farbe als Gewichtung pro Vertex (male 0 auf den Stamm und 1 auf die Astspitzen). Leaf Flutter legt zusätzlich eine schnelle, kleine Bewegung darüber.

Setze Wind Strength am Renderer auf 0, um den Wind ganz abzuschalten, zum Beispiel für eine windstille Nachtszene.

Der Vegetation-Shader

RTS Terrain Studio/Vegetation ist ein Lit-Shader für Blattwerk, gedacht für die Materialien von Baum- und Pflanzen-Prefabs. Er unterstützt den SRP Batcher, GPU-Instancing mit Tönung pro Instanz (_RTS_InstanceTint), Forward+, DBuffer-Decals, den Rendering Debugger, APV und Lightmaps sowie den Fallback für den Nebel des Krieges. Andere Instancing-fähige Materialien (etwa URP Lit) funktionieren ebenfalls, allerdings ohne Tönung pro Instanz, Transluzenz und Wind.

EigenschaftStandardBeschreibung
Albedo, ColorweißBasistextur (Alpha wird für das Clipping verwendet) und Farbe.
Alpha Clipping, Alpha Cutoffan, 0.5Alpha-Test für Blätter. Das Clipping ist ohne Keyword aktiv; das invertierte Keyword _ALPHATEST_OFF schaltet es aus.
Use Normal Map, Normal Map, Normal Strengthaus, –, 1Optionale Normal-Map (_NORMALMAP).
Smoothness, Specular Color0.2, dunkelgrauSpekulare Reflexion.
Wrap Lighting0.35Macht den Lichtabfall auf Blättern weicher (0–1).
Translucency, Translucency Color0.6, hellgrünLicht, das bei Blick in Richtung Sonne durch die Blätter scheint.
Wind ModeObject HeightOff, Vertex Color R oder Object Height.
Wind Strength, Wind Reference Height, Leaf Flutter1, 6, 0.5Windskalierung auf Materialseite (multipliziert mit dem globalen Wind des Renderers).
CullOffFace Culling. Mit Off werden beide Seiten der Blattflächen gerendert.
Receive ShadowsanIst die Option aus, wird das Keyword _RECEIVE_SHADOWS_OFF gesetzt.

Für Code, der Vegetationsmaterialien erzeugt: Alpha Clipping ist standardmäßig aktiv. Aktiviere das Keyword _ALPHATEST_OFF (RTSVegetationShaderIDs.VegetationAlphaTestOffKeyword) nur, wenn du das Clipping deaktivieren willst.

Qualitätsmultiplikatoren

Die drei Qualitätsfelder skalieren die Werte der Bibliothek, ohne die Bibliothek selbst zu ändern. Sie eignen sich ideal für Grafikoptionen im Menü deines Spiels:

using RTSTerrainStudio;
using UnityEngine;

/// Applies a vegetation quality level (0 = low, 1 = medium, 2 = high) to every terrain in the scene.
public static class VegetationQuality
{
    public static void Apply(int level)
    {
        foreach (RTSTerrain terrain in RTSTerrain.All)
        {
            if (!terrain.TryGetComponent(out RTSVegetationRenderer vegetation)) continue;

            switch (level)
            {
                case 0:
                    vegetation.detailDensityMultiplier = 0.4f;   // fewer grass blades (regenerates details)
                    vegetation.detailDistanceMultiplier = 0.6f;  // grass disappears earlier
                    vegetation.treeDistanceMultiplier = 0.7f;    // instanced trees culled earlier
                    vegetation.crossedQuadCount = 2;             // 2 quads per grass tuft instead of 3
                    break;
                case 1:
                    vegetation.detailDensityMultiplier = 0.75f;
                    vegetation.detailDistanceMultiplier = 0.8f;
                    vegetation.treeDistanceMultiplier = 1f;
                    vegetation.crossedQuadCount = 3;
                    break;
                default:
                    vegetation.detailDensityMultiplier = 1f;
                    vegetation.detailDistanceMultiplier = 1f;
                    vegetation.treeDistanceMultiplier = 1.2f;
                    vegetation.crossedQuadCount = 3;
                    break;
            }
            // The renderer notices the changed values on its next update and rebuilds what is needed.
        }
    }
}

Baum-API und Abbau

MemberBeschreibung
bool RemoveTree(uint id)Entfernt den Baum aus der Karte, deaktiviert sofort sein GameObject oder seinen Collider-Proxy, benachrichtigt das Terrain (MapLayers.Trees, damit das Pathing die Grundfläche wieder freigibt und der Nebel des Krieges den Sichtblocker entfernt) und löst TreeRemoved aus. Dabei wird kein Undo-Schritt aufgezeichnet. Gibt false zurück, wenn kein Baum diese ID hat.
event Action<RTSVegetationRenderer, TreeInstanceData> TreeRemovedWird von RemoveTree mit den Daten des entfernten Baums ausgelöst.
bool TryFindNearestTree(Vector3 worldPosition, float maxDistance, out TreeInstanceData tree)Nächstgelegener Baum, gemessen auf der Bodenebene (XZ). Mit Mathf.Infinity wird die ganze Karte durchsucht.
GameObject GetTreeObject(uint id)Die gespawnte Prefab-Instanz (Modus GameObject) oder der Collider-Proxy (Instanced mit Collidern). Sonst null.
Vector3 GetTreeWorldPosition(in TreeInstanceData tree)Weltposition des Baumfußes (Bodenhöhe + Offset).
void RebuildAll(), void ProcessChanges()Vollständiger Neuaufbau; ausstehende Kartenänderungen sofort anwenden (normalerweise automatisch in jedem Frame).
VisibleDetailInstances, VisibleTrees, TotalDetailInstances, SpawnedTreeObjects, TreeColliderCountStatistiken. Die „Visible“-Werte gelten für die zuletzt gerenderte Game- oder Scene-Kamera.
RTSTreeInstance: TreeId, Owner, IsColliderProxy, TryGetData(out TreeInstanceData), Remove()Komponente auf gespawnten Bäumen und Collider-Proxys. Remove() entspricht Owner.RemoveTree(TreeId).
using RTSTerrainStudio;
using UnityEngine;

/// A minimal lumberjack: chops the nearest tree in reach, or the tree under the mouse.
public class Lumberjack : MonoBehaviour
{
    [SerializeField] RTSVegetationRenderer vegetation;
    [SerializeField] Camera viewCamera;
    [SerializeField] float reach = 2f;
    public int wood;

    void OnEnable()
    {
        if (vegetation != null) vegetation.TreeRemoved += HandleTreeRemoved;
    }

    void OnDisable()
    {
        if (vegetation != null) vegetation.TreeRemoved -= HandleTreeRemoved;
    }

    /// Physics-free: finds the nearest tree around this unit on the ground plane.
    public bool ChopNearestTree()
    {
        if (vegetation == null) return false;
        if (!vegetation.TryFindNearestTree(transform.position, reach, out TreeInstanceData tree)) return false;
        return vegetation.RemoveTree(tree.id);
    }

    /// Physics based: needs "Colliders" on the tree prototype.
    public bool ChopTreeUnderMouse()
    {
        if (viewCamera == null) return false;
        Ray ray = viewCamera.ScreenPointToRay(RTSInput.PointerPosition);
        if (!Physics.Raycast(ray, out RaycastHit hit, 500f)) return false;

        // Works for collider proxies (instanced trees) and for colliders inside spawned tree prefabs.
        var tree = hit.collider.GetComponentInParent<RTSTreeInstance>();
        return tree != null && tree.Remove();
    }

    void HandleTreeRemoved(RTSVegetationRenderer renderer, TreeInstanceData tree)
    {
        wood += 10;
        Vector3 position = renderer.GetTreeWorldPosition(tree);
        Debug.Log($"Tree {tree.id} felled at {position}. Wood: {wood}");
    }
}

Die Beispielskripte enthalten eine vollständige Abbau-Demo (RTSTreeHarvestDemo: Arbeiter laufen zu einem Baum, fällen ihn und spielen mit RTSFallingTreeEffect eine Fallanimation ab). Darin findest du ein komplettes Gameplay-Beispiel, das auch zeigt, wie abgebaute Bäume am Ende eines Spieltests wiederhergestellt werden.

Das Entfernen eines Baums verändert die Karte. Im In-Game-Editor oder im Unity Editor wird die Karte danach als Karte mit nicht gespeicherten Änderungen gekennzeichnet. Spielcode arbeitet meist mit einer Karte, die für das Match geladen wurde, und speichert sie nie zurück.

Wasser

RTSWaterRenderer rendert das gemalte Wasser als transparente Meshes, aufgeteilt in Chunks. Jeder als Wasser markierte Vertex hat eine eigene Oberflächenhöhe, sodass Seen auf verschiedenen Klippenstufen möglich sind. Die Meshes folgen MapGeometryView.SampleWater – also genau dem, was auch Gameplay-Abfragen und das Pathing verwenden. Wasser-Chunks verwenden die Chunk-Größe des Terrains, werden von Burst-Jobs erzeugt (WaterChunkMeshJob) und existieren nur dort, wo es Wasser gibt. Neu aufgebaut werden nur die Chunks, die von einer Bearbeitung betroffen sind.

Einrichtung

  1. Stelle sicher, dass das Terrain-GameObject einen Water Renderer hat (Add Component ▸ RTS Terrain Studio ▸ Water Renderer oder die Schaltfläche Add im Inspector von RTSTerrain).
  2. Öffne das aktive URP-Asset, aktiviere Depth Texture und Opaque Texture und setze Opaque Downsampling auf None (siehe unten). Tools ▸ RTS Terrain Studio ▸ Validate Project prüft und korrigiert das. Eine Kamera kann beide Texturen auch für sich selbst einschalten.
  3. Male Wasser mit den Wasserwerkzeugen (Taste 6 in der Scene View während der Bearbeitung).
  4. Ändere das Aussehen im Abschnitt Water des Tileset-Inspectors (das Wasserprofil des Tilesets).

URP-Voraussetzungen

Einstellung (URP-Asset oder Kamera)Benötigt fürOhne sie
Depth Texture: anTiefenabhängige Farbe, Uferschaum und weiche Uferlinie rund um Objekte, die im Wasser stehen, sowie eine Lichtbrechung, die Objekte vor dem Wasser ausspart (_CameraDepthTexture)Das Wasser verwendet die in seinem Mesh gespeicherte Tiefe. Tiefenabhängige Farbe, Schaum und weiches Ufer bleiben erhalten, ignorieren aber Objekte, die im Wasser stehen.
Opaque Texture: anLichtbrechung (_CameraOpaqueTexture)Einfaches Alpha-Blending ohne Lichtbrechung.
Opaque Downsampling: None (URP-Asset)Scharfer Seegrund und scharfe Objekte unter WasserDie Unterwasserszene wirkt unscharf (der URP-Standard ist 2x Bilinear).

URP entscheidet pro Kamera: Die eigenen Einstellungen Depth Texture und Opaque Texture einer Kamera (im Abschnitt Rendering ihres Inspectors) überschreiben das URP-Asset, und die Scene View verwendet immer das Asset. Das Wasser passt sein Material an jede Kamera an, unmittelbar bevor diese rendert. So bekommt jede Ansicht das bestmögliche Wasser. Kameras, die den Layer des Wassers nicht rendern, werden übersprungen, und Overlay-Kameras erhalten nie die Opaque Texture.

Der Inspector der Komponente Water Renderer zeigt für jede fehlende Einstellung eine Meldung mit der Schaltfläche Select URP Asset. Im Play Mode berücksichtigt er außerdem die eigenen Einstellungen der Hauptkamera. Eine fehlende Textur führt nie zu fehlerhaftem Wasser: Ansichten ohne diese Textur verwenden den oben beschriebenen Fallback. Mit beiden Texturen sieht das Wasser allerdings deutlich besser aus.

Im Code: RTSWaterMaterialUtility.CheckPipelineSupport(Camera camera, out bool depthTexture, out bool opaqueTexture) gibt an, was URP für eine einzelne Kamera erzeugt, und wertet die Einstellungen dabei genauso aus wie URP selbst (übergib null, um nur das URP-Asset zu prüfen). CheckPipelineSupport(out bool depthTexture, out bool opaqueTexture) prüft die Ansichten, die das Wasser zeigen: in Builds die Hauptkamera (oder das URP-Asset, wenn es keine gibt), im Editor zusätzlich das URP-Asset, weil die Scene View es verwendet. Beide geben false zurück, wenn URP nicht aktiv ist. RTSWaterMaterialUtility.IsOpaqueTextureDownsampled() prüft das Downsampling.

Renderer-Einstellungen

EinstellungStandardBeschreibung
Material TemplateleerOptionales Material. Sein Shader und seine Eigenschaften werden kopiert, danach wird das Wasserprofil des Tilesets darüber angewendet. Damit stellst du Werte ein, die das Profil nicht abdeckt: Reflection Strength, Specular Strength, Foam Tile Size, Foam Lines, Shore Fade.
Cast ShadowsausWasseroberflächen empfangen normalerweise nur Schatten.
Receive ShadowsanSchatten des Hauptlichts auf dem Wasser. Das funktioniert auch für das Laufzeitmaterial in Player-Builds.
Render Layer4 (Water)GameObject-Layer der Wasser-Chunks (RTSWaterRenderer.DefaultLayer ist Unitys eingebauter Layer Water).
Wave Amplitude Multiplier1Skaliert die Wave Height des Profils (0–4, 0 = stilles Wasser).
Cell Subdivisions2Quads pro Zellseite (1–4). Höhere Werte ergeben glattere Uferlinien und Wellen, kosten aber mehr Vertices.
Light Probe UsageOffOff beleuchtet alle Chunks mit der Ambient Probe. Würden die Probes überblendet, bekäme jeder Chunk eine leicht andere Umgebungsfarbe, und entlang der Chunk-Grenzen wären Stufen zu sehen.
Reflection Probe UsageBlend ProbesIn Forward+ werden Probes pro Pixel ausgewertet. In Forward wählt jeder Chunk die Probes anhand seiner eigenen Bounds, sodass bei mehreren Probes benachbarte Chunks unterschiedliche Probes spiegeln können.

Schaltflächen im Inspector: Rebuild (RebuildAll()) und Refresh Material (RefreshMaterial(), wendet Vorlage und Profil erneut an). Der Inspector zeigt außerdem das Chunk-Raster, die Chunks mit Wasser, die Gesamtzahl der Vertices und die Herkunft des Profils.

Wasserprofil (Tileset)

Aussehen und Pathing-Regeln stammen aus RTSTileset.water (einem WaterProfile). Ohne Tileset wird ein gemeinsames Standardprofil verwendet. Behandle es als schreibgeschützt und verwende WaterMath.CreateDefaultProfile() oder WaterProfile.Clone(), wenn du eine bearbeitbare Kopie brauchst.

FeldStandardBeschreibung
Shallow Colorblaugrün, Alpha 0.55Farbe des Flachwassers (Alpha = Deckkraft).
Deep Colordunkelblau, Alpha 0.95Farbe, die bei Color Depth erreicht wird (Alpha = Deckkraft).
Color Depth3 mTiefe, bei der die Tiefwasserfarbe vollständig erreicht ist.
Shallow Depth Limit0.9 mWasser, das tiefer ist als dieser Wert, gilt als Tiefwasser (nicht begehbar). Flacheres Wasser ist begehbar, wie in klassischen RTS-Spielen.
Normal Map, Normal Strength, Normal Tile Sizekeine, 0.6, 8 mKräuselung der Oberfläche. Ohne Normal-Map nutzt die Oberfläche nur die Vertex-Wellen.
Flow Speed(0.03, 0.02)Scrollgeschwindigkeit der Normal-Map.
Wave Height0.05 mAmplitude der Vertex-Wellen (multipliziert mit dem Multiplikator des Renderers).
Smoothness0.92Glätte der Oberfläche.
Refraction0.03Stärke des Versatzes durch Lichtbrechung (0–0.2).
Foam Color, Foam Distance, Foam Textureweiß 0.8, 0.6 m, keineUferschaum. Foam Distance beruhigt außerdem die Wellen im Flachwasser.
Minimap ColorblauFarbe des Wassers auf der Minikarte.

Der Water-Shader

RTS Terrain Studio/Water (RTSWaterMaterialUtility.ShaderName) ist ein transparenter URP-Shader. Die Chunk-Meshes liefern kartenlokale Positionen sowie Wasserdaten pro Vertex (Tiefe, Abdeckung und Abstand zum Ufer, bis zu ShoreSearchRadius = 4 Zellen). Funktionen:

  • Tiefenabhängige Farbe von flach bis tief, mit weichen Uferlinien, die dort auslaufen, wo das Wasser auf den Boden trifft.
  • Vier gerichtete Vertex-Wellen, die im Flachwasser beruhigt werden, damit sich die Oberfläche nie über den Strand hebt.
  • Scrollende Kräuselung per Normal-Map, Lichtbrechung der Unterwasserszene, Sonnenglanzlichter und Reflection Probes.
  • Uferschaum (prozedurale Linien oder eine Schaumtextur).
  • Haupt- und Zusatzlichter (auch Zusatzlichter pro Vertex), Light Cookies, Light Layers, empfangene Schatten und der Fallback für den Nebel des Krieges. Kompatibel mit dem SRP Batcher.

Das Material wird zur Laufzeit erzeugt (WaterMaterial, von allen Chunks gemeinsam genutzt) und mit RTSWaterMaterialUtility.ApplyProfile befüllt. Der Shader wird über ein serialisiertes Feld der Komponente referenziert und ist daher in Builds enthalten.

Werden Zusatzlichter pro Vertex berechnet, liefern sie nur diffuses Licht (keine Glanzlichter, keine Schatten) – genau wie die Vertex-Beleuchtung von URP selbst.

API des Wasser-Renderers

MemberBeschreibung
Material WaterMaterialLaufzeitmaterial, das alle Chunks gemeinsam nutzen (null, solange die Komponente deaktiviert ist).
Material MaterialTemplate { get; set; }Optionale Vorlage. Beim Setzen wird das Material aktualisiert.
WaterProfile ProfileProfil des Tilesets oder das gemeinsame Standardprofil.
Transform ChunkRoot, ChunkCountX, ChunkCountZ, ActiveChunkCount, VertexCount, bool HasWaterChunk-Raster und Statistiken.
MeshRenderer GetChunkRenderer(Vector2Int chunkCoord)Renderer eines Chunks oder null, wenn der Chunk kein Wasser enthält.
void RebuildAll(), void RebuildCells(RectInt cellRect), void RefreshMaterial()Vollständiger Neuaufbau, teilweiser Neuaufbau, Aktualisierung des Materials.
event Action<RTSWaterRenderer> MeshesRebuiltWird nach einem vollständigen oder teilweisen Neuaufbau ausgelöst.
RTSWaterChunk: Owner, Terrain, ChunkCoord, CellRectMarker-Komponente auf den verborgenen Chunk-Objekten.

WaterMath: Wasser für das Gameplay

WaterMath beantwortet Fragen zum Wasser ohne Physik. Alle Positionen sind in Weltkoordinaten angegeben.

MethodeRückgabe
GetWaterHeight(RTSTerrain, Vector3, out float height), GetWaterHeight(Vector3, out float height)Welt-Y der ruhenden Wasseroberfläche. False, wenn die Stelle trocken ist oder außerhalb des Terrains liegt. Die zweite Überladung findet das Terrain mit RTSTerrain.FindAt.
GetWaterDepth(RTSTerrain, Vector3, out float depth)Wasseroberfläche minus Boden in Metern. False, wenn die Stelle trocken ist.
Classify(RTSTerrain, Vector3, out float depth)WaterDepthClass.Dry, Shallow oder Deep (anhand von Shallow Depth Limit).
IsShallowWater(…), IsDeepWater(…), GetShallowDepthLimit(RTSTerrain)Kurzformen für die Klassifizierung.
GetWaterSurface(RTSTerrain, Vector3, float time, out float height, out Vector3 normal)Die animierte Oberfläche genau so, wie sie gerendert wird: Ruhehöhe + Wellen, in Ufernähe beruhigt, einschließlich des Wave Amplitude Multiplier des Renderers.
float ShaderTimeDie Zeitbasis, mit der der Shader animiert (Time.time im Play Mode und in Player-Builds, Time.realtimeSinceStartup im Edit Mode). Übergib sie als time, damit CPU- und GPU-Wellen übereinstimmen.
SampleWaveOffset(Vector3, float waveHeight, float time)Wellenversatz ohne Beruhigung am Ufer.
EvaluateWaves(float2, float time), EvaluateWaveSurface(float2, float time, float amplitude, out float offset, out float3 normal), WaveAttenuation(float depth, float foamDistance)Burst-kompatible Bausteine für Job-Systeme (zum Beispiel Tausende schwimmender Einheiten).
GetProfile(RTSTerrain), CreateDefaultProfile()Das verwendete Profil; ein neues Standardprofil, das du gefahrlos ändern kannst.
using RTSTerrainStudio;
using UnityEngine;

/// Makes a boat float on the animated water surface and keeps it out of shallow water.
public class FloatingBoat : MonoBehaviour
{
    [SerializeField] float draft = 0.15f;        // how deep the hull sits in the water
    [SerializeField] float heading;              // yaw in degrees, set by your movement code
    [SerializeField] float tiltSmoothing = 4f;

    void LateUpdate()
    {
        Vector3 p = transform.position;
        RTSTerrain terrain = RTSTerrain.FindAt(p);
        if (terrain == null) return;

        // ShaderTime is the clock the water shader uses, so the boat bobs in sync with the rendered waves.
        if (!WaterMath.GetWaterSurface(terrain, p, WaterMath.ShaderTime, out float height, out Vector3 normal))
            return; // dry ground: leave the boat alone

        transform.position = new Vector3(p.x, height - draft, p.z);

        // Tilt with the wave normal, keep our own heading.
        Quaternion target = Quaternion.FromToRotation(Vector3.up, normal) * Quaternion.Euler(0f, heading, 0f);
        transform.rotation = Quaternion.Slerp(transform.rotation, target, 1f - Mathf.Exp(-tiltSmoothing * Time.deltaTime));
    }

    /// Boats need deep water: use this before accepting a move order.
    public static bool CanSailTo(Vector3 world)
    {
        RTSTerrain terrain = RTSTerrain.FindAt(world);
        return terrain != null && WaterMath.Classify(terrain, world, out _) == WaterDepthClass.Deep;
    }
}

Dekor

RTSDecorManager hält die Szene mit dem Dekor der Karte synchron (Doodads: Felsen, Kisten, Ruinen, Zäune, beliebige Prefabs). Er reagiert auf die Änderungs-Events des Terrains und arbeitet inkrementell. Eine Bearbeitung betrifft nur die Instanzen, die hinzugefügt, entfernt oder geändert wurden. Objekte werden wiederverwendet, wenn sich nur ihr Transform ändert, und beim Entfernen in einem Pool abgelegt. Höhenänderungen am Terrain setzen die Instanzen im geänderten Bereich neu auf den Boden. Das Laden einer Karte oder eine Änderung der Dekorbibliothek baut alles neu auf.

Spawn-Modi

Jeder Prototyp in der Dekorbibliothek legt einen Spawn Mode fest:

GameObject (Standard)Instanced
DarstellungEine Kopie des Prefabs (bzw. der Variante) unter dem Kindobjekt „RTS Decor“, mit einer Komponente RTSDecorInstanceGezeichnet mit Graphics.RenderMeshInstanced (erste LOD-Stufe des Prefabs), bis zu 1023 Instanzen pro Draw Call
CullingUnitys eigenesPro Patch (Frustum + Max Draw Distance)
Skripte, Collider, AnimationJaNein (es gibt keine Objekte)
TönungÜber einen MaterialPropertyBlock (_BaseColor und _Color)Nur bei Shadern, die das unterstützen (siehe Tönung pro Instanz)
Ideal fürInteraktive oder zerstörbare Props, einige hundert bis einige tausend ObjekteStatische Szenerie in großer Zahl

Platzierungsregel: Position = LocalToWorld(x, ground + y + prototype yOffset, z) oder, mit dem Flag KeepHeight, die gespeicherte absolute Höhe. Rotation = Terrain-Rotation × Instanzrotation, Skalierung = Instanzskalierung. Gespawnte Prefabs behalten zusätzlich die Rotation und Skalierung, die an ihrem Root-Objekt festgelegt sind.

Instanziertes Dekor benötigt bei seinen Materialien die Option Enable GPU Instancing. Ist sie aus, wird eine Laufzeitkopie mit Instancing verwendet, und eine Warnung fordert dich auf, das Kontrollkästchen zu aktivieren. Mit dem Projektvalidator behebst du das für alle Materialien auf einmal.

Die Felder der Prototypen (Varianten, zufällige Rotation und Skalierung, Tönung, Grundfläche, Sichtblockierung, Schatten) sind unter Dekorbibliothek beschrieben.

Manager-Einstellungen

EinstellungStandardBeschreibung
Show In Scene ViewanZeichnet instanziertes Dekor in den Kameras der Scene View. Gespawnte GameObjects sind immer sichtbar.
Max Draw Distance0Instanziertes Dekor, das weiter als dieser Wert von der Kamera entfernt ist, wird nicht gezeichnet (0 = keine Grenze).
Shadow Culling Margin12 mZusätzlicher Rand um die Patches beim Frustum Culling, damit Props knapp außerhalb des Sichtfelds trotzdem Schatten hineinwerfen.
Instanced TintanÜbergibt Tönungen pro Instanz an instanziertes Dekor, dessen Shader das unterstützt.
Patch Size16Seitenlänge eines Patches für Culling und Abfragen, in Zellen (4–64). Eine Änderung baut das Dekor neu auf.
Pool Capacity Per Prefab64Entferntes GameObject-Dekor wird inaktiv zur Wiederverwendung aufbewahrt, bis zu dieser Anzahl Objekte pro Prefab.
Log Missing PrototypesanGibt eine Warnung pro fehlender Prototyp-ID, fehlender Bibliothek oder zerstörtem Prefab aus.

Der Inspector der Komponente Decor Manager zeigt Instances, GameObjects, Instanced und Sync (ob Änderungen ausstehen) sowie die Schaltflächen Rebuild (RebuildAll(): zerstört alles und erzeugt es neu, leert die Pools, liest die Prefabs neu ein), Sync Now (SyncNow()) und Select Library.

Platzhalter

Wenn eine Karte auf eine Prototyp-ID verweist, die in der Bibliothek fehlt, wenn das Terrain gar keine Dekorbibliothek hat oder wenn ein Prefab gelöscht wurde, wird die Instanz als kleiner magentafarbener Würfel angezeigt. Dabei geht nichts verloren: Die Instanz bleibt in der Karte und erscheint wieder normal, sobald der Prototyp zurück ist. IsPlaceholder(id) verrät dir, ob eine Instanz ein Platzhalter ist. Platzhalter zählen als instanziert.

Magentafarbene Würfel nach dem Öffnen einer fremden Karte bedeuten meist, dass das Terrain eine andere Dekorbibliothek verwendet als die, mit der die Karte erstellt wurde. Weise im Inspector von RTSTerrain die richtige Bibliothek zu.

Tönung pro Instanz mit eigenen Shadern

Instanziertes Dekor wird nur dann pro Instanz getönt, wenn der Shader die Tönung aus dem Instancing-Puffer lesen kann. URP Lit, Simple Lit und Unlit behalten ihre Materialfarbe. Shader aktivieren die Unterstützung über ein SubShader-Tag:

TagKonstanteDer Shader muss
"RTSInstanceTint" = "True"RTSDecorManager.InstanceTintShaderTagseine Albedo mit dem instanzierten _RTS_InstanceTint multiplizieren (rgb = Tönung, a = 1). Der Shader RTS Terrain Studio/Vegetation des Pakets wird automatisch erkannt.
"RTSInstancedColor" = "True"RTSDecorManager.InstancedColorShaderTag_BaseColor und _Color mit UNITY_ACCESS_INSTANCED_PROP aus einem UNITY_INSTANCING_BUFFER lesen. Geschrieben wird jeweils Materialfarbe × Tönung.

Dekor-API: Picking, Abfragen und Events

Picking und Abfragen benötigen keine Collider. Jede Instanz wird als orientierte Box geprüft, die aus den Mesh-Bounds des Prefabs gebildet wird. Deshalb funktionieren sie in beiden Spawn-Modi, sogar bei instanziertem Dekor ohne GameObject.

MemberBeschreibung
bool TryPick(Ray worldRay, float maxDistance, out uint instanceId) (+ Überladung mit out float hitDistance)Die nächstgelegene Instanz, die ein Strahl in Weltkoordinaten trifft.
int QueryRadius(Vector3 world, float radius, List<uint> results)Fügt die IDs innerhalb eines Radius auf der Bodenebene hinzu. Die Liste wird vorher nicht geleert. Arbeitet allokationsfrei. Eine Überladung mit IEnumerable<uint> legt eine neue Liste an.
int QueryLocalRect(Rect localRect, List<uint> results)Fügt die IDs hinzu, deren Pivot in einem kartenlokalen XZ-Rechteck liegt (Rechteckauswahl).
GameObject GetInstanceObject(uint id)Das gespawnte Objekt oder null (bei instanziertem Dekor oder unbekannter ID).
ContainsInstance, TryGetInstance(uint, out DecorInstanceData), IsInstanced, IsPlaceholderNachschlagen von Instanzen (Daten auf dem Stand der letzten Synchronisierung).
TryGetWorldBounds(uint, out Bounds), TryGetObjectMatrix(uint, out Matrix4x4)Bounds in Weltkoordinaten; Matrix des Objekt-Roots einschließlich der im Prefab festgelegten Rotation und Skalierung des Roots.
Matrix4x4 GetWorldMatrix(in DecorInstanceData), GetWorldMatrix(in DecorInstanceData, string prototypeId)Weltmatrix des Pivots, auch für Daten, die noch nicht in der Karte sind (Platzierungsvorschauen).
SpawnedCount, InstancedCount, InstanceCount, Container, PatchSize, HasPendingChangesZustand und Statistiken.
InstancesRebuilt, InstancesChangedNach einem vollständigen Neuaufbau; nach einer inkrementellen Aktualisierung.
InstanceSpawned, InstanceDespawningEin Objekt wurde gespawnt oder aus dem Pool genommen; ein Objekt wird gleich in den Pool zurückgelegt oder zerstört. Objekte, die einen weichen Neuaufbau überstehen, lösen InstanceSpawned nicht erneut aus.
RTSDecorInstance: InstanceId (0, solange im Pool), PrototypeId, Variant, Owner, Prototype, IsLive, TryGetData, static FromCollider(Collider)Komponente auf dem Root jedes gespawnten Objekts. Ihre Felder werden gefüllt, bevor Awake/OnEnable des Prefabs selbst laufen. Deine Prefab-Skripte können sie dort also schon lesen.
using System.Collections.Generic;
using RTSTerrainStudio;
using Unity.Mathematics;
using UnityEngine;

/// Click a decor object to destroy it; Shift+click destroys everything within 5 m of the click.
public class DecorDemolisher : MonoBehaviour
{
    [SerializeField] RTSDecorManager decor;
    [SerializeField] Camera viewCamera;

    // Reused every click: QueryRadius appends to it and never allocates.
    readonly List<uint> nearby = new List<uint>();

    void Update()
    {
        if (decor == null || viewCamera == null || !RTSInput.GetMouseButtonDown(0)) return;

        Ray ray = viewCamera.ScreenPointToRay(RTSInput.PointerPosition);
        // Works for GameObject and instanced decor alike, without colliders.
        if (!decor.TryPick(ray, 500f, out uint id, out float distance)) return;

        if (RTSInput.Shift)
        {
            nearby.Clear();
            decor.QueryRadius(ray.GetPoint(distance), 5f, nearby);
            foreach (uint other in nearby) Remove(other);
        }
        else
        {
            Remove(id);
        }
    }

    void Remove(uint id)
    {
        RTSTerrain terrain = decor.Terrain;
        MapData map = terrain.Map;
        int index = map.FindDecorIndex(id);
        if (index < 0) return;

        DecorInstanceData data = map.Decor[index];
        map.Decor.RemoveAt(index);

        // Tell the terrain what changed. The decor manager removes (pools) the object,
        // and the pathing map reopens the footprint automatically.
        int2 cell = map.LocalToCell(data.position.xz);
        terrain.NotifyChanged(MapLayers.Decor, new RectInt(cell.x, cell.y, 1, 1));
    }
}
using RTSTerrainStudio;
using UnityEngine;

/// Gives every spawned decor object of the category "Destructible" some hit points.
public class DestructibleDecorSetup : MonoBehaviour
{
    [SerializeField] RTSDecorManager decor;

    void OnEnable()
    {
        if (decor != null) decor.InstanceSpawned += HandleSpawned;
    }

    void OnDisable()
    {
        if (decor != null) decor.InstanceSpawned -= HandleSpawned;
    }

    void HandleSpawned(RTSDecorManager manager, RTSDecorInstance instance)
    {
        // Prototype looks up the library entry, e.g. its category or footprint.
        DecorPrototype prototype = instance.Prototype;
        if (prototype == null || prototype.category != "Destructible") return;

        // Pooled objects come back here when they are reused, so add the component only once.
        if (!instance.TryGetComponent(out Health health)) health = instance.gameObject.AddComponent<Health>();
        health.hitPoints = 100;
    }
}

/// Example gameplay component.
public class Health : MonoBehaviour
{
    public int hitPoints;
}

Nur Prototypen im GameObject-Modus lösen InstanceSpawned aus, weil instanziertes Dekor keine Objekte erzeugt.

Umgebung

Jede Karte speichert ihre eigene Beleuchtung in MapData.Environment: Farbe, Intensität und Rotation der Sonne, die Farbe des Umgebungslichts und den Nebel. Eine verschneite Karte kann deshalb kaltes Licht mitbringen, eine Wüstenkarte warmes. Die Komponente RTSEnvironment überträgt diese Werte auf die Szene, sobald eine Karte geladen wird, und jedes Mal, wenn sich die Umgebung ändert.

Einrichtung

  1. Wähle GameObject ▸ RTS Terrain Studio ▸ Environment. Dadurch entsteht ein GameObject „Environment“ mit RTSEnvironment und zusätzlich ein Directional Light „Sun“, falls die Szene noch keins hat. Enthalten die geöffneten Szenen bereits eine Umgebung, kannst du stattdessen diese auswählen. Mehrere Umgebungen würden um dieselben Beleuchtungseinstellungen konkurrieren.
  2. Alternativ fügst du die Komponente über Add Component ▸ RTS Terrain Studio ▸ Environment dem Terrain-GameObject hinzu (im Inspector von RTSTerrain gibt es dafür die Schaltfläche Add).
  3. Bearbeite die Werte im Tab Map des Editorfensters (Tools ▸ RTS Terrain Studio ▸ Editor Window), im Panel Environment: Sun Colour, Sun Intensity, Sun Rotation (x = Höhenwinkel über dem Horizont, y = Drehwinkel), Ambient Colour, Fog, Fog Colour, Fog Density. Änderungen lassen sich rückgängig machen und werden mit der Karte gespeichert.
  4. Oder beleuchte die Szene von Hand und klicke auf Capture From Scene (Editorfenster) bzw. Use Current Lighting (Inspector der Komponente). So speicherst du die aktuelle Sonne, das Umgebungslicht und den Nebel als einen einzigen Undo-Schritt in der Karte.

Die angewendete Beleuchtung ist eine Vorschau. Sie wird nicht im Undo-Verlauf erfasst und markiert die Szene nicht als geändert. Unmittelbar vor dem Speichern der Szene wird die ursprüngliche Szenenbeleuchtung wiederhergestellt und danach die Kartenbeleuchtung erneut angewendet. Die Kartenbeleuchtung landet deshalb nie in deiner Szenendatei. Restore Scene Lighting im Inspector holt die Beleuchtung zurück, die vor dem ersten Anwenden aktiv war.

Einstellungen der Komponente

EinstellungStandardBeschreibung
TerrainleerTerrain, dessen Kartenbeleuchtung angewendet wird. Leer = das RTSTerrain auf diesem GameObject, sonst das Haupt-Terrain (RTSTerrain.Main). Ein später aktiviertes Terrain wird automatisch übernommen.
SunleerDirectional Light, das als Sonne dient. Leer = RenderSettings.sun oder das hellste aktive Directional Light.
Apply In Edit ModeanWendet die Kartenbeleuchtung während der Bearbeitung an. Ist die Option aus, wird sie nur im Play Mode angewendet.
Apply SunanWendet Farbe, Intensität und Rotation der Sonne an (in Weltkoordinaten).
Apply AmbientanWendet das Umgebungslicht an.
Ambient ModeTrilightFlat = eine einzige Umgebungsfarbe. Trilight = Verlauf Himmel / Äquator / Boden, abgeleitet aus der Umgebungsfarbe der Karte.
Sky Factor1.2Trilight: Himmelsfarbe = Umgebungsfarbe × dieser Faktor (0–3).
Ground Factor0.55Trilight: Bodenfarbe = Umgebungsfarbe × dieser Faktor (0–3).
Apply FoganWendet den Nebelzustand (an/aus), die Nebelfarbe und die Nebeldichte an.
Fog ModeExponential SquaredAbnahmemodus, mit dem der Nebel angewendet wird (die Karte speichert nur Farbe und Dichte).
Restore On DisableanStellt beim Deaktivieren der Komponente die Szenenbeleuchtung wieder her, die vor dem ersten Anwenden aktiv war.

Standardwerte einer neuen Karte: Sonnenfarbe warmes Weiß (1, 0.956, 0.84), Intensität 1.2, Rotation (50, −30), Umgebungslicht (0.45, 0.5, 0.58), Nebel aus, Nebelfarbe (0.6, 0.68, 0.75), Dichte 0.004.

Für einen Tag-Nacht-Zyklus in deinem Spiel steuerst du das Sonnenlicht direkt aus deinem eigenen Skript. RTSEnvironment wendet die Kartenwerte nur dann erneut an, wenn eine Karte geladen wird oder sich ihre Umgebung ändert. Alternativ deaktivierst du Apply Sun und übernimmst nur Umgebungslicht und Nebel aus der Karte.

Umgebungs-API

MemberBeschreibung
void Apply()Wendet die Umgebung der Karte sofort auf die Szene an (berücksichtigt Apply In Edit Mode).
void CaptureFromScene(RTSEditSession session)Schreibt die Szenenbeleuchtung als einen Undo-Schritt der Sitzung in die Karte.
void CaptureFromScene(RTSToolContext ctx = null)Schreibt die Szenenbeleuchtung in die Karte. Ein Undo-Schritt wird nur innerhalb von RTSEditSession.RunOperation aufgezeichnet.
void RestoreSceneState()Stellt die Beleuchtung wieder her, die vor dem ersten Anwenden gesichert wurde.
RTSTerrain Terrain { get; set; }, Light Sun { get; set; }, bool CanApplyZugeordnetes Terrain, Sonnenlicht und ob das Anwenden gerade erlaubt ist.
static Light FindSun()RenderSettings.sun, wenn es ein Directional Light ist, sonst das hellste aktive Directional Light.
using RTSTerrainStudio;
using UnityEngine;

/// Switches the current map to a stormy mood. RTSEnvironment applies it immediately.
public static class StormLighting
{
    public static void Apply(RTSTerrain terrain)
    {
        MapData map = terrain != null ? terrain.Map : null;
        if (map == null) return;

        // Work on a copy, then assign it: this is how the editor tools change the environment too.
        EnvironmentSettings env = map.Environment.Clone();
        env.sunColor = new Color(0.7f, 0.75f, 0.85f);
        env.sunIntensity = 0.45f;
        env.ambientColor = new Color(0.3f, 0.33f, 0.4f);
        env.fog = true;
        env.fogColor = new Color(0.45f, 0.48f, 0.52f);
        env.fogDensity = 0.012f;
        map.Environment = env;

        // MapLayers.Environment tells RTSEnvironment (and the in-game editor) that the lighting changed.
        terrain.NotifyChanged(MapLayers.Environment, map.CellBounds);
    }
}

Performance-Leitfaden

Mit den Standardeinstellungen laufen typische RTS-Karten (128–256 Zellen pro Seite) auf dem Desktop gut. Dieser Abschnitt erklärt, was jede Einstellung kostet, damit du große Karten und mobile Builds gezielt abstimmen kannst.

Kurze Checkliste

  • Lasse das Burst-Paket installiert und die Burst-Kompilierung aktiviert (Jobs ▸ Burst ▸ Enable Compilation).
  • Erstelle die Textur-Arrays des Tilesets im Editor bei aktivierter Option Compress Arrays, und zwar für die Plattform, auf der du dein Spiel veröffentlichst (der Inspector des Tilesets zeigt das Format und eine geschätzte Größe).
  • Aktiviere Enable GPU Instancing in jedem Material instanzierter Bäume, Details und Dekorobjekte (der Projektvalidator erledigt das mit einem Klick).
  • Verwende den Spawn-Modus Instanced für Bäume und Props, die nur Kulisse sind. Behalte den Modus GameObject für Objekte mit Gameplay-Funktion bei.
  • Wähle Splat-Auflösung und Array-Auflösung passend zur Plattform (siehe unten). Beide kosten vor allem Speicher.
  • Führe Validate Map aus (Tab Map ▸ Validation). Die Validierung warnt vor zu vielen Bäumen (20 000), GameObject-Bäumen (1 500), Dekorobjekten (6 000), GameObject-Dekorobjekten (2 000), Detailschichten (8) und vor großen Karten (über 512 Zellen).

Die Chunk-Größe wählen

Chunk SizeDraw Calls (256 × 256-Karte)Neuaufbaukosten pro BearbeitungVerwendung
16256 ChunksAm geringsten: schnellste Rückmeldung bei PinselstrichenKleine Karten, viel Bearbeitung (In-Game-Editor auf langsameren Rechnern)
32 (Standard)64 ChunksAusgewogenDie meisten Karten
6416 ChunksAm höchsten: Jede Bearbeitung baut bis zu 64 × 64 Zellen neu auf, an Rändern zusätzlich die NachbarnGroße Karten, die selten bearbeitet werden (ausgelieferte Karten, die Spieler nur ansehen)

Jeder Chunk kostet einen Draw Call pro gerenderter Ansicht (die Hauptkamera und jede Schattenkaskade). Der SRP Batcher fasst sie günstig zusammen, weil sie sich ein Material teilen. Chunks sind außerdem die Einheit für das Culling: Mit kleineren Chunks arbeitet das Culling präziser, wenn die Kamera nah am Boden ist.

Unterteilungen

Die Vertexanzahl des glatten Bodens wächst quadratisch mit Surface Subdivisions:

Surface SubdivisionsVertices eines flachen 32 × 32-ChunksEffektive Cliff Subdivisions beim Standardwert 4 (bei 8)
11 0894 (8)
2 (Standard)4 2254 (8)
39 4096 (12)
416 6414 (8)

Cliff Subdivisions wird auf eine gerade Zahl aufgerundet, die zugleich ein Vielfaches von Surface Subdivisions ist. So teilen sich Klippenzellen und glatte Zellen jeden Randvertex. Bei 3 Oberflächenunterteilungen wird das Unterraster der Klippen zu einem Vielfachen von 6, und das ist teuer. Bevorzuge 1, 2 oder 4. Jede Klippenzelle kostet ungefähr (Klippenunterteilungen)² × 2 Dreiecke plus ihre Wände (so viele Reihen pro Stufe, wie Cliff Wall Segments angibt). Karten mit vielen Klippen profitieren deshalb am meisten von moderaten Klippeneinstellungen.

Ein Chunk-Vertex belegt 28 Byte. Die Indizes sind 16-Bit-Werte, solange ein Chunk unter 65 536 Vertices bleibt. Eine flache 256 × 256-Karte mit 2 Unterteilungen hat rund 260 000 Vertices, also etwa 10 MB Vertex- und Indexdaten. Klippen kommen noch hinzu, und Chunk-Collider brauchen ihre eigenen gecookten Daten.

Splat-Auflösung

Die Splat-Auflösung (Texel pro Zelle: 1, 2, 4 oder 8, Standard 4) legst du beim Erstellen einer Karte fest (im New Map Wizard). Sie bestimmt, wie fein sich Bodentexturen malen lassen. Splat-Daten bestehen immer aus 4 Splatmaps im Format RGBA32 (16 Byte pro Texel) und liegen dreimal vor: in den Kartendaten, in der CPU-Kopie der Texturen und auf der GPU (die beiden letzten mit Mipmaps, +33 %).

Kartengröße (Zellen)Splat-Auflösung 2Splat-Auflösung 4 (Standard)Splat-Auflösung 8
128 × 1281 MiB Daten + 1.3 MiB GPU4 MiB + 5.3 MiB16 MiB + 21 MiB
256 × 2564 MiB + 5.3 MiB16 MiB + 21 MiB64 MiB + 85 MiB
512 × 51216 MiB + 21 MiB64 MiB + 85 MiB256 MiB + 341 MiB
1024 × 102464 MiB + 85 MiB256 MiB + 341 MiB1 GiB + 1.3 GiB

(Für die CPU-Kopie der Texturen rechnest du den GPU-Wert noch einmal hinzu.) Das Malen kostet wenig: Nur die geänderten Zeilen werden kopiert. Bei Texturen mit mehr als 1 Million Texel wird bei einem geänderten Bereich bis 512 × 512 Texel nur dieser Bereich hochgeladen. Seine Mip-Kette wird auf der CPU von einem Burst-Job (SplatMipDownsampleJob) neu berechnet und auf der GPU an die passende Stelle kopiert.

Auflösung 2 reicht für den harten Kachel-Look klassischer RTS-Spiele (Tile Paint). Auflösung 4 ergibt weiche, detaillierte Übergänge. Auflösung 8 ist nur für sehr kleine Zellgrößen oder nah herangezoomte Kameras gedacht.

Auflösung der Textur-Arrays

Jedes Tileset packt seine Boden- und Klippentexturen in zwei Texture2DArrays (Albedo/Höhe und Normalen) mit je 20 Slices, und zwar in der Array Resolution des Tilesets (256, 512, 1024 als Standard, 2048). Speicherbedarf beider Arrays zusammen, einschließlich Mipmaps:

Array ResolutionUnkomprimiert RGBA32BC7 / DXT5 (Desktop, Konsolen)ASTC 6×6 (Android, iOS)
25613 MiB3.3 MiB1.5 MiB
51253 MiB13 MiB5.9 MiB
1024 (Standard)213 MiB53 MiB24 MiB
2048853 MiB213 MiB95 MiB

Ist Compress Arrays aktiviert, backt der Inspector des Tilesets komprimierte Arrays für die aktive Build-Plattform: BC7 für Windows, macOS, Linux und Konsolen, ASTC 6×6 für Android, iOS, tvOS und visionOS, für Web die Texturkomprimierung des Builds (DXT5, ASTC 6×6 oder ETC2 RGBA8), sonst DXT5. Fehlen gebaute Arrays oder sind sie im Editor veraltet, erstellt der Renderer stattdessen unkomprimierte RGBA32-Arrays auf der GPU. Für schnelles Iterieren ist das in Ordnung, es kostet aber viermal so viel Speicher wie BC7. Player-Builds erstellen veraltete Arrays vor dem Build neu (siehe Textur-Arrays in Player-Builds). Alle Terrains mit demselben Tileset teilen sich die Arrays.

Kann eine GPU oder ein Browser das gebackene Format nicht samplen, dekomprimiert Unity die Arrays beim Laden. Sie belegen dann etwa viermal so viel Speicher. Player-Builds geben die CPU-Kopie gebauter Arrays bei der ersten Verwendung frei. In einem Player-Build belegen die gebauten Arrays daher nur den in der Tabelle angegebenen GPU-Speicher.

Um zu prüfen, welche Arrays ein laufendes Spiel verwendet, erstelle im Memory Profiler einen Snapshot und suche nach Render Textures namens „RTS Albedo Array (…)“ und „RTS Normal Array (…)“. Findest du sie, sind die zur Laufzeit erstellten unkomprimierten Arrays im Einsatz.

Detaildistanz und Vegetation

  • Details (Gras und Kleinpflanzen) sind auf der GPU der wahrscheinlichste Engpass, weil Gras aus vielen kleinen Dreiecken mit Alpha Clipping besteht. Die sichtbare Anzahl skaliert mit Render Distance² × Dichte. Halbierst du die Detaildistanz, sinkt die Menge des gezeichneten Grases auf etwa ein Viertel.
  • Speicher für Details: Jeder bemalte Patch hält seine Instanzen unabhängig von der Kamera auf der GPU, mit 32 Byte pro Instanz. Beispiel: Eine Detailschicht mit voller Dichte (Max Density 6, Detailauflösung 2 = 4 Texel pro Zelle) über eine ganze 256 × 256-Karte ergibt etwa 1.6 Millionen Instanzen, also rund 50 MB. Male Gras dort, wo es darauf ankommt, oder senke Max Density.
  • Detailschatten sind standardmäßig aus. Schaltest du sie ein, wird das gesamte Gras für jede Schattenkaskade erneut gerendert.
  • Bäume: Instanzierte Bäume kosten einen Draw Call pro 1 023 Instanzen, je Teil und je Patch-Gruppe. Verwende eine LODGroup mit einem günstigen LOD1. GameObject-Bäume kosten jeweils ein GameObject (bleibe unter etwa 1 500).
  • Dekor: Lege für instanziertes Dekor in großen Karten Max Draw Distance fest. GameObject-Dekor wird von Unity gecullt, kostet aber trotzdem jeweils ein GameObject (bleibe unter etwa 2 000).
  • Wasser: Cell Subdivisions 1 viertelt die Wasser-Vertices gegenüber dem Standardwert 2.

Burst und Jobs

Alle rechenintensiven Schleifen laufen in Burst-kompilierten Jobs: Chunk-Meshes (BuildChunkGeometryJob, CopyChunkPartsJob), Splat-Mips (SplatMipDownsampleJob), Wasser-Meshes (WaterChunkMeshJob), Verteilung der Details (DetailScatterJob), Baummatrizen (TreeMatrixJob) und Dekorplatzierung (DecorPlacementJob, DecorPartMatricesJob). Pinsel, Pathing, Nebel des Krieges, der Pathfinder und der Minikarten-Baker nutzen ebenfalls Burst. Chunk-Collider werden parallel auf Worker-Threads gebacken.

  • Die Mesh-Jobs werden synchron kompiliert. So übernimmt beim ersten Aufbau nach einem Domain Reload kein langsamer Managed-Fallback die Arbeit.
  • Miss die Performance in einem Player-Build oder mit ausgeschalteten Burst-Safety-Checks (Jobs ▸ Burst ▸ Safety Checks). Safety Checks im Editor und der Jobs Debugger machen Jobs spürbar langsamer.
  • Ist die Burst-Kompilierung deaktiviert, funktioniert weiterhin alles, aber Mesh-Neuaufbauten und Pinselstriche werden um ein Vielfaches langsamer.
  • Web-Builds haben keine Worker-Threads, daher laufen alle Jobs nacheinander auf dem Hauptthread. Vorgänge, die die ganze Karte betreffen, dauern dort spürbar länger (siehe Rendering und Performance bei Web-Builds).

Profiling

Öffne Window ▸ Analysis ▸ Profiler und suche auf dem Main Thread nach diesen Markern:

MarkerWas er misst
RTSVegetation.UpdateÜbertragen von Kartenänderungen auf die Vegetation (enthält die beiden Build-Marker darunter).
RTSVegetation.BuildDetailsNeuerzeugung veränderter Detail-Patches.
RTSVegetation.BuildTreesNeuaufbau instanzierter Baumgruppen.
RTSVegetation.DrawCulling und Absetzen der Vegetations-Draw-Calls für eine Kamera.

Schalte im CPU-Modul auf Timeline um, dann siehst du die oben genannten Job-Namen auf den Worker-Threads. Für die GPU zählst du mit dem Frame Debugger (Window ▸ Analysis ▸ Frame Debugger) die Draws von Terrain-Chunks, Details und instanzierten Objekten. Die Inspectors von Vegetation Renderer, Water Renderer und Decor Manager zeigen live die Anzahl der Instanzen und Vertices. Der Inspector von RTSTerrain zeigt die Anzahl der Chunks und den Speicherbedarf der Karte.

Speicherbedarf in Zahlen

Die Kartendaten selbst (der Wert Map Memory im Inspector von RTSTerrain, MapData.GetMemoryUsage()) belegen:

  • 11 Byte pro Vertex (Höhe, Klippenstufe, Flags, Klippentyp, Wasserhöhe),
  • 16 Byte pro Splat-Texel,
  • 1 Byte pro Pathing-Texel,
  • 1 Byte pro Detail-Texel und Detailschicht.
Kartengröße (Standardwerte: Splat 4, Detail 2, Pathing 2, 1 Detailschicht)KartendatenSplat-Texturen (GPU)Textur-Arrays (1024, BC7)
128 × 128 (256 m bei Zellen von 2 m)≈ 4.3 MiB≈ 5.3 MiB53 MiB pro Tileset
256 × 256≈ 17 MiB≈ 21 MiB53 MiB pro Tileset
512 × 512≈ 69 MiB≈ 85 MiB53 MiB pro Tileset
1024 × 1024≈ 275 MiB≈ 341 MiB53 MiB pro Tileset

Hinzu kommen die Chunk-Meshes und -Collider (siehe Unterteilungen), die Detailinstanzen (je 32 Byte), die Pathing-Karte und die Texturen für den Nebel des Krieges. Der Undo-Verlauf des Editors hat ein eigenes Speicherbudget (siehe die Einstellungen des Editorfensters).

Empfohlene Kartengrößen

Karten können 16 bis 1024 Zellen pro Seite groß sein, in Schritten von 16. Standard sind 128 × 128 Zellen zu je 2 m.

Größe (Zellen)Typische VerwendungHinweise
64–1281v1- und kleine Gefechtskarten, MobilgeräteAlles wird sofort neu aufgebaut. Eine gute Größe für den In-Game-Editor auf jedem Gerät.
128–2562–4 SpielerDer ideale Bereich. Alle Standardwerte sind darauf abgestimmt.
256–512Große Karten, 4–8 SpielerDesktop. Erwäge Splat-Auflösung 2–4 und Chunk Size 32–64.
über 512Sehr große KartenDie Kartenvalidierung zeigt einen Info-Hinweis: Bearbeitung und Pathing-Neuaufbau dauern länger. Behalte den Splat-Speicher im Blick (Tabelle oben) und erwäge, die Welt auf mehrere RTSTerrains aufzuteilen.

Mehrere Terrains lassen sich nebeneinander platzieren, jedes mit eigenem Transform. RTSTerrain.FindAt und RTSTerrain.RaycastAll finden das passende Terrain für eine Weltposition.

Hinweise für Mobilgeräte

  • Textur-Arrays: Erstelle sie bei aktiver Android- bzw. iOS-Plattform, damit sie als ASTC 6×6 gebacken werden (sobald sie einmal erstellt sind, halten Player-Builds sie für die aktive Plattform aktuell). Auf Smartphones reicht meist eine Array Resolution von 512.
  • Splat-Auflösung: 2 Texel pro Zelle vierteln den Splat-Speicher gegenüber dem Standardwert 4.
  • Schichten: Jede an einem Pixel sichtbare Bodenschicht kostet Textur-Samples. Verwende weniger Schichten pro Bereich und setze Anti Tiling bei Schichten, deren Kachelung nicht auffällt, auf 0 (das spart das zweite Sample).
  • Beleuchtung: Verwende ein Override-Material mit ausgeschalteten Specular Highlights und Environment Reflections (siehe oben).
  • OpenGL ES und WebGL 2.0: Die Shader sind für Shader Model 3.5 ausgelegt und laufen daher auf Geräten mit OpenGL ES 3.0 und im Browser. Diese APIs garantieren nur 16 Fragment-Texturen pro Draw Call, und der Forward Pass des Terrains belegt selbst schon 10 davon. Das URP Asset Mobile aus den URP-Projektvorlagen von Unity (Schatten des Hauptlichts, Reflection Probe Blending) fügt bereits 4 weitere hinzu, und mit Light Cookies ist die Grenze erreicht. Eine Shader-Variante, die mehr Texturen braucht, lässt sich nicht laden, und das Terrain verschwindet oder wird pink dargestellt. Schalte im URP Asset für GLES-Geräte und Web-Builds Reflection Probe Blending aus, wenn deine Szenen keine Probes überblenden. Lasse außerdem Light Cookies, Decals, SSAO, Schatten zusätzlicher Lichter und Adaptive Probe Volumes deaktiviert, solange du sie nicht brauchst. Der Kommentar Texture slot budget in Shaders/Include/RTSTerrainInput.hlsl listet die Kosten jeder URP-Funktion auf.
  • Wasser: Schaltest du im mobilen URP Asset Depth Texture oder Opaque Texture aus, um Bandbreite zu sparen, wechselt das Wasser auf seinen Fallback: Ohne Opaque Texture gibt es keine Lichtbrechung, und ohne Depth Texture ignorieren die Ufer Objekte im Wasser. Eine Kamera, die die beiden Texturen für sich selbst einschaltet, bekommt weiterhin das vollständige Wasser. Verwende Cell Subdivisions 1.
  • Vegetation: niedrigere Multiplikatoren für Detail Density und Detail Distance, Crossed Quad Count 2, Detailschatten aus.
  • Physik: Wenn dein Spiel auf dem Terrain keine Unity-Physik nutzt, setze Collider Mode auf None und verwende für das Picking RTSTerrain.Raycast / RaycastAll.
  • Geometrie: Surface Subdivisions 1 (der klassische RTS-Look) und Cliff Wall Segments 2 reduzieren die Anzahl der Vertices deutlich.

Ein Low-End-Preset per Code

using RTSTerrainStudio;
using UnityEngine;

/// Applies cheap settings to a terrain and its systems, e.g. from a "Low" graphics option.
public static class LowEndPreset
{
    public static void Apply(RTSTerrain terrain)
    {
        if (terrain == null) return;

        // Terrain mesh: fewer vertices, larger chunks, no colliders (picking uses RTSTerrain.Raycast).
        RTSTerrainSettings s = terrain.Settings;
        s.surfaceSubdivisions = 1;
        s.cliffSubdivisions = 4;
        s.cliffWallSegments = 2;
        s.chunkSize = ChunkSize._64;
        s.colliderMode = ColliderMode.None;
        // Settings changed from code are applied by a rebuild (the inspector does this automatically).
        terrain.RebuildAll();

        if (terrain.TryGetComponent(out RTSVegetationRenderer vegetation))
        {
            vegetation.detailDensityMultiplier = 0.4f;
            vegetation.detailDistanceMultiplier = 0.5f;
            vegetation.treeDistanceMultiplier = 0.7f;
            vegetation.crossedQuadCount = 2;
        }

        if (terrain.TryGetComponent(out RTSWaterRenderer water))
        {
            water.cellSubdivisions = 1;   // rebuilt automatically when the value changes
        }

        if (terrain.TryGetComponent(out RTSDecorManager decor))
        {
            decor.maxDrawDistance = 120f; // instanced props beyond 120 m are skipped
        }
    }
}

ColliderMode.None entfernt die Chunk-Collider. Danach funktioniert in deinem Spiel nichts mehr, was Physics.Raycast gegen das Terrain einsetzt. Dasselbe gilt für Rigidbodies, die darauf stehen, und für das Backen eines NavMesh aus den Chunk-Collidern. Stelle nur dann um, wenn du die physikfreien Abfragen verwendest.

Gameplay-Systeme

RTS Terrain Studio ist mehr als ein Karteneditor. Deine Karten bringen die Systeme mit, die ein klassisches RTS-Spiel braucht: einen Nebel des Krieges, der Klippenstufen und Bäume berücksichtigt, eine Pathing-Karte mit eingebauter Wegfindung, fertige Gameplay-Beispielskripte (Kamera, Einheiten, Auswahl, Befehle, Minikarte, HUD) und einen Demo-Inhaltsgenerator, der Texturen, Modelle, Karten und spielbare Demoszenen erzeugt.

Dieses Kapitel stellt die Systeme nacheinander vor. Jeder Abschnitt zeigt zuerst, was du ohne Code erreichst, listet dann die Komponenten und ihre Einstellungen auf und endet mit der Scripting-API für Programmierer.

Willst du einfach nur spielen? Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus, doppelklicke im Project-Fenster auf die dabei ausgewählte Szene RTS Terrain Studio - Gameplay Demo und drücke Play. Siehe Demoszenen.

Nebel des Krieges in der Gameplay Demo: Einheiten und ein Startgebäude, der Bereich hinter einer höheren Klippe ist schwarz
Nebel des Krieges in der Gameplay Demo: Einheiten decken ihre Umgebung auf. Das Plateau hinter der höheren Klippe bleibt schwarz, weil höher gelegener Boden die Sichtlinie blockiert (klassische RTS-Regeln).

Nebel des Krieges

Der Nebel des Krieges verbirgt, was ein Spieler nicht sehen kann. Er folgt den klassischen RTS-Regeln:

  • Pro Team. Die Sichtbarkeit wird für bis zu 32 Teams erfasst (Teamindizes 0 bis 31). Jede Einheit und jedes Gebäude gibt seinem eigenen Team Sicht.
  • Drei Zustände. Boden, den ein Team noch nie gesehen hat, ist schwarz (unerkundet). Boden, den es schon gesehen hat, aber gerade nicht sieht, ist abgedunkelt (erkundet). Boden, den es gerade sieht, wird normal dargestellt (sichtbar).
  • Erkundungsgedächtnis. Jedes Team merkt sich, was es erkundet hat, bis du die Erkundung zurücksetzt.
  • Sichtlinie mit Klippenstufen. Eine Einheit sieht ihre eigene Klippenstufe und alles darunter. Höher gelegener Boden ist von unten nie sichtbar und verdeckt die Zellen dahinter.
  • Blocker. Bäume (und Dekor, das du als sichtblockierend markierst) verdecken die Zellen dahinter.
  • Allianzen und geteilte Sicht. Teams können ihre Sicht in eine Richtung oder in beide Richtungen teilen.
  • Verborgene Gegner. Gegnerische Einheiten verschwinden, solange du sie nicht sehen kannst. Gebäude bleiben als „Geisterbilder“ sichtbar, sobald du sie entdeckt hast.

Die Sichtbarkeit wird einige Male pro Sekunde von Burst-Jobs berechnet und in einer Textur gespeichert. Diese Textur wird zeitlich geglättet und auf der GPU weichgezeichnet. So bewegen sich die Nebelränder sanft, statt von Zelle zu Zelle zu springen.

Schnellstart: Nebel des Krieges zu deiner Szene hinzufügen

  1. Öffne eine Szene, die ein RTS Terrain enthält.
  2. Wähle GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager. Dadurch wird ein GameObject Fog of War mit einer Komponente RTSFogOfWarManager erstellt und ausgewählt.
  3. Wenn dein URP-Renderer das Renderer Feature für den Nebel des Krieges noch nicht hat, bietet ein Dialog an, es hinzuzufügen. Klicke auf Add Feature. (Du kannst das auch später über Tools ▸ RTS Terrain Studio ▸ Validate Project oder von Hand erledigen, siehe Rendering.)
  4. Füge jeder Einheit und jedem Gebäude, das sehen soll, eine Komponente RTSFogOfWarRevealer hinzu (Add Component ▸ RTS Terrain Studio ▸ Fog of War Revealer). Stelle dort Team und Radius ein.
  5. Füge jedem Objekt, das Gegner nicht sehen sollen, eine Komponente RTSFogOfWarHideable hinzu (Add Component ▸ RTS Terrain Studio ▸ Fog of War Hideable). Stelle dort Team ein. Verwende Hide When Not Visible für Einheiten und Ghost When Explored für Gebäude.
  6. Drücke Play. Die Karte ist schwarz, außer rund um die Sichtquellen des unter Local Team eingestellten Teams (standardmäßig Team 0).

Der Nebel läuft nur im Play Mode. Um ihn schon beim Bearbeiten zu sehen, aktiviere am Manager Preview In Edit Mode (oder klicke in seinem Inspector auf Enable Preview). Verbergbare Objekte werden nur im Play Mode ausgeblendet; im Edit Mode bleibt alles sichtbar.

Die Beispieleinheit RTSUnit setzt die Komponenten für Sichtquelle und verbergbares Objekt bereits voraus und konfiguriert sie. Wenn du die Beispiele (oder RTSDemoGame) verwendest, musst du diese Komponenten nicht von Hand hinzufügen. RTSDemoGame erstellt sogar den Manager für dich, wenn die Szene noch keinen hat.

Wie die Sichtbarkeit berechnet wird

Die Karte ist in Nebelzellen unterteilt. Mit dem Standardwert 2 für Resolution Per Cell und der Standard-Zellgröße der Karte von 2 m ist eine Nebelzelle 1 m breit. Für jede Sichtquelle geht der Manager so vor:

  1. Er bestimmt die Augenhöhe: die Klippenstufe an der genauen Position der Sichtquelle plus ihren Height Bonus.
  2. Er durchläuft jede Nebelzelle innerhalb des unter Radius eingestellten Sichtradius.
  3. Je nach Einstellung von Line Of Sight zieht er eine Linie von der Sichtquelle zur Zelle. Die Zelle ist sichtbar, wenn ihre eigene Klippenstufe nicht höher als die Augenhöhe ist und keine Zelle auf dem Weg höher als die Augenhöhe liegt oder (mit Blockern) einen Baum oder sichtblockierendes Dekor enthält. Die blockierende Zelle selbst ist sichtbar. Du siehst also den Rand eines Waldes, aber nicht, was dahinter liegt.
  4. Er markiert die Zelle als sichtbar und erkundet – für das Team der Sichtquelle und für jedes Team, das die Sicht dieses Teams erhält.
Modus von Line Of SightVerhaltenVerwendung
NoneSichtquellen sehen einen vollen Kreis. Klippen und Bäume blockieren nicht. Günstigster Modus.Spiele ohne Sichtlinie, sehr viele Einheiten.
CliffLevelsKlassische RTS-Regel: Eine Sichtquelle sieht ihre eigene Klippenstufe und alles darunter. Höher gelegener Boden blockiert die Sicht.Offene Karten oder wenn Bäume die Sicht nicht blockieren sollen.
CliffLevelsAndBlockers (Standard)Wie CliffLevels, zusätzlich verdecken Bäume und sichtblockierendes Dekor die Zellen dahinter.Das volle klassische RTS-Erlebnis.

Welche Objekte die Sicht blockieren, legst du in den Bibliotheken fest:

  • Bäume: die Option Blocks Vision eines Baum-Prototyps in der Vegetationsbibliothek (standardmäßig an). Der blockierte Bereich ist der Footprint Radius des Baums mal die Skalierung der Instanz.
  • Dekor: die Option Blocks Vision eines Dekor-Prototyps in der Dekorbibliothek (standardmäßig aus). Der blockierte Bereich ist die Grundfläche des Prototyps (Kreis oder gedrehtes Rechteck), skaliert mit der Instanz.

Wachtürme, Hügel und fliegende Späher: Gib ihrer Sichtquelle einen Height Bonus von einer oder mehreren Stufen, damit sie über höhere Klippen blicken können, oder aktiviere für Lufteinheiten Ignore Line Of Sight, damit nichts ihre Sicht blockiert.

Kartenänderungen werden automatisch erkannt (im Editor, im In-Game-Editor und bei Gameplay-Code, der RTSTerrain.NotifyChanged aufruft). Bei Änderungen an Klippenstufen (MapLayers.CliffLevels / VertexFlags) werden nur die geänderten Zellen plus ein Rand von einer Zelle neu abgetastet. Nach Änderungen an Bäumen und Dekor wird das Sichtblocker-Raster der ganzen Karte bei der nächsten Aktualisierung neu aufgebaut. Ein Aufruf von NotifyChangedAll baut das Klippenraster vollständig neu auf.

RTSFogOfWarManager

Ein Manager pro Szene berechnet und rendert den Nebel für ein Terrain. Erstelle ihn über GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager oder Add Component ▸ RTS Terrain Studio ▸ Fog of War Manager. Sind mehrere Manager aktiviert, wird nur der erste (RTSFogOfWarManager.Active) verwendet, und der Inspector zeigt eine Warnung.

EinstellungStandardBeschreibung
Map
TerrainleerTerrain, das der Nebel abdeckt. Leer bedeutet RTSTerrain.Main (das erste aktivierte Terrain).
Resolution Per Cell2Nebelzellen pro Kartenzelle und Achse: 1, 2 oder 4. Höhere Werte ergeben weichere Ränder, kosten aber mehr. Der Wert wird automatisch gesenkt, damit das Nebelraster höchstens 2048 Zellen pro Achse hat. Eine Änderung baut das Raster neu auf und setzt die Erkundung zurück.
Simulation
Update Interval0.1Sekunden zwischen zwei Sichtbarkeitsaktualisierungen. Bei 0 wird jeden Frame aktualisiert.
Local Team0Team, dessen Sicht gerendert und von den verbergbaren Objekten verwendet wird (0 bis 31). Wechsle es im Inspector, um zu prüfen, was andere Spieler sehen.
Shared Visionalle 0Für jedes empfangende Team eine Bitmaske der Teams, die ihre Sicht mit ihm teilen (Bit t = Team t). Wird meist per Code gesetzt, siehe Allianzen.
Line Of SightCliffLevelsAndBlockersWie Klippen und Objekte die Sicht blockieren. Siehe Wie die Sichtbarkeit berechnet wird.
Compute All TeamsausBerechnet die Sichtbarkeit für jedes Team, nicht nur für das lokale. Nötig für Server (Interest Management) und für KI-Code, der abfragt, was ein anderes Team sehen kann.
Start ExploredausDie ganze Karte ist von Anfang an erkundet: keine schwarzen Bereiche, nur der abgedunkelte Nebel. Wird beim Erstellen des Nebelrasters angewendet.
Reveal AllausDebug: Für jedes Team ist alles sichtbar und erkundet.
Look
Unexplored ColorSchwarz, Alpha 1Farbe von Bereichen, die nie erkundet wurden. Alpha bestimmt die Stärke.
Explored Color(0.3, 0.32, 0.38), Alpha 0.6Farbton, mit dem erkundete, gerade nicht sichtbare Bereiche multipliziert werden. Alpha bestimmt die Stärke.
Fade Speed4Wie schnell der Nebel ein- und ausblendet (vollständige Überblendungen pro Sekunde). 0 bedeutet sofort.
Blur Iterations1Weichzeichner-Durchgänge auf der GPU für die Nebeltextur (0 bis 4, 0 = aus).
Blur Upscale2Auflösungsfaktor der weichgezeichneten Textur (1 bis 4). Höhere Werte ergeben rundere Ränder. Wird automatisch gesenkt, damit die weichgezeichnete Textur höchstens 2048 Pixel groß ist.
Editor
Preview In Edit ModeausLässt den Nebel des Krieges im Edit Mode als Vorschau laufen.
Shaders
Blur Shader, Minimap Shaderautomatisch zugewiesenHier referenziert, damit die versteckten Shader in Player-Builds enthalten sind. Lasse sie unverändert.

Der Inspector zeigt außerdem Live-Statistiken (Zustand, Größe des Nebelrasters, Speicher, aktive Sichtquellen, Anzahl der Aktualisierungen), eine Live-Vorschau der Nebeltextur (Rot = gerade sichtbar, Grün = erkundet, Schwarz = nie gesehen) und diese Schaltflächen:

SchaltflächeFunktion
Explore AllMarkiert die ganze Karte für das lokale Team als erkundet.
Reset ExplorationVergisst die Erkundung aller Teams.
Update NowBerechnet die Sichtbarkeit sofort neu.
Rebuild Terrain DataLiest Klippenstufen und Blocker erneut aus dem Terrain.

RTSFogOfWarRevealer

Gibt seinem Team Sicht rund um das GameObject. Füge die Komponente Einheiten, Gebäuden, Wachposten und allem anderen hinzu, was sehen soll. Die Sichtquelle meldet sich beim Aktivieren selbst beim Manager an. Der Manager liest ihre Position bei jeder Aktualisierung, daher kostet die Komponente pro Frame nichts. Wähle sie aus, um ihren Sichtradius als gelben Kreis in der Scene View zu sehen.

EinstellungStandardBeschreibung
Team0Team, das die Sicht erhält (0 bis 31).
Radius12Sichtradius in Metern.
Height Bonus0Zusätzliche Klippenstufen, über die die Sichtquelle hinwegsehen kann (Wachtürme, Hügel, fliegende Späher).
Ignore Line Of SightausSieht über Klippen und Blocker hinweg (Lufteinheiten).
Reveal When DisabledausDeckt weiter auf, während diese Komponente oder ihr GameObject deaktiviert ist, bis sie zerstört wird.

Sichtquellen mit Reveal When Disabled auf GameObjects, die beim Laden der Szene bereits inaktiv sind, werden einmalig eingesammelt, wenn der Manager aktiviert wird. Objekte, die du später inaktiv instanziierst (Objekt-Pools, additive Szenen), führen nie Awake aus. Registriere sie daher selbst mit RTSFogOfWarManager.RegisterRevealer(revealer) oder setze RevealWhenDisabled = true per Code.

RTSFogOfWarHideable

Blendet die Renderer (und optional Canvases und Collider) eines gegnerischen Objekts aus, solange das lokale Team es nicht sehen kann. Objekte des lokalen Teams und der Teams, die ihre Sicht mit ihm teilen, werden nie ausgeblendet. Wird das Objekt wieder sichtbar, werden nur die Komponenten wieder aktiviert, die beim Ausblenden aktiviert waren. So funktioniert deine eigene Logik zum Aktivieren und Deaktivieren weiterhin.

EinstellungStandardBeschreibung
Team0Team, dem das Objekt gehört (0 bis 31).
ModeHideWhenNotVisibleHideWhenNotVisible: wird nur angezeigt, solange seine Position gerade sichtbar ist (Einheiten). GhostWhenExplored: bleibt nach der Entdeckung angezeigt, auch wenn der Bereich wieder im Nebel liegt (Gebäude und neutrale Props, wie in klassischen RTS-Spielen).
Require SightinganNur im Geisterbild-Modus: Das Objekt muss einmal gesehen worden sein. Ist die Option aus, reicht ein erkundeter Bereich, um es anzuzeigen.
Hide CanvasesanDeaktiviert auch Komponenten vom Typ Canvas (Lebensbalken, Namensschilder).
Hide CollidersausDeaktiviert im verborgenen Zustand auch Collider, damit verborgene Gegner nicht angeklickt werden können. Lasse die Option auf autoritativen Servern und Hosts aus: Deren Physikabfragen müssen auch Einheiten treffen, die der lokale Spieler nicht sieht.
PivotleerOptionaler Transform, dessen Position geprüft wird. Leer bedeutet: der eigene Transform.

Die Komponente löst das Event VisibilityChanged(bool) aus, wenn das Objekt ein- oder ausgeblendet wird. Rufe RefreshComponents() auf, nachdem du zur Laufzeit untergeordnete Renderer hinzugefügt oder entfernt hast.

Rendering: Renderer Feature und Shader-Fallback

Der Nebel kann auf zwei Arten gezeichnet werden. Die Wahl fällt pro Kamera, sodass der Nebel nie doppelt angewendet wird.

WegWann er verwendet wirdWas abgedunkelt wird
RTS Fog Of War renderer feature (empfohlen)Die Kamera rendert mit einem Universal Renderer, der das Feature enthält.Alles auf dem Bildschirm: Terrain, Vegetation, Wasser, Dekor, Einheiten und beliebige eigene Materialien. Der Himmel bleibt unverändert.
Shader-FallbackDer Renderer der Kamera enthält das Feature nicht.Nur die paketeigenen Terrain-, Wasser- und Vegetations-Shader. Einheiten und Props mit anderen Materialien werden nicht abgedunkelt (gegnerische Einheiten werden als verbergbare Objekte trotzdem ausgeblendet).

Renderer Feature von Hand hinzufügen:

  1. Wähle dein Asset vom Typ Universal Renderer Data aus (den Renderer, den deine Spielkameras verwenden).
  2. Klicke in seinem Inspector auf Add Renderer Feature und wähle RTS Fog Of War.
  3. Wiederhole das für jeden Renderer, den Spielkameras verwenden, damit das Ergebnis überall gleich ist.

Oder lasse Tools ▸ RTS Terrain Studio ▸ Validate Project das beheben: Es fügt das Feature dem Standard-Renderer jedes verwendeten URP-Assets hinzu.

Feature-EinstellungStandardBeschreibung
Show In Scene ViewanZeichnet den Nebel auch in Kameras der Scene View. Ist die Option aus, zeigen Scene-View-Kameras dieses Renderers die Karte ohne Nebel.
Render Pass EventAfterRenderingTransparentsWann der Nebel gezeichnet wird. Der Standardwert liegt vor dem Post-Processing, sodass Bloom und Tonemapping über dem Nebel angewendet werden.

Das Feature fordert die Tiefentextur der Kamera selbst an und rekonstruiert damit die Weltposition jedes Pixels. RTSFogOfWarRendererFeature.IsActive gibt an, ob in einem Renderer des aktiven URP-Assets ein aktiviertes Feature vorhanden ist; der Wert dient nur zur Information.

Eigene Shader (fortgeschritten). Der Manager stellt den Nebel als globale Shader-Variablen bereit: _RTS_FogOfWarTex (R = sichtbar, G = erkundet), _RTS_FogOfWarParams (min X, min Z, 1/Größe X, 1/Größe Z in Weltkoordinaten), _RTS_FogOfWarUnexplored, _RTS_FogOfWarExplored und _RTS_FogOfWarEnabled. Die Shader des Pakets binden Shaders/Include/RTSCommon.hlsl ein (nach Core.hlsl von URP) und wenden dessen Hilfsfunktion auf die beleuchtete Farbe an. Ein eigener URP-Shader kann für den Fallback-Weg dasselbe tun:

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// Include RTSCommon.hlsl after Core.hlsl. Adjust the path if you moved the package folder.
#include "Assets/RTSTerrainStudio/Shaders/Include/RTSCommon.hlsl"

half4 frag(Varyings input) : SV_Target
{
    half3 color = /* your lit colour */ half3(1, 1, 1);
    // Darkens unexplored and fogged areas. Does nothing while the renderer feature handles this camera.
    color = RTS_ApplyFogOfWar(color, input.positionWS);
    return half4(color, 1);
}

Nebel des Krieges per Skript steuern

Alle Typen für den Nebel des Krieges liegen im Namespace RTSTerrainStudio. Den Manager der Szene erhältst du über RTSFogOfWarManager.Active (abonniere das statische Event ActiveChanged, wenn Manager hinzukommen und wegfallen).

MemberBeschreibung
Abfragen
bool IsVisible(Vector3 world, int team)True, wenn das Team die Position gerade sieht (eigene und geteilte Sicht). False für Teams, die nicht berechnet werden (siehe IsTeamComputed).
bool IsExplored(Vector3 world, int team)True, wenn das Team die Position mindestens einmal gesehen hat.
bool IsVisibleToLocalTeam(Vector3), bool IsExploredByLocalTeam(Vector3)Kurzformen für das lokale Team.
uint GetVisibilityMask(Vector3 world)Bitmaske der berechneten Teams, die die Position sehen (Bit t = Team t). Ideal für Interest Management auf dem Server.
uint GetExploredMask(Vector3 world)Bitmaske der Teams, die die Position erkundet haben.
bool IsTeamComputed(int team)Mit Compute All Teams immer true, sonst nur für das lokale Team.
bool TryGetFogCell(Vector3, out int2), Vector3 FogCellToWorld(int2)Rechnen zwischen Weltpositionen und Nebelzellen um.
bool IsCellVisible(int2, int team), bool IsCellExplored(int2, int team)Zellbasierte Varianten der Abfragen.
int GetCellLevel(int2), bool IsCellBlocked(int2)Klippenstufe und Blocker-Zustand, die für die Sichtlinie verwendet werden.
Vector2 SampleDisplayedFog(Vector2 normalizedMapPosition)Geglättete Werte, die für das lokale Team auf dem Bildschirm angezeigt werden (x = sichtbar, y = erkundet). Gibt (1, 1) zurück, wenn kein Nebel aktiv ist.
Aufdecken und Erkundung
void RevealArea(Vector3 world, float radius, int team, float duration, bool ignoreLineOfSight = false)Deckt einen Kreis für ein Team auf (Leuchtkugeln, Fähigkeiten wie „Kartenbereich aufdecken“). duration zählt im Play Mode in Spielzeit, in der Vorschau im Edit Mode in Echtzeit. Bei 0 oder weniger wird nur für eine Aktualisierung aufgedeckt.
void ClearTemporaryReveals()Entfernt alle mit RevealArea aufgedeckten Bereiche.
void ExploreAll(int team), void ExploreAllTeams()Markieren die ganze Karte als erkundet.
void ResetExploration(), void ResetExploration(int team)Vergessen die Erkundung (gerade sichtbare Bereiche gelten bei der nächsten Aktualisierung wieder als erkundet).
Allianzen und geteilte Sicht
void SetAlliance(int teamA, int teamB, bool allied)Teilt die Sicht in beide Richtungen oder hebt das Teilen auf.
void SetSharedVision(int sourceTeam, int receiverTeam, bool shared)Lässt receiverTeam sehen, was sourceTeam sieht (eine Richtung).
void SetSharedVisionMask(int receiverTeam, uint sourceTeams), uint GetSharedVision(int receiverTeam)Ersetzen oder lesen die ganze Maske eines empfangenden Teams.
bool HasSharedVision(int sourceTeam, int receiverTeam), uint GetViewMask(int team)Lesen die geteilte Sicht aus; die Sichtmaske enthält auch das Team selbst.
bool IsFriendlyToLocalTeam(int team)True für das lokale Team und für Teams, die ihre Sicht mit ihm teilen. Deren Objekte werden nie ausgeblendet.
Steuerung der Aktualisierung
void RequestUpdate(), void ForceUpdate()Berechnen bei der nächsten Aktualisierung oder sofort neu.
void MarkTerrainDirty()Baut das Klippenstufen-Raster vollständig neu auf. Kartenänderungen werden automatisch erkannt; du brauchst das selten.
void MarkBlockersDirty()Baut das Blocker-Raster neu auf, nachdem du Bäume oder Dekor per Code verschoben hast, ohne RTSTerrain.NotifyChanged aufzurufen.
event Action UpdatedWird nach jeder Sichtbarkeitsaktualisierung ausgelöst, nachdem die verbergbaren Objekte aktualisiert wurden.
Zustand
IsSimulating, IsRunning, IsRenderingFog, HasGrid, HasVisibilityDataOb der Nebel läuft, ein Raster hat und mindestens einmal berechnet wurde.
FogTexture, RawFogTexture, FogWidth, FogHeight, FogCellSize, Version, ActiveRevealerCount, MemoryUsageInformationen zu Raster und Textur.
int EffectiveResolutionPerCellTatsächlich verwendete Nebelzellen pro Kartenzelle. Auf riesigen Karten kleiner als ResolutionPerCell, weil das Raster dort auf MaxFogGridSize (2048) begrenzt wird.
RTSTerrain Terrain, RTSTerrain BoundTerrainDas abzudeckende Terrain (null = RTSTerrain.Main) und das Terrain, für das das Raster gerade aufgebaut ist.
Einstellungen (wie im Inspector)
ResolutionPerCell, LocalTeam, LineOfSight, ComputeAllTeams, RevealAll, FadeSpeed, BlurIterations, BlurUpscaleSetzbare Eigenschaften. Eine Änderung von ResolutionPerCell baut das Raster neu auf und setzt die Erkundung zurück.
const int MaxTeams (32), const int MaxFogGridSize (2048)Höchstzahl an Teams und größte Seitenlänge des Nebelrasters.
Registries
static IReadOnlyList<RTSFogOfWarRevealer> Revealers, static IReadOnlyList<RTSFogOfWarHideable> HideablesAlle aktivierten Sichtquellen und verbergbaren Objekte aller Manager.
static RegisterRevealer / UnregisterRevealer, RegisterHideable / UnregisterHideableWerden von den Komponenten selbst aufgerufen; du brauchst sie nur für eigene Komponenten.
RTSFogOfWarHideable und RTSFogOfWarRevealer
bool IsVisible, bool HasBeenSighted (verbergbares Objekt)Aktueller Zustand und ob das Objekt je gesehen wurde (Geisterbild-Modus).
Vector3 TestPosition (verbergbares Objekt)Die Position, die gegen den Nebel geprüft wird (der Pivot oder der Transform).
void Reevaluate(), void ResetSighting() (verbergbares Objekt)Jetzt neu prüfen; vergessen, dass das Objekt gesichtet wurde.
event Action<bool> VisibilityChanged (verbergbares Objekt)Wird ausgelöst, wenn das Objekt für das lokale Team erscheint oder verschwindet.
bool IsRevealing (Sichtquelle)True, solange die Sichtquelle zählt (aktiviert oder Reveal When Disabled ist an).

Beispiel: eine Leuchtkugel-Fähigkeit und eine Sichtbarkeitsprüfung.

using RTSTerrainStudio;
using UnityEngine;

// Reveals an area for a few seconds and tells whether a target can be attacked.
public class ScoutAbilities : MonoBehaviour
{
    [SerializeField] int team = 0;          // the team that casts the flare
    [SerializeField] float flareRadius = 15f;
    [SerializeField] float flareSeconds = 5f;

    // Call this with the world position the player clicked.
    public void CastFlare(Vector3 worldPosition)
    {
        var fog = RTSFogOfWarManager.Active;
        if (fog == null) return; // no fog of war in this scene

        // ignoreLineOfSight: true lets the flare see over cliffs and trees, like an air unit.
        fog.RevealArea(worldPosition, flareRadius, team, flareSeconds, ignoreLineOfSight: true);
    }

    // True when our team may target something at this position.
    public bool CanTarget(Vector3 targetPosition)
    {
        var fog = RTSFogOfWarManager.Active;
        // Without a running fog, everything is visible.
        if (fog == null || !fog.IsRunning) return true;
        // For teams other than the local one, enable "Compute All Teams" on the manager,
        // otherwise IsVisible returns false for them.
        return fog.IsVisible(targetPosition, team);
    }
}

Beispiel: Allianzen und eine per Code gespawnte Einheit.

using RTSTerrainStudio;
using UnityEngine;

public class TeamSetup : MonoBehaviour
{
    [SerializeField] GameObject unitPrefab;

    void Start()
    {
        var fog = RTSFogOfWarManager.Active;
        if (fog != null)
        {
            fog.SetAlliance(0, 2, true);       // teams 0 and 2 see what the other sees
            fog.SetSharedVision(3, 0, true);   // team 0 also sees what team 3 sees (one way)
        }
        SpawnUnit(1, new Vector3(40f, 0f, 40f));
    }

    // Creates an enemy unit that sees and can be hidden by the fog of war.
    void SpawnUnit(int team, Vector3 position)
    {
        var go = Instantiate(unitPrefab, position, Quaternion.identity);

        // Reuse components the prefab may already have (RTSFogOfWarHideable allows only one per object).
        if (!go.TryGetComponent(out RTSFogOfWarRevealer revealer))
            revealer = go.AddComponent<RTSFogOfWarRevealer>();
        revealer.Team = team;
        revealer.Radius = 12f;               // metres

        if (!go.TryGetComponent(out RTSFogOfWarHideable hideable))
            hideable = go.AddComponent<RTSFogOfWarHideable>();
        // Subscribe BEFORE setting Team and Mode: in Play Mode both setters re-test the object immediately,
        // so a unit spawned in the fog is hidden (and the event raised) right away.
        // React when the unit appears or disappears for the local player (sounds, minimap pings ...).
        hideable.VisibilityChanged += visible => Debug.Log(go.name + (visible ? " spotted" : " lost"));
        hideable.Team = team;
        hideable.Mode = FogOfWarHideMode.HideWhenNotVisible;
        // The current state after configuring.
        Debug.Log(go.name + (hideable.IsVisible ? " starts visible" : " starts hidden"));
    }
}

Minikarte mit Nebel des Krieges

RTSFogOfWarMinimapUtility dunkelt ein Bild der Minikarte mit dem Nebel des lokalen Teams ab, wie auf der Minikarte klassischer RTS-Spiele. Die Minikarte muss genau den Kartenbereich abdecken, mit UV (0, 0) an der Ursprungsecke der Karte und UV (1, 1) an der gegenüberliegenden Ecke. Mit RTSMinimapBaker gebackene Bilder haben dieses Layout. Das Beispiel RTSMinimapUI verwendet diese Hilfsklasse.

MethodeBeschreibung
static bool Composite(Texture minimap, RenderTexture destination, RTSFogOfWarManager manager = null)GPU-Pfad, ideal für eine laufend aktualisierte Minikarte. Rufe die Methode nach dem Event Updated oder alle paar Frames auf. Ohne aktiven Nebel wird die Minikarte unverändert kopiert. Gibt false zurück, wenn der Shader nicht verfügbar ist.
static bool CompositeCpu(Texture2D source, Texture2D destination, RTSFogOfWarManager manager = null)CPU-Pfad (Burst-Job), der ohne Render Textures auskommt, zum Beispiel für Screenshots. source muss lesbar sein; destination muss eine lesbare RGBA32-Textur derselben Größe sein (es darf auch dieselbe Textur sein).
static bool CompositeCpu(NativeArray<Color32> source, NativeArray<Color32> destination, int width, int height, RTSFogOfWarManager manager = null)CPU-Pfad auf rohen Pixelpuffern.
static void ReleaseResources()Zerstört das gecachte Material und den Command Buffer. Wird vor Domain Reloads automatisch aufgerufen; rufe die Methode selbst auf, wenn die Minikarte endgültig geschlossen wird.
using RTSTerrainStudio;
using UnityEngine;

// Bakes the map once and keeps a fogged copy in a render texture.
// Show Output with a RawImage (uGUI) or a UI Toolkit Image.
public class FoggedMinimap : MonoBehaviour
{
    [SerializeField] int size = 256;      // longest side in pixels

    Texture2D baked;
    RenderTexture output;
    RTSFogOfWarManager fog;

    public RenderTexture Output => output;

    void OnEnable()
    {
        var terrain = RTSTerrain.Main;
        if (terrain == null) return;

        baked = RTSMinimapBaker.Bake(terrain, size);   // map image with cliffs, water, trees ...
        if (baked == null) return;
        output = new RenderTexture(baked.width, baked.height, 0, RenderTextureFormat.ARGB32);

        fog = RTSFogOfWarManager.Active;
        if (fog != null) fog.Updated += Refresh;          // refresh after each visibility update
        Refresh();
    }

    void OnDisable()
    {
        if (fog != null) fog.Updated -= Refresh;
        if (output != null) { output.Release(); Destroy(output); }
        if (baked != null) Destroy(baked);
    }

    void Refresh()
    {
        if (baked != null && output != null)
            RTSFogOfWarMinimapUtility.Composite(baked, output, fog);
    }
}

Hinweise für Multiplayer

  • Jeder Client berechnet einen eigenen Nebel des Krieges für sein Local Team. Der Nebel wird nie über das Netzwerk gesendet.
  • Aktiviere auf einem autoritativen Server Compute All Teams und entscheide mit GetVisibilityMask(position), welche Clients welche Einheiten erhalten. Eine einzelne Abfrage pro Einheit ist sehr günstig.
  • Lasse Hide Colliders bei verbergbaren Objekten auf dem Server und auf einem Listen-Server-Host ausgeschaltet, damit serverseitige Raycasts weiterhin Einheiten treffen, die der Host-Spieler nicht sieht. Filtere die Auswahl des Spielers stattdessen mit IsVisibleToLocalTeam.
  • Allianzen sind lokale Einstellungen. Rufe SetAlliance auf dem Server und auf den Clients auf, wenn die Clients die Sicht von Verbündeten anzeigen sollen.

Die Mirror-Integration erledigt all das für dich: RTSNetworkFogOfWar konfiguriert den Manager (der Server berechnet alle Teams, ein Client nur sein eigenes), und RTSFogOfWarInterestManagement sendet nie gegnerische Einheiten, die vom Nebel verborgen sind (Anti-Maphack). Siehe das Kapitel zur Mirror-Integration.

Performance und Grenzen

  • Die Sichtbarkeit wird von Burst-Jobs alle Update Interval Sekunden berechnet (standardmäßig 0.1), nicht in jedem Frame. Sichtquellen und verbergbare Objekte werden in internen Listen geführt, die Szene wird also nicht in jedem Frame durchsucht.
  • Der Speicherbedarf liegt bei etwa 18 Byte pro Nebelzelle. Eine Karte mit 128 × 128 Zellen und Resolution Per Cell 2 hat ein Raster von 256 × 256 Zellen, also etwa 1.2 MB.
  • Große Karten: Das Raster ist auf 2048 Zellen pro Achse begrenzt, und die Auflösung pro Zelle wird automatisch gesenkt, um innerhalb dieser Grenze zu bleiben.
  • None ist der günstigste Sichtlinienmodus; CliffLevelsAndBlockers ist der teuerste, aber Sichtlinien werden nur für Sichtquellen verfolgt, in deren Nähe eine höhere Zelle oder ein Blocker liegt.
  • Die Nebeltextur ist achsparallel zur Karte ausgerichtet. Bei einem gedrehten Terrain wird eine Warnung ausgegeben.
  • Nach Bearbeitungen an Bäumen oder Dekor wird das Raster der Sichtblocker für die gesamte Karte neu aufgebaut; Bearbeitungen an Klippen aktualisieren nur den geänderten Bereich.

Nichts liegt im Nebel? Prüfe, ob der Manager aktiviert ist und du dich im Play Mode befindest (oder Preview In Edit Mode eingeschaltet ist), ob Reveal All ausgeschaltet ist und ob das Terrain eine Karte hat. Wenn nur das Terrain im Nebel liegt, Einheiten und Props aber nicht, füge das Renderer Feature RTS Fog Of War hinzu. Wenn alles schwarz ist, ist keine Sichtquelle des lokalen Teams (oder eines Teams, das seine Sicht mit ihm teilt) aktiv. Die Meldung „No revealers exist“ im Inspector erscheint nur, wenn die Szene überhaupt keine Sichtquelle enthält. Vergleiche daher auch das Team deiner Sichtquellen mit Local Team.

Pathing und Wegfindung

Jedes RTS Terrain führt eine Pathing-Karte: ein Raster, das für jedes kleine Quadrat der Karte angibt, ob Bodeneinheiten dort laufen können, ob Gebäude platziert werden können, ob Lufteinheiten fliegen können und ob dort Wasser ist. Sie wird automatisch aus dem Terrain abgeleitet und während der Bearbeitung aktualisiert. Darauf aufbauend enthält das Paket einen schnellen integrierten Pathfinder (Burst A*), den du ganz ohne Drittanbieterpaket verwenden kannst.

Wenn du lieber das A* Pathfinding Project oder Unitys NavMesh verwendest, bauen die Integrationen ihre Graphen aus derselben Pathing-Karte; siehe die jeweiligen Kapitel.

Die Pathing-Karte

Die Pathing-Karte hat PathingResolution Pathing-Zellen pro Kartenzelle und Achse (1, 2 oder 4, beim Erstellen der Karte festgelegt; Standard 2). Bei Kartenzellen in der Standardgröße von 2 m ist eine Pathing-Zelle 1 m breit. Jede Zelle speichert eine Kombination von PathingFlags:

FlagBedeutung
WalkableBodeneinheiten können hier laufen.
BuildableHier können Gebäude platziert werden.
FlyableLufteinheiten können hier fliegen. Wird überall gesetzt, sofern kein Unflyable-Effekt das Flag entfernt: ein gemalter Pathing-Override, die Bodenschichtregel des Tilesets oder die Einstellung Footprint Effect eines Dekor-Prototyps.
ShallowWaterFlachwasser: begehbar, standardmäßig nicht bebaubar.
DeepWaterTiefwasser: nur für See-Einheiten.
CliffDie Zelle berührt eine Klippenwand.
BlockedDie Zelle wird von der Grundfläche eines Baums oder Dekorobjekts blockiert.
SteepDie Zelle ist für Bodeneinheiten zu steil.

So werden die Flags abgeleitet. Die Regeln werden in dieser Reihenfolge als Burst-Jobs angewendet:

  1. Terrain. Klippenwände (verbreitert um Cliff Margin) werden zu Cliff. Hänge mit einer Neigung über Max Walkable Slope werden zu Steep. Wasser, das tiefer ist als die Flachwasser-Grenztiefe des Tilesets, wird zu DeepWater, sonst zu ShallowWater. Flyable wird überall gesetzt. Buildable = begehbar, flacher als Max Buildable Slope und trocken (oder Flachwasser mit Shallow Water Buildable). Danach wird der Pathing-Effekt der dominanten Bodenschicht angewendet (zum Beispiel „Unbuildable“ auf rauem Fels). Eine gültige Rampe zwischen zwei Klippenstufen wird nur anhand ihrer Feinhöhe geprüft und bleibt daher begehbar; Rampen sind nie bebaubar.
  2. Grundflächen. Bäume blockieren einen Kreis (Footprint Radius des Baum-Prototyps × Instanzskalierung × Tree Footprint Scale): Sie setzen Blocked und entfernen Walkable und Buildable. Dekor wendet den Footprint Effect seines Prototyps (standardmäßig Unwalkable und Unbuildable) in einem Kreis oder einem gedrehten Rechteck an. Dekorinstanzen mit dem Flag IgnorePathing werden übersprungen.
  3. Manuelle Overrides, die mit den Pathing-Werkzeugen gemalt werden (siehe unten). Ein Unwalkable-Effekt entfernt auch Buildable, sofern nicht Force Buildable gesetzt ist.

Die Karte wird inkrementell neu aufgebaut: Wenn du einen Bereich bearbeitest, wird nur dieser Bereich (plus ein kleiner Rand) neu abgeleitet.

Pathing-Regeln (RTSPathingSettings)

Wähle das RTS Terrain aus und öffne im Inspector den Abschnitt Pathing Rules. Wenn du einen Wert änderst, wird die Pathing-Karte neu aufgebaut.

EinstellungStandardBeschreibung
Max Walkable Slope42Boden mit einer Neigung über diesem Winkel (in Grad) ist nicht begehbar.
Max Buildable Slope18Boden mit einer Neigung über diesem Winkel (in Grad) ist nicht bebaubar.
Cliff Margin0.25Wie weit (als Anteil einer Pathing-Zelle) der blockierte Bereich über eine Klippenwand hinausreicht. Größere Werte halten Einheiten weiter von Klippenkanten fern.
Shallow Water BuildableausErlaubt Gebäude im Flachwasser.
Tree Footprint Scale1Multiplikator für den Radius der Pathing-Grundfläche jedes Baums.
Use Ground Layer RulesanWendet den Pathing-Effekt der dominanten Bodenschicht an (pro Schicht im Tileset festgelegt, z. B. „Unbuildable“ auf rauem Fels).

Im Code weist du mit terrain.PathingSettings = settings neue Einstellungen zu (das löst einen Neuaufbau aus), oder du änderst Felder der aktuellen Instanz und rufst terrain.RebuildPathing() auf.

Pathing von Hand malen

Manchmal brauchst du etwas anderes, als die automatischen Regeln ergeben: eine unsichtbare Wand am Kartenrand, eine Bauverbotszone, die eine Expansion freihält, oder eine begehbare Brücke. Verwende dafür die Pathing-Werkzeuge (mit 7 wählst du in der Scene View die Kategorie Pathing aus):

Werkzeug / OverrideWirkung
Paint Pathing: BlockerNicht begehbar und nicht bebaubar: eine unsichtbare Wand (Kartenränder, Dekorationsbereiche).
Paint Pathing: UnwalkableBodeneinheiten können hier nicht laufen.
Paint Pathing: UnbuildableHier können keine Gebäude platziert werden (um Wege und Expansionen freizuhalten).
Paint Pathing: UnflyableLufteinheiten können hier nicht fliegen.
Paint Pathing: Force WalkableBodeneinheiten können hier laufen, auch wenn Neigung, Wasser oder Grundflächen das verhindern würden.
Paint Pathing: Force BuildableHier können Gebäude platziert werden, auch wenn die abgeleiteten Regeln es verbieten.
Clear PathingEntfernt alle gemalten Overrides unter dem Pinsel, sodass wieder das automatische Pathing gilt.

Halte bei Paint Pathing Shift gedrückt, um den ausgewählten Override zu entfernen. Wenn du einen Modus malst, wird der Override entfernt, der ihm widerspricht (malst du Force Walkable, wird Unwalkable entfernt), sodass eine Zelle nie widersprüchliche Regeln enthält.

Pathing-Overlays

Ein Overlay färbt das Terrain nach seinem Pathing ein, wie die Pathing-Ansicht klassischer RTS-Karteneditoren. Du schaltest es so ein:

  • über das Dropdown-Menü Pathing im Overlay der Scene View (Pathing: Off, Walkability, Buildability, Flyability, Water, Combined);
  • über das Dropdown-Menü Pathing Overlay und den Schieberegler für die Deckkraft im Fenster von RTS Terrain Studio;
  • mit P in der Scene View, womit du Off → Walkability → Buildability → Combined → Off durchschaltest (Tastenkürzel RTS Terrain Studio/Cycle Pathing Overlay, neu belegbar unter Edit ▸ Shortcuts..., unter macOS Unity ▸ Shortcuts...);
  • über die Pathing-Ansicht des In-Game-Editors.
ModusFarben
WalkabilityGrün = begehbar, Rot = blockiert, Dunkelrot = Klippe, Orange = zu steil, Blau = Tiefwasser, Cyan = Flachwasser.
BuildabilityGrün = bebaubar, Gelb = begehbar, aber nicht bebaubar, Rot / Dunkelrot = nicht begehbar.
FlyabilityHellblau = befliegbar, Magenta = nicht befliegbar.
WaterCyan = Flachwasser, Blau = Tiefwasser.
CombinedAlles gleichzeitig (Pathing-Ansicht im Stil klassischer RTS-Spiele).

In deinem eigenen Spiel zeigst du ein Overlay mit einem PathingOverlayController an. Er hält die Textur aktuell, wenn sich das Pathing ändert:

using RTSTerrainStudio;
using UnityEngine;

// Shows the buildability overlay on the terrain while this component is enabled (e.g. in build mode).
public class BuildModeOverlay : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;
    [SerializeField, Range(0f, 1f)] float opacity = 0.6f;

    PathingOverlayController overlay;

    void OnEnable()
    {
        if (terrain == null) terrain = RTSTerrain.Main;
        if (terrain == null) return;
        overlay = new PathingOverlayController(terrain, PathingOverlayMode.Buildability);
        // The texture can be replaced (for example after a map resize): re-assign it when that happens.
        overlay.TextureChanged += texture => terrain.SetOverlayTexture(texture, opacity);
        terrain.SetOverlayTexture(overlay.Texture, opacity);
    }

    void OnDisable()
    {
        if (terrain != null) terrain.SetOverlayTexture(null, 0f);
        overlay?.Dispose();   // destroys the overlay texture
        overlay = null;
    }
}

Wechsle die angezeigte Information mit overlay.Mode = PathingOverlayMode.Walkability (das zeichnet die Textur neu), oder erzwinge mit overlay.Refresh() ein vollständiges Neuzeichnen; du musst keinen neuen Controller erstellen.

Für einmalige Texturen verwendest du PathingOverlay.CreateOrUpdate(map, mode, ref texture), PathingOverlay.UpdateRegion(...) und PathingOverlay.DestroyTexture(ref texture); PathingOverlay.GetColor(flags, mode) gibt die Legendenfarbe einer Flag-Kombination zurück.

Die Pathing-Karte per Code auslesen

Member (von RTSTerrain)Beschreibung
PathingFlags GetPathing(Vector3 world)Flags an einer Weltposition (None außerhalb der Karte oder bevor das Pathing aufgebaut ist).
bool IsWalkable(Vector3 world), bool IsBuildable(Vector3 world)Kurzformen für die zwei meistgenutzten Flags.
PathingMap PathingDas Pathing-Raster: Width, Height, CellSize, Flags (ein Byte pro Zelle, Index = z × Width + x), Get(x, z), Version.
event Action<RTSTerrain, RectInt> PathingChangedWird nach einem vollständigen oder teilweisen Neuaufbau ausgelöst und übergibt das geänderte Rechteck in Pathing-Zellen. Bleibt beim Neuladen einer Karte erhalten. Die Beispieleinheiten nutzen es, um neu zu planen.
RTSPathingSettings PathingSettings, void RebuildPathing()Regeln und vollständiger Neuaufbau.
ClearanceMap GetPathingClearance()Die Freiraumkarte, auf den aktuellen Stand gebracht (siehe Einheitengröße).
void RegisterPathingReader(JobHandle), void CompletePathingReaders()Für eigene Jobs, die Flags oder den Freiraum lesen: Das Terrain wartet auf sie, bevor es Pathing-Daten schreibt.

Einen Pfad finden: RTSPathfinder

RTSPathfinder sucht mit einem Burst-kompilierten A* einen Pfad auf der Pathing-Karte: 8 Richtungen ohne Abschneiden von Ecken, Freiraum für große Einheiten und String Pulling für glatte Pfade. Ein blockierter Start oder ein blockiertes Ziel wird durch die nächstgelegene freie Zelle ersetzt. Der Pathfinder läuft synchron auf dem Hauptthread. Für einige Anfragen pro Frame und im Edit Mode ist das kein Problem.

using System.Collections.Generic;
using RTSTerrainStudio;
using UnityEngine;

public class PathDebug : MonoBehaviour
{
    [SerializeField] Transform target;
    readonly List<Vector3> path = new List<Vector3>();

    void OnDrawGizmos()
    {
        var terrain = RTSTerrain.Main;
        if (terrain == null || target == null) return;

        // A unit with a radius of 0.6 m: the path keeps that distance from obstacles.
        var options = PathRequestOptions.ForUnit(0.6f);
        PathResult result = RTSPathfinder.FindPathDetailed(terrain, transform.position, target.position, options, path);

        // Success = reached the goal, Partial = closest reachable point.
        Gizmos.color = result.status == PathStatus.Success ? Color.green : Color.yellow;
        for (int i = 1; i < path.Count; i++) Gizmos.DrawLine(path[i - 1], path[i]);
    }
}
MethodeBeschreibung
static PathStatus FindPath(RTSTerrain, Vector3 startWorld, Vector3 endWorld, List<Vector3> resultWorld)Verwendet PathRequestOptions.Default.
static PathStatus FindPath(RTSTerrain, Vector3, Vector3, PathRequestOptions, List<Vector3>)resultWorld wird geleert und erhält die Wegpunkte, einschließlich Start und Ende.
static PathResult FindPathDetailed(RTSTerrain, Vector3, Vector3, PathRequestOptions, List<Vector3>)Gibt zusätzlich Länge und Statistiken zurück und meldet, ob Start oder Ziel verschoben wurde.
static void DisposeSharedBuffers()Gibt die Suchpuffer frei (geschieht auch automatisch beim Domain Reload und beim Beenden).

PathRequestOptions (gehe von PathRequestOptions.Default oder PathRequestOptions.ForUnit(radius) aus):

FeldStandardBeschreibung
unitRadius0Radius der Einheit in Welteinheiten. Pfade halten diesen Abstand zu Hindernissen. Der Freiraum wird nur berücksichtigt, wenn required das Flag Walkable enthält: Bei Anfragen für See-Einheiten (DeepWater) oder Lufteinheiten (Flyable) hat unitRadius keine Wirkung. Die Freiraumkarte misst Abstände zu nicht begehbaren Zellen, und der Kartenrand zählt als Hindernis. Dasselbe gilt für PathingQueries.FindNearest, LineOfSight und Raycast.
requiredWalkableFlags, die jede Zelle des Pfads haben muss (None wird als Walkable behandelt).
allowShallowWatertrueErlaubt das Durchqueren von Flachwasser (Furten).
maxSearchNodes500000Maximale Anzahl expandierter Knoten, bevor die Suche aufgibt (0 = unbegrenzt).
smoothPathtrueEntfernt unnötige Wegpunkte mithilfe der Sichtlinie im Raster (String Pulling).
snapToGroundtrueSetzt die Y-Koordinate jedes Wegpunkts auf die Bodenhöhe.
allowPartialtrueGibt bei unerreichbarem Ziel einen Pfad zur nächstgelegenen erreichbaren Zelle zurück.
goalSearchRadius16Wenn die Zielzelle blockiert ist, wird in diesem Umkreis (in Welteinheiten) nach der nächstgelegenen freien Zelle gesucht.
PathStatusBedeutung
SuccessEin Pfad zum Ziel (oder zur nächstgelegenen freien Zelle eines blockierten Ziels) wurde gefunden.
PartialDas Ziel ist unerreichbar; der Pfad führt zu der erreichbaren Zelle, die ihm am nächsten liegt.
NoPathEs existiert kein Pfad (oder das Suchlimit wurde erreicht), und Teilpfade waren nicht erlaubt.
InvalidStartDer Start liegt außerhalb der Karte, es gibt keine Pathing-Daten, oder in der Nähe des Starts gibt es keine passierbare Zelle.

Felder von PathResult: requestId, terrain, status, points (Wegpunkte in Weltkoordinaten), length (horizontal, in Welteinheiten), searchedNodes, startAdjusted, goalAdjusted, userData und HasPath (Success oder Partial mit mindestens einem Wegpunkt).

Viele Einheiten: RTSPathfindingService

Für viele Einheiten verwendest du RTSPathfindingService. Der Service sammelt die Anfragen eines Frames, führt sie in LateUpdate als parallele Burst-Jobs aus (nachdem das Terrain seine Bearbeitungen angewendet hat) und liefert die Ergebnisse im Update des nächsten Frames über Callbacks. Füge ihn über Add Component ▸ RTS Terrain Studio ▸ Pathfinding Service hinzu, oder rufe im Play Mode RTSPathfindingService.GetOrCreate() auf (das erstellt ein Service-Objekt, das Szenenwechsel übersteht). Der Service läuft nur im Play Mode; im Edit Mode verwendest du RTSPathfinder.

EinstellungStandardBeschreibung
Max Requests Per Frame64Pfadsuchen, die pro Frame gestartet werden. Weitere Anfragen warten in einer Warteschlange.
Max Parallel Jobs4Parallel laufende Jobs (1 bis 16). Jeder Job braucht eigene Suchpuffer (etwa 24 Byte pro Pathing-Zelle).
Memory Budget MB256Obergrenze für den Speicher der Suchpuffer. Auf sehr großen Rastern werden weniger parallele Jobs verwendet; eine Warnung wird ausgegeben, wenn schon ein einzelner Puffersatz diese Grenze überschreitet.
Complete In Same FrameausSchließt die Jobs am Ende des Frames ab, in dem sie eingeplant wurden (geringere Latenz, weniger Parallelität).
MemberBeschreibung
int RequestPath(RTSTerrain, Vector3 startWorld, Vector3 endWorld, PathRequestOptions, Action<PathResult> callback, List<Vector3> resultBuffer = null, object userData = null)Stellt eine Anfrage in die Warteschlange und gibt ihre ID zurück. Mit resultBuffer werden die Wegpunkte in deine Liste geschrieben (die vorher geleert wird). Das vermeidet eine Allokation pro Anfrage.
int RequestPath(RTSTerrain, Vector3, Vector3, Action<PathResult>)Dasselbe mit den Standardoptionen.
bool Cancel(int requestId)Bricht eine Anfrage ab, die in der Warteschlange steht, gerade läuft oder fertig, aber noch nicht zugestellt ist. Ihr Callback wird nicht ausgeführt.
void CancelAll(), void CompleteAll()Bricht alles ab oder schließt sofort alles ab (die Callbacks laufen, bevor die Methode zurückkehrt).
PendingCount, InFlightCountStatistiken zu Warteschlange und Jobs.
static Instance, static GetOrCreate()Der erste aktivierte Service oder ein neuer.
using RTSTerrainStudio;
using UnityEngine;

// Asks the service for a path and replaces the previous request when a new order arrives.
public class AsyncMover : MonoBehaviour
{
    [SerializeField] float radius = 0.5f;
    int requestId;   // 0 = no request pending

    public void OrderMove(Vector3 target)
    {
        var terrain = RTSTerrain.Main;
        var service = RTSPathfindingService.GetOrCreate(); // null outside Play Mode
        if (terrain == null || service == null) return;

        if (requestId != 0) service.Cancel(requestId);       // the old callback will not run
        requestId = service.RequestPath(terrain, transform.position, target,
            PathRequestOptions.ForUnit(radius), OnPathFound);
    }

    void OnPathFound(PathResult result)
    {
        requestId = 0;
        if (!result.HasPath) { Debug.Log("Can't reach that place."); return; }
        // result.points holds the waypoints. Copy them if you keep them.
        Debug.Log($"{result.points.Count} waypoints, {result.length:0.0} m, status {result.status}");
    }
}

Einheitengröße: Freiraum

Große Einheiten dürfen sich nicht durch Lücken zwängen, die für kleine gedacht sind. Das Terrain führt dafür eine Freiraumkarte: Sie enthält für jede begehbare Zelle den Abstand zur nächsten nicht begehbaren Zelle (begrenzt auf 64 Zellen). Wenn eine Anfrage einen unitRadius hat, verwendet der Pathfinder nur Zellen, in die die Einheit passt. Die Freiraumkarte wird nach Bearbeitungen inkrementell aktualisiert.

  • PathingQueries.GetClearance(terrain, world) gibt den freien Radius an einer Position in Welteinheiten zurück (negativ auf nicht begehbaren Zellen).
  • ClearanceMap (aus terrain.GetPathingClearance()) bietet GetClearanceCells(x, z), GetClearanceMetres(x, z) und ClearanceMap.RequiredValue(unitRadius, cellSize). In der Nähe diagonaler Hindernisecken kann der Wert um bis zu etwa 0.13 Zellen zu großzügig sein. Dadurch können Einheiten mit einem Radius von 0.3 bis 0.5 Zellen weiterhin einzellige Korridore nutzen, durch die sie physisch passen.

Gameplay-Abfragen: PathingQueries

PathingQueries beantwortet die Fragen, die im RTS-Gameplay ständig anfallen. Alle Positionen sind in Weltkoordinaten angegeben, Größen und Radien in Welteinheiten.

MethodeBeschreibung
bool FindNearestWalkable(RTSTerrain, Vector3 world, float maxRadius, float unitRadius, out Vector3 result)Begehbare Position, die world am nächsten liegt und an der eine Einheit mit unitRadius Platz hat (Spawn-Punkte, Sammelpunkte).
bool FindNearest(RTSTerrain, Vector3 world, float maxRadius, float unitRadius, PathingFlags required, out Vector3 result)Dasselbe für beliebige Flags, z. B. DeepWater für Schiffe.
Vector3 SnapToBuildGrid(RTSTerrain, Vector3 world, Vector2 sizeMetres)Richtet eine Gebäudeposition so aus, dass ihre Grundfläche ganze Pathing-Zellen abdeckt (gerade Größen auf einer Zellecke, ungerade Größen auf einer Zellmitte). Das Ergebnis liegt auf dem Boden.
bool IsAreaBuildable(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres)Liefert true, wenn jede Pathing-Zelle der Grundfläche Buildable ist.
bool IsAreaClear(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres, PathingFlags required)Dasselbe für beliebige Flags. Außerhalb der Karte liefert die Methode false.
bool TryGetAreaCells(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres, out RectInt cells)Pathing-Zellen, die von einer Grundfläche abgedeckt werden. Liefert false, wenn die Grundfläche nicht vollständig innerhalb der Karte liegt.
float GetClearance(RTSTerrain, Vector3 world)Freier Radius um eine Position.
bool LineOfSight(RTSTerrain, Vector3 fromWorld, Vector3 toWorld, PathingFlags required = Walkable, float unitRadius = 0)Liefert true, wenn ein gerader Weg möglich ist (Sichtlinie im Raster, kein Abschneiden von Ecken). Hier geht es um Bewegung, nicht um die Sicht im Nebel des Krieges.
bool Raycast(RTSTerrain, Vector3 fromWorld, Vector3 toWorld, PathingFlags required, float unitRadius, out Vector3 lastFreeWorld)Läuft die Strecke ab und meldet die letzte freie Position vor einem Hindernis (Sturmangriffe, Rückstöße).

Gebäudeplatzierung und Pathing-Änderungen zur Laufzeit

Gebäudeplatzierung im Stil klassischer RTS-Spiele besteht aus drei Schritten: die Grundfläche am Raster ausrichten, sie prüfen und sie auf der Pathing-Karte blockieren. Zum Blockieren schreibst du manuelle Overrides in MapData.PathingOverrides und teilst das dem Terrain mit. Das Terrain baut den Bereich inkrementell neu auf und löst PathingChanged aus. Einheiten, die gerade unterwegs sind, planen ihren Weg um das neue Gebäude herum neu.

using RTSTerrainStudio;
using UnityEngine;

public static class BuildingFootprint
{
    // Tries to place a footprint of sizeMetres (X/Z) at the cursor and blocks it for walking and building.
    // Returns the covered pathing cells and their previous override bytes, so Remove can restore them
    // (cells may already hold painted overrides, for example Force Walkable on a bridge).
    public static bool TryPlace(RTSTerrain terrain, Vector3 cursorWorld, Vector2 sizeMetres,
        out RectInt pathingCells, out byte[] previous)
    {
        pathingCells = default;
        previous = null;
        if (terrain == null || terrain.Map == null) return false;

        // 1. Align the footprint with the pathing grid.
        Vector3 center = PathingQueries.SnapToBuildGrid(terrain, cursorWorld, sizeMetres);

        // 2. Every cell must be buildable.
        if (!PathingQueries.IsAreaBuildable(terrain, center, sizeMetres)) return false;
        if (!PathingQueries.TryGetAreaCells(terrain, center, sizeMetres, out pathingCells)) return false;

        // 3. Keep the old bytes, then write Unwalkable | Unbuildable overrides (one byte per pathing cell).
        MapData map = terrain.Map;
        var overrides = map.PathingOverrides;
        previous = new byte[pathingCells.width * pathingCells.height];
        byte block = (byte)(PathingOverride.Unwalkable | PathingOverride.Unbuildable);
        int n = 0;
        for (int z = pathingCells.yMin; z < pathingCells.yMax; z++)
            for (int x = pathingCells.xMin; x < pathingCells.xMax; x++, n++)
            {
                int i = map.PathingIndex(x, z);
                previous[n] = overrides[i];
                overrides[i] = (byte)(overrides[i] | block);
            }

        Notify(terrain, pathingCells);
        return true;
    }

    // Restores the override bytes stored by TryPlace (call it when the building is destroyed).
    public static void Remove(RTSTerrain terrain, RectInt pathingCells, byte[] previous)
    {
        if (terrain == null || terrain.Map == null || previous == null) return;
        MapData map = terrain.Map;
        var overrides = map.PathingOverrides;
        int n = 0;
        for (int z = pathingCells.yMin; z < pathingCells.yMax; z++)
            for (int x = pathingCells.xMin; x < pathingCells.xMax; x++, n++)
                overrides[map.PathingIndex(x, z)] = previous[n];
        Notify(terrain, pathingCells);
    }

    // 4. NotifyChanged expects map cells, so convert the pathing rectangle.
    static void Notify(RTSTerrain terrain, RectInt pathingCells)
    {
        int res = Mathf.Max(1, terrain.Map.PathingResolution);
        int xMin = pathingCells.xMin / res, zMin = pathingCells.yMin / res;
        int xMax = Mathf.CeilToInt(pathingCells.xMax / (float)res);
        int zMax = Mathf.CeilToInt(pathingCells.yMax / (float)res);
        terrain.NotifyChanged(MapLayers.PathingOverrides, new RectInt(xMin, zMin, xMax - xMin, zMax - zMin));
    }
}

Um ein Gebäude zu entfernen, übergib die gespeicherten Zellen und Bytes an BuildingFootprint.Remove: Die Methode schreibt die vorherigen Override-Bytes zurück und ruft erneut NotifyChanged auf. Die Beispielkomponente RTSDemoBuilding macht genau das, siehe Demo zur Gebäudeplatzierung.

Bäume entfernst du auf die gleiche Weise über den Vegetations-Renderer: RTSVegetationRenderer.RemoveTree(id) benachrichtigt das Terrain, sodass die Pathing-Karte die Grundfläche des Baums freigibt und der Nebel des Krieges den Sichtblocker automatisch entfernt. Siehe Demo zum Baumabbau.

Fortgeschritten: Pathing-Daten in eigenen Jobs

Für eigene Burst-Jobs (Flow Fields, Influence Maps) erstellst du mit new PathingGrid(terrain.Pathing, terrain.GetPathingClearance()) eine schreibgeschützte Ansicht. Sie bietet IsPassable, LineOfSight, Trace und FindNearestPassable mit Pathing-Zellen als Maßeinheit, gefiltert durch einen PathingFilter (PathingFilter.ForUnit(required, allowShallowWater, unitRadius, cellSize)). Registriere das Handle deines Jobs mit terrain.RegisterPathingReader(handle), damit das Terrain den Abschluss des Jobs abwartet, bevor es Pathing-Daten ändert.

PathingMapBuilder baut Pathing-Karten für Objekte vom Typ MapData, die nicht von einem Terrain angezeigt werden (zum Beispiel auf einem Server, der Karten ohne Rendering lädt): PathingMapBuilder.Build(map, pathing, settings, tileset, vegetation, decor).

Gameplay-Beispielskripte

Der Ordner Samples/Scripts enthält ein kleines, aber vollständiges RTS-Gameplay-Gerüst: eine RTS-Kamera, Einheiten, die dem Terrain folgen, Auswahl mit Kontrollgruppen, Bewegungsbefehle mit Formationen, eine Minikarte, ein HUD und zwei Demos, die die Karte zur Laufzeit verändern. Die Skripte liegen in der Assembly RTSTerrainStudio.Samples und im Namespace RTSTerrainStudio.Samples.

Nutze sie, um deine Karten sofort auszuprobieren, als Ausgangspunkt für dein eigenes Spiel oder als Referenz dafür, wie du die Laufzeit-API verwendest. Jedes Skript ist kommentiert, und seine Unity-Messages sind, wo es sinnvoll ist, protected virtual, sodass du eigene Klassen davon ableiten kannst.

Alle Beispielkomponenten findest du unter Add Component ▸ RTS Terrain Studio ▸ Samples. Eingaben werden über RTSInput gelesen, daher funktionieren die Beispiele sowohl mit dem Paket Input System als auch mit dem alten Input Manager. Die UI basiert auf UI Toolkit.

Schnellstart: jede Karte spielbar machen

  1. Öffne eine Szene mit einem RTS Terrain, dessen Karte Startpositionen hat (platziere sie mit den Marker-Werkzeugen).
  2. Wähle GameObject ▸ RTS Terrain Studio ▸ Demo Game. Dadurch entsteht ein Objekt RTS Demo Game mit der Komponente RTSDemoGame.
  3. Klicke auf Play. Das Demospiel erstellt den Nebel des Krieges, den Kamera-Controller, Auswahl, Befehle, Minikarte und HUD, spawnt Einheiten an jeder Startposition (du bist Spieler 1, die anderen streifen umher) und bewegt die Kamera zu deiner Basis.
  4. Drücke F1, um die Steuerungshilfe ein- oder auszublenden.

Standardsteuerung

EingabeAktion
W A S D / PfeiltastenKamera verschieben. Solange Einheiten ausgewählt sind, stoppt S sie, statt die Kamera zu verschieben (die Pfeiltasten verschieben die Kamera immer).
Bildschirmrand, Ziehen mit mittlerer MaustasteKamera verschieben / den Boden greifen und ziehen.
MausradZoomen (je näher, desto flacher der Blickwinkel).
Q / E, Alt + Ziehen mit rechter MaustasteAnsicht drehen.
Linksklick / ZiehenEinheit auswählen / Auswahlrechteck aufziehen.
Shift + KlickEinheiten hinzufügen oder entfernen.
Doppelklick, Ctrl + KlickAlle Einheiten dieses Typs auf dem Bildschirm auswählen.
EscAktuellen Modus abbrechen, danach die Auswahl aufheben. Bei einem Spieltest im In-Game-Editor führt das nächste Esc zurück zum Editor.
Ctrl+1…9, 0Kontrollgruppe speichern (Shift+Zahl fügt der Gruppe Einheiten hinzu).
1…9, 0Gruppe abrufen (zweimal drücken zentriert die Kamera auf die Gruppe).
RechtsklickIn Formation bewegen (Shift reiht den Befehl ein und setzt die Formation fort).
S / H / PStoppen / Position halten / Patrouillieren (danach den zweiten Patrouillenpunkt per Linksklick setzen).
MinikarteLinksklick oder Ziehen: Kamera bewegen. Rechtsklick: ausgewählte Einheiten bewegen.
BGebäude platzieren (Pathing-Änderung zur Laufzeit).
DeleteGebäude unter dem Cursor entfernen.
TBaum unter dem Cursor fällen.
Rechtsklick auf einen BaumAusgewählte Arbeiter gehen hin und fällen ihn.
F1Hilfe-Panel ein- oder ausblenden.

Alle Tasten sind Felder vom Typ RTSKey in den Komponenten (Kamera, Auswahl, Befehle, Demos, HUD), sodass du sie im Inspector neu belegen kannst.

RTSDemoGame: der Einstiegspunkt

Macht aus jeder Szene mit einem RTS Terrain ein kleines spielbares RTS. Beim Spielstart erledigt die Komponente Folgendes:

  1. sie stellt sicher, dass ein RTSFogOfWarManager (lokales Team = Local Team, Erkundung zurückgesetzt) und der Wegfindungsdienst vorhanden sind;
  2. sie sucht Kamera-Controller, Auswahl, Befehle, Minikarte, HUD sowie die Gebäude- und die Baumdemo und erstellt die fehlenden;
  3. sie spawnt Einheiten an jeder Startposition (Team = Spielerindex; Spieler 1 ist Team 0) und gibt gegnerischen Einheiten eine Komponente RTSWanderAI, sodass du sie im Nebel auftauchen und verschwinden siehst;
  4. sie bewegt die Kamera zur Startposition des lokalen Spielers.

In-Game-Editor: Die Komponente reagiert auf RTSRuntimeEditorEvents.PlaytestChanged. Startest du im In-Game-Editor einen Spieltest, startet das Spiel. Beim Beenden des Spieltests werden die Einheiten despawnt, Demo-Gebäude entfernt, abgebaute Bäume wiederhergestellt (optional), die Gameplay-Systeme deaktiviert und die Kamera wieder an den Editor übergeben. Ist Auto Start aktiviert und befindet sich kein In-Game-Editor in der Szene, startet das Spiel sofort.

EinstellungStandardBeschreibung
Start
Auto StartanStartet, sobald die Szene läuft und kein In-Game-Editor vorhanden ist (sonst startet das Spiel mit dem Spieltest des Editors).
Local Team0Team des lokalen Spielers (0 = Spieler 1). Setzt du den Wert per Code, werden auch der Nebel des Krieges und die Auswahl aktualisiert.
TerrainleerTerrain, auf dem gespielt wird. Leer = RTSTerrain.Main.
Units
Unit PrefabsleerEinheiten-Prefabs, reihum verwendet. Leer = prozedurale Platzhaltereinheiten.
Units Per Player8Anzahl gespawnter Einheiten pro Spieler (1 bis 60).
Max Players8Maximale Anzahl an Spielern, die Einheiten erhalten.
Enemy Wander AIanGegnerische Einheiten streifen umher.
Wander Radius30Radius in Metern, in dem die gegnerische KI umherstreift.
Systems
Create Missing SystemsanErstellt Kamera-Controller, Auswahl, Befehle, Minikarte, HUD und Demos, falls sie fehlen. Der Einheiten-Spawner wird immer erstellt.
Fog Of WaranAktiviert den Nebel des Krieges während des Spiels.
MinimapanZeigt die Minikarte an.
HudanZeigt das HUD an (Info-Panel, Aktionsleiste, Hilfe über F1).
Show Help On StartanZeigt beim Spielstart das Hilfe-Panel an.
Building DemoanAktiviert die Demo zur Gebäudeplatzierung (B).
Tree Harvest DemoanAktiviert die Demo zum Baumabbau (T, Arbeiter).
Restore Map On StopanStellt abgebaute Bäume wieder her, wenn das Spiel endet, sodass die bearbeitete Karte nach einem Spieltest unverändert ist.
References
Camera Controller, Selection Manager, Command Controller, Minimap UI, Demo HUD, Spawner, Building Placement, Tree Harvest, Fog Of War ManagerleerZu verwendende Systeme. Leer = in der Szene gesucht oder neu erstellt.

API: StartGame(), StopGame(), IsRunning, AutoStart, LocalTeam, die Events GameStarted und GameStopped sowie die Eigenschaften Spawner, CameraController, SelectionManager, CommandController. Überschreibe protected virtual bool DetectInGameEditor(), wenn du einen eigenen In-Game-Editor verwendest.

Du baust dein eigenes Spiel? Nutze RTSDemoGame als Vorlage, nicht als Abhängigkeit: Übernimm die Reihenfolge seiner Einrichtungsschritte in deinen eigenen Game Manager und lasse weg, was du nicht brauchst. Für Multiplayer mit Mirror füge RTSDemoGame nicht hinzu: Die Einheiten spawnt der Network Manager.

RTSCameraController

Eine klassische RTS-Kamera: Verschieben per Tastatur, Bildschirmrand und Ziehen, Zoom mit Neigungskurve (je näher, desto flacher der Blickwinkel), Drehung, weiches Folgen der Bodenhöhe und Kartengrenzen. Sie läuft in LateUpdate. RTSCameraController.Main ist der erste aktivierte Controller.

EinstellungStandardBeschreibung
Target CameraleerKamera, die bewegt wird. Leer = die Camera-Komponente auf diesem GameObject, sonst Camera.main.
TerrainleerTerrain für Bodenhöhe und Grenzen. Leer = RTSTerrain.Main.
Keyboard Pan, Pan Forward/Back/Left/Right Key, Arrow Keysan, W/S/A/D, anVerschieben per Tastatur.
Yield Keys To OrdersanVerschiebetasten, die zugleich Befehlstasten des Befehls-Controllers sind (S = Stopp), verschieben die Kamera nicht, solange Einheiten ausgewählt sind.
Pan Speed / Fast Pan Multiplier35 / 2Meter pro Sekunde beim Standardzoom (skaliert mit dem Zoomabstand); Multiplikator, solange Shift gedrückt ist.
Edge Scrolling, Edge Size, Edge Speed Multiplieran, 12 px, 1Verschiebt die Kamera, wenn die Maus den Bildschirmrand berührt. Das Rand-Scrollen pausiert, solange das Spielfenster keinen Fokus hat oder sich der Mauszeiger außerhalb des Fensters befindet. In Web-Builds gilt das auch, wenn die Maus die Seite oder ihr iframe verlassen hat. Auf Touchscreens pausiert es ebenfalls.
Middle Mouse DraganHalte die mittlere Maustaste gedrückt, um den Boden zu greifen und zu verschieben.
Min / Max Distance12 / 75Abstand zum Drehpunkt bei maximal hinein- / herausgezoomter Ansicht (Meter).
Zoom Step, Start Zoom0.08, 0.6Zoomänderung pro Mausrad-Raste; anfänglicher Zoom (0 = am nächsten, 1 = am weitesten entfernt).
Distance Curve, Pitch Curve-Bilden den Zoom (0 bis 1) auf den Abstand und auf die Kameraneigung in Grad ab (etwa 55° beim Standardzoom).
Keyboard Rotate, Rotate Left/Right Key, Rotate Speedan, Q/E, 100Drehen per Tastatur, in Grad pro Sekunde.
Alt Right Drag Rotate, Drag Rotate Sensitivityan, 0.3Drehen mit Alt + Ziehen mit rechter Maustaste.
Snap Back Rotation, Snap Back Delay, Default Yawaus, 0.25, 0Kehrt ohne Dreheingabe zum Standardwinkel zurück (klassisches RTS-Verhalten). Bei einem Drehwinkel von 0 blickt die Kamera entlang +Z (Norden).
Follow Ground Height, Height Smooth Timean, 0.3Hält den Drehpunkt auf Bodenhöhe, geglättet, damit Klippen die Ansicht nicht springen lassen.
Clamp To Terrain, Bounds Marginan, 4Hält den Drehpunkt innerhalb der Karte (negative Randabstände erlauben den Blick über den Kartenrand hinaus).
Min Camera Clearance2Mindesthöhe der Kamera über dem Boden darunter.
Pan / Zoom / Rotation Smooth Time0.08 / 0.12 / 0.08Glättung in Sekunden (0 = sofort).
Ignore Mouse Over UIanIgnoriert Mausrad, Rand-Scrollen und Ziehen, solange sich der Mauszeiger über der UI befindet.
Use Unscaled TimeanDie Kamera bewegt sich auch, während das Spiel pausiert ist.

API: JumpTo(Vector3), FocusOn(Vector3), FocusOn(IReadOnlyList<RTSUnit>, bool instant = false), SetZoom(float, bool instant = false), SetYaw(float, bool instant = false), GetViewFootprint(Vector3[4]) (Bildschirmecken auf dem Boden, von der Minikarte genutzt), InitializeFromCamera(), InputEnabled sowie die schreibgeschützten Eigenschaften Pivot, Zoom, Yaw, Pitch, Distance, IsDragging.

RTSUnit und Hilfsklassen für Einheiten

Eine einfache RTS-Einheit: Team, Bewegung auf dem Terrain, Pfadverfolgung mit weichen Kurven, lokale Kollisionsvermeidung und Sicht im Nebel des Krieges. Sie fragt ihren Pfad-Provider nach einem Pfad und folgt ihm mit einem Vorausschaupunkt. Sie bremst bei der Ankunft ab, hält Abstand zu anderen Einheiten, betritt nie unbegehbare Zellen und plant ihren Pfad neu, wenn sie feststeckt oder sich die Pathing-Karte unter ihrem Pfad ändert (Gebäude platziert, Bäume gefällt, Terrain im In-Game-Editor bearbeitet). Sie benötigt und konfiguriert einen RTSFogOfWarRevealer (Team, Sichtradius) und einen RTSFogOfWarHideable (Team).

EinstellungStandardBeschreibung
Team0Team- bzw. Spielerindex (0 = Spieler 1). Einheiten des lokalen Teams lassen sich auswählen und befehligen.
Use Team Color, Player Coloran, RotÜbernimmt bei jedem Teamwechsel die Farbe aus der klassischen Spielerfarben-Tabelle; genutzt für Auswahlkreise, Punkte auf der Minikarte und Renderer für die Teamfarbe.
Unit Type"Unit"Typname für „Doppelklick wählt alle desselben Typs aus“ (z. B. Footman, Worker). Die Baumdemo verwendet „Worker“.
Move Speed, Turn Speed, Acceleration5, 540, 25Meter pro Sekunde, Grad pro Sekunde, Meter pro Sekunde².
Radius, Height0.5, 1.8Kollisionsradius (Freiraum bei der Wegfindung, Kollisionsvermeidung, Auswahlkreis) und Höhe (Anklicken, Auswahlrechteck).
Align To Ground, Ground Align Speedaus, 8Neigt die Einheit passend zum Boden (Fahrzeuge, Tiere). Ausgeschaltet bleibt Infanterie aufrecht.
Look Ahead, Arrival Distance, Slow Down Distance1.5, 0.15, 1.5Pfadverfolgung (Meter).
Stuck Check Interval0.6Sekunden zwischen Fortschrittsprüfungen. Eine Einheit ohne Fortschritt plant ihren Pfad neu.
Path Request Timeout3Sekunden, die auf einen Pfad gewartet wird, bevor die Anfrage einmal erneut gesendet wird; die Bewegung schlägt fehl, wenn auch die zweite Anfrage ins Timeout läuft.
Avoidance, Separation Strength, Personal Spacean, 8, 0.1Einfache Abstandshaltung. Untätige Einheiten weichen sich bewegenden Einheiten aus.
Sight Radius14Sichtradius für den Nebel des Krieges in Metern.
SelectableanDer Spieler kann diese Einheit auswählen.
TerrainleerTerrain, auf dem die Einheit läuft. Leer = das Terrain unter der Einheit (oder RTSTerrain.Main).
Path ProviderleerOptionale Komponente, die IRTSUnitPathProvider implementiert. Leer = der globale Standard (siehe Pfad-Provider).
Team Color Renderers, Team Color Modekeine, PropertyBlockRenderer, die die Spielerfarbe zeigen, entweder über einen MaterialPropertyBlock (beliebiges Material) oder über ein gemeinsames Material pro Farbe (bestes Batching).
Draw Path GizmosanZeichnet den aktuellen Pfad in der Scene View.

Befehle: MoveTo(Vector3, bool queue = false), Stop(), HoldPosition(), Patrol(Vector3 a, Vector3 b), Warp(Vector3, float? yaw = null), FaceTowards(Vector3), Repath(). Events: OnArrived, OnPathFailed (kein Pfad, wegen Blockade aufgegeben oder am nächstgelegenen erreichbaren Punkt angehalten), OnStateChanged. Zustand: State (Idle, Moving, Patrolling, Holding), IsMoving, IsWaitingForPath, IsPathPartial, LastMoveReachedDestination, Destination, CurrentPath, QueuedDestinations, PlannedEndPosition, Velocity, Position, Yaw, IsSelected, IsVisibleToLocalTeam.

using RTSTerrainStudio;
using RTSTerrainStudio.Samples;
using UnityEngine;

// Spawns a knight for player 2 and listens to its movement events.
public class KnightSpawner : MonoBehaviour
{
    [SerializeField] Transform target;

    void Start()
    {
        var terrain = RTSTerrain.Main;
        // Nearest walkable spot where a unit of radius 0.75 m fits.
        Vector3 position = RTSUnitFactory.FindSpawnPosition(terrain, transform.position, 0.75f);
        RTSUnit knight = RTSUnitFactory.CreatePlaceholder(1, position, 180f, RTSUnitFactory.Knight);

        knight.OnArrived += unit => Debug.Log(unit.name + " arrived.");
        knight.OnPathFailed += unit => Debug.Log(unit.name + " can't reach that place.");
        if (target != null) knight.MoveTo(target.position);
    }
}

Hilfsklassen:

KlasseZweck
RTSUnitFactoryProzedurale Platzhaltereinheiten: Stile Footman, Worker, Scout, Knight und DefaultMix; CreatePlaceholder(team, position, yaw, style, parent), Spawn(prefab, team, position, yaw, parent) (fügt einem Prefab RTSUnit hinzu, falls es fehlt), FindSpawnPosition(terrain, position, radius, searchRadius = 16).
RTSUnitSpawnerSpawnt Einheiten pro Spieler an den Startpositionen der Karte (oder an den Custom Spawn Points oder in gegenüberliegenden Ecken). Einstellungen: Unit Prefabs, Units Per Player (6), Mix Placeholder Types, Terrain, Use Start Locations, Custom Spawn Points, Max Players, Spacing (2 m), Start Offset (5 m, Platz für ein Rathaus), Container, Spawn On Start. API: SpawnAll(), SpawnForPlayer(...), SpawnUnit(team, position, yaw, variant), DespawnAll(), TryGetStartLocation(player, out position, out yaw).
RTSWanderAILässt eine Einheit zu zufälligen Punkten rund um ihren Ausgangspunkt laufen. Einstellungen: Wander Radius (25 m), Min/Max Wait (2 / 7 s), Home Is Start Position, Home Position.
RTSUnitRegistryAlle Einheiten und ein räumlicher Hash: All, GetTeam(team), QueryRadius(...), QueryScreenRect(...), PickAtScreen(...), Events UnitRegistered, UnitUnregistered, UnitTeamChanged.

Pfad-Provider: IRTSUnitPathProvider

Einheiten sind nicht an eine bestimmte Wegfindungslösung gebunden. Sie fragen einen IRTSUnitPathProvider, der in dieser Reihenfolge gewählt wird: der eigene Path Provider der Einheit (oder PathProviderOverride), sonst RTSUnitPathProviderRegistry.Default, sonst der eingebaute Provider RTSBuiltinPathProvider (das Burst-A* des Pakets, im Play Mode über RTSPathfindingService). Hier docken die Integrationen für das A* Pathfinding Project und NavMesh an.

Es gibt zwei Arten von Providern:

  • Pfad-Provider (HandlesMovement = false) liefern über RequestPath Wegpunkte zurück; die Einheit folgt ihnen selbst (Steuerung, Kollisionsvermeidung, Bodenausrichtung).
  • Bewegungs-Provider (HandlesMovement = true) bewegen die Einheit selbst (zum Beispiel ein NavMeshAgent). Die Einheit leitet ihre Befehle über OnUnitMoveCommand und OnUnitStop weiter; der Provider meldet das Ergebnis mit unit.NotifyArrived() oder unit.NotifyPathFailed(). Solange ein solcher Provider für eine Einheit zuständig ist, verändert die Einheit ihren Transform nie selbst.

Der Vertrag: Rufe den Callback onComplete genau einmal auf, und zwar auf dem Hauptthread, sofort oder später. Übergib bei Erfolg die Wegpunkte (einschließlich Start und Ziel) und true, bei einem Fehlschlag eine leere Liste oder null und false. Du darfst auch einen Pfad zum nächstgelegenen erreichbaren Punkt mit true zurückgeben; die Einheit erkennt, dass er vor dem Ziel endet. Der Provider, der eine Bewegung erhalten hat, behält sie, bis die Bewegung endet. Wechselt der Provider während einer Bewegung oder wird er zerstört, erhält der alte OnUnitStop, und der Befehl geht an den neuen. Eine Anfrage, die innerhalb der unter Path Request Timeout eingestellten Zeit keine Antwort erhält, wird einmal wiederholt und schlägt dann fehl.

Leite von RTSUnitPathProviderComponent ab, um eine Szenenkomponente zu erstellen, die sich selbst als Standard registrieren kann (Make Default, standardmäßig an) oder die du in das Feld Path Provider einer Einheit ziehst:

using System;
using System.Collections.Generic;
using RTSTerrainStudio;
using RTSTerrainStudio.Samples;
using UnityEngine;

// A path provider for heavy units that must never cross water (not even shallow fords).
// Turn off "Make Default" and drag it into the Path Provider field of the units that should use it.
public class NoWaterPathProvider : RTSUnitPathProviderComponent
{
    [SerializeField] int maxSearchNodes = 200000;
    readonly List<Vector3> path = new List<Vector3>(64);

    // The unit follows the waypoints itself.
    public override bool HandlesMovement => false;

    public override void RequestPath(RTSUnit unit, Vector3 from, Vector3 to, Action<List<Vector3>, bool> onComplete)
    {
        RTSTerrain terrain = unit != null ? unit.Terrain : RTSTerrain.Main;
        if (terrain == null) { onComplete(null, false); return; }

        var options = PathRequestOptions.ForUnit(unit != null ? unit.Radius : 0.5f);
        options.allowShallowWater = false;
        options.maxSearchNodes = maxSearchNodes;

        // Synchronous search; the list is reused for the next request, the unit copies what it keeps.
        PathStatus status = RTSPathfinder.FindPath(terrain, from, to, options, path);
        onComplete(path, status == PathStatus.Success || status == PathStatus.Partial);
    }
}

Im Code schaltet RTSUnitPathProviderRegistry.Default = myProvider; alle Einheiten ohne eigenen Provider um; Default = null kehrt zum eingebauten Pathfinder zurück. RTSBuiltinPathProvider stellt Options (der darin enthaltene unitRadius wird durch den Radius der jeweiligen Einheit ersetzt) und UseAsyncService bereit.

RTSSelectionManager

Auswahl per Klick, Rechteck, Shift und Doppelklick, Kontrollgruppen sowie Hover- und Auswahlringe. Nur Einheiten des lokalen Teams lassen sich auswählen. RTSSelectionManager.Main ist der erste aktivierte Manager.

EinstellungStandardBeschreibung
Target CameraleerKamera zum Anklicken von Einheiten. Leer = Camera.main.
Use Fog Of War Local TeamanVerwendet das lokale Team des aktiven Managers für den Nebel des Krieges.
Local Team0Team, dessen Einheiten auswählbar sind (wenn es nicht aus dem Nebel des Krieges übernommen wird).
Drag Threshold, Double Click Time6 px, 0.3 sAb wann ein Klick zum Auswahlrechteck wird; Zeitfenster für Doppelklicks.
Max Selection0Maximale Anzahl ausgewählter Einheiten (0 = unbegrenzt; 12 ist eine übliche Grenze in klassischen RTS-Spielen).
Pick Tolerance6 pxZusätzlicher Klickradius, damit sich kleine Einheiten leicht anklicken lassen.
Keep Selection On Empty ClickanEin Klick auf leeren Boden oder ein Auswahlrechteck ohne eigene Einheiten behält die Auswahl bei, wie in klassischen RTS-Spielen.
Clear Selection KeyEscapeHebt die Auswahl auf.
Control Groups, Group Double Tap Timean, 0.35 sCtrl+Zahl speichert, Shift+Zahl fügt hinzu, die Zahl allein ruft ab, zweimal gedrückt zentriert sie die Kamera. Es gibt zehn Gruppen (RTSSelectionManager.GroupCount = 10) auf den Tasten 1–9 und 0; der Gruppenindex i von StoreGroup, RecallGroup und GetGroup entspricht der Zahlentaste i (Index 0 ist Taste 0).
Show Selection Circles, Show Hover, Use Team Color For Circles, Selection Color, Circle Scale, Circle Width, Box Color-Darstellung der Ringe und des Auswahlrechtecks.

API: Selection, Count, Select(unit, additive), Deselect, ToggleSelection, SetSelection(units, additive), ClearSelection(), SelectAllOfTypeOnScreen(unit, additive), GetSelectionCenter(), StoreGroup(i), AddToGroup(i), RecallGroup(i), GetGroup(i), ClearGroups(), HoveredUnit, Events SelectionChanged und GroupRecalled.

RTSCommandController und Formationen

Wandelt Rechtsklicks und Befehlstasten in Befehle für die ausgewählten Einheiten um. RTSCommandController.Main ist der erste aktivierte Controller.

EinstellungStandardBeschreibung
Selection, Target CameraleerLeer = RTSSelectionManager.Main / Camera.main.
Stop Key, Hold Key, Patrol Key, Cancel KeyS, H, P, EscapeBefehlstasten.
Use FormationanBewegt Gruppen in einer Rasterformation (aus = jede Einheit läuft zum angeklickten Punkt).
Spacing Multiplier, Extra Spacing, Width To Depth1.15, 0.3, 1.5Abstand der Formationsplätze als Vielfaches des größten Einheitendurchmessers, zusätzliche Lücke in Metern, bevorzugtes Verhältnis von Spalten zu Reihen.
Show Marker, Move Marker Color, Patrol Marker ColoranAnimierter Ring am Befehlsziel.

API: IssueMove(Vector3 target, bool queue = false), IssueMove(IReadOnlyList<RTSUnit> units, Vector3 target, bool queue = false), IssuePatrol(Vector3), IssueStop(), IssueHold(), BeginPatrolTargeting(), CancelTargeting(), IsOrderKeyActive(RTSKey), Events MoveIssued, CommandIssued, TargetingChanged.

RTSFormation.ComputeGrid(units, target, slots, spacingMultiplier, extraSpacing, widthToDepth, fromPlannedEnd) berechnet einen Formationsplatz pro Einheit und weist jeder Einheit den Platz zu, der ihrer Position in der Gruppe am nächsten liegt, sodass sich Pfade selten kreuzen. Die Methode gibt die Blickrichtung zurück. Mit fromPlannedEnd (verwendet für per Shift eingereihte Befehle) setzt die Formation dort an, wo die Einheiten nach ihren aktuellen Befehlen stehen werden.

Eingabekoordination. RTSGameInput sorgt dafür, dass jeder Klick von genau einem System verarbeitet wird: Ein Modus wie die Gebäudeplatzierung beansprucht den Zeiger (Acquire, Release, CanUsePointer), und ein System, das einen Klick oder eine Taste verarbeitet hat, ruft Consume / ConsumeKey auf. Die Beispiele laufen in einer festen Reihenfolge (Gebäudeplatzierung, Baumabbau, Befehle, Auswahl, Einheiten, Kamera, UI), die durch die Konstanten RTSGameInput.Order… festgelegt ist. Nutze denselben Mechanismus in deinen eigenen Modi, damit sie mit den Beispielen zusammenarbeiten.

RTSMinimapUI

Eine Minikarte auf Basis von UI Toolkit für das laufende Spiel. Das Kartenbild wird mit RTSMinimapBaker gebacken und bei Kartenänderungen (gedrosselt) neu gebacken. Darüber wird der Nebel des Krieges des lokalen Teams gelegt. Einheiten erscheinen als Punkte in ihrer Spielerfarbe (Gegner nur, solange sie sichtbar sind), und der Sichtbereich der Kamera wird als Umriss gezeichnet. Ein Linksklick oder Ziehen bewegt die Kamera, ein Rechtsklick schickt die ausgewählten Einheiten an diese Stelle.

EinstellungStandardBeschreibung
Terrain, Camera Controller, Command ControllerleerLeer = RTSTerrain.Main, RTSCameraController.Main, RTSCommandController.Main.
CornerBottomLeftBildschirmecke (klassische RTS-Spiele nutzen die Ecke unten links).
Size, Margin248, 16Rahmengröße und Abstand zu den Bildschirmrändern in UI-Pixeln (Referenzauflösung 1920 × 1080).
Resolution256Auflösung des gebackenen Kartenbilds (längste Seite).
Rebake Delay, Min Rebake Interval0.35 s, 1 sMehrere Änderungen werden zu einem einzigen Backvorgang zusammengefasst.
Bake Options-Aussehen des gebackenen Bilds (Bodenmodus, Reliefschattierung, Klippen, Wasser, Bäume, Dekor, Startpositionen, Spielerfarben).
Show Fog, Fog Refresh Intervalan, 0.1 sDunkelt unerkundete und vom Nebel verdeckte Bereiche ab, wie in der 3D-Ansicht.
Show Units, Unit Dot Size, Show Camera View, Camera View Color, Overlay Ratean, 4, an, weiß, 20Punkte, Kameraumriss und wie oft sie neu gezeichnet werden.
Left Click Moves Camera, Right Click Moves Unitsan, anInteraktion.

API: Visible, Terrain, MapTexture, BakeOptions, Rebake().

RTSDemoHUD und RTSRuntimeUI

RTSDemoHUD zeigt ein Info-Panel für die Auswahl, eine Aktionsleiste (Stop, Hold, Patrol, Build), die Framerate, kurze Rückmeldungen („Can't reach that place.“, „Can't build there.“, „Can't build in unexplored land.“) und das F1-Hilfe-Panel. Einstellungen: Help Key (F1), Show Help On Start, Show Fps, Show Feedback Messages, Message Duration (2.5 s) sowie Referenzen auf Auswahl, Befehle und Demos. API: Visible, HelpVisible, SetMode(string), ShowMessage(string). Solange die Komponente aktiviert ist, schaltet sie RTSInput.SetBrowserShortcutGuard ein. In Web-Builds öffnet F1 deshalb die HUD-Hilfe statt der Hilfeseite des Browsers. Der Schutz unterdrückt außerdem die browsereigene Aktion für die übrigen Funktionstasten bis F10 (darunter F5, das die Seite neu lädt) und für Ctrl+S/O/D/A (siehe Der In-Game-Editor im Browser). Auf anderen Plattformen hat er keine Wirkung.

RTSRuntimeUI ist der kleine Baukasten auf Basis von UI Toolkit, den die Beispiele gemeinsam nutzen: CreateDocument, CreatePanel, CreateLabel, CreateButton(text, tooltip, action), AttachTooltip, IsScreenPointOverUI und die Farbpalette der Beispiele. Damit kannst du eigene Panels im gleichen Stil hinzufügen.

Alle Oberflächen der Beispiele teilen sich ein Panel (RTSRuntimeUI.PanelSettings), das mit der Bildschirmgröße skaliert (Referenzauflösung 1920 × 1080). Wie beim In-Game-Editor skaliert es nie unter RTSRuntimeUI.MinimumScale (Standard 0.85; in Web-Builds in CSS-Pixeln gemessen). So bleiben HUD, Minikarte und Auswahl auch in kleinen Fenstern und Browser-Canvas-Elementen lesbar. Große Bildschirme sind davon nicht betroffen. Setze den Wert auf 0, um das Minimum auszuschalten.

RTSBuildingPlacementDemo

Zeigt, wie Spielcode die Pathing-Karte zur Laufzeit verändert. Drücke B: Eine halbtransparente Vorschau der Grundfläche folgt dem Cursor und rastet am Pathing-Raster ein; jede Zelle wird grün (bebaubar) oder rot (blockiert) gezeichnet. Ein Linksklick platziert ein einfaches Gebäude in der Teamfarbe (halte Shift gedrückt, um mehrere zu platzieren). Die Grundfläche wird in die Pathing-Overrides geschrieben, das Terrain baut den Bereich neu auf, und Einheiten in Bewegung planen ihren Weg um das Gebäude herum neu. Ein Klick auf eine blockierte Stelle zeigt einen roten Marker und eine Meldung. Ein Rechtsklick oder Esc bricht ab; Delete entfernt das Gebäude unter dem Cursor und stellt das Pathing wieder her.

EinstellungStandardBeschreibung
Size In Cells2 × 2Größe der Grundfläche in Kartenzellen (X × Z).
Building Height3Höhe des prozeduralen Gebäudes in Metern.
Building PrefableerOptionales Prefab anstelle des prozeduralen Gebäudes. Sein Pivot wird in der Mitte der Grundfläche auf den Boden gesetzt.
Team-1Besitzerteam. -1 = das lokale Team des Auswahl-Managers (oder des Nebels des Krieges).
Sight Radius10Sichtradius von Gebäuden (0 = keine Sicht). Außerdem werden Gebäude zu verbergbaren Objekten im Geisterbild-Modus.
Footprint FlagsUnwalkable, UnbuildablePathing-Overrides, die für die Grundfläche geschrieben werden.
Push Units OutanEinheiten, die auf der Grundfläche stehen, gehen aus dem Weg.
Require ExploredanErlaubt das Bauen nur auf Boden, den das lokale Team erkundet hat (solange ein Nebel des Krieges aktiv ist).
Place Key, Cancel Key, Demolish KeyB, Escape, DeleteTasten.
Valid Color, Invalid Colorgrün, rotFarben der Grundflächenzellen.
Terrain, Target CameraleerLeer = das Terrain unter dem Cursor / Camera.main.

API: BeginPlacement(), CancelPlacement(), IsPlacing, GetFootprintSize(terrain), CanPlaceAt(terrain, worldCenter), GetPlacementError(terrain, worldCenter) (eine Meldung für den Spieler oder null), PlaceBuilding(terrain, worldCenter, owner), RemoveBuilding(building), RemoveAllBuildings(), Buildings, die Events BuildingPlaced, BuildingRemoved, PlacementModeChanged, PlacementFailed(Vector3, string). Jedes platzierte Gebäude hat eine Komponente RTSDemoBuilding (Team, PathingCells, ContainsPoint, Remove()), die die vorherigen Overrides speichert und sie wiederherstellt, wenn das Gebäude entfernt oder zerstört wird.

Um daraus echte Gebäude zu machen, weise der Einstellung Building Prefab dein Prefab zu, abonniere BuildingPlaced, um die Kosten abzuziehen und den Bau zu starten, und rufe GetPlacementError auf, um dem Spieler den Grund anzuzeigen, wenn er nicht bauen kann.

RTSTreeHarvestDemo

Zeigt, wie Bäume im laufenden Spiel entfernt werden. Bewege den Cursor über einen Baum (er bekommt einen gelben Ring) und drücke T, um ihn sofort zu fällen. Oder wähle Arbeiter aus (Einheitentyp „Worker“) und klicke mit der rechten Maustaste auf einen Baum: Sie gehen hin, drehen sich ihm zu, hacken einen Moment lang (ein Ring zieht sich um den Stamm zusammen) und fällen ihn. Der Baum wird mit RTSVegetationRenderer.RemoveTree entfernt, sodass das Pathing die Stelle wieder freigibt und der Nebel des Krieges den Sichtblocker aufhebt. An der alten Position wird eine Fallanimation (RTSFallingTreeEffect) abgespielt.

EinstellungStandardBeschreibung
Harvest KeyTFällt den Baum unter dem Cursor.
Workers Harvest On Right ClickanEin Rechtsklick auf einen Baum schickt die ausgewählten Arbeiter los, um ihn zu fällen.
Worker Type"Worker"Einheitentyp, der Bäume abbauen kann.
Pick Radius1.5Maximaler Abstand zwischen dem Cursorstrahl und einem Baum, damit dieser ausgewählt wird.
Chop Time, Work Distance1.5 s, 1.2 mWie lange ein Arbeiter hackt und wie nah er am Stamm steht.
Fall Duration, Fade Duration1.1 s, 0.5 sFallanimation und abschließendes Schrumpfen.
Highlight Color, Show Chop Progress, Chop Progress Color-Ringe unter dem Baum, über dem der Cursor steht, und unter dem Baum, der gerade gefällt wird.
Target CameraleerKamera, mit der Bäume per Cursor ausgewählt werden.

API: TryGetHoveredTree(out RTSVegetationRenderer, out TreeInstanceData), HarvestTree(vegetation, treeId, fallAwayFrom), OrderHarvest(workers, vegetation, tree), RestoreHarvestedTrees(), CancelAllJobs(), HarvestedCount, das Event TreeHarvested.

using RTSTerrainStudio;
using UnityEngine;

// Cuts the tree nearest to a point, e.g. when a catapult hits a forest.
public static class TreeCutter
{
    public static bool CutNearest(RTSTerrain terrain, Vector3 worldPoint, float maxDistance)
    {
        var vegetation = terrain != null ? terrain.GetComponent<RTSVegetationRenderer>() : null;
        if (vegetation == null) return false;

        if (!vegetation.TryFindNearestTree(worldPoint, maxDistance, out TreeInstanceData tree)) return false;
        // Removes the tree from the map and notifies the terrain:
        // pathing and fog of war blockers update automatically.
        return vegetation.RemoveTree(tree.id);
    }
}

Kleine Hilfsklassen

KlasseZweck
RTSMoveMarkerAnimierter Ring am Zielpunkt eines Befehls: RTSMoveMarker.Spawn(position, color, radius, seconds).
RTSGroundRingEin Ring, der sich an das Terrain anschmiegt (Auswahlkreise, Reichweiten): RTSGroundRing.Create(name, color, radius, width, parent), danach Place(position).
RTSSampleMaterials, RTSSampleMeshesZwischengespeicherte URP-Materialien pro Farbe und einfache Grundform-Meshes für Platzhalter.
RTSSampleUtilityErmitteln des zuständigen Terrains und des Bodenpunkts an einer Bildschirmposition, XZ-Abstände und PathingRectToCellRect(map, pathingRect).

Demo-Inhaltsgenerator

Das Paket enthält keine Grafiken von Drittanbietern. Stattdessen erzeugt ein Generator alle Demo-Inhalte prozedural direkt in deinem Projekt: kachelbare Boden-, Klippen- und Wassertexturen, Gras- und Blumen-Sprites, Low-Poly-Bäume und -Props, Materialien, Prefabs, zwei Tilesets, Vegetations- und Dekorbibliotheken, ein Auto-Paint-Profil, Pinselformen, drei Demokarten und drei Demoszenen. Die Ausgabe ist deterministisch (feste Seeds), daher erzeugt jeder Durchlauf dieselben Inhalte.

Die Demokarte Four Kingdoms (4p) mit Seen, Klippen, Straßen, Wäldern und dem verseuchten Zentrum
Die Demokarte Four Kingdoms (4p) in der Szene Editor Playground.

Demo-Inhalte erstellen

  1. Stelle sicher, dass du dich im Edit Mode befindest und deine Szene gespeichert hast (Unity erlaubt nur eine unbenannte Szene; der Generator fordert dich bei Bedarf zum Speichern auf).
  2. Wähle Tools ▸ RTS Terrain Studio ▸ Create Demo Content. Ein Fortschrittsbalken zeigt die einzelnen Schritte. Das dauert einen kurzen Moment.
  3. Wenn du gefragt wirst, ob die Demoszenen zu den Build-Einstellungen hinzugefügt werden sollen, wähle Not Now, es sei denn, du willst einen Player-Build mit den Demos erstellen (du kannst sie später unter File ▸ Build Profiles hinzufügen).
  4. Der Generator wählt die Szene RTS Terrain Studio - Gameplay Demo im Project-Fenster aus. Doppelklicke darauf, um sie zu öffnen.

Der Generator läuft auch, wenn du im Abschnitt zu den Demo-Inhalten im Welcome-Fenster (Tools ▸ RTS Terrain Studio ▸ Welcome) auf Create klickst oder wenn du das Angebot annimmst, das erscheint, sobald du ein RTS Terrain in einem Projekt ohne Tileset erstellst. Er schließt oder ersetzt nie deine geöffneten Szenen: Die Demoszenen werden zusätzlich angelegt.

Der Generator führt diese Schritte aus (jeden davon kannst du auch aus Code aufrufen):

SchrittMethodeWas erzeugt wird
TexturenCreateTextures()Boden-, Klippen-, Wasser-, Baum- und Prop-Texturen (512 px), Gras- und Blumen-Sprites sowie Pinselstempel (256 px).
MaterialienCreateMaterials()Baum- und Pflanzenmaterialien (Shader RTS Terrain Studio/Vegetation, Wind), Prop-Materialien (URP Lit), die von den Beispielen genutzten Vorlagenmaterialien, das Himmelsmaterial und das Post-Processing-Profil.
Meshes und PrefabsCreateModels()Low-Poly-Bäume, Büsche, Felsen und Props sowie deren Prefabs.
Tilesets, Bibliotheken und PinselCreateDataAssets()Die Tilesets Summer und Winter, die Bibliotheken Demo Vegetation und Demo Decor (mit Paletten-Icons), das Profil Summer Auto Paint und Pinselformen.
Shader-Referenzen und UI-EinstellungenUpdateResources()Sorgt dafür, dass alle Shader des Pakets und die Runtime-Panel-Einstellungen von UI Toolkit in Player-Builds enthalten bleiben.
DemokartenCreateMaps()Drei .rtsmap-Dateien (siehe unten).
DemoszenenCreateScenes(bool interactive)Drei Szenen (siehe Demoszenen). Fügt außerdem das Renderer Feature für den Nebel des Krieges zu deinem URP-Renderer hinzu.

Was du bekommst

  • Tileset Summer (ID rts-demo-summer) mit zehn Bodenschichten: Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow und Blight. Rock und Blight sind auf Unbuildable gesetzt – ein gutes Beispiel für Pathing-Regeln von Bodenschichten.
  • Tileset Winter (ID rts-demo-winter): dieselben Texturen mit kalten Farbtönen und derselben Schichtreihenfolge, sodass jede Demokarte zwischen Summer und Winter wechseln kann.
  • Summer Auto Paint: Flecken aus sattem Gras und Erde auf den Grasflächen, Erde und steinige Erde entlang der Klippenkanten, Sand an den Ufern, Fels an steilen Hängen und Schnee in hohen Lagen.
  • Die Bibliotheken Demo Vegetation und Demo Decor: Gras, hohes Gras, gelbe und lila Blumen und Farne; Kiefern, Laubbäume, abgestorbene Bäume und Büsche; Felsen, ein Findling, eine verfallene Säule, Kisten, Fässer, Zäune, Baumstümpfe und Pilze.
  • Drei Karten:
    • Two Rivers (1v1): 128 × 128 Zellen, punktsymmetrisch. Zwei mäandernde Flüsse treffen sich in einem See in der Mitte; über flache Furten können Einheiten sie überqueren.
    • Four Kingdoms (4p): 192 × 192 Zellen, vierfache Rotationssymmetrie, gestapelte Plateaus und ein verseuchtes Zentrum mit Ruinen.
    • Sandbox: 96 × 96 Zellen, größtenteils flach mit einem Plateau und einem Teich, zum Ausprobieren der Werkzeuge.
    Alle Karten haben gepflasterte Plätze an den Startpositionen, Wälder mit Waldboden und kleine Gruppen von Props. Alles ist symmetrisch platziert, damit die Karten fair bleiben.

Wo die Dateien landen

Der Generator schreibt nur in seine eigenen Ordner unterhalb des Paketordners (Assets/RTSTerrainStudio, sofern du ihn nicht verschoben hast; das Paket funktioniert in jedem Ordner):

OrdnerInhalt
Content/TexturesGenerierte Texturen in den Unterordnern Ground, Cliffs, Water, Details, Props und Brushes.
Content/Materials, Content/Meshes, Content/PrefabsMaterialien, Meshes und Prefabs der Bäume und Props.
Content/TilesetsSummer.asset, Winter.asset und Summer Auto Paint.asset.
Content/LibrariesDemo Vegetation.asset, Demo Decor.asset und ihre Icons.
Content/BrushesPinselformen.
Content/MapsTwo Rivers (1v1).rtsmap, Four Kingdoms (4p).rtsmap, Sandbox.rtsmap.
Content/SettingsRender-Einstellungen (Post-Processing-Profil).
Samples/ScenesDie drei Demoszenen.
Resources/RTSTerrainStudioShader-Referenzen und UI/RTSRuntimePanelSettings.asset.

Neu generieren

Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content erneut aus und bestätige mit Regenerate. Vorhandene Demo-Assets werden an Ort und Stelle überschrieben, sodass Karten, Szenen und Prefabs, die auf sie verweisen, weiter funktionieren. Deine eigenen Assets werden nie angerührt. Kartendateien, deren Inhalt sich nicht geändert hat, werden nicht neu geschrieben; so tauchen in der Versionsverwaltung keine unnötigen Änderungen auf.

Beim Neugenerieren werden alle Änderungen überschrieben, die du an den generierten Assets selbst vorgenommen hast (zum Beispiel ein angepasstes Demo-Tileset oder eine bearbeitete Demokarte). Um Demo-Inhalte anzupassen, dupliziere zuerst das Asset (Ctrl+D) oder speichere die Karte unter einem neuen Namen, und bearbeite dann die Kopie.

Aus Code (Editor-Assembly, Namespace RTSTerrainStudio.Editor):

MemberBeschreibung
RTSDemoContent.CreateAll(bool openScene)Führt alle Schritte mit Fortschrittsbalken aus. Gibt false zurück, wenn abgebrochen wurde oder ein Schritt fehlgeschlagen ist.
RTSDemoContent.CreateAllNonInteractive()Zeigt keine Dialoge und öffnet keine Szene; für Automatisierung und den Batch-Modus. Gibt eine kurze Zusammenfassung zurück.
RTSDemoContent.IsCreatedTrue, wenn die wichtigsten Demo-Assets vorhanden sind.
RTSDemoContent.ContentFolder, ScenesFolder, SummerTilesetPath, TwoRiversMapPath, GameplayScenePath, …Pfade der generierten Ordner und Assets.
RTSDemoContent.RenderPreview(scenePath, pngPath, width, height)Rendert ein Vorschaubild einer Szene.
// Editor script (place it in an Editor folder): create the demo content from a build pipeline or a CI job.
using RTSTerrainStudio.Editor;
using UnityEngine;

public static class BuildDemoContent
{
    // Run with: Unity -batchmode -executeMethod BuildDemoContent.Run -quit
    public static void Run()
    {
        if (RTSDemoContent.IsCreated) return;               // nothing to do
        Debug.Log(RTSDemoContent.CreateAllNonInteractive()); // logs where the content was written
    }
}

Demoszenen

Der Generator erstellt drei Szenen in Samples/Scenes. Jede davon enthält ein Licht Sun, ein Volume Post Processing, ein RTS Terrain (mit Komponenten für Vegetation, Wasser, Dekor und Umgebung) und eine Main Camera, die die für Wasser nötigen Depth- und Opaque-Texturen einschaltet.

SzeneKarteInhalt
RTS Terrain Studio - Gameplay DemoTwo Rivers (1v1)RTS-Kamera, Nebel des Krieges und RTSDemoGame mit Auto Start. Klicke auf Play, und das Spiel beginnt.
RTS Terrain Studio - In-Game Editor DemoSandboxDer In-Game-Editor. Nebel des Krieges und RTS-Kamera bleiben während der Bearbeitung aus; Einheiten spawnen, wenn du einen Spieltest startest.
RTS Terrain Studio - Editor PlaygroundFour Kingdoms (4p)Kein Gameplay. Eine große Karte zum Ausprobieren der Werkzeuge im Unity Editor.

Gameplay Demo: Das kannst du ausprobieren

  1. Klicke auf Play. Deine Einheiten (Spieler 1) stehen auf dem Platz deiner Startposition; der Rest der Karte ist schwarz.
  2. Wähle mit einem Auswahlrechteck ein paar Einheiten aus und klicke mit der rechten Maustaste auf die andere Seite des Flusses. Beobachte, wie sie in Formation eine Furt durch das Flachwasser wählen.
  3. Bewege Einheiten neben eine Klippe. Beachte, dass Einheiten unterhalb das Plateau darüber nicht sehen können und dass ein Wald verdeckt, was hinter ihm liegt. Führe sie dann eine Rampe hinauf und schaue hinunter.
  4. Ziehe deine Einheiten aus einem Gebiet ab: Es bleibt abgedunkelt (erkundet), statt wieder schwarz zu werden. Gegnerische Einheiten, die dorthin wandern, verschwinden, sobald du sie aus den Augen verlierst.
  5. Drücke B und platziere ein Gebäude im Weg laufender Einheiten. Sie gehen darum herum. Drücke Delete, während der Cursor darüber steht, um es zu entfernen.
  6. Bewege den Cursor über einen Baum und drücke T, oder wähle Arbeiter aus und klicke mit der rechten Maustaste auf einen Baum. Die Lücke im Wald wird begehbar, und du kannst hindurchsehen.
  7. Speichere eine Gruppe mit Ctrl+1, scrolle weg und drücke zweimal 1, um zurückzuspringen. Nutze die Minikarte, um die Kamera zu bewegen und Einheiten loszuschicken.
  8. Beende den Play Mode, wähle das Objekt Fog of War und probiere für Line Of Sight andere Modi aus, oder ändere in RTS Demo Game die Einstellung Local Team, um als anderer Spieler zu spielen.

In-Game Editor Demo: Das kannst du ausprobieren

  1. Klicke auf Play. Der In-Game-Editor öffnet sich mit der Karte Sandbox; die ganze Karte ist sichtbar.
  2. Bearbeite die Karte: Hebe ein Plateau an, texturiere den Boden, pflanze einen Wald und platziere Startpositionen mit den Marker-Werkzeugen.
  3. Klicke auf Play Test (F5). RTSDemoGame spawnt Einheiten an den Startpositionen und schaltet den Nebel des Krieges und die RTS-Kamera ein. Deine Änderungen wirken sofort: Klippen blockieren die Sicht, Wälder blockieren Wege.
  4. Drücke Esc (nachdem du jeden aktiven Modus abgebrochen und die Auswahl aufgehoben hast) oder F5, oder klicke auf Back to Editor (Esc or F5). Die Einheiten verschwinden, und die Karte wird auf ihren Zustand vor dem Spieltest zurückgesetzt.

Editor Playground: Das kannst du ausprobieren

  1. Öffne die Szene und das Fenster von RTS Terrain Studio (Tools ▸ RTS Terrain Studio ▸ Editor Window).
  2. Wähle das RTS Terrain aus und probiere die Werkzeuge auf der Karte Four Kingdoms aus: Klippen, Rampen, Texturieren, Vegetation und Dekor.
  3. Drücke in der Scene View P, um die Pathing-Overlays durchzuschalten, und sieh dir an, wie Klippen, Hänge, Wasser, Bäume und die Bodenschicht Blight die Begehbarkeit und Bebaubarkeit beeinflussen.
  4. Willst du sie spielen? Füge GameObject ▸ RTS Terrain Studio ▸ Demo Game hinzu und klicke auf Play: Einheiten spawnen an den Startpositionen aller vier Spieler.

Die Integrationen haben eigene Demoszenen, die bei Bedarf erstellt werden: Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene, Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene und Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Unity NavMesh ▸ Create Demo Scene. Sie benötigen die Demo-Inhalte und das passende Drittanbieterpaket.

Die Demo sieht falsch aus oder hat keine Texturen? Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und wende die vorgeschlagenen Korrekturen an (URP-Asset, Renderer Feature, Kameratexturen). Fehlt eine Demoszene, führe den Generator erneut aus: Er erstellt fehlende Szenen neu und lässt deine eigenen Szenen unverändert.

Kartengenerierung und Kartenwerkzeuge

Der Tab Map im Fenster von RTS Terrain Studio (Tools ▸ RTS Terrain Studio ▸ Editor Window, letzter Tab vor Settings) enthält die Panels, die auf die gesamte Karte wirken: Map, New Map, Resize, Generator, Heightmap, Minimap, Validation und Environment. Dieses Kapitel erklärt den prozeduralen Generator, den Import und Export von Heightmaps, das Backen der Minikarte und den Kartenvalidator. All das kannst du auch aus deinem eigenen Code nutzen; die Beispiele kompilieren mit dem Paket im Auslieferungszustand.

Alle Panels arbeiten mit dem Terrain, das in der Kopfzeile des Fensters ausgewählt ist. Wenn eine Schaltfläche dich auffordert: „Select this terrain in the window header first“, wähle das Terrain im Terrain-Feld der Kopfzeile aus (ein Terrain, das du in der Szene auswählst, wird dort ebenfalls ausgewählt).

Kartengenerator

Der Generator (RTSMapGenerator) erzeugt aus einem Seed (Startwert für den Zufallsgenerator) ein vollständiges klassisches RTS-Layout: durch saubere Klippen getrennte Plateaus, Rampen dazwischen, Seen oder Inseln, einen optionalen Kartenrand, Bodentexturierung, Gras und Wälder, Dekor und Startpositionen der Spieler. Er arbeitet mit der vorhandenen Karte und behält deren Größe bei. Derselbe Seed mit denselben Einstellungen und derselben Kartengröße erzeugt immer dieselbe Karte, sodass du statt einer Datei auch nur einen Seed weitergeben kannst.

Beim Generieren werden immer Höhen, Klippenstufen, Rampen, Klippentypen, Wasser und alle Pathing-Overrides ersetzt (nur der Rand wird wieder eingemalt, daher werden gemalte Overrides auch dann gelöscht, wenn Border auf None steht). Bodentexturierung, Detailschichten, Bäume, Dekor und Startpositionen werden nur ersetzt, wenn ihre Option aktiv ist: Paint Mode ist nicht KeepExisting; Vegetation bzw. Decor ist Enabled, das Terrain hat eine Vegetations- bzw. Dekorbibliothek, und Clear Existing ist eingeschaltet; Start Locations ist Enabled (Max Players wird aktualisiert, wenn Update Max Players eingeschaltet ist). Andernfalls bleibt der vorhandene Inhalt erhalten, und bei ausgeschaltetem Clear Existing kommen neue Bäume und Dekorobjekte zu den vorhandenen hinzu. Regionen, Marker und die Umgebungseinstellungen bleiben immer unberührt. Der gesamte Durchlauf ist ein einziger Undo-Schritt, du kannst also gefahrlos experimentieren.

Deine erste generierte Karte (Unity Editor)

  1. Wenn dein Projekt noch kein Tileset und keine Bibliotheken hat, führe einmal Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus. Der Generator texturiert mit dem Tileset und platziert Pflanzen und Dekor aus den Bibliotheken des Terrains; ohne Bibliotheken überspringt er Vegetation und Dekor.
  2. Wähle dein RTS Terrain aus (oder erstelle eines mit GameObject ▸ RTS Terrain Studio ▸ RTS Terrain) und öffne Tools ▸ RTS Terrain Studio ▸ Editor Window.
  3. Klicke auf den Tab Map und klappe das Panel Generator auf.
  4. Wähle unter Style einen Stil (zum Beispiel Highlands) und klicke auf Apply Style Preset. Damit werden die Rausch-, Klippen-, Rampen- und Wasserwerte geladen, mit denen der Stil richtig aussieht.
  5. Wähle unter Symmetry eine Symmetrie: Rotate2 für eine faire 1v1-Karte, Rotate4 für 4 Spieler auf einer quadratischen Karte.
  6. Öffne Start Locations und setze Count auf die Anzahl der Spieler.
  7. Klicke auf Randomize Seed, dann auf Generate, und bestätige den Dialog.
  8. Lies den Bericht unter den Schaltflächen: Anzahl der Plateaustufen, Rampen, Startpositionen, Bäume und Dekorobjekte sowie die benötigte Zeit. Warnungen (zum Beispiel only 3 of 4 start locations found) stehen dort und in der Console.
  9. Nicht zufrieden? Drücke Ctrl+Z, klicke auf Randomize Seed und generiere erneut. Wenn dir das Ergebnis gefällt, speichere die Karte (Save in der Kopfzeile des Fensters oder im Inspector des RTS Terrain).
Generierte Karten sind ein Ausgangspunkt. Nutze danach die normalen Werkzeuge: Verschiebe eine Rampe, grabe einen zusätzlichen See, male einen Weg, platziere ein paar handverlesene Doodads. Mit dem Symmetrie-Malen im Pinsel-Panel bleiben auch deine Änderungen fair.

Die übrigen Schaltflächen des Panels:

SchaltflächeFunktion
Randomize SeedWählt einen neuen zufälligen Seed. Alles andere bleibt unverändert.
Apply Style PresetLädt die Rausch-, Klippen-, Rampen- und Wasserwerte des ausgewählten Stils. Seed, Symmetrie, Rand, Texturierung, Vegetation, Dekor und die Einstellungen der Startpositionen bleiben erhalten.
ResetSetzt alle Generator-Einstellungen auf die Standardwerte des ausgewählten Stils zurück.
GenerateFragt nach einer Bestätigung und generiert die Karte als einen einzigen Undo-Schritt mit dem Namen Generate Map.

Die Generator-Einstellungen des Fensters werden in den Editor-Voreinstellungen gespeichert, sodass sie auch nach einem Neustart von Unity noch da sind.

Eine ganz neue Karte generieren (New Map Wizard)

Tools ▸ RTS Terrain Studio ▸ New Map Wizard... erstellt eine neue Kartendatei. Wähle im Schritt Terrain die Option Generate statt Flat, lege ein Preset (die sechs Stile sowie jedes Asset vom Typ RTSMapGeneratorPreset in deinem Projekt, aufgelistet als Preset: name), einen Seed und eine Symmetrie fest und klicke auf Preview, um die Minikarte des Ergebnisses zu sehen, bevor etwas gespeichert wird. Der Assistent übernimmt die Spielerzahl aus seiner eigenen Spielereinstellung und schreibt sie in die Startpositionen.

Generieren im In-Game-Editor

Spieler können auch in einem fertigen Spiel Karten generieren. Öffne im In-Game-Editor in der Werkzeugleiste Map ▸ Generate.... Der Dialog bietet eine vereinfachte Auswahl an Optionen:

OptionEntspricht
StyleWendet das Stil-Preset an (setzt die Formoptionen darunter zurück).
Seed / Randomseed
Symmetrysymmetry
Terrain: Cliffs, Plateaus, Ramps, Ramp Count, Water, Lake Coverage, Island, Bordercliffs.enabled, cliffs.plateauCount, ramps.enabled, ramps.count, water.enabled, water.lakeCoverage, water.islandMode, border.mode
Content: Textures, Vegetation, Forests, Decor, Decor Count, Start Locations, Playerspaint.mode, vegetation.enabled, vegetation.forestCoverage, decor.enabled, decor.count, startLocations.enabled, startLocations.count

Drücke Enter (oder klicke auf Generate), um zu generieren, und Esc, um abzubrechen. Das Ergebnis ist ein einziger Undo-Schritt (Ctrl+Z), und der Dialog merkt sich seine Einstellungen, bis das Spiel beendet wird.

Stil-Presets

Ein Stil (TerrainStylePreset) verändert die Form des Basis-Höhenfelds und lädt über MapGeneratorSettings.ApplyPreset passende Standardwerte.

StilAussehenStufen / PlateausRampenSeenanteil
PlainsGrößtenteils flaches Land mit ein paar niedrigen Plateaus. Eine klassische offene Melee-Karte.2–3 / 230.05
Hills (Standard)Sanfte Hügel (starke Feinhöhe) mit einigen Plateaus und Rampen. Ein guter Ausgangspunkt für alle Zwecke.2–3 / 240.06
HighlandsViele gestapelte Plateaus und Klippen mit Rampen dazwischen.2–6 / 580.04
IslandsLandmassen, umgeben von Wasser (Inselmodus, tieferes Wasser, breitere Ufer).2–4 / 350.38
CanyonRidged-Rauschen, geformt zu gewundenen Schluchten zwischen hohen Plateaus.2–5 / 360.03
ArenaEine tiefliegende zentrale Arena, umgeben von einem Ring aus Anhöhen.2–4 / 360.03

Eigene Presets speichern

Du hast Einstellungen gefunden, die dir gefallen? Speichere sie als Asset: Assets ▸ Create ▸ RTS Terrain Studio ▸ Map Generator Preset erstellt ein RTSMapGeneratorPreset mit einer Description und einem vollständigen Satz Settings (es startet mit den Werten von Hills). Der New Map Wizard listet jedes Preset-Asset des Projekts auf, und dein Code kann preset.settings an den Generator übergeben (siehe Generieren aus Code). Presets sind normale Assets und können daher mit deinem Spiel ausgeliefert werden, zum Beispiel als Auswahl wie „Canyon für 2 Spieler“ oder „Inseln für 4 Spieler“ in einem Skirmish-Menü.

Symmetrie: faire Karten für Wettkampfspiele

Mit Symmetrie erzeugt der Generator einen Sektor der Karte und kopiert ihn, sodass jeder Spieler dasselbe Terrain, dieselben Rampen, dieselbe Menge an Bäumen und Dekor und eine gleichwertige Startposition erhält. Geneigte Dekorobjekte neigen sich in gespiegelten Kopien entsprechend in die gespiegelte Richtung.

SymmetrieKopienTypische VerwendungQuadratische Karte nötig
None1Kampagnenkarten oder frei gestaltete Karten.Nein
MirrorX2Spiegelung links ↔ rechts (2 Spieler, die sich horizontal gegenüberstehen).Nein
MirrorZ2Spiegelung oben ↔ unten.Nein
MirrorXZ4Spiegelung an beiden Achsen (4 Ecken).Nein
Rotate2 (Standard)2180°-Punktsymmetrie, das klassische 1v1-Layout.Nein
Rotate4490°-Rotationssymmetrie, das klassische Layout für 4 Spieler.Ja (sonst Rückfall auf Rotate2)
MirrorDiagonal2Spiegelung an der Diagonalen (x ↔ z).Ja (sonst Rückfall auf MirrorX)
Verwende eine Anzahl an Startpositionen, die ein Vielfaches der Anzahl der Kopien ist (2, 4, 6 … bei Rotate2). Andernfalls warnt der Generator, dass das Layout nicht fair sein kann. MapGenerationReport.symmetryUsed verrät dir, welche Symmetrie tatsächlich verwendet wurde.

Referenz der Generator-Einstellungen (MapGeneratorSettings)

Das Panel des Generators zeigt alle Felder von MapGeneratorSettings, in Ausklappbereiche gegliedert. Die Spalte Standard zeigt die Werte von new MapGeneratorSettings(); sie entsprechen dem Stil Hills. Die Gruppen Noise, Cliffs, Ramps und Water ändern sich mit dem gewählten Stil. Werte außerhalb des gültigen Bereichs werden vor dem Generieren von Validate() auf diesen Bereich begrenzt.

General

EinstellungStandardBeschreibung
Seed (seed)12345Startwert für den Zufallsgenerator. Derselbe Seed mit denselben Einstellungen erzeugt immer dieselbe Karte.
Style (style)HillsForm des Terrains. Klicke auf Apply Style Preset (oder rufe ApplyPreset auf), um passende Standardwerte zu laden.
Symmetry (symmetry)Rotate2Generiert einen Sektor und kopiert ihn, damit die Karte fair ist (siehe Symmetrie).

Noise (noise, GeneratorNoiseSettings)

EinstellungStandardBeschreibung
Scale85 mGröße der großen Terrain-Formen in Metern. Größere Werte ergeben größere Plateaus.
Octaves4Anzahl der Rauschschichten (1–8). Mehr Oktaven fügen den Umrissen kleinere Details hinzu.
Persistence0.5Amplitudenfaktor pro Oktave (0.5 = jede Oktave ist halb so stark).
Lacunarity2Frequenzfaktor pro Oktave.
Ridged Amount0Überblendung hin zu Ridged-Rauschen (scharfe Grate und Schluchten). Der Stil Canyon verwendet 1.
Warp0.4Stärke des Domain Warping. Verbiegt die Umrisse zu natürlicheren, fließenden Formen.
Contrast1Höhere Werte verschieben mehr Fläche auf die niedrigsten und die höchsten Plateaus.
Smoothing2Weichzeichnungsdurchgänge, bevor das Feld in Klippenstufen umgewandelt wird (entfernt winzige, verrauschte Strukturen).
Fine Height Amplitude1.6 mAmplitude des Feinhöhen-Rauschens (Hügel auf den Plateaus).
Fine Height Scale22 mStrukturgröße des Feinhöhen-Rauschens.
Fine Height Octaves3Anzahl der Oktaven des Feinhöhen-Rauschens.
Cliff Edge Fade4 ZellenStrecke, über die die Feinhöhe zu den Klippenkanten hin ausläuft, damit die Oberseiten der Klippen sauber bleiben.

Cliffs (cliffs, GeneratorCliffSettings)

EinstellungStandardBeschreibung
EnabledanGeneriert Klippenstufen. Ist die Einstellung aus, liegt die ganze Karte auf der Stufe von Min Level, und nur die Feinhöhen variieren.
Min Level2Klippenstufe des niedrigsten Plateaus (0–15). Stufe 2 lässt Platz, um darunter Seen zu graben.
Max Level3Klippenstufe des höchsten Plateaus.
Plateau Count2Anzahl unterschiedlicher Plateaustufen zwischen Min Level und Max Level.
Hysteresis0.04Band um jeden Stufenschwellwert (Anteil am Höhenbereich). Vertices innerhalb dieses Bands übernehmen die Stufe ihrer Nachbarn; so bleiben die Klippenlinien glatt.
Min Feature Width8 ZellenKleinste Breite jedes Plateaus, jeder Terrasse, jedes Tals und jedes Sees. Schmalere Teile (dünne Terrassen zwischen zwei Klippen, Grate, Gräben, winzige Plateaus) werden der benachbarten Stufe zugeschlagen, und jedes Plateau hält zur Klippe darunter einen Abstand von etwa drei Vierteln dieser Breite. Seen dürfen ein Viertel schmaler sein. Die Presets verwenden 6–10.
Cleanup Passes2Durchgänge, die isolierte Vertices und Spitzen entfernen.
Cliff Type0Klippentyp (Klippenschicht 0–3 des Tilesets), der in alle Vertices geschrieben wird.

Ramps (ramps, GeneratorRampSettings)

EinstellungStandardBeschreibung
EnabledanPlatziert Rampen zwischen benachbarten Plateaus.
Count4Anzahl der Rampen im generierten Sektor (die Symmetriekopien kommen noch hinzu).
Width3 ZellenBreite einer Rampe entlang der Klippe.
Length2 ZellenLänge einer Rampe pro Klippenstufe, quer zur Klippe. 2 ergibt eine sanfte Neigung.
Ensure ConnectivityanFügt bei Bedarf zusätzliche Rampen hinzu, damit jedes Plateau von jedem anderen Plateau aus erreichbar ist.
Min Spacing12 ZellenMindestabstand zwischen zwei Rampen. Überlappende Rampen werden ausgelassen.
Min Plateau Size40Die Erreichbarkeitsprüfung ignoriert Plateaus mit weniger Vertices.

Water (water, GeneratorWaterSettings)

EinstellungStandardBeschreibung
EnabledanGeneriert Seen oder Meer samt ihren Wasseroberflächen.
Sea Level Offset−0.25Höhe der Wasseroberfläche in Klippenstufen, relativ zum niedrigsten Plateau (−0.9 bis 2). Negative Werte lassen das niedrigste Plateau trocken, positive Werte überfluten es.
Lake Coverage0.06Anteil der Karte (die tiefsten Bereiche), der zu Seen oder Meer wird (0–0.7).
Max Depth2.5 mTiefster Punkt der Seen unter der Wasseroberfläche.
Shore Width4 ZellenBreite des abfallenden Ufers von der Uferlinie bis zur vollen Tiefe.
Island ModeausSenkt das Terrain zu den Kartenrändern hin ab, sodass die Karte zu einer oder mehreren Inseln wird. Inselkarten bekommen immer mindestens 15 % Meer (der Bericht warnt, wenn der Anteil angehoben wurde).
Border Falloff0.25Im Island Mode: Breite des Abnahmebereichs zum Kartenrand hin, als Anteil der Kartengröße.

Border (border, GeneratorBorderSettings)

EinstellungStandardBeschreibung
ModeNoneNone: kein besonderer Rand. Unwalkable: malt nicht begehbare und nicht bebaubare Pathing-Overrides entlang des Rands. HighCliffs: errichtet entlang des Rands einen Ring hoher Klippen (ebenfalls nicht begehbar).
Width2 ZellenBreite des Rands.
Cliff Level0Klippenstufe des Rands im Modus HighCliffs. 0 = automatisch (zwei Stufen über dem höchsten Plateau).

Paint (paint, GeneratorPaintSettings)

EinstellungStandardBeschreibung
ModeAutoPaintKeepExisting: lässt die Bodentexturierung unverändert. FillBaseLayer: füllt die ganze Karte mit der unter Base Layer gewählten Schicht. AutoPaint: texturiert den Boden mit einem Auto-Paint-Profil anhand von Höhe, Neigung, Klippenstufe und Wasser.
ProfilekeineEin RTSAutoPaintProfile (Assets ▸ Create ▸ RTS Terrain Studio ▸ Auto Paint Profile). Leer = eingebaute Standardregeln, die anhand der Schichtnamen des Tilesets zugeordnet werden.
Base Layer0Bodenschicht, die FillBaseLayer verwendet.

Vegetation (vegetation, GeneratorVegetationSettings)

EinstellungStandardBeschreibung
EnabledanGeneriert Detailschichten und Wälder (benötigt eine Vegetationsbibliothek auf dem Terrain).
Use Whole Library When EmptyanSind die Listen unten leer, werden alle Prototypen der Vegetationsbibliothek verwendet.
Clear ExistinganEntfernt vor dem Generieren vorhandene Detailschichten und Bäume.
DetailsleerZu generierende Detailschichten (Gras), siehe nächste Tabelle.
Tree Prototype IdsleerIDs der Baum-Prototypen, die für Wälder verwendet werden.
Forest Coverage0.22Anteil des geeigneten Bodens, der von Wald bedeckt wird.
Forest Noise Scale55 mStrukturgröße des Wald-Rauschens.
Tree Spacing2.6 mAbstand zwischen den Bäumen.
Tree Density0.85Wahrscheinlichkeit, dass ein freier Platz in einem Wald einen Baum erhält (niedrigere Werte ergeben lichte Wälder).
Max Tree Slope26°Keine Bäume an steileren Hängen.
Start Location Clearance14 mHält diesen Abstand rund um die Startpositionen frei von Bäumen und Details.

Jeder Eintrag in Details (GeneratorDetailEntry):

EinstellungStandardBeschreibung
Prototype Id""ID des Detail-Prototyps in der Vegetationsbibliothek.
Density0.6Dichte in den bedeckten Bereichen (1 = maximale Dichte des Prototyps).
Coverage0.55Anteil des geeigneten Bodens, der bedeckt wird.
Noise Scale28 mStrukturgröße des Rauschens für die Bedeckung.
Max Slope32°Keine Details an steileren Hängen.

Decor (decor, GeneratorDecorSettings)

EinstellungStandardBeschreibung
EnabledanVerteilt Dekor (benötigt eine Dekorbibliothek auf dem Terrain).
Use Whole Library When EmptyanIst die Liste unten leer, werden alle Prototypen der Dekorbibliothek verwendet.
Clear ExistinganEntfernt vor dem Generieren vorhandenes Dekor.
Prototype IdsleerIDs der zu verteilenden Dekor-Prototypen.
Count60Anzahl der Dekorobjekte im generierten Sektor (die Symmetriekopien kommen noch hinzu).
Min Spacing3 mMindestabstand zwischen Dekorobjekten (ist der Abstand des Prototyps selbst größer, gilt dieser).
Max Slope30°Kein Dekor an steileren Hängen.
Start Location Clearance12 mHält diesen Abstand rund um die Startpositionen frei von Dekor.

Generiertes Dekor berücksichtigt die Einstellungen des jeweiligen Dekor-Prototyps: zufälliger Drehwinkel und Drehwinkel-Schrittweite, Skalierungsbereich, Tönungen, Varianten, Ausrichtung an der Normalen und maximale zufällige Neigung.

Start Locations (startLocations, GeneratorStartLocationSettings)

EinstellungStandardBeschreibung
EnabledanPlatziert Startpositionen. Ist die Einstellung aus, bleiben die vorhandenen Startpositionen erhalten.
Count2Anzahl der Startpositionen (1–16).
Flat Radius5 ZellenRadius um eine Startposition, der flach sein muss (eine Stufe, keine Klippen, Rampen oder Wasser).
Edge Margin8 ZellenMindestabstand zum Kartenrand.
Update Max PlayersanSchreibt den Wert von Count in die Einstellung Max Players der Karte (MapInfo.maxPlayers).
Hat die Karte zu wenig ebene Fläche, platziert der Generator so viele Startpositionen, wie hineinpassen, und meldet „Only N of M“. Verringere Flat Radius oder Edge Margin, verwende weniger Plateaus oder vergrößere die Karte.

Generieren per Code

Drei Einstiegspunkte decken alle Anwendungsfälle ab:

MethodeVerwendung
RTSMapGenerator.Generate(RTSTerrain, MapGeneratorSettings, RTSToolContext ctx = null)Spielcode. Generiert in die Karte des Terrains, aktualisiert das Terrain und protokolliert Warnungen. Lässt sich nur rückgängig machen, wenn du den ctx eines Aufrufs von RTSEditSession.RunOperation übergibst.
RTSMapGenerator.Generate(RTSEditSession, MapGeneratorSettings)Editorwerkzeuge und Erweiterungen des In-Game-Editors: ein Undo-Schritt namens Generate Map.
RTSMapGenerator.GenerateInto(MapData, settings, tileset, vegetation, decor)Generieren in ein MapData-Objekt, das nicht angezeigt wird (Vorschauen, Stapelgenerierung, Tests). Es gibt keine Benachrichtigung und keinen Undo-Schritt. Gibt einen MapGenerationReport zurück.
using RTSTerrainStudio;
using UnityEngine;

// Generating maps from game code, e.g. for a "Random map" button in a skirmish menu.
public class GeneratorExample : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;
    [SerializeField] RTSMapGeneratorPreset preset;   // optional: settings saved as an asset

    // Generates a random 4-player layout into the current map (the map size is kept). No undo.
    public void GenerateRandomMap(int seed)
    {
        // Start from a saved preset or from a style preset, then change what you need.
        MapGeneratorSettings settings = preset != null
            ? preset.settings.Clone()                                  // never modify the asset itself
            : MapGeneratorSettings.CreatePreset(TerrainStylePreset.Highlands);
        settings.seed = seed;                       // same seed + settings + map size = same map
        settings.symmetry = SymmetryMode.Rotate4;   // needs a square map, otherwise Rotate2 is used
        settings.startLocations.count = 4;          // a multiple of the symmetry copies is fairest
        settings.water.lakeCoverage = 0.1f;
        settings.Validate();                        // clamps every value into its valid range

        RTSMapGenerator.Generate(terrain, settings);   // generates, refreshes the terrain, logs warnings
    }

    // Generates a map in memory without showing it (previews, batch generation, tests).
    public static MapData GenerateOffscreen(RTSTerrain template, int seed)
    {
        MapCreationSettings creation = MapCreationSettings.Default;   // 128 x 128 cells of 2 m
        creation.width = 192;
        creation.height = 192;
        MapData map = MapData.Create(creation);

        MapGeneratorSettings settings = MapGeneratorSettings.CreatePreset(TerrainStylePreset.Islands);
        settings.seed = seed;
        // Pass the tileset and libraries so painting, vegetation and decor can be generated.
        MapGenerationReport report = RTSMapGenerator.GenerateInto(map, settings,
            template.Tileset, template.VegetationLibrary, template.DecorLibrary);
        foreach (string warning in report.warnings) Debug.LogWarning(warning);
        Debug.Log(report);   // plateaus, ramps, start locations, trees, decor and time

        // You own the map: Dispose() it, or give it to a terrain with terrain.SetMap(map).
        return map;
    }
}

MapGenerationReport enthält symmetryUsed, plateauLevels, ramps, startLocations, detailLayers, trees, decor, milliseconds und die Liste warnings; ToString() liefert eine einzeilige Zusammenfassung. RTSMapGenerator.GeneratedLayers enthält alle MapLayers-Flags, die der Generator schreibt. Genau diese Flags übergibst du an RunOperation, wenn du GenerateInto selbst kapselst.

Heightmap-Import und -Export

Das Panel Heightmap im Tab Map importiert und exportiert die Terrain-Höhe als 16-Bit-PNG oder 16-Bit-RAW. Damit übernimmst du Terrain aus World Machine, Gaea, Photoshop oder einer Quelle realer Höhendaten, oder du gibst die Karte an ein solches Werkzeug weiter und holst sie anschließend zurück.

Wie Heightmaps auf das Terrain abgebildet werden

  • Eine Heightmap deckt das Vertex-Raster ab: Eine Karte mit 256 × 256 Zellen hat 257 × 257 Vertices, eine ideale Heightmap hat also 257 × 257 Pixel. Bilder jeder anderen Größe werden bilinear neu abgetastet.
  • Pixelwerte werden nach height = offset + value / 65535 × scale umgerechnet. 8-Bit-Bilder funktionieren ebenfalls, sie haben nur weniger Abstufungen.
  • Der Standardwert von Height Scale deckt alle 16 Klippenstufen ab: 16 × Cliff Level Height (32 m bei der Standard-Stufenhöhe von 2 m). HeightmapIO.DefaultHeightScale(map) liefert diesen Wert.
  • Zeile 0 einer RAW-Datei und die unterste Zeile eines PNG entsprechen Z = 0 (der ersten Vertex-Reihe). Erscheint deine Karte gespiegelt, aktiviere Flip Vertically.
  • Jeder Import ist ein Undo-Schritt namens Import Heightmap.

Importieren

  1. Öffne den Tab Map und klappe das Panel Heightmap auf.
  2. Wähle unter Mode den Importmodus (Tabelle unten). Für eine typische externe Heightmap verwende Cliff Levels And Fine Heights; um Hügel auf vorhandene Plateaus zu setzen, verwende Fine Heights Only.
  3. Belasse Height Scale auf 0 (automatisch), es sei denn, du weißt, wie vielen Metern dein Bild entspricht.
  4. Klicke auf Import PNG… (PNG, JPG oder EXR; Graustufen mit 8 oder 16 Bit, verwendet wird der Rotkanal) oder auf Import RAW… (.raw / .r16, 16 Bit).
  5. Prüfe das Ergebnis. Drücke Ctrl+Z, um den Import rückgängig zu machen, und probiere einen anderen Modus oder eine andere Skalierung.
ImportmodusSchreibtBeschreibung
Fine Heights OnlyHöhenNur die Feinhöhe (Feinhöhe = Wert × Skalierung + Offset). Die Klippenstufen bleiben erhalten, und ihre Höhe kommt hinzu; das Bild setzt also Hügel auf die vorhandenen Plateaus.
Cliff Levels And Fine HeightsHöhen, KlippenstufenDas Bild ist die absolute Bodenhöhe. Es wird in Klippenstufen (auf die nächste Stufe gerundet, isolierte Vertices bereinigt) und Feinhöhen aufgeteilt, sodass die Bodenhöhe exakt dem Bild entspricht. Am besten geeignet für terrassierte Heightmaps; sanfte Hänge, die über eine Stufengrenze verlaufen, werden zu kleinen Klippen.
Absolute Heights Keep LevelsHöhenDas Bild ist die absolute Bodenhöhe, aber die vorhandenen Klippenstufen bleiben erhalten (Feinhöhe = Höhe − Stufe × Stufenhöhe). Damit importierst du einen in einem externen Werkzeug bearbeiteten Export mit der Quelle Ground Height erneut, ohne die Klippen zu verändern.
ImporteinstellungStandardBeschreibung
Modesiehe obenWie das Bild in Stufen und Höhen umgewandelt wird.
Height Scale0 (automatisch)Meter, die dem vollen 16-Bit-Bereich entsprechen. 0 = 16 × Stufenhöhe.
Height Offset0Meter, die zu jeder importierten Höhe addiert werden.
Flip VerticallyausSpiegelt das Bild entlang Z (aktiviere die Option, wenn die Karte gespiegelt erscheint).
RAW Width / RAW Height0Werte pro Zeile und Anzahl der Zeilen von RAW-Dateien. 0 = aus der Dateigröße schätzen (nur bei quadratischen Dateien).
RAW Little EndiananByte-Reihenfolge von RAW-Dateien. Windows und die meisten Werkzeuge schreiben Little Endian.
Das sauberste Ergebnis erhältst du, wenn du die Heightmap im externen Werkzeug mit genau Zellen + 1 Pixeln pro Seite exportierst und sie auf die Stufenhöhe terrassierst. Glätte verbleibende Unebenheiten mit dem Werkzeug Smooth und füge Rampen dort hinzu, wo Einheiten nach oben gelangen sollen.

Exportieren

Exporteinstellung / SchaltflächeStandardBeschreibung
SourceGround HeightGround Height: Klippenstufen plus Feinhöhe (absolut). Fine Heights: nur die Feinhöhe über den Stufen.
Height Scale0 (automatisch)Meter, die dem Wert 65535 entsprechen.
Height Offset0Höhe, die als 0 geschrieben wird.
RAW Little EndiananByte-Reihenfolge exportierter RAW-Dateien.
Export PNG…16-Bit-Graustufen-PNG.
Export RAW…16-Bit-RAW-Datei.
Export Splat PNG…Ein Bild der Bodentexturierung (Farben der dominanten Schicht) für die Dokumentation oder externe Werkzeuge.

Export und Reimport: Exportiere mit Ground Height, bearbeite das Bild extern und importiere es mit Absolute Heights Keep Levels und derselben Skalierung sowie demselben Offset erneut.

Heightmaps per Code

HeightmapIO bietet ImportPng, ImportRaw16 und ImportHeights (normalisierte Werte als float[] oder NativeArray<float>, zum Beispiel aus deinem eigenen Rauschen) für Terrains, ApplyHeights für ein MapData ohne Terrain, ExportPng16, ExportRaw16 und ExportSplatPng (Modi DominantLayerColor und SplatWeights) sowie TryGuessRawSize und GetImportLayers(mode) für die Undo-Aufzeichnung. Jeder Exporter hat eine Überladung mit byte[] und eine mit Dateipfad.

using RTSTerrainStudio;
using UnityEngine;

// Heightmap import and export from code.
public static class HeightmapExample
{
    public static bool Import(RTSTerrain terrain, string pngPath)
    {
        // Imports need a height scale > 0. DefaultHeightScale = 16 cliff levels (all levels + headroom).
        float scale = HeightmapIO.DefaultHeightScale(terrain.Map);
        var options = new HeightmapImportOptions { heightOffset = 0f, flipZ = false };

        // The image is resampled to the vertex grid (cells + 1 per side) and the terrain is refreshed.
        // Pass an RTSToolContext (inside RTSEditSession.RunOperation) as the 5th argument to make it undoable.
        return HeightmapIO.ImportPng(terrain, pngPath, HeightmapImportMode.CliffLevelsAndFineHeights, scale,
            null, options);
    }

    public static bool Export(RTSTerrain terrain, string pngPath)
    {
        // 16-bit grayscale PNG of the absolute ground height. Scale 0 = DefaultHeightScale.
        return HeightmapIO.ExportPng16(terrain.Map, pngPath, 0f, 0f, HeightmapExportSource.GroundHeight);
    }
}
Anders als das Fenster verwenden die Importmethoden den übergebenen heightScale unverändert. Übergib immer einen Wert größer als 0, zum Beispiel HeightmapIO.DefaultHeightScale(map). Bei Exporten steht 0 für automatisch.

Backen der Minikarte

RTSMinimapBaker rendert eine Draufsicht der Karte: Bodenfarben, Reliefschattierung, Klippen, nach Tiefe eingefärbtes Wasser, Bäume, Dekor, Startpositionen und Regionen. Überall im Paket wird derselbe Baker verwendet:

  • das Panel Minimap im Tab Map (Vorschau und PNG-Export),
  • die Vorschau von .rtsmap-Assets im Project-Fenster und im Inspector sowie die Vorschau des New Map Wizard,
  • der Kartenbrowser des In-Game-Editors (neben jeder Karte wird ein kleines Vorschaubild gespeichert, gesteuert über die Einstellung Write Preview Images von RTSRuntimeEditor),
  • das Beispiel RTSMinimapUI (Minikarte im Spiel mit Nebel des Krieges, Einheitenpunkten, Kamerarahmen, Klick zum Verschieben der Kamera und Rechtsklick zum Bewegen von Einheiten). Ändert sich die Karte, backt es die Minikarte automatisch und gedrosselt neu.

Backen im Editor

  1. Öffne den Tab Map und klappe das Panel Minimap auf.
  2. Wähle unter Size eine Größe (128, 256, 512 oder 1024 px für die längere Seite; nicht quadratische Karten behalten ihr Seitenverhältnis).
  3. Passe bei Bedarf die Bake Options an (Tabelle unten).
  4. Klicke auf Bake Preview, um das Ergebnis im Panel zu sehen, oder auf Save PNG…, um es in eine Datei zu schreiben, zum Beispiel für einen Ladebildschirm oder ein Kartenauswahlmenü.

Backoptionen (MinimapBakeOptions)

EinstellungStandardBeschreibung
Ground ModeBlendedLayersBlendedLayers mischt die Farben aller gemalten Schichten; DominantLayer verwendet pro Pixel die stärkste Schicht (schärfer, im Stil klassischer RTS-Spiele). Die Schichtfarben stammen aus dem Tileset (die Minikartenfarbe der Schicht oder die Durchschnittsfarbe ihrer Textur).
Level Brightness0.05Helligkeitsänderung pro Klippenstufe relativ zu Stufe 2 (höhere Plateaus wirken heller).
Hill Shading / Strengthan / 1Verdunkelt Hänge, die vom Licht abgewandt sind.
Light Direction(−0.55, 0.75, 0.45)Richtung zum Licht in kartenlokalen Koordinaten (x = Osten, y = oben, z = Norden).
Use Map Sun DirectionausVerwendet die Sonnenrichtung aus der Umgebung der Karte statt Light Direction.
Draw Cliffs / Cliff Color / Cliff Opacityan / schwarz / 0.8Färbt Klippenzellen ein. Schwarz = Klippenfarbe des Tilesets verwenden.
Draw WateranWasser, nach Tiefe eingefärbt (Wasserprofil des Tilesets).
Draw Trees / Tree Color / Tree Dot Sizean / dunkelgrün / 0.75 pxBäume als Punkte (Werte unter 1 zeichnen einzelne Pixel).
Draw Decor / Decor Color / Decor Dot Sizeaus / braun / 0.75 pxDekorobjekte als Punkte.
Draw Start Locations / Start Location Sizean / 4 pxMarker in Spielerfarbe.
Scale MarkersanSkaliert die Markergrößen mit der Texturgröße (Referenzgröße 256).
Player Colors16 klassische RTS-SpielerfarbenFarbe pro Spielerindex. Für fehlende Einträge gilt MinimapBakeOptions.DefaultPlayerColors.
Draw RegionsausZeichnet die Umrisse der Regionen in ihrer Regionsfarbe.

Minikarten per Code

RTSMinimapBaker.Bake(terrain, size, options) gibt eine neue, lesbare Texture2D im sRGB-Format zurück (zerstören musst du sie selbst). Update(existing, terrain, size, options) backt erneut in eine vorhandene Textur und erstellt nur dann eine neue, wenn sich die alte nicht wiederverwenden lässt. BakePng(terrain, size) gibt direkt PNG-Bytes zurück, und die Überladungen für MapData backen Karten, die gerade nicht angezeigt werden.

using RTSTerrainStudio;
using UnityEngine;
using UnityEngine.UIElements;

// Keeps a minimap image up to date in a UI Toolkit Image named "minimap".
public class MinimapExample : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;
    [SerializeField] UIDocument document;
    [SerializeField] int size = 256;      // longer side in pixels
    Texture2D minimap;

    void OnEnable()
    {
        if (terrain == null) return;
        terrain.MapLoaded += OnMapUpdated;
        terrain.ChangesProcessed += OnChangesProcessed;
        if (terrain.Map != null) Refresh();
    }

    void OnDisable()
    {
        if (terrain == null) return;
        terrain.MapLoaded -= OnMapUpdated;
        terrain.ChangesProcessed -= OnChangesProcessed;
    }

    void OnMapUpdated(RTSTerrain t) => Refresh();
    // Called after every processed edit. While someone paints, throttle this (the RTSMinimapUI sample
    // waits a moment after the last change and limits the bakes per second).
    void OnChangesProcessed(RTSTerrain t, System.Collections.Generic.IReadOnlyList<DirtyRegion> regions) => Refresh();

    void Refresh()
    {
        var options = new MinimapBakeOptions { drawDecor = true, hillShadingStrength = 1.2f };
        // Update reuses the texture when it can. When it returns a new texture, destroy the old one.
        Texture2D result = RTSMinimapBaker.Update(minimap, terrain, size, options);
        if (minimap != null && result != minimap) Destroy(minimap);
        minimap = result;
        Image target = document != null ? document.rootVisualElement?.Q<Image>("minimap") : null;
        if (target != null) target.image = minimap;
    }

    void OnDestroy()
    {
        if (minimap != null) Destroy(minimap);
    }
}

Kartenvalidierung

Der Kartenvalidator (MapValidator) prüft eine Karte auf Probleme, die leicht zu übersehen sind: fehlende Texturen, unerreichbare Startpositionen, defekte Objektverweise und Performance-Grenzen. Führe ihn aus, bevor du eine Karte auslieferst oder für Spieler veröffentlichst.

  1. Öffne den Tab Map und klappe das Panel Validation auf.
  2. Klicke auf Validate Map. Die Zusammenfassung zeigt die Anzahl der Fehler, Warnungen und Infos oder die Meldung No problems found.
  3. Klicke auf ein Problem, um seine Position in der Scene View anzuzeigen (bei Problemen, die zu einem Ort gehören, etwa zu einer Startposition).
  4. Behebe die Probleme und validiere erneut.
Pathing-basierte Prüfungen (begehbare Startpositionen, Erreichbarkeit, begehbarer Kartenrand) benötigen die Pathing-Karte des Terrains. Ohne sie meldet der Validator mit einer Info, dass diese Prüfungen übersprungen wurden.
Dies ist der Validator für Karten. Der Validator für das Projekt (Tools ▸ RTS Terrain Studio ▸ Validate Project) prüft URP, Shader, Eingabeverarbeitung und Pakete; siehe Projekteinrichtung.

Was geprüft wird

BereichPrüfungSchweregrad
TilesetKein Tileset zugewiesen (das Terrain wird mit neutralen Ersatzfarben gerendert)Warning
Das Tileset hat keine BodenschichtenWarning
Die Karte speichert keine Tileset-ID (wird beim Speichern mit einem Tileset automatisch gesetzt)Info
Die Karte wurde für ein anderes Tileset erstellt als das zugewieseneWarning
GeometrieVertices mit ungültigen Höhen (NaN oder unendlich)Error
Klippenzellen verwenden einen Klippentyp, den das Tileset nicht definiertWarning
Die Karte hat Klippen, aber das Tileset hat keine KlippenschichtenWarning
BodentexturierungSplat-Texel, deren Gewichtungen nicht zusammen 255 ergeben (beschädigte Texturierung)Warning
Eine gemalte Bodenschicht ist im Tileset nicht vorhandenWarning
Eine gemalte Bodenschicht hat keine Albedo-TexturInfo
StartpositionenDie Karte hat keine StartpositionenError
Weniger Startpositionen als Max PlayersWarning
Ein Spieler hat mehr als eine StartpositionWarning
Eine Startposition liegt außerhalb der KarteError
Eine Startposition liegt auf einer KlippenkanteWarning
Eine Startposition ist von Tiefwasser bedeckt / liegt in FlachwasserError / Info
Zwei Startpositionen liegen sehr nah beieinander (näher als Min Start Distance Cells)Warning
Eine Startposition ist nicht begehbarError
ErreichbarkeitEin Spieler kann nicht zu einem anderen Spieler laufen (Flächenfüllung über die Pathing-Karte, beginnend auf der eigenen Klippenstufe der Startposition)Error
Ein großer Teil des Kartenrands ist begehbar (erwäge einen nicht begehbaren Rand)Info
Bäume, Dekor, DetailsBäume, Dekor oder Detailschichten sind vorhanden, aber es ist keine Vegetations- bzw. Dekorbibliothek zugewiesenWarning
Objekte verweisen auf einen Prototyp-Index, der nicht in der Palette der Karte enthalten istError
Objekte oder Detailschichten verwenden einen Prototyp, der in der Bibliothek fehltWarning
Ein Prototyp hat kein Prefab (seine Objekte sind unsichtbar)Warning
Objekte außerhalb der Karte, doppelte Instanz-IDs oder ein Zähler für Instanz-IDs, der kleiner ist als vorhandene IDsWarning
RegionenEine Region hat keine Fläche; mehrere Regionen haben denselben Namen (Spielcode, der Regionen über ihren Namen sucht, findet womöglich die falsche)Info
PerformanceZu viele Bäume, Dekorobjekte oder DetailschichtenWarning
Zu viele Bäume oder Dekorobjekte im Spawn-Modus GameObjectWarning
Große Karte (Speicherverbrauch und Build-Zeiten)Info

Schwellenwerte (MapValidationSettings)

EinstellungStandardBeschreibung
maxTrees20000Übersteigt die Anzahl der Bäume diesen Wert, wird eine Performance-Warnung angezeigt.
maxGameObjectTrees1500Übersteigt die Anzahl der Bäume im Spawn-Modus GameObject diesen Wert, wird eine Warnung angezeigt.
maxDecor6000Übersteigt die Anzahl der Dekorobjekte diesen Wert, wird eine Warnung angezeigt.
maxGameObjectDecor2000Übersteigt die Anzahl der Dekorobjekte im Spawn-Modus GameObject diesen Wert, wird eine Warnung angezeigt.
maxDetailLayers8Übersteigt die Anzahl der Detailschichten diesen Wert, wird eine Warnung angezeigt.
largeMapSize512Karten, die größer als dieser Wert sind (Zellen pro Seite), erhalten eine Info zu Speicherverbrauch und Build-Zeiten.
minStartDistanceCells12Startpositionen, die näher als dieser Wert beieinanderliegen, erzeugen eine Warnung.
walkableEdgeFraction0.1Übersteigt der Anteil begehbarer Zellen im äußeren Zellring diesen Wert, wird die Info zum begehbaren Kartenrand angezeigt.
checkReachabilitytruePrüft, ob alle Startpositionen einander erreichen können.

Validieren per Code

MapValidator.Validate(terrain) gibt eine List<MapIssue> zurück. Jedes MapIssue enthält einen Schweregrad Severity (Info, Warning, Error), eine Meldung Message, optional eine WorldPosition und die betroffenen Layers. Überladungen akzeptieren MapValidationSettings oder direkt ein MapData-Objekt samt Tileset, Bibliotheken und Pathing-Karte.

using RTSTerrainStudio;
using UnityEngine;

// Checks a map before it is published (for example in a "Publish map" button of your game).
public static class ValidationExample
{
    public static bool CanPublish(RTSTerrain terrain)
    {
        // Optional thresholds; null uses the defaults.
        var settings = new MapValidationSettings { maxTrees = 10000, checkReachability = true };
        var issues = MapValidator.Validate(terrain, settings);

        foreach (MapIssue issue in issues)
        {
            // WorldPosition is set for issues that belong to a place on the map (e.g. a start location).
            string where = issue.WorldPosition.HasValue ? " at " + issue.WorldPosition.Value : "";
            switch (issue.Severity)
            {
                case MapIssueSeverity.Error: Debug.LogError(issue.Message + where, terrain); break;
                case MapIssueSeverity.Warning: Debug.LogWarning(issue.Message + where, terrain); break;
                default: Debug.Log(issue.Message + where, terrain); break;
            }
        }
        return MapValidator.Count(issues, MapIssueSeverity.Error) == 0;
    }
}

Integrationen

RTS Terrain Studio funktioniert eigenständig: Es bringt einen eingebauten Burst-Pathfinder, einen eigenen Nebel des Krieges und Beispiel-Gameplay mit. Drei optionale Integrationen verbinden es mit beliebten Paketen. Sie gehören zum Paket (Ordner Integrations) und schalten sich selbst ein, sobald das benötigte Paket installiert ist.

IntegrationVoraussetzungWas du bekommstMenü für die Demoszene
MirrorMirror 96 oder neuerOnline-Spiel auf jeder Karte: Karten-Streaming zu den Clients, serverautoritative Einheiten, Nebel des Krieges pro Team, Interest Management gegen Maphacks.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene
A* Pathfinding ProjectA* Pathfinding Project Pro 5.4.4 oder neuer, unter 6.0Grid Graphs, die aus der Pathing-Karte erzeugt und inkrementell aktualisiert werden; Einheiten bewegen sich mit AIPath und lokaler Kollisionsvermeidung per RVO.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene
Unity NavMeshAI Navigation (com.unity.ai.navigation) 2.0 oder neuerEin NavMesh, das aus den Terrain-Chunks und der Pathing-Karte gebaut und während der Kartenbearbeitung aktuell gehalten wird; Einheiten folgen NavMesh-Pfaden oder bewegen sich per NavMeshAgent.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Unity NavMesh ▸ Create Demo Scene

Das Fenster Welcome (Tools ▸ RTS Terrain Studio ▸ Welcome) zeigt den Status jeder Integration: Active (kompiliert und bereit), Installed (die Abhängigkeit wurde gefunden, aber die Integration ist nicht kompiliert, meist wegen einer nicht passenden Version) oder Not installed.

Alle Demoszenen benötigen die Demo-Inhalte. Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content einmal aus, bevor du eine Integrationsdemo erstellst.

So funktionieren die optionalen Assemblies

Jede Integration liegt in eigenen Assembly Definitions. Unity kompiliert eine Assembly nur, wenn alle ihre Define Constraints erfüllt sind. Die Symbole stammen aus zwei Quellen:

  • Version Defines (versionDefines in der .asmdef): Die Assembly definiert für sich selbst ein Symbol, wenn ein Paket mit passender Version installiert ist. A* und NavMesh nutzen das und reagieren so automatisch darauf, dass das Paket installiert oder entfernt wird.
  • Scripting Define Symbols der Player Settings: Mirror wird meist als Asset-Store-Ordner importiert, nicht als Paket. Deshalb verwendet seine Assembly das Symbol MIRROR, das Mirror selbst zu den Player Settings hinzufügt.
AssemblyVersion DefineDefine Constraints
RTSTerrainStudio.Integrations.Mirror und .Mirror.EditorkeineMIRROR
RTSTerrainStudio.Integrations.Mirror.DefineCleanup.Editorkeinekeine (wird immer kompiliert; entfernt veraltete Mirror-Symbole, siehe Integrationen entfernen)
RTSTerrainStudio.Integrations.AStar und .AStar.Editorcom.arongranberg.astar in [5.4.4,6.0.0) → RTS_ASTARRTS_ASTAR, !RTS_ASTAR_DISABLED
RTSTerrainStudio.Integrations.NavMesh und .NavMesh.Editorcom.unity.ai.navigation 2.0.0 oder neuer → RTS_AI_NAVIGATIONRTS_AI_NAVIGATION

Das Ergebnis: Ohne die Abhängigkeit wird die Integration schlicht übersprungen, nichts geht kaputt, und die Einheiten verwenden den eingebauten Pathfinder. Für A* und NavMesh musst du keine Defines von Hand verwalten.

Pfad-Provider. Die Beispieleinheit (RTSUnit) fragt einen IRTSUnitPathProvider nach ihren Pfaden. Standardmäßig wird der eingebaute Provider verwendet; die Provider für A* und NavMesh registrieren sich als RTSUnitPathProviderRegistry.Default, wenn Make Default eingeschaltet ist, oder du weist einzelnen Einheiten im Feld Path Provider einen Provider zu. Deine eigene Wegfindungslösung lässt sich auf dieselbe Weise einbinden, indem sie IRTSUnitPathProvider implementiert.
Die Integrations-Assemblies referenzieren RTSTerrainStudio.Samples (für RTSUnit sowie die Auswahl- und Befehls-Controller). Behalte den Ordner Samples/Scripts, solange du eine Integration verwendest.

Multiplayer mit Mirror

Spiele Karten von RTS Terrain Studio online mit Mirror (Version 96 oder neuer):

  • Karten-Streaming: Der Server sendet seine Karte an jeden Client, der beitritt. So können Spieler Partien auf Karten beitreten, die sie selbst nicht haben, einschließlich Karten aus dem In-Game-Editor. Heruntergeladene Karten werden zwischengespeichert, sodass die nächste Partie auf derselben Karte sofort startet.
  • Serverautoritative Einheiten: Wegfindung und Bewegung laufen nur auf dem Server. Clients senden Befehle, und der Server prüft, ob die Einheiten dem Spieler gehören.
  • Nebel des Krieges pro Team auf jedem Client.
  • Interest Management gegen Maphacks: Einheiten, die der Nebel des Krieges verbirgt, werden nie an gegnerische Clients gesendet.

Schnellstart: die Demoszene

  1. Importiere Mirror (Asset Store oder GitHub). Mirror fügt das Scripting Define Symbol MIRROR hinzu, und die Integration wird kompiliert.
  2. Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus (einmal pro Projekt).
  3. Führe Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene aus. Der Befehl erstellt in Integrations/Mirror/Demo: die Szene RTS Mirror Demo.unity, die Prefabs Prefabs/RTS Network Unit.prefab (eine Platzhalter-Kapsel mit RTSUnit, RTSNetworkUnit, NetworkIdentity und NetworkTransformReliable) und Prefabs/RTS Network Player.prefab, die Einheitenmaterialien und – wenn das Projekt keine Karte mit mindestens zwei Startpositionen hat – eine generierte Karte für 4 Spieler namens RTS Mirror Demo Map.rtsmap.
  4. Wenn du gefragt wirst, lasse den Builder das Renderer Feature für den Nebel des Krieges hinzufügen und die Szene an die erste Stelle der Szenenliste des Builds setzen.
  5. Drücke Play und klicke im Mirror-HUD auf Host (Server + Client). Du spielst als Team 1 (rot), und deine Einheiten spawnen an deiner Startposition.
  6. Starte eine zweite Instanz (siehe unten), klicke auf Client und behalte die Adresse localhost bei. Der Client lädt die Karte herunter (dabei wird ein Fortschrittsbalken angezeigt), tritt als Team 2 (blau) bei und erhält eigene Einheiten. Gegnerische Einheiten erscheinen erst, wenn deine Einheiten sie sehen können.

Host und Client testen

MethodeVorgehen
Player-Build (am zuverlässigsten)Öffne File ▸ Build Profiles. Stelle sicher, dass RTS Mirror Demo die erste Szene ist, und erstelle dann den Build. Starte den Build zweimal oder den Build und den Editor. Klicke in der einen Instanz auf Host, in der anderen auf Client.
ParrelSync (kostenlos, GitHub)Öffne ParrelSync ▸ Clones Manager, erstelle einen Klon und öffne ihn in einem neuen Editor. Drücke in beiden Editoren Play und klicke dann im Original auf Host, im Klon auf Client. Der Klon teilt sich den Assets-Ordner mit dem Original. Führe den Demo-Builder deshalb nur im Original aus.
Multiplayer Play Mode (com.unity.multiplayer.playmode)Öffne Window ▸ Multiplayer ▸ Multiplayer Play Mode. Aktiviere einen virtuellen Spieler und drücke Play. Klicke im Haupteditor auf Host und im virtuellen Spieler auf Client.
Über ein NetzwerkGib die IP-Adresse des Hosts in das Adressfeld des Clients ein. KCP verwendet UDP-Port 7777; öffne ihn in der Firewall des Hosts.
Dedizierter ServerErstelle den Build mit einem Build-Profil Dedicated Server. Der Network Manager verwendet Headless Start Mode = Auto Start Server, daher startet der Server von selbst. Clients verbinden sich über Client.

Jeder Rechner speichert heruntergeladene Karten in Application.persistentDataPath/MapCache. Verwende Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Clear Map Cache oder … ▸ Open Map Cache Folder, um einen frischen Download zu testen. Der Editor und ein Windows-Player desselben Projekts teilen sich diesen Ordner.

Willst du in der Demo zusehen, wie die Karte gestreamt wird? Host und Client starten beide mit der Demokarte, und der Client erkennt, dass er bereits genau die Karte des Servers anzeigt – deshalb wird nichts heruntergeladen. Schalte am RTSNetworkMapSync des Clients Reuse Loaded Map und Use Cache aus, oder ändere die Karte auf dem Host, bevor du hostest (zum Beispiel mit dem In-Game-Editor).

Multiplayer zu deiner eigenen Szene hinzufügen

KomponenteHinzufügen zuZweck
RTSNetworkManagerNetwork-Manager-ObjektWeist Teams zu, wartet, bis der Client die Karte hat, und spawnt dann den Spieler und seine Starteinheiten an seiner Startposition. Despawnt die Einheiten, wenn ein Spieler die Partie verlässt.
RTSNetworkMapSyncDasselbe Objekt (wird automatisch hinzugefügt)Streamt die Karte in Chunks zu den Clients, mit Prüfsumme, Cache und Fortschritts-Events.
RTSFogOfWarInterestManagementDasselbe ObjektInterest Management von Mirror: Jeder Client erhält nur die Einheiten, die sein Team sehen kann.
RTSNetworkFogOfWarDasselbe ObjektKonfiguriert den RTSFogOfWarManager: Der Server berechnet alle Teams, ein Client nur sein eigenes Team.
RTSNetworkStatusUIDasselbe Objekt (optional)Fortschrittsbalken für den Download und „You are Player N“ (IMGUI).
RTSNetworkPlayerSpieler-PrefabSyncVar für das Team. Richtet den lokalen Spieler ein (Nebel-Team, Auswahl, Kamera) und sendet Befehle als Commands.
RTSNetworkUnitEinheiten-PrefabsSyncVar für das Team. Die Bewegung läuft nur auf dem Server; Clients zeigen die Einheit nur an.
RTSNetworkCommandRelayWird auf Clients automatisch hinzugefügtSendet die Befehle des Beispiel-Controllers RTSCommandController an den Server.
  1. Baue eine normale Szene mit RTS Terrain Studio auf: ein RTSTerrain mit einer Karte, die Startpositionen hat, ein RTSFogOfWarManager, ein RTSCameraController, ein RTSSelectionManager und ein RTSCommandController. Füge kein RTSDemoGame hinzu: Die Einheiten spawnt stattdessen der Network Manager.
  2. Erstelle ein leeres Objekt und füge KcpTransport, RTSNetworkManager (er fügt RTSNetworkMapSync hinzu), RTSFogOfWarInterestManagement, RTSNetworkFogOfWar und bei Bedarf NetworkManagerHUD hinzu. Weise den Transport dem Feld Transport des Managers zu.
  3. Erstelle ein Spieler-Prefab mit NetworkIdentity und RTSNetworkPlayer und weise es dem Feld Player Prefab zu.
  4. Erstelle ein Einheiten-Prefab mit NetworkIdentity, RTSUnit, RTSNetworkUnit und NetworkTransformReliable (Sync Direction Server To Client, Sync Scale aus). Weise es dem Feld Unit Prefab zu; es wird automatisch zu den spawnbaren Prefabs hinzugefügt. Jedes andere vernetzte Prefab, das du spawnst, muss in Registered Spawnable Prefabs eingetragen sein.
  5. Lege Units Per Player fest (Standard 5) und bei Bedarf Max Players Override (0 = so viele Spieler, wie die Karte Startpositionen hat).
  6. Trage KI- oder andere Logikkomponenten der Einheiten-Prefabs (zum Beispiel RTSWanderAI oder deine eigenen KI-Skripte) in Server Only Behaviours von RTSNetworkUnit ein, damit Clients sie nicht ausführen.
EinstellungStandardBeschreibung
RTSNetworkManager
TerrainleerTerrain, auf dem gespielt wird. Leer = RTSTerrain.Main.
Unit PrefableerStarteinheit jedes Spielers (benötigt NetworkIdentity, RTSUnit und RTSNetworkUnit).
Units Per Player5Anzahl der Starteinheiten (0–64).
Max Players Override0Weniger Spieler, als die Karte Plätze hat (0 = alle Startpositionen).
Unit Spacing2 mAbstand zwischen den Starteinheiten.
Spawn Offset4 mAbstand der ersten Reihe der Starteinheiten von der Startposition, in Richtung Kartenmitte.
Despawn Units On DisconnectanZerstört die Einheiten eines Spielers, der die Partie verlässt.
Reserve Team Of Kept UnitsanWenn die Einheiten bleiben: Hält den Team-Platz reserviert, solange noch eine von ihnen lebt.
Reject Disconnect Delay0.5 sSekunden zwischen der Ablehnungsnachricht und dem Trennen der Verbindung, damit die Nachricht ankommt.
RTSNetworkUnit
Server Only BehavioursleerBehaviours, die Clients deaktivieren, weil der Server die Logik der Einheit ausführt.
RTSNetworkPlayer
Jump Camera To StartanBewegt die RTS-Kamera zu Spielbeginn zur Startposition.
Create Command RelayanReine Clients: Fügt ein RTSNetworkCommandRelay hinzu, wenn die Szene keines hat, damit der Beispiel-Befehls-Controller seine Befehle an den Server sendet.
Max Units Per Command, Max Commands Per Second256, 20Serverseitige Validierung von Befehlen (siehe Serverautoritative Einheiten).
Formation (Server)-Formationseinstellungen, die der Server verwendet; halte sie mit dem RTSCommandController synchron.

Spielbeitritt und Karten-Streaming

client connects
  server: team = first free player slot of the map (rejected when the map is full)
  server -> client: map header (name, size, CRC-32)
  client: same map already loaded, or in the cache?  yes -> "no data needed"
                                                     no  -> download the chunks
  client: check the CRC-32 -> terrain.SetMap(RTSMapSerializer.Deserialize(bytes)) -> "map ready"
  client: NetworkClient.Ready() + AddPlayer()
  server: spawns the RTSNetworkPlayer and the starting units (owned by the connection)

Der Server ignoriert Ready und AddPlayer eines Clients, bis dieser die richtige Kartenprüfsumme gemeldet hat. So erhält ein Client nie Weltobjekte für eine Karte, die er nicht hat. Im Host-Modus verwendet der lokale Client das Terrain des Servers und tritt sofort bei.

  • Als Nutzdaten wird die .rtsmap-Datei übertragen (bereits mit Deflate komprimiert). Eine 128 × 128 große Karte mit Vegetation umfasst meist einige Hundert KB.
  • Wenn die Karte des Servers seit dem Laden aus ihrem Asset oder ihrer Datei unverändert ist, werden die ursprünglichen Dateibytes gesendet. Sie sind in jeder Sitzung identisch, daher funktioniert der Client-Cache sitzungsübergreifend. Eine bearbeitete oder generierte Karte wird bei Bedarf serialisiert.
  • Der Server akzeptiert nur die Bestätigung des ältesten unbestätigten Chunks, sodass ein manipulierter Client nicht die ganze Karte auf einmal abrufen kann.
  • Szenenwechsel (ServerChangeScene oder die Online-Szene von Mirror) senden die Karte der neuen Szene erneut an jeden Spieler; Clients treten der neuen Szene erst bei, wenn sie diese Karte haben.
Einstellung von RTSNetworkMapSyncStandardBeschreibung
TerrainleerTerrain, dessen Karte gesendet (Server) bzw. ersetzt (Client) wird. Leer = RTSTerrain.Main.
Chunk Size16 KBBytes pro Chunk-Nachricht; der Wert wird automatisch verringert, damit er in die maximale Nachrichtengröße des Transports passt.
Chunks In Flight8Unbestätigte Chunks auf dem Weg zu einem Client. Höhere Werte laden bei hohem Ping schneller herunter; niedrigere Werte lassen mehr Bandbreite für das Spiel.
Transfer Timeout30 sSekunden ohne Fortschritt, nach denen eine Übertragung fehlschlägt.
Load Timeout120 sServer: Zeit, die ein Client nach dem Download brauchen darf, um seine Szene zu laden und die Karte anzuwenden. Clients senden Keep-Alive-Nachrichten, während sie warten.
Disconnect On FailureanServer: Trennt Clients, deren Übertragung fehlgeschlagen ist (ohne die Karte können sie nicht spielen).
Max Map Size MB64Client: Größte Karte, die der Client akzeptiert (schützt vor defekten oder feindseligen Servern).
Reuse Loaded MapanÜberspringt den Download, wenn der Client bereits genau die Karte des Servers anzeigt.
Use Cache / Max Cached Mapsan / 20Speichert heruntergeladene Karten in Application.persistentDataPath/MapCache (die am längsten nicht gespielten werden zuerst entfernt, 0 = unbegrenzt).
Log TransfersanProtokolliert Beginn und Ende jeder Übertragung.

Events für Ladebildschirme: ClientHeaderReceived, ClientProgressChanged(float), ClientMapReady, ClientTransferFailed(string); auf dem Server ServerClientReady und ServerClientFailed. SendMapToAll() sendet eine geänderte Karte (zum Beispiel nach einer Kartenabstimmung in einer Lobby) erneut an jeden verbundenen Client.

Serverautoritative Einheiten

  • Auf dem Server plant RTSUnit Pfade und bewegt sich wie im Einzelspielermodus; NetworkTransformReliable repliziert Position und Rotation.
  • Auf Clients wird RTSNetworkUnit mit HandlesMovement = true zum Pfad-Provider der Einheit, sodass sich die Einheit nie selbst bewegt; sie übernimmt nur, was der Network Transform schreibt.
  • Lokale Befehle gehen an den Server: Rechtsklick (Bewegung in Formation, Shift reiht den Befehl ein), Stop, Hold und Patrol über RTSNetworkCommandRelay sowie jeder andere Aufruf von RTSUnit.MoveTo auf einer Client-Einheit über RTSNetworkUnit. Rufe aus deinem eigenen Code RTSNetworkPlayer.RequestMove, RequestMoveTo, RequestPatrol, RequestStop oder RequestHold auf, oder füge [Command]-Methoden zu einer von RTSNetworkPlayer abgeleiteten Klasse hinzu.
  • Formationen werden auf dem Server mit den Einstellungen Formation (Server) von RTSNetworkPlayer berechnet. Gib ihnen dieselben Werte wie dem RTSCommandController der Szene, falls du dessen Formationseinstellungen geändert hast.
  • Der Server validiert jeden Befehl: Die Einheiten existieren und gehören zum Team des Spielers, das Ziel ist eine endliche Position auf einem Terrain, höchstens Max Units Per Command (Standard 256) Einheiten pro Befehl und höchstens Max Commands Per Second (Standard 20) Befehle pro Sekunde.
  • Spieler, die die Partie verlassen: Mit Despawn Units On Disconnect (Standard) werden ihre Einheiten zerstört, und der Platz wird frei. Ohne diese Einstellung bleiben die Einheiten; Reserve Team Of Kept Units (standardmäßig an) hält den Platz reserviert, solange noch eine von ihnen lebt. Ein abgelehnter Client (zum Beispiel weil das Spiel voll ist) erhält den Grund, bevor die Verbindung getrennt wird (RTSNetworkManager.ClientRejected, LastRejectReason).

Nebel des Krieges und Anti-Maphack

Der Server berechnet den Nebel des Krieges jedes Teams. Alle Rebuild Interval Sekunden prüft RTSFogOfWarInterestManagement jede Einheit mit RTSNetworkUnit:

  • Eigene und verbündete Einheiten (von Teams, die über RTSFogOfWarManager.SetAlliance ihre Sicht teilen) werden immer gesendet.
  • Gegnerische Einheiten werden nur gesendet, solange der Nebel des Krieges sie dem Team des Clients zeigt. Linger Time behält eine Einheit noch einen Moment, nachdem sie die Sicht verlassen hat, damit Einheiten am Rand des Sichtbereichs nicht flackern.
  • Objekte ohne RTSNetworkUnit (Spieler, Manager) werden immer gesendet.

Ein verborgener Gegner wird auf dem Client despawnt, und keine seiner Bewegungen wird gesendet, sodass ein manipulierter Client ihn nicht aufdecken kann. Auf Clients verbirgt RTSFogOfWarHideable außerdem Gegner, die sich zwar in Netzwerkreichweite befinden, aber noch nicht sichtbar sind.

EinstellungStandardBeschreibung
Fog Of WarleerServerseitiger Nebel des Krieges. Leer = RTSFogOfWarManager.Active.
Rebuild Interval0.2 sSekunden zwischen den Sichtbarkeitsprüfungen. Halte den Wert bei 0.1–0.3 s: Jedes Mal, wenn eine Einheit sichtbar wird, erhält der Client eine vollständige Spawn-Nachricht.
Linger Time0.5 sSekunden, die eine Einheit beobachtet bleibt, nachdem sie die Sicht eines Teams verlassen hat.
Share With AlliesanTeams mit geteilter Sicht sehen immer die Einheiten der jeweils anderen.
Show All Without FoganSolange auf dem Server kein Nebel des Krieges läuft, sieht jede Verbindung jede Einheit. Aus: nur die eigenen Einheiten.
Spectators See AllausVerbindungen ohne Team (Zuschauer) sehen jede Einheit.

RTSNetworkFogOfWar bietet Compute All Teams On Server (an, für das Interest Management erforderlich), ein Update-Intervall für den Server (0 = den Wert des Managers beibehalten; niedrigere Werte lassen Einheiten früher erscheinen) und Disable Reveal All Online (an). Eine Sichtbarkeitsprüfung besteht pro Einheit aus einer einzigen Abfrage im Nebelraster, daher kosten Tausende Einheiten deutlich weniger als eine Millisekunde. Der Aufwand des Servers für den Nebel wächst mit der Anzahl der Teams; 2–8 Teams sind günstig.

Scripting mit Mirror

using RTSTerrainStudio.Integrations.Mirror;
using RTSTerrainStudio.Samples;
using UnityEngine;

// Needs Mirror (96 or newer). Sends orders of the local player to the server and shows the map download.
public class MirrorExample : MonoBehaviour
{
    [SerializeField] RTSNetworkMapSync mapSync;   // on the network manager object

    void OnEnable()
    {
        if (mapSync == null) return;
        mapSync.ClientProgressChanged += OnProgress;          // 0-1 while the map downloads
        mapSync.ClientMapReady += OnMapReady;                 // the map is applied to the terrain
        mapSync.ClientTransferFailed += OnTransferFailed;
    }

    void OnDisable()
    {
        if (mapSync == null) return;
        mapSync.ClientProgressChanged -= OnProgress;
        mapSync.ClientMapReady -= OnMapReady;
        mapSync.ClientTransferFailed -= OnTransferFailed;
    }

    void OnProgress(float progress) => Debug.Log($"Downloading map: {progress:P0}");
    void OnMapReady() => Debug.Log("Map ready");
    void OnTransferFailed(string reason) => Debug.LogWarning("Map download failed: " + reason);

    // For your own UI (e.g. a "Move here" button): the server validates the order (ownership,
    // target, unit count, rate limit) before the units move.
    public void SendSelectedTo(Vector3 target, bool queue)
    {
        RTSNetworkPlayer player = RTSNetworkPlayer.Local;
        RTSSelectionManager selection = RTSSelectionManager.Main;
        if (player == null || selection == null) return;
        player.RequestMove(selection.Selection, target, queue);
    }

    // Host only: after changing the map (map vote, in-game editor), send it to every client again.
    public void ResendMap()
    {
        if (mapSync != null) mapSync.SendMapToAll();
    }
}
MemberBeschreibung
RTSNetworkManager (RTSNetworkManager.Instance)
ServerPlayerJoined(conn, team), ServerConnectionRejected(conn)Server: Eine Verbindung hat ein Team erhalten (vor der Kartenübertragung) oder wurde abgelehnt, weil alle Plätze belegt sind.
ServerPlayerSpawned(conn, player), ServerUnitSpawned(conn, unit)Server: Das Spielerobjekt wurde gespawnt (nachdem seine Karte bereit war); eine Einheit wurde gespawnt, auch durch SpawnUnit. Nutze diese Events, um Spielern ihre Startobjekte zu geben.
ServerPlayerLeft(conn, team)Server: Eine Verbindung hat das Spiel verlassen; das Event wird ausgelöst, bevor ihre Objekte zerstört werden.
ClientJoinedGame, ClientDisconnected, ClientRejected(reason)Client: Nach dem Laden der Karte dem Spiel beigetreten; die Verbindung wurde beendet; der Server hat diesen Client abgelehnt.
RTSNetworkUnit SpawnUnit(owner, team, position, yaw, prefab = null)Server: Spawnt eine Einheit (sie wird an die nächste begehbare Stelle verschoben). owner null = kein Besitzer, zum Beispiel für neutrale Creeps oder Gebäude. Das Prefab muss ein registriertes spawnbares Prefab mit RTSNetworkUnit sein.
GetUnits(conn), TryGetTeam(conn, out team), IsTeamReserved(team)Server: die Einheiten einer Verbindung, ihr Team und ob Einheiten eines Spielers, der das Spiel verlassen hat, noch einen Team-Platz belegen.
RejectConnection(conn, reason)Server: Sendet den Grund an den Client und trennt ihn nach Ablauf von Reject Disconnect Delay.
RTSNetworkUnit und RTSNetworkPlayer
static RTSNetworkUnit.Spawned, Despawned, AllWird auf jedem Rechner ausgelöst, wenn eine Netzwerkeinheit gespawnt (nachdem ihr Team gesetzt wurde) oder despawnt wurde; alle auf diesem Rechner bekannten Einheiten.
static RTSNetworkPlayer.LocalPlayerStarted, LocalPlayerStopped, LocalDas lokale Spielerobjekt ist erschienen oder verschwunden; der aktuelle lokale Spieler.
using Mirror;
using RTSTerrainStudio.Integrations.Mirror;
using UnityEngine;

// Server: gives every player an extra starting object (for example a town hall) next to its start location.
// Put it on the network manager object.
public class StartingObjectsExample : MonoBehaviour
{
    [SerializeField] GameObject townHallPrefab;   // NetworkIdentity + RTSUnit + RTSNetworkUnit, in Registered Spawnable Prefabs

    RTSNetworkManager manager;

    void Awake() => manager = GetComponent<RTSNetworkManager>();

    void OnEnable()
    {
        if (manager != null) manager.ServerPlayerSpawned += OnPlayerSpawned;
    }

    void OnDisable()
    {
        if (manager != null) manager.ServerPlayerSpawned -= OnPlayerSpawned;
    }

    void OnPlayerSpawned(NetworkConnectionToClient conn, RTSNetworkPlayer player)
    {
        if (player == null || townHallPrefab == null) return;
        // Start location of the player's team (a fallback position when the map has none).
        RTSNetworkUtility.GetStartLocation(manager.Terrain, player.Team, manager.MaxPlayers, out Vector3 start, out float yaw);
        // Owned by the connection, so the player can select and order it.
        manager.SpawnUnit(conn, player.Team, start, yaw, townHallPrefab);
    }
}

Einschränkungen und Fehlerbehebung

  • Kartenänderungen während eines Spiels (In-Game-Editor auf dem Host) werden nicht live übertragen. Rufe RTSNetworkMapSync.SendMapToAll() auf, um die Karte erneut zu senden.
  • RTSBuildingPlacementDemo und RTSTreeHarvestDemo laufen lokal und sind nicht vernetzt. RTSDemoGame ist ein Bootstrapper für den Einzelspielermodus und gehört nicht in eine Netzwerkszene.
  • Auf Clients bleibt RTSUnit.State auf Idle. Verwende RTSUnit.Velocity (zum Beispiel für Laufanimationen) oder füge eine eigene SyncVar hinzu.
  • Allianzen werden serverseitig im Nebel des Krieges festgelegt. Clients erhalten sie nicht; rufe SetAlliance auch auf den Clients auf, wenn die Sicht der Verbündeten dort sichtbar sein soll.
  • Beim additiven Laden von Szenen bleibt die aktuelle Karte erhalten. Server und Clients müssen dasselbe Tileset und dieselben Bibliotheken verwenden (dieselben Projektinhalte).
ProblemLösung
„The server's fog of war computes only its local team“Füge RTSNetworkFogOfWar zum Netzwerkmanager hinzu oder aktiviere Compute All Teams am Fog of War Manager des Servers.
Client kommt nie über „Connecting...“ hinausDer Server ist nicht erreichbar (Adresse, Firewall, UDP-Port 7777) oder die Serverszene hat keinen RTSNetworkManager.
„Could not join the game: The game is full“Die Karte hat weniger freie Plätze als Spieler. Füge Startpositionen hinzu, erhöhe den Wert Max Players der Karte oder prüfe Max Players Override. Plätze von Spielern, die das Spiel verlassen haben, bleiben reserviert, solange ihre Einheiten leben.
Kompilierfehler wegen fehlender Mirror-Typen nach dem Entfernen von MirrorDas Define-Symbol MIRROR ist noch gesetzt. Siehe Integrationen entfernen.
Einheiten bewegen sich auf dem Client nichtDas Einheiten-Prefab braucht NetworkTransformReliable, und die Szene braucht einen RTSCommandController.
Feindliche Einheiten sind immer sichtbarRTSFogOfWarInterestManagement muss auf einem aktiven Objekt liegen, und der Fog of War Manager muss auf dem Server laufen.
Die Karte wird jedes Mal heruntergeladenDer Server sendet eine Karte, die nach dem Laden bearbeitet oder generiert wurde. Speichere sie vor dem Hosten in ihrem Asset oder ihrer Datei.

A* Pathfinding Project

Diese Integration wandelt die Pathing-Karte des Terrains in Grid Graphs von A* um und hält sie aktuell, während du die Karte bearbeitest (im Editor und im Spiel). Außerdem lässt sie die Beispieleinheiten mit A* laufen: entweder entlang von A*-Pfaden mit ihrer eigenen Steuerung oder mit AIPath von A* und lokaler RVO-Kollisionsvermeidung.

Voraussetzungen: A* Pathfinding Project Pro 5.4.4 oder neuer, aber unter 6.0 (getestet mit 5.4.7). Die Integration verwendet TraversalConstraint (neu in 5.4) und die lokale RVO-Kollisionsvermeidung (RVOController, RVOSimulator), die nur in der Pro-Version enthalten sind. Die kostenlose Version verwendet denselben Paketnamen, daher kann Unity die beiden nicht unterscheiden: Füge bei der kostenlosen Version RTS_ASTAR_DISABLED unter Project Settings ▸ Player ▸ Scripting Define Symbols hinzu (oder lösche den Ordner der Integration).

Schnellstart: die Demoszene

  1. Installiere A* Pathfinding Project Pro 5.4 oder neuer.
  2. Führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content einmal aus.
  3. Führe Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene aus. Die Szene wird als Integrations/AStarPathfinding/Demo/RTS AStar Demo.unity gespeichert. Wenn die Demo-Inhalte keine Karte enthalten, wird daneben RTS AStar Demo Map.rtsmap (Plateaus, Rampen, Seen) generiert.
  4. Drücke Play. Wähle Einheiten mit der linken Maustaste aus und bewege sie per Rechtsklick. Ritter verwenden den Graphen RTS Large, alle anderen Einheiten RTS Ground.
  5. Wähle das Objekt A* Pathfinding aus und schalte die Einstellung Mode der Komponente A* Path Provider zwischen Agent Movement und Path Only um, um beide Bewegungsarten zu vergleichen.

A* zur eigenen Szene hinzufügen

Wähle dein RTS Terrain aus und führe Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Add A* To Selected Terrain aus. Dadurch wird ein GameObject A* Pathfinding mit folgenden Komponenten hinzugefügt:

KomponenteZweck
AstarPathDie Hauptkomponente von A*. Scan on Startup ist ausgeschaltet (der Graph-Builder übernimmt das Scannen), das Pfad-Logging ist auf Fehler beschränkt.
RTSAstarGraphBuilder (A* Graph Builder)Erzeugt die Grid Graphs aus der Pathing-Karte und aktualisiert sie bei jeder Kartenänderung.
RVOSimulatorLokale Kollisionsvermeidung für Agenten mit einem RVOController.
RTSAstarPathProvider (A* Path Provider)Lässt alle Einheiten A* verwenden (registriert als RTSUnitPathProviderRegistry.Default).

Die manuelle Einrichtung funktioniert genauso: Füge diese Komponenten einem beliebigen GameObject hinzu (Add Component ▸ RTS Terrain Studio ▸ Integrations) und weise dem Graph-Builder das Terrain zu. Alternativ legst du den Builder auf das GameObject des Terrains, wo er das Terrain selbst findet. Schalte bei AstarPath die Option Scan On Startup aus.

Mehrere Terrains: Jedes Terrain braucht einen eigenen Graph-Builder mit eigenen Graph-Namen. Wenn du Add A* To Selected Terrain für ein zweites Terrain ausführst, werden dessen Graphen nach dem Terrain benannt, zum Beispiel RTS Ground (Island). Ein Graph, den bereits ein anderer Builder befüllt hat, wird nie gemeinsam genutzt.

Wie die Graphen erstellt werden

  • Exaktes Layout. Ein Knoten pro Pathing-Zelle, auf der Karte zentriert und mit der Y-Rotation des Terrains gedreht. Wenn du das Terrain verschiebst oder drehst, werden die Graphen neu erstellt (im Edit Mode, sobald das Terrain 0.2 Sekunden lang stillgestanden hat).
  • Keine Physik nötig. Die Kollisions- und Höhentests von A* sind ausgeschaltet. Die Knotenhöhen stammen von der Terrain-Oberfläche (dieselben Werte wie bei RTSTerrain.GetHeight); Begehbarkeit, Tags und Penalties stammen aus den Pathing-Flags. Die Daten werden von Burst-Jobs berechnet.
  • Tags (im Builder änderbar): 0 Boden, 1 Flachwasser, 2 Tiefwasser, 3 Klippenkante. Nutze sie in den Einstellungen Tags des Seekers, um pro Einheit bestimmte Bodenarten zu verbieten oder mit Penalties zu belegen.
  • Graph-Profile. Ein Grid Graph von A* pro Profil (zum Beispiel einer pro Einheitengröße). Jedes Profil hat einen Graph-Namen, eine Einstellung Traversal (Land, Shallow Water, Deep Water) und einen Unit Radius: Knoten, die näher als dieser Radius an einem Hindernis liegen, werden unbegehbar, sodass große Einheiten nie in Hindernisse ragen. Dazu kommen Penalties pro Bodenart (1000 ≈ ein zusätzlicher Meter pro Knoten), Use Water Surface Height für Graphen, die See-Einheiten nutzen, und eine maximale Steighöhe. Einheiten wählen den kleinsten Graphen, der zu ihrem Radius passt.
  • See- und Amphibieneinheiten. Setze die Einstellung Traversal des Profils auf Deep Water (Boote) oder Land | Shallow Water | Deep Water und aktiviere Use Water Surface Height. Gib Booten ein RTSAstarUnitSetup mit Required Traversal = Deep Water. Im Code erzeugen die Factory-Methoden RTSAstarGraphProfile.Ground() („RTS Ground“, Radius 0.5 m, Land | Shallow Water), Naval() („RTS Naval“, Radius 1 m, Deep Water, Höhe der Wasseroberfläche) und Amphibious() („RTS Amphibious“, Radius 0.75 m, alle Bodenarten, Höhe der Wasseroberfläche) fertige Profile. Ein neues Profil verwendet standardmäßig Land | Shallow Water, eine Flachwasser-Penalty von 600 und eine Klippenkanten-Penalty von 200.
  • Aktualisierungen. Eine neue Karte, ein verschobenes Terrain oder geänderte Einstellungen lösen eine komplette Neuerstellung aus. Jede Kartenänderung (RTSTerrain.PathingChanged) schreibt nur die betroffenen Knoten, ihren Freiraumbereich und ihre Verbindungen in einem Aufruf von AstarPath.AddWorkItem neu.
  • Sehr große Karten. A* begrenzt Grid Graphs auf 1024 Knoten pro Achse (8192 mit dem Optimierungs-Define ASTAR_LARGER_GRIDS). Größere Pathing-Karten verwenden automatisch mehrere Pathing-Zellen pro Knoten; Cells Per Node macht dasselbe gezielt, um schnellere Graphen zu erhalten.
  • Build In Edit Mode zeigt die Graphen als A*-Gizmos an, während du die Karte bearbeitest.
Member von RTSAstarGraphBuilderBeschreibung
Cells Per Node (Einstellung, Standard 1)Pathing-Zellen pro Graph-Knoten entlang jeder Achse. 1 = exakte Übereinstimmung mit der Pathing-Karte; wird auf sehr großen Karten automatisch erhöht (EffectiveCellsPerNode).
Log Rebuilds (Einstellung, standardmäßig aus)Schreibt bei jeder kompletten Neuerstellung eine Zeile in die Console.
Profiles, TagsDie Graph-Profile und Tag-Nummern. Rufe RequestRebuild() auf, nachdem du sie per Code geändert hast.
RequestRebuild()Plant eine komplette Neuerstellung für das nächste Update ein (wiederholte Aufrufe kosten kaum etwas).
bool RebuildNow()Erstellt alle Graphen sofort neu. Gibt false zurück, wenn es kein aktives AstarPath oder keine Karte gibt.
UpdateRegion(RectInt pathingRect)Schreibt die Knoten eines Rechtecks aus Pathing-Zellen neu. Wird automatisch von RTSTerrain.PathingChanged aufgerufen; rufe die Methode selbst auf, wenn du PathingMap.Flags ohne dieses Event geändert hast.
ProcessPendingChanges()Wendet ausstehende Aktualisierungen sofort an (für Pfade, die im selben Frame wie eine Kartenänderung angefordert werden).
GetGraph(index), GetGraph(name), GetProfileIndex(name)Der A*-GridGraph eines Profils.
FindProfileFor(radius, traversal), FindGraphFor(radius, traversal)Das kleinste Profil (bzw. der kleinste Graph), das zu einer Einheit passt; -1 / null, wenn kein Profil diese Fortbewegungsart erlaubt.
GetStats(index)RTSAstarGraphStats: Größe, Knotengröße, begehbare Knoten, Zeiten für Neuerstellung und Aktualisierung, isBuilt.
IsBuilt, IsProfileBuilt(index), bool TryGetProblem(out string problem)Ob die Graphen die aktuelle Karte enthalten; warum sie nicht erstellt werden können (kein AstarPath, kein Terrain, keine Karte).
GraphsRebuilt, GraphsUpdatedEvents nach einer kompletten Neuerstellung und nach einer inkrementellen Aktualisierung (mit dem Pathing-Rechteck).
static FindFor(terrain), static AllDer Builder eines Terrains; alle aktivierten Builder.

Provider-Modi und RVO

Modus (RTSAstarMovementMode)Verhalten
Agent Movement (Standard)HandlesMovement = true. Bewegungsbefehle setzen das Ziel des AIPath der Einheit und starten eine Suche; Stop und Hold löschen den Pfad, Einheiten mit Hold-Befehl sperren ihren RVOController. Ankunft und Fehlschlag werden an die Einheit zurückgemeldet, sodass eingereihte Befehle (Shift), Patrouillen und Einheiten-Events wie mit dem integrierten Pathfinder funktionieren. Die lokale RVO-Kollisionsvermeidung hält Einheiten in Bewegung auf Abstand zueinander.
Path OnlyHandlesMovement = false. A* berechnet nur die Wegpunkte (über den Seeker der Einheit, sodass Modifier wie der Funnel Modifier greifen); die Einheit folgt ihnen mit ihrer eigenen Steuerung und Separation. Bei Einheiten ohne Funnel Modifier werden die Pfade begradigt (Smooth Paths).
Provider-EinstellungStandardBeschreibung
ModeAgent MovementSiehe oben.
Graph BuilderleerBuilder, dessen Graphen verwendet werden. Leer = der Builder des Terrains der Einheit.
Auto Configure UnitsanFügt jeder Einheit RTSAstarUnitSetup hinzu und konfiguriert damit einen Seeker, einen FunnelModifier, ein RTSAstarAIPath (ein AIPath, das ohne Physik-Raycasts auf der Terrain-Oberfläche steht) und einen RVOController. Geschwindigkeit, Drehrate, Beschleunigung, Radius und Höhe werden von der RTSUnit übernommen.
Fallback To BuiltinanVerwendet den integrierten Pathfinder, solange kein A*-Graph verfügbar ist (im Edit Mode und vor dem ersten Scan).
Smooth PathsanPath Only: Begradigt die Pfade von Einheiten ohne Funnel Modifier; Abkürzungen bleiben auf erlaubten Tags und führen nie über teurere Knoten.
Stuck Time1.5 sAgent Movement: Sekunden, die ein Agent während einer Bewegung stillstehen darf, bevor er einmal einen neuen Pfad sucht und danach aufgibt.

Füge RTSAstarUnitSetup selbst zu einem Einheiten-Prefab hinzu, um einen Graphen über seinen Namen zu wählen oder um die lokale Kollisionsvermeidung oder die Pfadglättung pro Einheitentyp auszuschalten. Der Inspector von RTSAstarAIPath zeigt die üblichen Bewegungseinstellungen von A* sowie einen Abschnitt RTS Terrain (Follow Terrain Height, Stay On Water Surface, Terrain).

using RTSTerrainStudio.Integrations.AStar;
using RTSTerrainStudio.Samples;
using UnityEngine;

// Needs the A* Pathfinding Project Pro 5.4+. Put it into an assembly that references
// RTSTerrainStudio.Integrations.AStar (or into Assembly-CSharp, which references it automatically).
public class AstarExample : MonoBehaviour
{
    [SerializeField] RTSUnit unit;

    void Start()
    {
        // Configure one unit by hand (Seeker, FunnelModifier, RTSAstarAIPath and RVOController).
        RTSAstarUnitSetup setup = RTSAstarUnitSetup.Configure(unit, agentMovement: true);
        setup.Agent.maxSpeed *= 1.5f;

        // The graph that fits this unit's radius.
        RTSAstarGraphBuilder builder = RTSAstarGraphBuilder.FindFor(unit.Terrain);
        if (builder != null)
            Debug.Log("Unit uses graph " + builder.FindGraphFor(unit.Radius)?.name);
    }

    // After changing the pathing map from code in the middle of a frame, push the change to A* now
    // instead of waiting for the next update.
    public static void FlushGraphs(RTSUnit anyUnit)
    {
        RTSAstarGraphBuilder.FindFor(anyUnit.Terrain)?.ProcessPendingChanges();
    }
}

Einschränkungen

  • Die Regeln für Grid Graphs von A* (GridGraph.rules) haben keine Wirkung: Der Builder schreibt Begehbarkeit, Tags, Penalties und Positionen nach jedem Scan selbst. Verwende stattdessen die Tags und Penalties des Builders.
  • Terrains, die um X oder Z gedreht sind, werden nicht unterstützt (Grid Graphs sind horizontale Ebenen).
  • Ein gemalter Pathing-Override Unwalkable blockiert nur Bodeneinheiten. Schiffe werden durch Hindernisse und Klippen blockiert; forme Seewege mit Doodads oder über die Ränder des Tiefwassers. See-Einheiten brauchen den Modus Agent Movement.
  • Graph-Profile, die du aus dem Builder entfernst, hinterlassen ihren Graphen in der Komponente AstarPath; lösche ihn dort, wenn du ihn nicht mehr brauchst.
  • A* sucht Pfade nur im Play Mode. Im Edit Mode greifen Pfadanfragen auf den integrierten Pathfinder zurück.

Unity NavMesh (AI Navigation)

Diese Integration erstellt ein Unity-NavMesh für ein RTSTerrain und lässt die Beispieleinheiten darauf ihren Weg finden. Verwende sie, wenn dein Spiel bereits Unitys NavMesh nutzt (NavMeshAgents, NavMesh-Abfragen, NavMeshLinks) oder wenn du Unitys Crowd-Kollisionsvermeidung nutzen möchtest. Sie benötigt das Paket AI Navigation (com.unity.ai.navigation) 2.0 oder neuer und funktioniert im Editor und im Spiel, auch mit Karten, die zur Laufzeit geladen oder bearbeitet werden.

Integrationen entfernen

Du musst eine Integration nie entfernen: Ohne ihre Abhängigkeit wird sie nicht kompiliert. Entferne eine Integration, wenn du die Abhängigkeit deinstallierst oder ein kleineres Projekt haben möchtest. Lösche in jedem Fall zuerst den Ordner Demo der Integration, falls du eine Demoszene erstellt hast: Ihre Szenen und Prefabs verwenden die Komponenten der Integration und würden fehlende Skripte anzeigen.

A* Pathfinding Project oder AI Navigation entfernen

  1. Lösche Integrations/AStarPathfinding/Demo (bzw. Integrations/UnityNavMesh/Demo), falls vorhanden.
  2. Entferne die Komponenten der Integration aus deinen eigenen Szenen und Prefabs (Graph-Builder, Pfad-Provider und Unit-Setup bzw. NavMesh-Builder und NavMesh-Pfad-Provider).
  3. Deinstalliere das Paket (A* Pathfinding Project bzw. AI Navigation im Package Manager). Das Versions-Define verschwindet zusammen mit dem Paket, und die Assemblies der Integration werden automatisch übersprungen.
  4. Falls du für die kostenlose Version von A* RTS_ASTAR_DISABLED hinzugefügt hast, kannst du es wieder aus den Scripting Define Symbols entfernen.

Mirror entfernen

Mirror trägt die Scripting Define Symbols MIRROR, MIRROR_89_OR_NEWER, MIRROR_90_OR_NEWER, MIRROR_93_OR_NEWER und MIRROR_96_OR_NEWER in die Player Settings ein; wenn du Mirror löschst, bleiben sie dort erhalten. Solange MIRROR noch definiert ist, kompiliert Unity die Integration weiterhin gegen Mirror-Assemblies, die nicht mehr existieren, und die Kompilierfehler blockieren jedes Skript im Projekt.

  1. Lösche zuerst Integrations/Mirror/Demo.
  2. Lösche den Ordner Mirror im Unity Editor (Project-Fenster). Die kleine Editor-Assembly RTSTerrainStudio.Integrations.Mirror.DefineCleanup.Editor erkennt das Löschen, entfernt die Symbole von Mirror aus allen Build-Zielen und protokolliert, was sie entfernt hat. Sie läuft außerdem jedes Mal, wenn der Editor ohne Mirror startet. Über Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Remove Stale Mirror Defines stößt du denselben Vorgang manuell an.
  3. Prüfe Project Settings ▸ Player ▸ Other Settings ▸ Scripting Define Symbols für jede Plattform, für die du Builds erstellst: Es darf kein Eintrag MIRROR oder MIRROR_..._OR_NEWER übrig bleiben. Entferne Reste von Hand, zum Beispiel wenn Mirror bei geschlossenem Editor gelöscht wurde und der Editor danach im Safe Mode gestartet ist. Das Edgegap-Plugin von Mirror fügt außerdem EDGEGAP_PLUGIN_SERVERS hinzu; es ist harmlos, du kannst es aber ebenfalls entfernen.

Die Skripte der Integration können im Projekt bleiben: Sie werden erst kompiliert, wenn Mirror wieder installiert ist.

Integrationsordner löschen

Jede Integration ist in sich abgeschlossen. Wenn du Integrations/Mirror, Integrations/AStarPathfinding oder Integrations/UnityNavMesh (im Project-Fenster) löschst, ist die jeweilige Integration vollständig entfernt; nichts anderes im Paket verweist auf diese Ordner. Du kannst auch den gesamten Ordner Integrations löschen.

Entferne die Komponenten der Integration aus deinen Szenen und Prefabs, bevor du ihren Ordner löschst, sonst bleiben sie als fehlende Skripte zurück. Wenn du Integrations/Mirror löschst, während Mirror installiert bleibt, und Mirror später ebenfalls entfernst, musst du die Define Symbols von Mirror von Hand entfernen (die automatische Bereinigung liegt in diesem Ordner).

Scripting-Leitfaden

Alles, was du in den Editorfenstern tun kannst, lässt sich auch aus C# heraus erledigen. Dieses Kapitel zeigt, wie du das Terrain abfragst, die Karte per Code änderst (mit und ohne Rückgängigmachen), eigene Werkzeuge schreibst, auf Events reagierst und Karten zur Laufzeit speicherst und lädst. Jedes der folgenden Beispiele kompiliert gegen das Paket im Auslieferungszustand; kopiere es in dein Projekt und passe es an.

Assemblies, Namespaces und Koordinaten

AssemblyNamespaceInhalt
RTSTerrainStudio.RuntimeRTSTerrainStudioTerrain, Kartendaten, Werkzeuge, Pinsel, Vegetation, Wasser, Dekor, Nebel des Krieges, Pathing, Generator, Serialisierung, In-Game-Editor.
RTSTerrainStudio.SamplesRTSTerrainStudio.SamplesRTS-Kamera, Einheiten, Auswahl, Befehle, Minikarten-UI, Demospiel.
RTSTerrainStudio.EditorRTSTerrainStudio.EditorEditorfenster, Werkzeuge für die Scene View, Inspectors, Importer, Einrichtungsfenster, RTSEditorContext.
RTSTerrainStudio.Integrations.*RTSTerrainStudio.Integrations.Mirror / .AStar / .NavMeshOptionale Integrationen.

Skripte ohne Assembly Definition (Assembly-CSharp sowie Assembly-CSharp-Editor für Skripte in Ordnern namens Editor) referenzieren diese Assemblies automatisch. Hat dein Code eine eigene .asmdef, füge ihren Referenzen RTSTerrainStudio.Runtime hinzu (und RTSTerrainStudio.Samples oder RTSTerrainStudio.Editor, wenn du sie verwendest), außerdem Unity.Mathematics und Unity.Collections, wenn du Typen wie float3 oder NativeArray verwendest.

Die Karte in drei Rastern

  • Zellen: Die Karte besteht aus Width × Height Zellen mit einer Kantenlänge von je CellSize Metern (Standard 2 m). Rechtecke, die du an NotifyChanged übergibst, sind in Zellen angegeben.
  • Vertices: die Zellecken, (Width + 1) × (Height + 1); Index z * VertexCountX + x (MapData.VertexIndex(x, z)). Hier werden Höhen, Klippenstufen, Flags, Klippentypen und Wasser gespeichert. Der Vertex (x, z) liegt bei (x * CellSize, z * CellSize).
  • Texel: Rasterschichten mit N Texeln pro Zelle: Splat (SplatResolution), Details (DetailResolution) und Pathing (PathingResolution). Der Mittelpunkt des Texels (x, z) liegt bei (x + 0.5, z + 0.5) * CellSize / N.

Kartenlokale Koordinaten beginnen am ersten Vertex: X/Z in Metern von 0 bis MapData.WorldSize, Y zeigt nach oben. Vom Terrain werden nur Position und Y-Rotation berücksichtigt (die Skalierung wird ignoriert). Zum Umrechnen dienen WorldToLocal, LocalToWorld, WorldToLocalDirection, LocalToWorldDirection, WorldToCell, CellCenterToWorld und VertexToWorld. Die Bodenhöhe eines Vertex ist CliffLevels[i] × CliffLevelHeight + Heights[i]. Rotationen sind in Grad angegeben, im Uhrzeigersinn von oben gesehen (0 = +Z, 90 = +X), wie bei Unitys Y-Rotation.

Das Terrain abfragen

Die Abfrage-API von RTSTerrain folgt exakt der sichtbaren Oberfläche, einschließlich der abgerundeten Klippenkonturen, und braucht keine Collider. Sie funktioniert im Edit Mode und zur Laufzeit.

MemberRückgabe
RTSTerrain.Main, RTSTerrain.AllDas erste aktivierte Terrain / alle aktivierten Terrains.
RTSTerrain.FindAt(Vector3 world)Das Terrain, dessen Fläche die Position enthält, oder null.
RTSTerrain.RaycastAll(Ray, out RTSTerrainHit, maxDistance)Der nächstgelegene Treffer über alle Terrains hinweg.
Raycast(Ray, out RTSTerrainHit, maxDistance)Analytischer Raycast gegen dieses Terrain. RTSTerrainHit enthält point, normal, distance, localPoint, cell und terrain.
GetHeight(world), SnapToGround(world)Y-Weltkoordinate des Bodens / die auf den Boden versetzte Position.
GetNormal(world)Oberflächennormale in Weltkoordinaten.
GetCliffLevel(world)Klippenstufe 0–15 an der Position.
IsWater(world, out depth), TryGetWaterHeight(world, out waterY)Ob die Position unter Wasser liegt, ihre Wassertiefe und die Y-Weltkoordinate der Wasseroberfläche.
GetPathing(world), IsWalkable(world), IsBuildable(world)PathingFlags (Walkable, Buildable, Flyable, ShallowWater, DeepWater, Cliff, Blocked, Steep). None außerhalb der Karte oder solange das Pathing noch nicht aufgebaut ist.
Contains(world), WorldBoundsOb die Position innerhalb der Kartenfläche liegt / die Begrenzungsbox in Weltkoordinaten.
GetGeometry()Eine Burst-taugliche MapGeometryView (kartenlokal: SampleHeight, SampleNormal, SampleCliffLevel, SampleWater, Raycast …) für deine eigenen Jobs.
RTSPathfinder.FindPath(terrain, from, to, options, list)Ein Pfad vom integrierten Burst-Pathfinder (PathStatus: Success, Partial, NoPath, InvalidStart).
using System.Collections.Generic;
using RTSTerrainStudio;
using UnityEngine;

// Logs what is under the mouse cursor when you left-click: height, cliff level, water and pathing.
// Works with any number of RTS terrains in the scene and needs no colliders.
public class TerrainQueryExample : MonoBehaviour
{
    [SerializeField] Camera viewCamera;

    void Update()
    {
        // RTSInput works with the Input System and with the legacy Input Manager.
        if (!RTSInput.GetMouseButtonDown(0)) return;

        Camera cam = viewCamera != null ? viewCamera : Camera.main;
        if (cam == null) return;
        Ray ray = cam.ScreenPointToRay(RTSInput.PointerPosition);

        // Analytic ray cast against every enabled RTS terrain (closest hit wins).
        if (!RTSTerrain.RaycastAll(ray, out RTSTerrainHit hit)) return;

        RTSTerrain terrain = hit.terrain;
        Vector3 p = hit.point;

        float groundY = terrain.GetHeight(p);           // world Y of the ground
        Vector3 normal = terrain.GetNormal(p);          // world space surface normal
        int level = terrain.GetCliffLevel(p);           // 0-15
        bool walkable = terrain.IsWalkable(p);          // ground units can walk here
        bool buildable = terrain.IsBuildable(p);        // buildings can be placed here
        PathingFlags flags = terrain.GetPathing(p);     // all pathing flags of the cell

        string water = "dry";
        if (terrain.IsWater(p, out float depth) && terrain.TryGetWaterHeight(p, out float waterY))
            water = $"water, depth {depth:0.00} m, surface at y = {waterY:0.00}";

        Debug.Log($"Cell {hit.cell} on '{terrain.name}': ground y = {groundY:0.00}, level {level}, " +
                  $"slope {Vector3.Angle(normal, Vector3.up):0} deg, {water}, " +
                  $"walkable {walkable}, buildable {buildable}, flags {flags}");
    }

    // Keeps an object standing on the ground of whatever terrain is below it.
    public static void PlaceOnGround(Transform t)
    {
        RTSTerrain terrain = RTSTerrain.FindAt(t.position);
        if (terrain != null) t.position = terrain.SnapToGround(t.position);
    }

    // Asks the built-in Burst pathfinder for a path (main thread, no third-party package needed).
    public static bool TryFindPath(RTSTerrain terrain, Vector3 from, Vector3 to, List<Vector3> waypoints)
    {
        // ForUnit sets the clearance for a unit of this radius (large units avoid narrow gaps).
        PathRequestOptions options = PathRequestOptions.ForUnit(0.5f);
        PathStatus status = RTSPathfinder.FindPath(terrain, from, to, options, waypoints);
        // Partial: the goal is unreachable, the path ends at the closest reachable cell.
        return status == PathStatus.Success || status == PathStatus.Partial;
    }
}
Verwende für das Picking mit der Maus besser RTSTerrain.RaycastAll als Physics.Raycast: Es ist exakt und funktioniert ohne Collider und auch im Edit Mode. Collider (Collider Mode in den Terrain-Einstellungen) sind trotzdem nützlich für Physikobjekte und für Raycasts, die auch andere Objekte treffen müssen.

Die Karte per Code bearbeiten

Alle Karteninhalte liegen in terrain.Map, einer Instanz von MapData. Änderungen folgen immer denselben drei Schritten:

  1. Ändere die Daten in terrain.Map (auf dem Hauptthread).
  2. Rufe terrain.NotifyChanged(layers, cellRect) mit den geänderten MapLayers und dem geänderten Rechteck (in Zellen) auf. Hast du die gesamte Karte geändert, rufe stattdessen NotifyChangedAll(layers) auf.
  3. Das war's. In seinem nächsten LateUpdate (im Edit Mode: beim nächsten Editor-Update) baut das Terrain nur neu auf, was als geändert markiert ist: Chunks, Splat-Texturen, Wasser, Vegetation, Dekor und die Pathing-Karte. Rufe terrain.ProcessPendingChanges() auf, wenn du die Ergebnisse noch im selben Frame brauchst (häufige Aufrufe kosten wenig).
Daten (MapData)Raster / TypFlag in MapLayers
HeightsVertices, float: Feinhöhe in Metern über der StufeHeights
CliffLevelsVertices, byte 0–15CliffLevels
VertexFlagsVertices, VertexFlags (Ramp, Water, User0, User1)VertexFlags
CliffTypesVertices, byte 0–3CliffTypes
WaterHeightsVertices, Wasseroberfläche in kartenlokalen Metern (gilt dort, wo das Flag Water gesetzt ist)Water
SplatMaps[0..3]Splat-Texel, 4 × RGBA32; Schicht i liegt in Splatmap i/4, Kanal i%4; die Gewichtungen ergeben zusammen 255Splat
DetailLayersDetail-Texel, ein Byte Dichte pro Schicht (DetailLayer.prototypeId, density)Details
Trees, DecorListen von TreeInstanceData / DecorInstanceData mit Palettenindizes in TreePrototypeIds / DecorPrototypeIdsTrees, Decor
PathingOverridesPathing-Texel, gemalte OverridesPathingOverrides
Regions, StartLocations, MarkersListenRegions, StartLocations, Markers
Info, EnvironmentKarteninfo und BeleuchtungInfo, Environment
Regeln, die die Karte gültig halten: Begrenze Klippenstufen auf 0–15 und Klippentypen auf 0–3; halte Feinhöhen im zulässigen Bereich (TerrainToolUtility.ClampFineHeight); sorge dafür, dass jedes Splat-Texel in der Summe genau 255 ergibt (verwende SplatRaster und SplatMath); vergib Instanz-IDs für neue Bäume und Dekorobjekte mit map.AllocateInstanceId() und Prototyp-Indizes mit GetOrAddTreePrototype / GetOrAddDecorPrototype. Halte niemals Referenzen auf MapData oder dessen Arrays über das Laden einer Karte oder eine Größenänderung hinweg: SetMap, LoadFromFile und ResizeMap ersetzen das gesamte Objekt und geben das alte frei.

Verformen und Texturieren zur Laufzeit: Krater und verseuchter Boden

Ein typischer Einsatz im Gameplay: Eine Explosion reißt einen Krater in den Boden, und ein Gebäude der Untoten breitet „verseuchten Boden“ um sich herum aus. Pathing, Vegetation und Dekorhöhen passen sich automatisch an.

using RTSTerrainStudio;
using UnityEngine;

// Runtime terrain changes from gameplay code: explosion craters and spreading "blight".
// Both edit MapData directly and then call NotifyChanged. The terrain rebuilds only the
// touched chunks (and the pathing map) in its next LateUpdate.
public static class TerrainEffects
{
    // Digs a bowl-shaped crater into the fine height around a world position.
    public static void Crater(RTSTerrain terrain, Vector3 worldPosition, float radius, float depth)
    {
        MapData map = terrain != null ? terrain.Map : null;
        if (map == null || radius <= 0f) return;

        // Work in map-local space: X/Z in metres from the first vertex.
        Vector3 local = terrain.WorldToLocal(worldPosition);

        // Vertex rectangle covered by the crater (vertices sit at x * CellSize, z * CellSize).
        int x0 = Mathf.Max(0, Mathf.FloorToInt((local.x - radius) / map.CellSize));
        int z0 = Mathf.Max(0, Mathf.FloorToInt((local.z - radius) / map.CellSize));
        int x1 = Mathf.Min(map.Width, Mathf.CeilToInt((local.x + radius) / map.CellSize));
        int z1 = Mathf.Min(map.Height, Mathf.CeilToInt((local.z + radius) / map.CellSize));
        if (x1 < x0 || z1 < z0) return; // completely outside the map

        for (int z = z0; z <= z1; z++)
        for (int x = x0; x <= x1; x++)
        {
            float dx = x * map.CellSize - local.x;
            float dz = z * map.CellSize - local.z;
            float t = Mathf.Sqrt(dx * dx + dz * dz) / radius;
            if (t >= 1f) continue;

            // Smooth bowl: deepest in the centre, zero at the rim.
            float falloff = 1f - t * t;
            int i = map.VertexIndex(x, z);
            // Heights is the fine height on top of the cliff level. Clamp it to the allowed range.
            map.Heights[i] = TerrainToolUtility.ClampFineHeight(map, map.Heights[i] - depth * falloff * falloff);
        }

        // Tell the terrain which cells changed. Meshes, colliders, water, vegetation, decor and
        // pathing follow automatically.
        var vertexRect = new RectInt(x0, z0, x1 - x0 + 1, z1 - z0 + 1);
        terrain.NotifyChanged(MapLayers.Heights, BrushUtility.VertexRectToCellRect(map, vertexRect));
    }

    // Paints a ground layer (for example a "blight" texture) around a position with a soft edge.
    // amount is the coverage reached in the centre (0-1).
    public static void PaintLayer(RTSTerrain terrain, Vector3 worldPosition, float radius, int layer, float amount = 1f)
    {
        MapData map = terrain != null ? terrain.Map : null;
        if (map == null || radius <= 0f) return;
        layer = Mathf.Clamp(layer, 0, MapData.MaxGroundLayers - 1);

        Vector3 local = terrain.WorldToLocal(worldPosition);
        int res = map.SplatResolution;            // splat texels per cell
        float texel = map.CellSize / res;         // texel size in metres

        // Texel rectangle (texel centres sit at (x + 0.5) * texel).
        int x0 = Mathf.Max(0, Mathf.FloorToInt((local.x - radius) / texel));
        int z0 = Mathf.Max(0, Mathf.FloorToInt((local.z - radius) / texel));
        int x1 = Mathf.Min(map.SplatWidth - 1, Mathf.CeilToInt((local.x + radius) / texel));
        int z1 = Mathf.Min(map.SplatHeight - 1, Mathf.CeilToInt((local.z + radius) / texel));
        if (x1 < x0 || z1 < z0) return;

        // SplatRaster gives read/write access to all 16 layer weights of a texel.
        var splat = new SplatRaster(map);
        for (int z = z0; z <= z1; z++)
        for (int x = x0; x <= x1; x++)
        {
            float dx = (x + 0.5f) * texel - local.x;
            float dz = (z + 0.5f) * texel - local.z;
            float t = Mathf.Sqrt(dx * dx + dz * dz) / radius;
            if (t >= 1f) continue;

            int index = splat.Index(x, z);
            SplatTexel weights = splat.Read(index);
            int target = Mathf.RoundToInt(Mathf.Clamp01(amount * (1f - t)) * SplatMath.Total);
            if (weights[layer] >= target) continue; // never remove blight that is already there

            // Sets the layer weight and rescales the other layers, so the texel still sums to 255.
            SplatMath.SetLayerWeight(ref weights, layer, target, 0);
            splat.Write(index, weights);
        }

        var texelRect = new RectInt(x0, z0, x1 - x0 + 1, z1 - z0 + 1);
        terrain.NotifyChanged(MapLayers.Splat, BrushUtility.TexelRectToCellRect(map, texelRect, res));
    }
}
So verwendest du das Ergebnis: TerrainEffects.Crater(RTSTerrain.FindAt(hitPoint), hitPoint, 4f, 1.5f); oder TerrainEffects.PaintLayer(terrain, building.position, 12f, blightLayer);. Für Effekte, die in jedem Frame Tausende von Vertices verändern, verlagere die Schleife in einen Burst-Job, wie es die integrierten Werkzeuge tun.

Rückgängig machbare Bearbeitungen: RTSEditSession.RunOperation

Direkte Bearbeitungen lassen sich nicht rückgängig machen, und das ist für das Gameplay auch richtig so. Für Editorwerkzeuge und Funktionen zur Kartenbearbeitung verwendest du eine RTSEditSession: RunOperation(name, layers, action) zeichnet die angegebenen Schichten der gesamten Karte auf, führt deine Aktion aus und fasst alles zu einem Undo-Schritt zusammen. Im Unity Editor verwendest du die gemeinsame Sitzung RTSEditorContext.Session; ihre Schritte sind Teil von Unitys Undo-System, daher funktionieren Ctrl+Z / Ctrl+Y wie gewohnt.

// Put this file into a folder named "Editor".
using RTSTerrainStudio;
using RTSTerrainStudio.Editor;
using UnityEditor;
using UnityEngine;

// Editor menu command that raises a plateau in the middle of the edited map.
// It runs through the shared edit session, so Ctrl+Z / Ctrl+Y undo and redo it in one step.
public static class PlateauMenu
{
    [MenuItem("Tools/My Game/Raise Centre Plateau")]
    static void RaiseCentrePlateau()
    {
        RTSTerrain terrain = RTSEditorContext.Terrain;   // the terrain selected in the RTS Terrain Studio window
        if (terrain == null || terrain.Map == null) return;

        // The shared session of the window and the Scene View. It always edits RTSEditorContext.Terrain.
        RTSEditSession session = RTSEditorContext.Session;

        // RunOperation records the listed layers of the whole map BEFORE the operation runs,
        // so inside it you only change data and notify the terrain.
        session.RunOperation("Raise Centre Plateau", MapLayers.CliffLevels | MapLayers.Heights, ctx =>
        {
            MapData map = ctx.Map;
            int size = Mathf.Min(map.Width, map.Height) / 4;          // plateau size in cells
            var vertexRect = new RectInt(map.Width / 2 - size / 2, map.Height / 2 - size / 2, size + 1, size + 1);

            for (int z = vertexRect.yMin; z < vertexRect.yMax; z++)
            for (int x = vertexRect.xMin; x < vertexRect.xMax; x++)
            {
                int i = map.VertexIndex(x, z);
                // Cliff levels are bytes 0-15. Ground height = level * CliffLevelHeight + fine height.
                map.CliffLevels[i] = (byte)Mathf.Min(MapData.MaxCliffLevel, map.CliffLevels[i] + 1);
                map.Heights[i] = 0f;                                   // flat top
            }

            ctx.MarkChanged(MapLayers.CliffLevels | MapLayers.Heights,
                BrushUtility.VertexRectToCellRect(map, vertexRect));
        });
    }
}

In einem laufenden Spiel verwendest du die Sitzung des In-Game-Editors (RTSRuntimeEditor.Session), damit deine Operation in dessen Undo-Verlauf landet, oder du erstellst eine eigene Sitzung:

using RTSTerrainStudio;
using UnityEngine;

// Undoable edits in a running game: use the session of the in-game editor, or your own session.
public class RuntimeUndoExample : MonoBehaviour
{
    RTSEditSession ownSession;

    public void FloodLowGround(RTSTerrain terrain, float waterLocalY)
    {
        // Prefer the in-game editor's session, so the edit shares its undo history (Ctrl+Z in the editor UI).
        var ingameEditor = FindFirstObjectByType<RTSRuntimeEditor>();
        RTSEditSession session = ingameEditor != null ? ingameEditor.Session : null;
        if (session == null)
        {
            // Without the in-game editor: a plain session works as well (call Dispose when done).
            if (ownSession == null) ownSession = new RTSEditSession();
            ownSession.Terrain = terrain;
            session = ownSession;
        }

        session.RunOperation("Flood Low Ground", MapLayers.Water | MapLayers.VertexFlags, ctx =>
        {
            MapData map = ctx.Map;
            const byte waterFlag = (byte)VertexFlags.Water;
            for (int i = 0; i < map.VertexCount; i++)
            {
                // Ground height of a vertex = cliff level * level height + fine height.
                float ground = map.CliffLevels[i] * map.CliffLevelHeight + map.Heights[i];
                if (ground >= waterLocalY) continue;
                // A vertex is wet when it has the Water flag; WaterHeights holds its surface height.
                map.VertexFlags[i] = (byte)(map.VertexFlags[i] | waterFlag);
                map.WaterHeights[i] = waterLocalY;
            }
            // Water changes only need the Water layer: terrain meshes are not rebuilt.
            ctx.Terrain.NotifyChangedAll(MapLayers.Water);
        });
    }

    public void UndoLast()
    {
        ownSession?.Undo();
    }

    void OnDestroy()
    {
        ownSession?.Dispose();
    }
}
In einem Werkzeug, das mit Pinselstrichen arbeitet, rufst du nicht RunOperation auf, sondern für jeden Tupfer ctx.Record und ctx.MarkChanged (siehe nächsten Abschnitt). RunOperation zeichnet ganze Schichten auf; gib auf großen Karten daher besser gezielt die betroffenen Schichten an statt MapLayers.All.

Ein eigenes Werkzeug schreiben

Werkzeuge sind gewöhnliche C#-Klassen, die von RTSTool erben. Das Paket findet per Reflection (RTSToolRegistry) jedes nicht abstrakte Werkzeug mit einem öffentlichen parameterlosen Konstruktor. Dein Werkzeug erscheint daher automatisch im Tab seiner Kategorie, sowohl im Fenster von RTS Terrain Studio als auch im In-Game-Editor. Es bekommt eine Einstellungsoberfläche, die aus seinen [ToolSetting]-Feldern erzeugt wird, sowie das Pinsel-Panel, die Symmetrie, Rückgängig/Wiederherstellen, den projizierten Pinselcursor und die Statusleiste. Im Editor bleiben seine Einstellungen zwischen den Sitzungen erhalten.

  1. Lege an beliebiger Stelle in deinem Projekt eine C#-Datei an (nicht in einem Editor-Ordner, damit das Werkzeug auch in Builds für den In-Game-Editor vorhanden ist).
  2. Leite von RTSTool ab und überschreibe Id (eindeutig, zum Beispiel "height.crater"), DisplayName und Category.
  3. Füge für die Optionen, die der Benutzer ändern kann, öffentliche Felder mit [ToolSetting("Label", ...)] hinzu.
  4. Überschreibe ApplyDab: Berechne die Pinselgewichte mit BrushUtility.ComputeWeights, rufe ctx.Record(layers, cells) vor dem Ändern der Daten auf, ändere die Daten und rufe ctx.MarkChanged(layers, cells) danach auf.
  5. Wechsle zurück zu Unity. Sobald das Skript kompiliert ist, erscheint das Werkzeug in seinem Tab.
  6. Wenn du das Werkzeug in einem Build mit einem Managed Stripping Level über Minimal auslieferst, markiere die Klasse mit [UnityEngine.Scripting.Preserve], damit sie beim Code-Stripping erhalten bleibt (siehe Code-Stripping und eigene Werkzeuge).
using RTSTerrainStudio;
using UnityEngine;

// A custom tool: every click digs a crater and paints scorched ground into it.
// No registration is needed. The class is found by reflection and appears in the Height tab
// of the RTS Terrain Studio window and of the in-game editor, with a UI for every [ToolSetting].
public sealed class CraterTool : RTSTool
{
    // Unique id. The part before the dot also picks a fallback icon ("height" = height tools).
    public override string Id => "height.crater";
    public override string DisplayName => "Crater";
    public override ToolCategory Category => ToolCategory.Height;
    public override string Description => "Digs a crater with a scorched floor at the click position.";
    public override string Hint => "Click: dig a crater | Shift+click: raise a mound";
    public override int Order => 100;                                   // after the built-in height tools
    public override ToolInputMode InputMode => ToolInputMode.Click;     // one dab per click
    public override bool UsesBrush => true;                             // keep the brush size / shape panel
    public override MapLayers ModifiedLayers => MapLayers.Heights | MapLayers.Splat;
    public override Color PreviewColor => new Color(1f, 0.45f, 0.1f, 1f);

    // Public fields with [ToolSetting] get a slider, toggle or palette automatically.
    [ToolSetting("Depth", Min = 0.1f, Max = 6f, Order = 0, Tooltip = "Depth of the crater centre in metres.")]
    public float depth = 1.5f;

    [ToolSetting("Paint Floor", Order = 1, Tooltip = "Paint a ground layer into the crater.")]
    public bool paintFloor = true;

    [ToolSetting("Floor Layer", Kind = ToolSettingKind.GroundLayer, Order = 2, Tooltip = "Ground layer of the crater floor.")]
    public int floorLayer = 0;

    public override void ApplyDab(RTSToolContext ctx, in BrushDab dab)
    {
        MapData map = ctx.Map;

        // 1. Brush weights (0-1) for every vertex under the brush. Shape, hardness, falloff and
        //    rotation are already applied; strength is not (use dab.strength). Dispose the result.
        using BrushWeights w = BrushUtility.ComputeWeights(map, dab, ctx.BrushMask, BrushGrid.Vertices, 1);
        if (w.IsEmpty) return;
        // Cells touched by the dab (covers both the vertex and the splat texel grid).
        RectInt cells = BrushUtility.GetCellRect(map, dab);

        // 2. Record for undo BEFORE changing anything. Record every layer you are going to write.
        MapLayers layers = paintFloor ? MapLayers.Heights | MapLayers.Splat : MapLayers.Heights;
        ctx.Record(layers, cells);

        // 3. Change the data. Shift (dab.invert) raises a mound instead.
        float delta = depth * dab.strength * (dab.invert ? 1f : -1f);
        for (int z = w.rect.yMin; z < w.rect.yMax; z++)
        for (int x = w.rect.xMin; x < w.rect.xMax; x++)
        {
            float weight = w[x, z];
            if (weight <= 0f) continue;
            int i = map.VertexIndex(x, z);
            map.Heights[i] = TerrainToolUtility.ClampFineHeight(map, map.Heights[i] + delta * weight);
        }

        if (paintFloor && !dab.invert)
        {
            // The splat maps use their own texel grid: compute weights for it as well.
            int res = map.SplatResolution;
            using BrushWeights sw = BrushUtility.ComputeWeights(map, dab, ctx.BrushMask, BrushGrid.Texels, res);
            var splat = new SplatRaster(map);
            int layer = Mathf.Clamp(floorLayer, 0, MapData.MaxGroundLayers - 1);
            for (int z = sw.rect.yMin; z < sw.rect.yMax; z++)
            for (int x = sw.rect.xMin; x < sw.rect.xMax; x++)
            {
                int target = Mathf.RoundToInt(sw[x, z] * SplatMath.Total);
                int index = splat.Index(x, z);
                SplatTexel t = splat.Read(index);
                if (t[layer] >= target) continue;
                SplatMath.SetLayerWeight(ref t, layer, target, 0);
                splat.Write(index, t);
            }
        }

        // 4. Notify the terrain AFTER the change (the same rectangle you recorded).
        ctx.MarkChanged(layers, cells);
    }
}

Was du überschreiben kannst

MemberStandardBeschreibung
Id, DisplayName, CategoryerforderlichEindeutige ID, Anzeigename in der Oberfläche und Tab (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers).
Description, Hint""Tooltip und Hilfetext; Bedienhinweis in der Statusleiste.
IconNameIdName des Icons. Bei unbekannten Namen wird ersatzweise das Icon des Gruppenpräfixes der ID (height., paint. …) verwendet, ansonsten ein allgemeines Schraubenschlüssel-Icon. Eigene Icon-Zeichnungen kannst du mit RTSIcons.RegisterIcon hinzufügen.
Order0Position innerhalb der Kategorie.
InputModeBrushBrush: Tupfer entlang des Zeigerwegs (im Pinselabstand). Click: ein Tupfer pro Klick. Drag: OnDragBegin / OnDragUpdate / OnDragEnd (Rechtecke, Linien).
UsesBrushtrue bei BrushZeigt das Pinsel-Panel an (Größe, Form, Stärke …).
RepeatRate0Tupfer pro Sekunde, solange die Maustaste ohne Bewegung gedrückt gehalten wird (für kontinuierliches Anheben oder Absenken).
ModifiedLayersNoneNur zur Information: für Overlays und die Statusleiste.
PreviewColorhellblauFarbe des projizierten Pinselcursors.
SnapNoneRichtet die Pinselmitte an Vertices (Klippenwerkzeuge) oder an Zellmitten aus.
SupportsPick + OnPickfalseCtrl+Klick nimmt einen Wert auf (eine Höhe, eine Schicht), statt zu malen.
OnActivated, OnDeactivated, OnSettingChangedLebenszyklus-Callbacks.
OnStrokeBegin, OnStrokeEnd, OnStrokeCancelledWerden zu Beginn und am Ende eines Pinselstrichs aufgerufen; ein abgebrochener Pinselstrich (Esc) wird anschließend rückgängig gemacht.
DrawGizmos(ctx, gizmos, hasHover, hoverLocal)Zusätzliche Vorschauen über IToolGizmos; im Editor mit Handles umgesetzt, im Spiel mit GL-Linien.
GetStatus(ctx, hoverLocal)nullText für die Statusleiste, zum Beispiel „Level 3 → 4“.

[ToolSetting] akzeptiert Min/Max (Bereich des Schiebereglers), Order, Tooltip und Kind. Auto wählt das Bedienelement anhand des Feldtyps (Schieberegler für float/int, Schalter für bool, Popup für Enums, Color, string). Spezielle Arten: GroundLayer und CliffType (Tileset-Paletten mit Vorschaubildern), CliffLevel, DetailPrototype / DetailPrototypeList, TreePrototype / TreePrototypeList, DecorPrototype / DecorPrototypeList (Bibliothekspaletten), Player (0–15) und Hidden.

Hidden-Einstellungen werden von den generischen Einstellungsoberflächen nicht angezeigt. Eine Ausnahme: Im Unity Editor zeigt das Editorfenster ein Hidden-Feld, dessen Typ ein UnityEngine.Object ist (eine Asset-Referenz wie AutoPaintTool.profile), trotzdem als Objektfeld an, weil die generischen Oberflächen Asset-Referenzen sonst nicht darstellen können. Der In-Game-Editor zeigt es nicht an. Eine verborgene Einstellung wird trotzdem zusammen mit den anderen Werkzeugeinstellungen gespeichert.

Gut zu wissen

  • Symmetrie funktioniert automatisch. ApplyDab wird für jede Symmetriekopie einmal aufgerufen; dab.symmetry verrät dir, um welche Kopie es sich handelt (mit ihrem Drehwinkelversatz und der Angabe, ob sie gespiegelt ist). dab.rotation ist bereits transformiert.
  • Der Tupfer (BrushDab) enthält localPosition, worldPosition, radius, strength, rotation, invert (Shift gedrückt), isFirstDab und dabIndex.
  • Pinselgewichte: BrushGrid.Vertices mit Auflösung 1 für Vertex-Schichten, BrushGrid.Texels mit map.SplatResolution, DetailResolution oder PathingResolution für Rasterschichten. Rechtecke rechnest du mit BrushUtility.VertexRectToCellRect, TexelRectToCellRect oder GetCellRect(map, dab) um. Rufe für die Gewichte immer Dispose auf.
  • Zufallswerte müssen für jede Symmetriekopie eines Tupfers gleich sein. ctx.Random erhält bei jedem Pinselstrich einen neuen Seed und liefert bei jedem Aufruf den nächsten Wert. Eine Kopie, die es aufruft, bekommt daher einen anderen Wert als das Original. Ziehe Zufallswerte nur, wenn dab.symmetry.index == 0 gilt, speichere sie für diesen dab.dabIndex zwischen und verwende sie für die anderen Kopien wieder – gespiegelt oder gedreht mit SymmetricScatter.TransformOffset und SymmetryPoint.TransformYaw. Die öffentliche Klasse SymmetricScatter (GetCandidates, GetSingleSeed) erledigt das für dich.
  • Harte Auswahl: Für Alles-oder-nichts-Änderungen im Stil klassischer RTS-Spiele leitest du von CliffTool, WaterTargetTool oder PathingTool ab (sie liefern Kategorie, Cursorfarbe und Statustext; WaterTargetTool ergänzt außerdem die Einstellungen für Water Height und das Aufnehmen per Ctrl+Klick) oder rufst TerrainToolUtility.BeginHardVertexSelection(ctx, dab, layers, out weights, out forcedIndex, out cells) auf. Betroffen sind Vertices mit einem Gewicht von mindestens TerrainToolUtility.HardSelectionThreshold (0.5) sowie der Vertex unter der Pinselmitte (forcedIndex), und Undo-Daten werden nur für diesen Bereich aufgezeichnet. Rufe Dispose für weights auch dann auf, wenn die Methode false zurückgibt.
  • Performance: Die integrierten Werkzeuge führen ihre Schleifen in Burst-Jobs aus (siehe RaiseLowerTool und PaintLayerTool in Runtime/Tools). Für kleine Pinsel reichen einfache Schleifen aus.
  • Eine mit [HideTool] markierte Klasse wird gar nicht registriert: Sie fehlt in beiden Editoren und in session.Tools, daher finden SetActiveTool, RTSEditorContext.ActivateTool und FindTool sie nicht. Um ein solches Werkzeug (zum Beispiel ein Debug-Werkzeug) aus Code heraus zu verwenden, weise direkt eine Instanz zu: session.ActiveTool = new MyDebugTool();. Setze im Unity Editor zusätzlich RTSEditorContext.EditingEnabled = true.
  • Die Registry durchsucht nur die Runtime-Assembly des Pakets und die Assemblies, die sie direkt oder über eine andere solche Assembly referenzieren. Ein Werkzeug erbt immer von RTSTool, daher referenziert seine Assembly die Runtime-Assembly ohnehin (Skripte ohne Assembly Definition liegen in Assembly-CSharp, die sie automatisch referenziert). Die Registry überspringt Assemblies, deren Namen mit System, Unity., UnityEngine, UnityEditor, Mono., mscorlib, netstandard oder nunit beginnen. Gib deiner eigenen Assembly keinen solchen Namen.
  • Code-Stripping: Werkzeuge werden per Reflection erzeugt. Bei einem Build mit einem Managed Stripping Level über Minimal erkennt das Code-Stripping deshalb nicht, dass sie verwendet werden. Die Datei Runtime/link.xml des Pakets sorgt dafür, dass die integrierten Werkzeuge erhalten bleiben; markiere deine eigenen mit [UnityEngine.Scripting.Preserve] oder trage sie in eine link.xml deines Projekts ein (siehe Code-Stripping und eigene Werkzeuge).
  • Aus Code aktivierst du ein Werkzeug mit session.SetActiveTool("height.crater") oder im Editor mit RTSEditorContext.ActivateTool("height.crater").

Die Sitzung per Code steuern

Pinsel, Symmetrie und Pinselstriche beider Editoren laufen über eine RTSEditSession. Mit derselben API kannst du dein eigenes Frontend bauen, zum Beispiel eine eigene Maloberfläche, ein Tutorial, das für den Spieler malt, oder ein Level-Skript.

MemberBeschreibung
BrushSettings BrushDer gemeinsam genutzte Pinsel: shape, customShape, radius, strength, hardness, useFalloffCurve, falloffCurve, rotation, randomRotation, spacing, jitter. Rufe Brush.MarkDirty() auf, nachdem du die Keys von falloffCurve per Code geändert hast: Die gebackene Pinselmaske erkennt Änderungen an Form, Härte, useFalloffCurve und customShape, aber nicht an den Keys der Kurve.
SymmetrySettings Symmetrymode (SymmetryMode) und center (normalisiert, 0.5/0.5 = Kartenmitte). GetPoints(localPos, mapWorldSize, mergeDistance, results) liefert den ursprünglichen Punkt und seine Kopien.
ScaleRadius(factor), AddStrength(delta)Entspricht den Tastenkürzeln [ ] und - =, begrenzt auf die gültigen Bereiche.
bool InvertModifierWie das Gedrückthalten von Shift: absenken statt anheben, radieren statt malen.
BeginStroke(worldPoint), UpdateStroke(worldPoint, deltaTime), EndStroke(), CancelStroke()Ein Pinselstrich vom Drücken bis zum Loslassen. Rufe UpdateStroke in jedem Frame auf, solange die Maustaste gedrückt ist (auch wenn sich der Zeiger nicht bewegt, damit wiederholt wirkende Werkzeuge weiterarbeiten). EndStroke legt einen Undo-Schritt an; CancelStroke macht den Pinselstrich rückgängig.
Pick(worldPoint)Das Aufnehmen eines Werts per Ctrl+Klick mit dem aktiven Werkzeug.
FindTool(id), FindTool<T>(), GetTools(category), SetActiveTool(id), ActiveToolWerkzeuge finden und aktivieren.
ActiveToolChanged, StrokeEnded, UndoRedoPerformed, SelectionChangedEvents für deine Oberfläche: Das aktive Werkzeug wurde gewechselt, ein Pinselstrich wurde abgeschlossen (wird bei abgebrochenen Pinselstrichen nicht ausgelöst), Rückgängig oder Wiederherstellen wurde ausgeführt, die Dekorauswahl hat sich geändert.
using RTSTerrainStudio;
using UnityEngine;

// Paints with the built-in tools from your own code, for example from a custom UI.
// Call PressAt / DragTo / Release with world positions on the terrain (for example from terrain.Raycast).
public class SessionDriverExample : MonoBehaviour
{
    public RTSTerrain terrain;
    RTSEditSession session;

    void OnEnable()
    {
        // In a scene with the in-game editor you can use RTSRuntimeEditor.Session instead.
        session = new RTSEditSession();
        session.Terrain = terrain;

        // Brush and symmetry are shared by every tool of this session.
        session.Brush.radius = 8f;
        session.Brush.strength = 0.6f;
        session.Symmetry.mode = SymmetryMode.Rotate2;          // 1v1 point symmetry
        session.Symmetry.center = new Vector2(0.5f, 0.5f);     // map centre

        session.SetActiveTool("height.raise");
        session.StrokeEnded += () => Debug.Log("Stroke finished (one undo step)");
    }

    void OnDisable()
    {
        session?.Dispose();
        session = null;
    }

    public void PressAt(Vector3 worldPoint, bool lower)
    {
        session.InvertModifier = lower;      // like holding Shift
        session.BeginStroke(worldPoint);
    }

    // Call every frame while the button is held.
    public void DragTo(Vector3 worldPoint) => session.UpdateStroke(worldPoint, Time.deltaTime);

    public void Release() => session.EndStroke();

    public void Abort() => session.CancelStroke(); // reverts the running stroke

    public void UseLinearFalloff()
    {
        session.Brush.useFalloffCurve = true;
        session.Brush.falloffCurve = AnimationCurve.Linear(0f, 1f, 1f, 0f);
        // Curve keys are not detected automatically: rebuild the baked brush mask.
        session.Brush.MarkDirty();
    }
}

Events

EventSignaturAusgelöst
MapLoadedAction<RTSTerrain>Nachdem eine neue Karte zugewiesen wurde (Laden, neue Karte, Größenänderung, Neuaufbau). Baue hier alles neu auf, was du aus der Karte ableitest.
MapUnloadingAction<RTSTerrain>Bevor die aktuelle Karte ersetzt oder freigegeben wird. Verwirf hier deine Referenzen auf sie.
MapChangedAction<RTSTerrain, MapChangeEventArgs>Sofort beim Aufruf von NotifyChanged / NotifyChangedAll, bevor die Meshes neu aufgebaut werden. MapChangeEventArgs enthält Layers, CellRect und IsFullRefresh. Wird während Pinselstrichen häufig ausgelöst: Halte die Handler schlank.
ChangesProcessedAction<RTSTerrain, IReadOnlyList<DirtyRegion>>Nachdem ausstehende Änderungen verarbeitet wurden: Meshes, Texturen und Pathing sind auf dem neuesten Stand. Jede DirtyRegion enthält layers und cellRect.
PathingChangedAction<RTSTerrain, RectInt>Nachdem die Pathing-Karte (teilweise) neu aufgebaut wurde; das Rechteck ist in Pathing-Zellen angegeben. Bleibt auch beim Neuladen der Karte bestehen (anders als PathingMap.Changed).
TilesetChangedAction<RTSTerrain>Die Tileset-Referenz oder der Inhalt des Tilesets hat sich geändert.
LibrariesChangedAction<RTSTerrain>Die Referenz auf eine Vegetations- oder Dekorbibliothek oder deren Inhalt hat sich geändert.
RTSTerrain.TerrainEnabled / TerrainDisabledstatic Action<RTSTerrain>Ein Terrain wurde aktiviert oder deaktiviert (Änderungen an der Registry).
RTSRuntimeEditorEvents.PlaytestChangedstatic Action<bool>Der In-Game-Editor hat einen Spieltest gestartet oder beendet. Spawne hier deine Gameplay-Objekte und räume sie wieder auf; die Karte wird danach wiederhergestellt.
RTSRuntimeEditorEvents.MapOpenedstatic Action<RTSTerrain>Der In-Game-Editor hat eine Karte geöffnet.
RTSEditSession: ActiveToolChanged, TerrainChanged, StrokeEnded, UndoRedoPerformed, SelectionChangedBearbeitungszustand einer Sitzung (Editor oder In-Game-Editor).
using System.Collections.Generic;
using RTSTerrainStudio;
using UnityEngine;

// Listens to all important terrain events. Put it on the RTS Terrain GameObject (or assign the terrain).
public class TerrainEventsExample : MonoBehaviour
{
    [SerializeField] RTSTerrain terrain;

    void OnEnable()
    {
        if (terrain == null) terrain = GetComponent<RTSTerrain>();
        if (terrain == null) return;
        terrain.MapLoaded += OnMapLoaded;               // new map assigned (load, new map, resize)
        terrain.MapChanged += OnMapChanged;             // immediately when NotifyChanged is called
        terrain.ChangesProcessed += OnChangesProcessed; // after meshes, textures and pathing are up to date
        terrain.PathingChanged += OnPathingChanged;     // after the pathing map was rebuilt
        terrain.TilesetChanged += OnTilesetChanged;     // tileset reference or content changed

        // The map may already be loaded before this component was enabled.
        if (terrain.Map != null) OnMapLoaded(terrain);
    }

    void OnDisable()
    {
        // Always unsubscribe: terrains survive scene changes in the editor and domain reloads.
        if (terrain == null) return;
        terrain.MapLoaded -= OnMapLoaded;
        terrain.MapChanged -= OnMapChanged;
        terrain.ChangesProcessed -= OnChangesProcessed;
        terrain.PathingChanged -= OnPathingChanged;
        terrain.TilesetChanged -= OnTilesetChanged;
    }

    void OnMapLoaded(RTSTerrain t)
    {
        // Rebuild everything your game derives from the map, e.g. spawn points from start locations.
        Debug.Log($"Map '{t.Map.Info.name}' loaded: {t.Map.Width} x {t.Map.Height} cells, " +
                  $"{t.Map.StartLocations.Count} start locations.");
    }

    void OnMapChanged(RTSTerrain t, MapChangeEventArgs e)
    {
        // Raised often during brush strokes: keep it cheap. Check which layers changed.
        if ((e.Layers & (MapLayers.Trees | MapLayers.Decor)) != 0)
            Debug.Log($"Objects changed in cells {e.CellRect} (full refresh: {e.IsFullRefresh})");
    }

    void OnChangesProcessed(RTSTerrain t, IReadOnlyList<DirtyRegion> regions)
    {
        // Meshes are rebuilt now, so GetHeight and Raycast return the new surface.
        foreach (DirtyRegion r in regions)
            if ((r.layers & MapLayers.Geometry) != 0)
                Debug.Log($"Ground changed in cells {r.cellRect}");
    }

    void OnPathingChanged(RTSTerrain t, RectInt pathingRect)
    {
        // The rectangle is in pathing cells (Map.PathingResolution per map cell). Re-plan paths here.
        Debug.Log($"Pathing changed in {pathingRect}");
    }

    void OnTilesetChanged(RTSTerrain t)
    {
        Debug.Log("Tileset is now " + (t.Tileset != null ? t.Tileset.name : "none"));
    }
}

Speichern und Laden zur Laufzeit, eigene Dateiabschnitte

Karten sind .rtsmap-Dateien. Dasselbe Format verwenden der Unity Editor (importiert als RTSMapAsset), der In-Game-Editor und das Karten-Streaming über Mirror. Eine von einem Spieler erstellte Karte öffnet sich daher unverändert in Unity und umgekehrt.

MemberBeschreibung
terrain.SaveToFile(path) / terrain.LoadFromFile(path)Speichern und Laden zur Laufzeit. Beide setzen CurrentFilePath; das Speichern setzt HasUnsavedChanges zurück.
terrain.MapAsset, terrain.LoadFromAsset()Verwendet eine Karte, die ins Projekt importiert wurde (Kampagnenkarten, mitgelieferte Gefechtskarten).
terrain.SetMap(MapData)Zeigt beliebige MapData an. Das Terrain wird Eigentümer der Daten und gibt die vorherige Karte frei.
RTSMapSerializer.Serialize(map) / Deserialize(bytes)Karten als Byte-Daten (Netzwerk, Cloud-Spielstände, Mod-Downloads). Eine deserialisierte Karte gehört dem Aufrufer.
RTSMapSerializer.SaveToFile, LoadFromFile, Write(map, stream), Read(stream)Datei- und Stream-Zugriff ohne Terrain.
RTSMapSerializer.TryReadHeaderFromFile(path, out header)Liest für Kartenbrowser nur den kleinen Dateikopf (Name, Autor, Beschreibung, Größe, Zellgröße, maximale Spielerzahl, Tileset-ID, Änderungszeitpunkt).
RTSMapSerializer.DefaultMapsFolder, FileExtensionDer Ordner Maps in StorageRoot (wird vom In-Game-Editor verwendet, sofern dort kein Maps Folder gesetzt ist) und ".rtsmap".
RTSMapSerializer.StorageRootDer Ordner für Dateien, die zwischen Sitzungen erhalten bleiben: Application.persistentDataPath. In Web-Builds ist es ein Ordner im Browserspeicher, der nach dem Firmen- und Produktnamen benannt ist, weil sich persistentDataPath dort mit der Adresse der Seite ändert.
RTSWebFileSystem.Flush(), DownloadFile(...), IsSupportedFür Web-Builds: geänderte Dateien in den Browserspeicher schreiben, damit sie ein Neuladen der Seite überstehen, und eine Datei vom Browser herunterladen lassen. RTSMapSerializer.SaveToFile und terrain.SaveToFile erledigen das Schreiben in den Browserspeicher selbst; rufe Flush() nach deinen eigenen Schreib- und Löschvorgängen mit System.IO auf. Auf anderen Plattformen tun die Methoden nichts. Siehe Scripting-API für Web-Builds.
using System.Collections.Generic;
using System.IO;
using RTSTerrainStudio;
using UnityEngine;

// Saving, listing and loading maps at runtime (for example for your own map browser or a save game).
public static class MapFilesExample
{
    // The folder the in-game editor uses too: Maps in RTSMapSerializer.StorageRoot.
    static string Folder => RTSMapSerializer.DefaultMapsFolder;

    public static void Save(RTSTerrain terrain, string fileName)
    {
        // SaveToFile creates the folder when needed and remembers the path in terrain.CurrentFilePath.
        terrain.SaveToFile(Path.Combine(Folder, fileName + RTSMapSerializer.FileExtension));
    }

    public static void Load(RTSTerrain terrain, string fileName)
    {
        string path = Path.Combine(Folder, fileName + RTSMapSerializer.FileExtension);
        if (File.Exists(path)) terrain.LoadFromFile(path); // raises MapLoaded
    }

    // Lists all maps with their header (name, size, players) without loading the whole file.
    public static List<RTSMapHeader> ListMaps()
    {
        var result = new List<RTSMapHeader>();
        if (!Directory.Exists(Folder)) return result;
        foreach (string path in Directory.GetFiles(Folder, "*" + RTSMapSerializer.FileExtension))
            if (RTSMapSerializer.TryReadHeaderFromFile(path, out RTSMapHeader header))
                result.Add(header);
        return result;
    }

    // Maps can also travel as bytes (network, cloud save, mod download).
    public static byte[] ToBytes(RTSTerrain terrain) => RTSMapSerializer.Serialize(terrain.Map);

    public static void FromBytes(RTSTerrain terrain, byte[] bytes)
    {
        // The terrain takes ownership of the new MapData and disposes the previous map.
        terrain.SetMap(RTSMapSerializer.Deserialize(bytes));
    }

    // Loads a .rtsmap that was imported into the project (an RTSMapAsset), e.g. for a campaign.
    public static void LoadAsset(RTSTerrain terrain, RTSMapAsset asset)
    {
        // Assigning a different asset loads it immediately (when the terrain is enabled).
        // Assigning the same asset again does nothing, so reload it explicitly.
        if (terrain.MapAsset == asset) terrain.LoadFromAsset();
        else terrain.MapAsset = asset;
    }
}

Dateiaufbau und eigene Dateiabschnitte

"RTSM"            4 bytes magic
int32             format version (RTSMapSerializer.CurrentVersion = 1)
int32 + bytes     uncompressed header (RTSMapHeader)
deflate stream    sections: [int32 tag][int32 length][payload] ...
                  (built-in sections first, then the CustomSectionWriter sections)
                  int32 0  end marker

Beim Lesen werden unbekannte Abschnitte übersprungen. Deshalb kannst du eigene Daten in einer Kartendatei speichern (Trigger, Startressourcen, Questdaten), und ältere Versionen oder andere Werkzeuge lesen die Karte trotzdem. Registriere RTSMapSerializer.CustomSectionWriter, um beim Speichern Abschnitte hinzuzufügen, und RTSMapSerializer.CustomSectionReader, um beim Laden jeden unbekannten Abschnitt zu erhalten. Verwende Tags, die mit 'X' beginnen, damit sie nie mit den eingebauten Abschnitten kollidieren. Tag 0 ist reserviert: Es markiert das Ende der Abschnitte.

Jedes System muss ein eigenes, eindeutiges Tag 'X…' verwenden, denn CustomSectionReader wird für jeden unbekannten Abschnitt aufgerufen. Schreiben zwei Writer dasselbe Tag mit unterschiedlichen Nutzdaten, erhält ein Reader Nutzdaten, die er nicht versteht, und das Laden der Karte schlägt fehl. So verwendet etwa StartingGoldSection im Abschnitt Eigene Dateiabschnitte das Tag XGLD, deshalb nutzt das folgende Beispiel XGMD.

using System.IO;
using System.Runtime.CompilerServices;
using RTSTerrainStudio;
using UnityEngine;

// Stores your own game data (here: starting gold and a victory text) inside the .rtsmap file.
// Older versions of the package and other games simply skip the unknown section.
public static class MapGameData
{
    public sealed class Data
    {
        public int startingGold = 500;
        public string victoryText = "";
    }

    // Section tag "XGMD" as a little-endian FourCC. Start your tags with 'X' so they never
    // collide with the built-in sections, and use a tag no other script of your project uses.
    const int Tag = 'X' | ('G' << 8) | ('M' << 16) | ('D' << 24);
    // Bump this when you change the payload layout, and keep reading the old layouts.
    const int PayloadVersion = 1;

    // One Data object per MapData instance. The table does not keep maps alive.
    static readonly ConditionalWeakTable<MapData, Data> s_Data = new ConditionalWeakTable<MapData, Data>();

    // Returns the game data of a map (created with defaults on first access).
    public static Data Get(MapData map) => s_Data.GetValue(map, _ => new Data());

    // Registers the hooks before any scene loads (play mode and builds) and, in the editor,
    // after every script reload, so maps saved from the RTS Terrain Studio window keep the data too.
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
#endif
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void Register()
    {
        // Remove first, so entering play mode without a domain reload never registers twice.
        RTSMapSerializer.CustomSectionWriter -= Write;
        RTSMapSerializer.CustomSectionWriter += Write;
        RTSMapSerializer.CustomSectionReader -= Read;
        RTSMapSerializer.CustomSectionReader += Read;
    }

    // Called while a map is saved: hand every section you want to write to addSection(tag, payload).
    static void Write(MapData map, System.Action<int, byte[]> addSection)
    {
        if (!s_Data.TryGetValue(map, out Data data)) return; // nothing set: write no section
        using var stream = new MemoryStream();
        using (var w = new BinaryWriter(stream))
        {
            w.Write(PayloadVersion);
            w.Write(data.startingGold);
            w.Write(data.victoryText ?? "");
        }
        addSection(Tag, stream.ToArray());
    }

    // Called for every section the serializer does not know. Ignore tags that are not yours,
    // other scripts may have registered readers too.
    static void Read(MapData map, int tag, byte[] payload)
    {
        if (tag != Tag) return;
        using var r = new BinaryReader(new MemoryStream(payload));
        int version = r.ReadInt32();
        var data = Get(map);
        data.startingGold = r.ReadInt32();
        if (version >= 1) data.victoryText = r.ReadString();
    }
}
Die Daten in diesem Beispiel gehören zu genau einem MapData-Objekt. Operationen, die ein neues Kartenobjekt erzeugen (Laden, Größenänderung, Clone), übernehmen sie nicht von selbst. Lies sie erneut aus der Datei, oder kopiere sie in deinem eigenen Code, wenn du eine Karte klonst oder ihre Größe änderst.

Häufig gestellte Fragen

Alles ist pink (magenta). Was stimmt nicht?

Pink bedeutet, dass ein Shader nicht verwendet werden konnte. RTS Terrain Studio verwendet handgeschriebene Shader ausschließlich für die Universal Render Pipeline.

  1. Öffne Tools ▸ RTS Terrain Studio ▸ Validate Project. Die Prüfung Universal Render Pipeline is active muss grün sein; Fix All (oder die Fix-Schaltfläche der Prüfung) weist ein URP-Asset zu.
  2. Kontrolliere im selben Fenster die Prüfung Package shaders compile. Hat ein Shader Fehler, nennt ihn die Console; importiere dann den Ordner Shaders des Pakets neu (Rechtsklick ▸ Reimport).
  3. Wenn nur deine eigenen Prefabs pink sind (Bäume, Felsen, Einheiten), verwenden ihre Materialien Shader der Built-in-Pipeline. Konvertiere sie mit dem Render Pipeline Converter von Unity, oder weise ihnen URP-Materialien zu.

Ich habe ein RTS Terrain hinzugefügt, aber es wird nichts gerendert.

  • Wähle das Terrain aus und sieh dir seinen Inspector an. Meldet er, dass keine Karte geladen ist, weise ein Karten-Asset zu, erstelle eine neue Karte (Fenster ▸ Tab Map ▸ New Map) oder verwende den New Map Wizard.
  • Stelle sicher, dass die Komponente RTSTerrain aktiviert ist. Eine deaktivierte Komponente entlädt ihre Karte.
  • Prüfe die Kamera: Die Chunks liegen auf dem Layer, der in den Terrain-Einstellungen festgelegt ist (Chunk Layer), und dieser Layer muss in der Culling Mask der Kamera enthalten sein. Hole das Terrain in der Scene View mit F ins Bild: Eine Karte mit 128 Zellen zu je 2 m ist 256 m breit.
  • Ein Terrain ohne Tileset ist nicht unsichtbar, sondern wird mit neutralen Ersatzfarben gerendert. Weise ein Tileset zu, oder führe Tools ▸ RTS Terrain Studio ▸ Create Demo Content aus und verwende das Demo-Tileset.
  • Klicke im Terrain-Inspector auf Rebuild Terrain, um alle Chunks, Texturen und die Pathing-Karte neu zu erstellen.

Der Pinsel malt nicht dort, wo sich mein Cursor befindet.

Der Editor verwendet Unitys DPI-korrekten Strahl der Scene View, und der Pinselcursor wird vom Terrain-Shader gezeichnet. Bei einer normalen Einrichtung liegt der Pinselauftrag daher immer genau unter dem Cursor, auch bei einer Anzeigeskalierung von 150 % oder mehr. Falls nicht:

  • Das Terrain oder ein übergeordnetes Objekt ist skaliert oder um X oder Z gedreht. Verwendet werden nur die Position und die Y-Rotation. Der Terrain-Inspector warnt vor beidem; setze die Skalierung auf 1 und die X/Z-Rotation auf 0 zurück, und ändere statt einer Skalierung die Zellgröße der Karte.
  • Das Terrain verwendet einen Material Override, dessen Shader den Pinselcursor nicht zeichnet. Du malst dann an der richtigen Stelle, siehst aber den Cursor nicht. Verwende den Terrain-Shader des Pakets, oder übernimm dessen Code für den Pinselcursor.

Der Nebel des Krieges ist nicht sichtbar.

  • Die Szene braucht genau einen RTSFogOfWarManager (GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager).
  • Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und behebe das Problem bei Fog of War renderer feature. Das Renderer Feature verdunkelt alles auf dem Bildschirm. Ohne dieses Feature berücksichtigen nur die paketeigenen Shader (Terrain, Wasser, Vegetation) den Nebel; deine eigenen Einheiten und Props bleiben unverdeckt.
  • Aktiviere im Edit Mode die Option Preview In Edit Mode am Manager.
  • Reveal All muss ausgeschaltet sein. Start Explored lässt die ganze Karte als erkundet erscheinen (abgedunkelt statt schwarz).
  • Deine Einheiten brauchen einen RTSFogOfWarRevealer, dessen Team das Local Team des Managers ist. Sichtquellen anderer Teams decken für das lokale Team nichts auf; ohne eine passende Sichtquelle bleibt die Karte daher verdeckt (standardmäßig schwarz).

Vegetation (oder Wasser oder das Terrain) fehlt in Builds, im Editor ist aber alles in Ordnung.

  • Führe Tools ▸ RTS Terrain Studio ▸ Validate Project aus und behebe das Problem bei Shaders included in builds. Das Paket listet seine Shader in einem Shader-Referenz-Asset unter Resources, das vor jedem Player-Build automatisch aktualisiert wird. Hast du es gelöscht oder verschoben, erstellt die Prüfung es neu.
  • Behebe das Problem bei GPU instancing on instanced prototypes: Instanzierte Bäume, Details und Dekorobjekte brauchen Materialien mit aktiviertem Enable GPU Instancing.
  • Stelle sicher, dass die Karte im Build enthalten ist: Karten, die als RTSMapAsset einem Terrain in einer Szene des Builds zugewiesen sind, werden automatisch aufgenommen; Kartendateien, die von der Festplatte geladen werden, müssen auf dem Rechner des Spielers vorhanden sein (zum Beispiel in RTSMapSerializer.DefaultMapsFolder).
  • Shader-Stripping oder ein anderes URP-Asset für die Zielplattform (Quality-Einstellungen) kann Funktionen entfernen. Prüfe, ob das URP-Asset des Builds dieselben Einstellungen hat wie das im Editor.
  • In einem Web-Build unter WebGL 2.0 kann ein URP-Asset mit vielen aktivierten Funktionen die Grenze von 16 Texturen pro Draw Call überschreiten, und das Terrain verschwindet. Siehe den Hinweis zu OpenGL ES und WebGL 2.0 unter Hinweise für Mobilgeräte.
  • Atmosphärischer Nebel fehlt in Builds: siehe Nebel in Player-Builds.

Wasser sieht schwarz oder grau aus oder hat keine Lichtbrechung.

  • Der Wasser-Shader liest die Tiefe und die Farbe der Szene. Aktiviere Depth Texture und Opaque Texture im URP-Asset: Tools ▸ RTS Terrain Studio ▸ Validate Project ▸ Depth and Opaque Texture enabled ▸ Fix.
  • Setze Opaque Downsampling des URP-Assets auf None; Downsampling macht die Lichtbrechung unscharf.
  • Prüfe die Kamera: Ihre Overrides für Depth Texture und Opaque Texture dürfen nicht auf Off stehen. Das Wasser richtet sich nach der Einstellung der jeweiligen Kamera, und die Scene View verwendet immer das URP-Asset.
  • Prüfe bei einem Web-Build das URP-Asset der Qualitätsstufe, die Web-Builds verwenden (die Prüfung Web quality levels have Depth and Opaque Texture, siehe Web-Prüfungen im Project Validator).
  • Die Wasserfarben stammen aus dem Water Profile des Tilesets. Prüfe dort die Farben für Flach- und Tiefwasser.

Wie erreiche ich die beste Performance?

  • Burst muss aktiviert sein (Jobs ▸ Burst ▸ Enable Compilation). Pinsel, Pathing, Nebel des Krieges, der Generator und die Minikarte laufen als Burst-Jobs.
  • Chunk size (Terrain-Einstellungen): 16, 32 (Standard) oder 64 Zellen. Kleinere Chunks werden beim Bearbeiten schneller neu aufgebaut, größere Chunks bedeuten im Spiel weniger Draw Calls.
  • Surface Subdivisions: 1 ergibt den klassischen RTS-Look mit den wenigsten Dreiecken, 2–4 ergeben sanftere Hügel.
  • Collider Mode None lässt die Mesh-Collider weg, wenn du nur die Abfrage-API verwendest (Raycast, GetHeight).
  • Verwende für Bäume und Dekor bevorzugt den Spawn-Modus Instanced; nutze den Modus GameObject nur für Objekte, die dein Gameplay als GameObjects braucht (abbaubare Bäume, Dekor mit Skripten).
  • Verringere für schwächere Hardware Detail Density Multiplier und Detail Distance Multiplier am Vegetation Renderer.
  • Nebel des Krieges: Resolution Per Cell 1 und ein größeres Update Interval kosten weniger Leistung.
  • Führe den Kartenvalidator aus: Seine Performance-Warnungen melden zu viele Bäume, Dekorobjekte, Objekte im GameObject-Modus oder Detailschichten.

Kann ich mehrere Terrains in einer Szene verwenden?

Ja. Jedes RTSTerrain hat seine eigene Karte, sein eigenes Tileset, eigene Bibliotheken und einen eigenen Transform. RTSTerrain.All listet alle auf, RTSTerrain.FindAt(position) findet das Terrain unter einer Position, und RTSTerrain.RaycastAll führt einen Raycast gegen alle aus. Im Editorfenster legst du das bearbeitete Terrain in der Kopfzeile fest (es wird auch übernommen, wenn du ein Terrain in der Szene auswählst); im In-Game-Editor verwendest du Map ▸ Edited terrain. Hinweise:

  • Der Undo-Verlauf gehört zum bearbeiteten Terrain: Wechselst du das bearbeitete Terrain, wird er gelöscht.
  • Der Manager für den Nebel des Krieges deckt ein Terrain ab (sein Feld Terrain oder RTSTerrain.Main, wenn es leer ist).
  • A* braucht pro Terrain einen Graph Builder mit eigenen Graph-Namen; die NavMesh-Integration braucht einen Builder pro Terrain.

Kann ich ein Terrain verschieben oder drehen?

Ja: Position und Y-Rotation werden vollständig unterstützt, auch zur Laufzeit. Alle Abfragen, Pinsel, Vegetation, Dekor, Wasser, Pathing, der Nebel des Krieges und die Integrationen folgen dem Transform. Rotation um X oder Z und Skalierung werden nicht unterstützt (der Inspector warnt davor); um eine Karte zu vergrößern, verwende Resize im Tab Map oder eine größere Zellgröße.

Funktioniert es mit dem Input System, dem alten Input Manager oder mit beiden?

Alle drei Einstellungen von Project Settings ▸ Player ▸ Active Input Handling funktionieren. Laufzeitcode liest Eingaben über RTSInput. Diese Klasse verwendet das Paket Input System, wenn es installiert und aktiv ist, und sonst den alten Input Manager. Die Prüfung Input handling von Tools ▸ RTS Terrain Studio ▸ Validate Project zeigt, was dein Projekt verwendet. Auch in deinem eigenen Gameplay-Code kannst du RTSInput verwenden (PointerPosition, GetMouseButtonDown, GetKey(RTSKey) …), damit er mit jeder dieser Einstellungen läuft. Für Tastenkürzel mit Buchstaben, die dem Tastaturlayout des Spielers folgen sollen, verwende GetKeyDownByLabel(RTSKey); für Symboltasten wie [ oder + verwende GetCharacterDown(char).

Kann ich mein Spiel oder den In-Game-Editor in einem Webbrowser veröffentlichen?

Ja. In Web-Builds (WebGL 2.0) laufen das Terrain, die Vegetation, das Wasser, der Nebel des Krieges, der In-Game-Editor und die Beispiele in Desktop-Browsern mit Maus und Tastatur. Installiere das Vollfenster-Template mit Tools ▸ RTS Terrain Studio ▸ Install WebGL Template. Die Einstellungen, das Speichern von Karten im Browser und Hinweise zum Hosting findest du unter Web-Builds (WebGL).

Kann ich das Paket in einen anderen Ordner verschieben?

Ja. Nichts hängt von einem festen Pfad ab; das Paket findet seine Dateien relativ zu seinem eigenen Speicherort.

Fehlerbehebung

Arbeite diese Checkliste von oben nach unten ab; die meisten Probleme sind nach den ersten drei Schritten gelöst.

  1. Lies die Console. Meldungen des Pakets beginnen mit [RTS Terrain Studio] und sind mit dem Objekt verknüpft, zu dem sie gehören (klicke auf die Meldung, um es auszuwählen). Behebe zuerst Kompilierfehler: Solange sich ein Skript nicht kompilieren lässt, wird nichts anderes aktualisiert.
  2. Validiere das Projekt. Tools ▸ RTS Terrain Studio ▸ Validate Project prüft URP, Depth Texture und Opaque Texture, das Renderer Feature für den Nebel des Krieges, die in Builds erhaltenen Modi des atmosphärischen Nebels, den Farbraum, Burst/Collections/Mathematics, die Eingabeverarbeitung, die Shader-Kompilierung, die Shader in Builds, die Textur-Arrays der Tilesets, GPU-Instancing, Terrains mit Tilesets und Terrains, die in Kartendateien speichern. Ist Web die aktive Plattform, kommen die Web-Qualitätsstufen und das Web-Template hinzu. Klicke auf Fix All (ein Dialog listet vorher jede Änderung auf) und dann auf Run Again.
  3. Prüfe den Terrain-Inspector. Ist eine Karte geladen, ein Tileset zugewiesen, die Komponente aktiviert? Gibt es Warnungen zu Skalierung oder Rotation? Klicke auf Rebuild Terrain.
  4. Validiere die Karte. Tab Map ▸ Validation ▸ Validate Map findet fehlende Prototypen, fehlerhafte Bodentexturierung, unerreichbare Startpositionen und Performance-Probleme.
  5. Prüfe die Inhalte. Fehlende Texturen, Pflanzen oder Dekorobjekte bedeuten meist, dass ein Tileset oder eine Bibliothek nicht zugewiesen ist oder dass eine Prototyp-ID der Karte in der Bibliothek nicht existiert. Wenn du Tools ▸ RTS Terrain Studio ▸ Create Demo Content erneut ausführst, werden die Demo-Assets wiederhergestellt.
  6. Prüfe die Integrationen. Kompilierfehler in Integrations nach dem Entfernen von Mirror bedeuten, dass das Symbol MIRROR noch definiert ist (Mirror entfernen). Eine Integration, die im Welcome-Fenster als Installed, aber nicht als Active markiert ist, hat eine Version, die ihre Anforderung nicht erfüllt.
  7. Play Mode ohne Domain Reload wird unterstützt. Verhält sich etwas beim zweiten Start des Play Mode anders, aktiviere einmal den Domain Reload, um zu sehen, ob dein eigener statischer Zustand die Ursache ist.
  8. Kommst du immer noch nicht weiter? Reproduziere das Problem in einer neuen Szene mit einem Terrain, das über GameObject ▸ RTS Terrain Studio ▸ RTS Terrain erstellt wurde, und mit den Demo-Inhalten. Gib die Unity-Version, die URP-Version, die Console-Ausgabe und die Schritte an, wenn du um Hilfe bittest.
SymptomWahrscheinliche UrsacheSiehe
Pinke MaterialienURP nicht aktiv, Shader-Fehler, Built-in-Materialien an deinen PrefabsFAQ
Terrain unsichtbarKeine Karte geladen, Komponente deaktiviert, Chunk-Layer per Culling ausgeblendetFAQ
Pinsel malt neben dem CursorSkaliertes Terrain oder X/Z-RotationFAQ
Kein Nebel des KriegesRenderer Feature fehlt, keine Sichtquelle des lokalen Teams, Reveal All anFAQ
Schwarzes WasserDepth / Opaque Texture ausFAQ
Fehlende Vegetation in BuildsShader-Referenzen, GPU-InstancingFAQ
Einheiten bewegen sich nichtPathing-Karte noch nicht erstellt, Start oder Ziel nicht begehbar, Provider ohne Graph oder NavMeshKartenvalidierung, Integrationen
Generator platziert weniger StartpositionenNicht genug ebene Fläche für Flat Radius / Edge MarginGenerator-Einstellungen
Heightmap-Import ist flach oder extremHöhenskalierung 0 im Code, falsche RAW-Größe oder Byte-ReihenfolgeHeightmaps
Kompilierfehler zu Mirror nach dessen EntfernenVeraltetes Define MIRRORIntegrationen entfernen
Dialog RTS Terrain Studio requires URPURP 17.3 nicht installiert oder Editor älter als Unity 6.3Unity und Render-Pipeline
Web-Build: winzige UI, verlorene Karten, Tasten öffnen BrowserfunktionenTemplate mit fester Größe, Browserspeicher, Tastenkürzel des BrowsersWeb-Builds (WebGL)

Änderungsprotokoll

Version 1.0.0 – Erstveröffentlichung

Erfordert Unity 6.3 LTS (6000.3) und die Universal Render Pipeline 17.3 mit Burst, Collections und Mathematics.

Kartenmodell

  • Raster aus Zellen und Eckvertices im Stil klassischer RTS-Spiele, 16 bis 1024 Zellen pro Seite (Vielfache von 16), frei wählbare Zellgröße.
  • 16 Klippenstufen mit prozedural erzeugten, abgerundeten Klippenwänden; Feinhöhe über jeder Stufe; Rampen; 4 Klippentypen pro Tileset.
  • 16 Bodenschichten pro Tileset in hochauflösenden Splatmaps, höhenbasierte Überblendung, optionales Triplanar-Mapping und Anti-Tiling; beliebige Texturgrößen und -formate.
  • Wasser pro Vertex mit Flach- und Tiefwasser.
  • Automatisch abgeleitete Pathing-Karte (begehbar, bebaubar, befliegbar, Flach- und Tiefwasser) plus gemalte Overrides.
  • Regionen, Startpositionen, Marker, Karteninfo und Umgebungsbeleuchtung werden in der Karte gespeichert.

Bearbeitung

  • Ein gemeinsamer Werkzeugsatz für den Unity Editor und den In-Game-Editor: Höhe, Klippen, Malen (weich, kachelweise, Füllen, automatische Texturierung), Vegetation, Dekor, Wasser, Pathing und Marker.
  • Pinsel mit Kreis-, Quadrat-, Rauten- und Texturstempelform, Abnahmekurve, Rotation, Abstand und Streuung; symmetrisches Malen (Spiegelung an X/Z, vierfache Spiegelung, zwei- und vierfache Rotation).
  • Rückgängigmachen und Wiederherstellen mit Speicherbudget, integriert in das Undo-System von Unity.
  • Andockbares Fenster von RTS Terrain Studio, Integration in die Scene View mit projiziertem Pinselcursor und Overlays, eigene Inspectors und Importer für alle Assets, Welcome-Fenster, New Map Wizard und Projektvalidator.
  • In-Game-Editor auf Basis von UI Toolkit: Werkzeugleiste, Werkzeug- und Pinsel-Panels, Paletten, Kartenbrowser, Tastenkürzel und Spieltest.

Kartenwerkzeuge

  • Prozeduraler Kartengenerator mit Seed, sechs Stilen, symmetrischen Layouts, Rampen, Seen und Inseln, Kartenrändern, automatischer Texturierung, Vegetation, Dekor und Startpositionen; speicherbare Presets.
  • Import (drei Modi) und Export von 16-Bit-Heightmaps im PNG- und RAW-Format, Export von Splat-Bildern.
  • Minikarten-Baker, Kartenvalidator, Größenänderung mit Anker.
  • Binäres Format .rtsmap, das Editor und Spiel gemeinsam nutzen, mit eigenen Dateiabschnitten.

Laufzeitsysteme

  • In Chunks unterteilte Terrain-Meshes, gebaut in Burst-Jobs, optionale Mesh-Collider und eine physikfreie Abfrage-API.
  • GPU-instanzierte Vegetation mit Wind und Culling; Bäume als Instanzen oder GameObjects.
  • Wasser-Rendering mit Tiefenfarbe und Lichtbrechung; Dekor-Manager für beliebige Prefabs.
  • Klassischer Nebel des Krieges wie in RTS-Spielen: Sicht pro Team, Gedächtnis für erkundete Gebiete, von Klippen und Blockern unterbrochene Sichtlinie, geteilte Sicht, Unterstützung der Minikarte.
  • Eingebauter A*-Pathfinder in Burst mit Einheitengrößen, die den Freiraum berücksichtigen, und Pfadglättung.
  • Beispiele: RTS-Kamera, Auswahl, Bewegungsbefehle mit Formationen, Testeinheiten, Minikarten-UI, Demoszenen und generierte Demo-Inhalte.

Plattformen

  • Shader für Shader Model 3.5, daher läuft das Paket auch unter OpenGL ES 3.0 und in Web-Builds (WebGL 2.0).
  • Web-Builds: Karten im Browserspeicher, Download Map, Import Map und Drag-and-drop, eine Warnung vor dem Verlassen der Seite bei nicht gespeicherten Änderungen, auf den Browser abgestimmte Tastenkürzel, ein fensterfüllendes Web-Template und Web-Prüfungen im Projektvalidator.
  • Die Textur-Arrays der Tilesets werden vor Player-Builds automatisch neu erstellt, passend zur Texturkomprimierung des Builds.
  • Ohne URP 17.3 bleibt das Paket inaktiv und erklärt, wie du URP installierst, statt Kompilierfehler anzuzeigen.

Integrationen

  • Mirror: Karten-Streaming mit Cache, serverautoritative Einheiten, Nebel des Krieges pro Team, Interest Management auf Basis des Nebels des Krieges.
  • A* Pathfinding Project Pro: Grid Graphs aus der Pathing-Karte mit inkrementellen Updates, AIPath- und RVO-Einheiten.
  • Unity AI Navigation: NavMesh aus dem Terrain und der Pathing-Karte, inkrementelle Updates, Pfad-Provider und NavMeshAgent-Modus.