RTS Terrain Studio 1.0.0
Manual · Versión 1.0.0

RTS Terrain Studio

Crea mapas RTS de estilo clásico con mesetas, acantilados nítidos, rampas, suelo pintado, bosques, agua y decoración, en el Unity Editor y dentro de tu juego. La niebla de guerra, la búsqueda de rutas y las consultas del terreno vienen integradas.

Unity 6.3 LTSURP 17.3En el editor y en el juegoMirror, A* Pathfinding Project, Unity NavMesh

Descripción general

RTS Terrain Studio es un editor de mapas para juegos de estrategia en tiempo real de estilo clásico, creado para Unity 6.3 LTS y el Universal Render Pipeline (URP) 17.3. Con él puedes esculpir mesetas con acantilados verticales nítidos, pintar el suelo, plantar bosques, llenar de agua lagos y ríos, colocar decoración, marcar las posiciones iniciales de los jugadores y comprobar por dónde pueden caminar las unidades y dónde se pueden levantar edificios. Cuando termines, el mismo mapa ya está listo para jugar: incluye niebla de guerra, búsqueda de rutas y consultas del terreno, y ninguna de ellas necesita un collider.

El editor funciona en dos lugares, y ambos usan las mismas herramientas y el mismo formato de archivo de mapa (.rtsmap):

  • En el Unity Editor, mediante Tools ▸ RTS Terrain Studio ▸ Editor Window y la Scene View. Aquí creas los mapas que se publican con tu juego.
  • En el juego, mediante el componente RTSRuntimeEditor. Es un editor completo con una interfaz de UI Toolkit. Tus jugadores y modders pueden usarlo en tu juego publicado para crear mapas, guardarlos, cargarlos y probarlos jugando.

El paquete no contiene arte de terceros. En su lugar, un solo comando de menú genera un conjunto completo de contenido de demostración: texturas, árboles y props low-poly, dos tilesets, tres mapas y tres escenas listas para jugar. En menos de un minuto tras importar el paquete ya tienes algo que explorar.

La escena Gameplay Demo: un mapa de mesetas con acantilados, ríos, bosques y unidades de ejemplo bajo la niebla de guerra
La escena RTS Terrain Studio - Gameplay Demo en el mapa de demostración Two Rivers (1v1).

Este manual está pensado para todos: desde tu primer mapa hasta la ampliación del código fuente. Si eres nuevo, sigue los pasos de Tus primeros cinco minutos, más abajo, y después lee los capítulos en orden. Si eres programador, el código fuente está completamente comentado, cada tipo público tiene documentación XML y la sección Estructura de carpetas muestra dónde está cada cosa.

Cómo se construye un mapa

Si has usado el editor de mapas de un RTS clásico, la mayoría de estas ideas te resultarán familiares. Un mapa es una cuadrícula de celdas cuadradas (de 2 m de ancho de forma predeterminada). Las esquinas de las celdas son los vértices, y la mayoría de los datos se guardan por vértice o en texturas de alta resolución que cubren el mapa.

ConceptoQué es
Nivel de acantiladoUn escalón de altura con valor entero, de 0 a 15, para cada vértice. Los vértices vecinos situados en niveles distintos forman una pared de acantilado vertical. La altura de nivel predeterminada es de 2 m y los mapas nuevos empiezan en el nivel 2, así que puedes excavar hacia abajo para crear lagos y ríos.
Altura finaUn desplazamiento de altura suave sobre el nivel de acantilado, para colinas y valles. La altura del suelo de un vértice es CliffLevel × CliffLevelHeight + Height.
RampaLas celdas cuyas esquinas están marcadas como rampa pasan suavemente de un nivel al siguiente, para que las unidades puedan subir.
Tipo de acantiladoEl material de las paredes de acantilado (por ejemplo, roca o acantilados cubiertos de hierba). Un tileset tiene hasta 4.
Capa de sueloUna de las hasta 16 texturas de suelo pintadas (hierba, tierra, roca…), guardadas en splat maps.
AguaUna altura de la superficie del agua por vértice. La profundidad determina si es agua poco profunda (transitable) o agua profunda (no transitable).
Detalles y árbolesHierba y flores pintadas (densidad por texel), además de árboles colocados uno a uno.
DecoraciónCualquier prefab que coloques en el mapa: rocas, ruinas, cajas, vallas, edificios.
PathingPara cada celda de pathing: transitable, edificable, sobrevolable, agua poco profunda o profunda, acantilado, obstruida o empinada. Se deriva automáticamente del mapa, y encima puedes pintar ajustes manuales.
MarcadoresRegiones con nombre (rectángulos con propiedades clave/valor para la lógica de tu juego), posiciones iniciales de los jugadores y puntos con nombre, como posiciones de cámara.
TilesetUn asset RTSTileset que define el aspecto: capas de suelo, tipos de acantilado y el estilo del agua, como los tilesets «Summer» o «Winter» de los editores de mapas de los RTS clásicos.
BibliotecasUn RTSVegetationLibrary (detalles y árboles) y un RTSDecorLibrary (prefabs de decoración). Son las paletas de las que eliges lo que pintas y colocas.

En una escena, un mapa se encuentra en un GameObject con el componente RTSTerrain. El menú GameObject ▸ RTS Terrain Studio ▸ RTS Terrain también le añade los componentes de los subsistemas (RTSVegetationRenderer, RTSWaterRenderer y RTSDecorManager). El terreno lee el mapa de un archivo .rtsmap de tu proyecto (un RTSMapAsset). Los meshes de los chunks (bloques de celdas), la hierba y el agua que muestra se generan a partir de ese archivo. Nunca se guardan en la escena, así que tus escenas siguen siendo pequeñas.

Tus primeros cinco minutos

Es la forma más rápida de ver todo en funcionamiento. No necesitas configurar nada más que importar el paquete.

  1. Abre Tools ▸ RTS Terrain Studio ▸ Validate Project y haz clic en Fix All. Esto configura URP, la renderer feature de la niebla de guerra, las texturas de cámara que usa el agua y el espacio de color. Antes de cambiar nada, un cuadro de diálogo de confirmación te explica los cambios.
  2. Ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content. Aparece una barra de progreso mientras se generan texturas, modelos, tilesets, mapas y escenas. Cuando termina, la escena RTS Terrain Studio - Gameplay Demo queda seleccionada en la ventana Project.
  3. Haz doble clic en esa escena para abrirla y después presiona Play.
  4. Selecciona tus unidades con el botón izquierdo del ratón (haz clic o arrastra un rectángulo) y haz clic con el botón derecho en el suelo para moverlas. Desplaza la cámara con WASD o las teclas de flecha, acerca y aleja con la rueda del ratón, y presiona F1 para ver todos los controles.
  5. Detén el modo Play y abre la escena RTS Terrain Studio - Editor Playground. Abre Tools ▸ RTS Terrain Studio ▸ Editor Window, elige una herramienta y pinta en la Scene View. Mantén presionada Shift para hacer lo contrario (por ejemplo, bajar en lugar de subir). Presiona Ctrl+Z para deshacer.
  6. Abre la escena RTS Terrain Studio - In-Game Editor Demo y presiona Play. Este es el editor que reciben tus jugadores. Presiona F5 para probar el mapa en el juego con unidades, y F2 para mostrar u ocultar el editor.

La ventana Tools ▸ RTS Terrain Studio ▸ Welcome se abre una vez por proyecto y muestra los mismos pasos en forma de botones. Vuelve a abrirla desde el menú cuando quieras.

Por dónde empezar

Quiero…Empieza aquí
Configurar un proyecto correctamenteConfiguración y Tools ▸ RTS Terrain Studio ▸ Validate Project
Crear mi primer mapa paso a pasoInicio rápido y Tools ▸ RTS Terrain Studio ▸ New Map Wizard...
Conocer la ventana del editor y todas las herramientasVentana del editor y Scene View
Permitir que los jugadores creen mapas en mi juegoEditor en el juego
Publicar mi juego o el editor de mapas en un navegador webBuilds web (WebGL)
Ocultar el mapa hasta que las unidades lo explorenNiebla de guerra
Usar Mirror, A* Pathfinding Project o Unity NavMeshIntegraciones
Consultar el terreno o escribir mis propias herramientas en C#Guía de scripting
Corregir algo que no se ve o no funciona bienSolución de problemas

Características

Cada herramienta de edición que aparece a continuación existe tanto en el Unity Editor como en el editor en el juego, porque ambos usan las mismas clases de herramienta (subclases de RTSTool). Los nombres de esta lista son los que ves en la interfaz de usuario.

La ventana del editor de RTS Terrain Studio con la pestaña Decor abierta
La ventana RTS Terrain Studio: categorías de herramientas, botones de herramienta, ajustes de simetría y la paleta de decoración con miniaturas generadas.

Terreno y acantilados

  • Mapas de 16 a 1024 celdas por lado, en incrementos de 16. El New Map Wizard ofrece los presets Tiny (64), Small (96), Medium (128), Large (192), Huge (256), Epic (384) y Epic XL (512), o cualquier tamaño personalizado. El tamaño de celda y la altura de nivel de acantilado se pueden ajustar libremente.
  • 16 niveles de acantilado (0–15) con paredes de acantilado redondeadas y generadas proceduralmente. Dentro de cada celda de acantilado se traza un contorno sobre una subcuadrícula, y el ruido y la inclinación hacen que las paredes parezcan roca natural (todo esto se configura en RTSTerrainSettings).
  • Altura fina sobre los niveles, para que las mesetas puedan tener colinas. El suelo suave puede usar de 1 a 4 quads por lado de celda: 1 da el aspecto clásico de RTS, y de 2 a 4 dan colinas más suaves.
  • Herramientas de Height: Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode.
  • Herramientas de Cliffs: Raise / Lower Level, Set Level, Ramp, Cliff Type, Clean Cliffs.
  • Hasta 4 tipos de acantilado por tileset, pintados por vértice, con proyección triplanar de texturas en las paredes.
  • Un MeshCollider opcional por chunk en la capa que elijas. Las consultas que no usan física también funcionan sin él: GetHeight, GetNormal, Raycast, GetCliffLevel, IsWater, GetPathing, IsWalkable e IsBuildable.
  • Cualquier número de terrenos en una misma escena. Cada terreno tiene su propia posición y rotación en Y. RTSTerrain.FindAt(worldPosition) y RTSTerrain.RaycastAll(ray, out hit) encuentran el terreno correcto.
  • Puedes redimensionar un mapa en ambos sentidos (ampliarlo o reducirlo) con un ancla. Puedes importar y exportar mapas de alturas como PNG de 16 bits o RAW de 16 bits, y exportar los splat maps como PNG.

Pintura

  • 16 capas de suelo por tileset, pintadas en cuatro splat maps RGBA. De forma predeterminada hay 4 texels de splat por celda (puedes elegir de 1 a 8), así que los bordes pintados son mucho más finos que la cuadrícula de celdas.
  • Herramientas de pintura: Paint Layer (pincel suave), Tile Paint (pintado de bordes duros, alineado a las celdas, al estilo clásico de RTS), Fill / Replace, Smooth Paint y Auto Paint.
  • Auto Paint pinta según reglas guardadas en un asset RTSAutoPaintProfile. Una regla puede comprobar la altura, la pendiente, el nivel de acantilado, la distancia a los acantilados, la distancia al agua y el ruido. Usa la herramienta Auto Paint como pincel, o deja que el generador de mapas aplique un perfil a todo el mapa.
  • Mezcla por altura entre capas, anti-tiling por capa (oculta la repetición visible de las texturas), una variación macro para todo el tileset, y tinte, suavidad, valor metálico e intensidad de normales por capa.
  • Una capa puede cambiar el pathing allí donde domina. Por ejemplo, los tilesets de demostración hacen que Rock y Blight no sean edificables, como las casillas no edificables de los RTS clásicos.
  • Pinceles: Circle, Square, Diamond o Custom con cualquier textura como sello (assets RTSBrushShape). Puedes ajustar el radio, la intensidad, la dureza, una curva de atenuación opcional, la rotación, la rotación aleatoria, el espaciado y la variación aleatoria.
  • Pintura simétrica para mapas competitivos equilibrados: Mirror X, Mirror Z, Mirror XZ, Rotate 2 (simetría central de 180°), Rotate 4 (90°, solo mapas cuadrados) y Mirror Diagonal (solo mapas cuadrados). La simetría funciona con todas las herramientas, incluida la colocación de objetos.

Vegetación

  • Detalles como hierba, flores y plantas pequeñas, pintados como una densidad por capa y dibujados en la GPU como meshes o quads cruzados, con viento.
  • Árboles colocados uno a uno o dispersados con un pincel. Un árbol se puede dibujar con instanciación por GPU (con un proxy de collider opcional) o generar como GameObject para mecánicas de juego como la recolección de madera.
  • Los árboles obstruyen el pathing automáticamente mediante su huella.
  • Herramientas de vegetación: Paint Details, Erase Details, Place Trees, Erase Trees.
  • El shader RTS Terrain Studio/Vegetation añade viento, translucidez y un tinte para cada instancia. También funciona cualquier otro material con instanciación por GPU.
  • Ajustes de calidad en RTSVegetationRenderer: multiplicadores de densidad de detalles, distancia de detalles y distancia de árboles.

Agua

  • Una altura del agua por vértice, para que los lagos puedan estar a distintas alturas. El agua se dibuja como meshes transparentes divididos en chunks que se construyen con jobs de Burst.
  • El agua poco profunda y el agua profunda se determinan por la profundidad (WaterProfile.shallowDepthLimit). El agua poco profunda es transitable y el agua profunda no, como en los RTS clásicos.
  • El shader RTS Terrain Studio/Water ofrece color según la profundidad, normales en movimiento, olas, refracción, espuma en la orilla y un desvanecimiento suave junto a la orilla. El estilo procede del WaterProfile del tileset.
  • Herramientas de agua: Paint Water, Erase Water, Flood Fill.

Decoración (doodads)

  • Cualquier prefab puede ser decoración. Los prototipos se guardan en un RTSDecorLibrary con miniaturas que se generan automáticamente.
  • Cada prototipo se genera como GameObject (para objetos con los que interactúa la lógica del juego) o se dibuja con instanciación por GPU (para grandes cantidades de props estáticos).
  • Rotación, escala y tinte aleatorios, alineación con la pendiente, ajuste a la cuadrícula y una huella de pathing (circular o rectangular).
  • Herramientas de decoración: Place Decor (colocación individual o dispersa), Erase Decor, Select Decor (mover, rotar, escalar, duplicar, eliminar).

Pathing y búsqueda de rutas

  • Un mapa de pathing que se deriva automáticamente y se mantiene actualizado mientras editas. Tiene en cuenta los acantilados, la pendiente, la profundidad del agua, las huellas de árboles y de decoración, y las reglas de las capas de suelo.
  • Herramientas de pathing: Paint Pathing (ajustes manuales como no transitable, no edificable o forzar transitable) y Clear Pathing. Las superposiciones de pathing de los editores muestran el resultado.
  • Un buscador de rutas integrado (RTSPathfinder): A* compilado con Burst sobre la cuadrícula de pathing, con 8 vecinos, sin cortar esquinas, teniendo en cuenta el radio de la unidad (holgura) y con suavizado de rutas. No necesita ningún paquete de terceros.
  • Solicitudes de ruta asíncronas para muchas unidades mediante RTSPathfindingService.

Niebla de guerra

  • Reglas clásicas de RTS: cada equipo tiene su propia visibilidad y se recuerdan las zonas exploradas. De forma predeterminada, una unidad solo ve su propio nivel de acantilado y los niveles inferiores. Los árboles y la decoración también pueden bloquear la visión (LineOfSightMode y la opción blocksVision de un prototipo).
  • Hasta 32 equipos, visión compartida entre aliados (RTSFogOfWarManager.SetAlliance), una textura de niebla de guerra suave y difuminada y compatibilidad con el minimapa.
  • Una renderer feature de URP (RTSFogOfWarRendererFeature) oscurece todo lo que hay en pantalla, sea cual sea el material que use. Si falta esta renderer feature, los shaders del propio paquete aplican la niebla de guerra por sí mismos.
  • Las unidades con RTSFogOfWarRevealer revelan el mapa, y los objetos con RTSFogOfWarHideable se ocultan mientras no son visibles. Los edificios y los props pueden seguir visibles como «fantasmas» una vez descubiertos.
  • La visibilidad se calcula con jobs de Burst.

Editor en el juego

  • Añádelo con GameObject ▸ RTS Terrain Studio ▸ In-Game Editor. Es una interfaz de UI Toolkit con barra de menús, barras de categorías y de herramientas, un panel de herramienta con paletas y miniaturas, una barra de estado, descripciones emergentes y notificaciones.
  • New, Open, Save y Save As con un explorador de mapas. De forma predeterminada, los mapas se guardan en Application.persistentDataPath y, en las builds web, en el almacenamiento del navegador; los comandos Download e Import permiten pasarlos del navegador al equipo y del equipo al navegador. También puedes generar un mapa, editar la información del mapa y redimensionarlo.
  • Deshacer y rehacer con un presupuesto de memoria, atajos de teclado, simetría y superposiciones de cuadrícula, pathing y marcadores.
  • Un interruptor de prueba de juego (F5) que pone en marcha la jugabilidad de ejemplo en el mapa que estás editando y, al terminar, te devuelve al editor.
  • Su propia cámara de editor (RTSRuntimeEditorCamera) y sus propios gizmos, sin ninguna dependencia de la API UnityEditor, así que funciona en las builds del juego.

Generador de mapas

  • Un generador procedural basado en semillas (RTSMapGenerator) con los presets de estilo Plains, Hills, Highlands, Islands, Canyon y Arena.
  • Crea mesetas y acantilados, rampas (con comprobaciones de conectividad), lagos y mares, un borde del mapa, pintado automático, detalles, bosques, decoración y posiciones iniciales, con cualquier modo de simetría.
  • La misma semilla siempre da el mismo mapa. Puedes guardar los ajustes como assets RTSMapGeneratorPreset.
  • Puedes ejecutarlo desde el New Map Wizard, la ventana del editor, el editor en el juego o tu propio código.

Jugabilidad de ejemplo

  • Una cámara RTS (desplazamiento, desplazamiento por bordes, zoom, rotación, límites del mapa), selección de unidades (clic, rectángulo, Shift, doble clic, grupos de control) y órdenes (mover en formación, encolar, detener, mantener posición, patrullar).
  • Unidades sustitutas que siguen la altura del terreno, despejan la niebla de guerra y usan cualquier proveedor de rutas.
  • Un minimapa en el que puedes hacer clic para mover la cámara, un HUD, una demo de colocación de edificios que respeta el indicador de edificable y una demo de tala de árboles.
  • RTSDemoGame configura todo esto en cualquier terreno con posiciones iniciales. Añádelo con GameObject ▸ RTS Terrain Studio ▸ Demo Game.

Integraciones

Son opcionales. Cada una solo se compila cuando su dependencia está instalada, así que quitar la dependencia nunca rompe tu proyecto.

IntegraciónQué añade
Mirror (multijugador)El host transmite su mapa a los clientes que se unen, para que los jugadores puedan jugar en línea con mapas creados por usuarios, y los mapas descargados se guardan en caché. El servidor tiene la autoridad sobre las unidades, cada cliente tiene la niebla de guerra de su propio equipo y RTSFogOfWarInterestManagement nunca envía enemigos ocultos por la niebla de guerra (protección contra maphack).
A* Pathfinding ProjectGrafos de cuadrícula generados a partir del mapa de pathing RTS y actualizados de forma incremental tras cada edición, un grafo por tamaño de unidad, y unidades que se mueven con AIPath y evasión local RVO.
Unity AI Navigation (NavMesh)Un NavMesh construido a partir de los chunks del terreno y del mapa de pathing, actualizado de forma incremental tras las ediciones, y un proveedor de rutas que devuelve rutas de NavMesh o controla componentes NavMeshAgent.

Flujo de trabajo en el editor

  • Una única ventana acoplable RTS Terrain Studio con pestañas para las 8 categorías de herramientas, además de Map y Settings. Tiene botones de herramienta grandes con iconos, ajustes contextuales, paletas con miniaturas y una barra de estado.
  • Integración con la Scene View. El shader del terreno dibuja el pincel y lo proyecta sobre el terreno. También hay superposiciones de cuadrícula y de pathing, guías de simetría y atajos: [ / ] tamaño del pincel, - / = intensidad, 1–8 categorías de herramientas, G cuadrícula, P superposición de pathing y R rotación aleatoria. Puedes reasignarlos en Edit ▸ Shortcuts... (en macOS, Unity ▸ Shortcuts...).
  • Las ediciones del mapa forman parte del historial de deshacer de Unity, así que Ctrl+Z y Ctrl+Y funcionan como siempre y muestran los nombres de los pasos. El presupuesto de memoria para deshacer se puede configurar.
  • Inspectores personalizados para tilesets (capas que se pueden arrastrar y soltar, vistas previas, creación automática de arrays de texturas), bibliotecas de vegetación y de decoración, formas de pincel, perfiles de pintado automático y el componente RTSTerrain. El importador de .rtsmap muestra una vista previa del minimapa.
  • Una ventana Welcome, un New Map Wizard y un validador de proyecto con correcciones de un solo clic.

Rendimiento

  • Los meshes del terreno se dividen en chunks de 16, 32 (predeterminado) o 64 celdas. Se construyen con Mesh.MeshDataArray en jobs de Burst, y solo se reconstruyen los chunks que editas.
  • El trabajo pesado se ejecuta en jobs compilados con Burst (Unity Jobs System): operaciones de pincel, generación de meshes, derivación del pathing, holgura, A*, niebla de guerra, instancias de vegetación, meshes de agua, el generador de mapas, el pintado automático y el precálculo del minimapa.
  • Los detalles se dibujan proceduralmente con Graphics.RenderMeshPrimitives. Los árboles y la decoración instanciada usan Graphics.RenderMeshInstanced. A todos ellos se les aplica culling por parche según el frustum y la distancia.
  • Los shaders son compatibles con el SRP Batcher, y todos los chunks comparten un único material de terreno.
  • Los arrays de texturas se comprimen para la plataforma activa (BC7, ASTC 6×6, ETC2 o DXT5) con la resolución que elijas (256–2048).
  • Sin asignaciones de memoria (GC) por fotograma en las rutas críticas, y toda la memoria nativa se libera cuando se desactivan los componentes.

Decisiones de diseño de un vistazo

Estas son las preguntas más frecuentes sobre las herramientas de terreno para RTS y cómo las resuelve RTS Terrain Studio.

PreguntaRespuesta
¿Cuántas texturas de suelo puede usar un mapa? 16 capas de suelo y 4 tipos de acantilado por tileset, y puedes tener tantos tilesets como quieras. El límite tiene una razón técnica: 16 capas caben exactamente en 4 splat maps RGBA (un canal por capa), y las 16 capas de suelo y las 4 capas de acantilado se empaquetan en dos arrays de texturas (albedo/altura y normales, 20 slices cada uno), de modo que un único material de terreno renderiza todos los chunks.
¿Tiene colliders el terreno? ¿Puedo consultarlo sin física? Ambas cosas. Cada chunk puede tener un MeshCollider en la capa que elijas (RTSTerrainSettings.colliderMode). La API de consultas (GetHeight, GetNormal, Raycast, GetCliffLevel, IsWater, TryGetWaterHeight, GetPathing, IsWalkable, IsBuildable) no usa física en absoluto. Funciona en el modo Edit, en tiempo de ejecución y con los colliders desactivados, y devuelve exactamente la superficie que ves.
¿Puedo crear mundos grandes a partir de varios mapas? Sí. Una escena puede contener cualquier número de componentes RTSTerrain, cada uno con su propia posición y rotación en Y. RTSTerrain.All, RTSTerrain.FindAt y RTSTerrain.RaycastAll funcionan con todos ellos. El renderizado por chunks hace viables los mapas de 512×512 celdas.
¿El pincel actúa justo donde está el cursor, incluso con escalado de pantalla? Sí. El Unity Editor usa HandleUtility.GUIPointToWorldRay, que gestiona correctamente el escalado de DPI, y el editor en el juego usa Camera.ScreenPointToRay con la posición del puntero que proporciona el Input System. El cursor del pincel lo dibuja el shader del terreno, proyectado sobre la superficie, así que pintas exactamente donde lo ves.
¿Qué formatos y tamaños de textura puedo usar? Texturas de Unity convencionales de cualquier tamaño y formato. El Inspector del tileset las redimensiona y las empaqueta por ti en objetos Texture2DArray (de 256 a 2048 píxeles por slice, comprimidos o sin comprimir). La distribución de canales está documentada: albedo en RGB con una altura opcional en el canal alfa o en un mapa de relieve aparte, más un mapa de normales.
¿Tengo que colocar el paquete en una carpeta fija? No. El editor encuentra la raíz del paquete a través de su definición de ensamblado de tiempo de ejecución, así que puedes mover RTSTerrainStudio a cualquier lugar dentro de Assets (por ejemplo, Assets/Plugins/RTSTerrainStudio).
¿Puedo cambiar el tamaño del mapa más adelante? Sí. Elige cualquier tamaño en el New Map Wizard y amplíalo o redúcelo después con Resize. Con un ancla eliges en qué lados se añaden o se quitan celdas.
¿Puedo leer y modificar el código? Sí. El código está dividido en definiciones de ensamblado, cada una con un único espacio de nombres. La API pública tiene documentación XML, las partes internas están comentadas, los jobs de Burst están en archivos *Jobs.cs separados y hay pruebas unitarias de EditMode.

Un primer vistazo a la API

Para que los programadores se hagan una idea de la API, aquí tienes un script completo. Consulta al terreno sobre el punto en el que haces clic y planifica una ruta hasta él con el buscador de rutas integrado. No necesita colliders ni componentes adicionales: añádelo a cualquier GameObject de una escena que tenga un 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 puede ser Success, Partial (el punto alcanzable más cercano), NoPath o InvalidStart. El capítulo Guía de scripting explica toda la API.

Requisitos

Unity y pipeline de renderizado

RequisitoVersiónNotas
Unity6.3 LTS (6000.3) o posteriorDesarrollado y probado con 6000.3.3f1.
Universal Render Pipeline17.3 (la versión incluida con Unity 6.3)Debe haber un asset de URP activo en la configuración de Graphics y de Quality. Desarrollado con la ruta de renderizado Forward+. El shader del terreno también tiene un pase GBuffer para la ruta Deferred.
Espacio de colorLinearGamma funciona, pero los colores, la iluminación y el agua se ven desvaídos.
GráficosShader Model 3.5 o superiorLos shaders de terreno, vegetación, detalles y agua usan #pragma target 3.5, así que también funcionan en WebGL 2.0 y OpenGL ES 3.0. Solo el pase GBuffer del terreno (ruta de renderizado Deferred) y la variante con structured buffers del shader de detalles usan 4.5, cuando la GPU lo admite.

El Built-in Render Pipeline y HDRP no son compatibles. Todos los shaders están escritos para URP y la renderer feature de niebla de guerra usa la API RenderGraph de URP.

¿Has importado el paquete en un proyecto sin URP? En ese caso, el paquete permanece inactivo en lugar de llenar la ventana Console de errores: sus scripts no se compilan, sus shaders se omiten y tu proyecto sigue funcionando como antes. Una vez por sesión del editor, el cuadro de diálogo RTS Terrain Studio requires URP explica qué hacer:

  • Built-in Render Pipeline: haz clic en Install URP. Cuando Unity haya compilado el paquete, abre Tools ▸ RTS Terrain Studio ▸ Validate Project y haz clic en Use URP, que crea un asset de URP y lo asigna en la configuración de Graphics y de Quality.
  • URP anterior a 17.3: haz clic en Open Package Manager y actualiza Universal Render Pipeline a la versión incluida con tu editor.
  • Unity anterior a 6.3: URP 17.3 necesita Unity 6.3 LTS, así que el cuadro de diálogo solo ofrece Open Manual y Later. Abre el proyecto con Unity 6.3 LTS o posterior.
  • HDRP o un pipeline de renderizado personalizado: en su lugar, importa el paquete en un proyecto con URP, por ejemplo, un proyecto nuevo creado a partir de la plantilla Universal 3D.

Tools ▸ RTS Terrain Studio ▸ Set Up Universal Render Pipeline... vuelve a mostrar el cuadro de diálogo. Es posible que Unity también registre advertencias de que los shaders del paquete no son compatibles; desaparecen en cuanto se instala URP.

Paquetes necesarios

Estos paquetes proceden del Unity Registry (Window ▸ Package Manager ▸ Unity Registry). La ventana Tools ▸ RTS Terrain Studio ▸ Validate Project detecta los paquetes que faltan y los instala con un solo clic (comprobación Burst, Collections and Mathematics).

PaqueteNombreVersión probadaSe usa para
Burstcom.unity.burst1.8.27Compila los jobs a código nativo rápido.
Collectionscom.unity.collections2.6.2Contenedores nativos para los datos del mapa y los jobs.
Mathematicscom.unity.mathematics1.3.3Matemáticas vectoriales en los jobs y en el modelo de datos.
Universal RPcom.unity.render-pipelines.universal17.3.0Renderizado.

Gestión de entrada

Toda la entrada en tiempo de ejecución pasa por la clase estática RTSInput. Es compatible con ambos sistemas de entrada:

  • Paquete Input System (com.unity.inputsystem, probado con 1.17.0). Se usa cuando el paquete está instalado y Active Input Handling está en Input System Package (new) o Both.
  • Input Manager (old). Se usa como alternativa cuando el proyecto utiliza la gestión de entrada antigua.

Esto se configura en Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Active Input Handling. La comprobación Input handling del validador de proyecto muestra qué modo está activo; consulta Gestión de entrada en el capítulo Configuración del proyecto.

Paquetes opcionales para las integraciones

IntegraciónDependenciaVersiónCómo se detecta
MirrorMirror (Asset Store o GitHub)96 o posteriorSímbolo de compilación MIRROR, que Mirror añade por sí mismo.
A* Pathfinding Projectcom.arongranberg.astarPro 5.4.4 hasta 6.0 (sin incluirla), probado con 5.4.7Símbolo de versión (version define) RTS_ASTAR. Añade RTS_ASTAR_DISABLED para desactivarla (necesario para la versión gratuita, que usa el mismo nombre de paquete).
Unity NavMeshAI Navigation, com.unity.ai.navigation2.0 o posterior, probado con 2.0.9Símbolo de versión RTS_AI_NAVIGATION.

Las integraciones de búsqueda de rutas necesitan la versión Pro de A* Pathfinding Project, porque la integración usa la evasión local RVO y TraversalConstraint. Antes de eliminar Mirror, lee Eliminar Mirror: Mirror deja sus símbolos de compilación en el proyecto, y RTS Terrain Studio incluye un pequeño ensamblado de editor que los elimina.

Configuración recomendada del proyecto

El validador de proyecto comprueba todos estos puntos y puede corregirlos por ti:

AjusteMotivo
Asset de URP activoLos shaders del paquete solo se renderizan en URP.
Depth Texture y Opaque Texture activados en el asset de URPEl agua usa la textura de profundidad para que su color de profundidad, su espuma y su orilla suave se adapten a los objetos que hay dentro del agua, y la textura opaca para la refracción. El ajuste propio de una cámara tiene prioridad sobre el del asset, y el agua respeta el ajuste de cada cámara. Las cámaras de demostración activan ambas por sí mismas, así que las escenas de demostración se ven bien en la Game View incluso sin este ajuste (la Scene View siempre usa el asset de URP).
Renderer feature de niebla de guerra en el rendererOscurece todo lo que está bajo la niebla de guerra, incluidos tus propios materiales. Sin ella, solo los shaders del paquete muestran la niebla de guerra.
Modos de niebla conservados en las builds del juegoUnity elimina las variantes de shader de niebla que no usa ninguna escena de la build, pero RTSEnvironment activa la niebla atmosférica en tiempo de ejecución. Si no se conservan los modos de niebla, los mapas con niebla no la muestran en las builds.
Espacio de color linealColores e iluminación correctos.
Referencias de shaders y arrays de texturas de los tilesets actualizadosLos shaders y los arrays comprimidos se incluyen en las builds del juego. Antes de cada build del juego, el asset de referencias de shaders se actualiza y los arrays de texturas obsoletos se reconstruyen automáticamente.

Plataformas

El código de tiempo de ejecución no tiene dependencias exclusivas del editor, así que los mapas se cargan, se renderizan y se pueden editar en las builds del juego en todas las plataformas que URP admite, incluidas las builds para Web (WebGL 2.0). Los shaders usan Shader Model 3.5, el nivel más bajo que admite arrays de texturas. El pase GBuffer del terreno para la ruta de renderizado Deferred necesita Shader Model 4.5 y se omite en las API basadas en OpenGL, igual que el pase GBuffer del propio shader Lit de URP. La hierba de detalles elige en tiempo de ejecución dónde guarda sus instancias: en structured buffers cuando la GPU los admite, y en una textura de coma flotante en WebGL 2.0 y OpenGL ES 3 (el resultado se ve igual).

El proceso de precálculo de los arrays de texturas elige la compresión según el destino de compilación activo: BC7 para escritorio y consolas, ASTC 6×6 para Android, iOS, tvOS y visionOS, la compresión de texturas configurada en la build para Web (DXT5, ASTC 6×6 o ETC2), y DXT5 para todo lo demás. El editor en el juego funciona en todas las plataformas y está pensado para ratón y teclado. Sus comandos para mostrar la carpeta de mapas y para salir de la aplicación solo aparecen donde la plataforma los admite: solo las builds de escritorio y el editor pueden mostrar carpetas, y WebGL, iOS y Android no pueden cerrar la aplicación. Las builds para Web tienen algunas diferencias más; consulta Builds web (WebGL).

Estructura de carpetas

El paquete está en una sola carpeta, de forma predeterminada Assets/RTSTerrainStudio. Puedes mover o renombrar esta carpeta: el editor encuentra la raíz del paquete a partir de la ubicación de RTSTerrainStudio.Runtime.asmdef, y el contenido de demostración y la documentación se localizan en relación con ella.

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)

Carpetas de primer nivel

CarpetaContenido¿Puedo eliminarla?
RuntimeTodo lo que se ejecuta en un juego: el componente RTSTerrain, los datos del mapa, el renderizado, todas las herramientas de edición, la vegetación, el agua, la decoración, la niebla de guerra, el pathing, la generación, el editor en el juego, los iconos y la entrada.No.
EditorSolo funciones del Unity Editor. Nada de esto se incluye en las builds del juego.No.
ShadersRTS Terrain Studio/Terrain, RTS Terrain Studio/Vegetation, RTS Terrain Studio/Water, Hidden/RTS Terrain Studio/Detail, Hidden/RTS Terrain Studio/FogOfWarFullscreen, los shaders internos de Hidden y los includes HLSL de Include (RTSCommon.hlsl contiene las variables globales de niebla de guerra, cursor del pincel y viento).No.
SamplesScripts de jugabilidad de ejemplo y las escenas de demostración. El ensamblado del editor y las integraciones hacen referencia al ensamblado de ejemplos (por ejemplo, para usar RTSUnit y RTSDemoGame).Solo junto con las integraciones. Las escenas se pueden eliminar en cualquier momento.
IntegrationsUna carpeta por cada integración de terceros, cada una con sus propios ensamblados, un README.md y una carpeta Editor con los generadores de escenas de demostración. Las escenas de demostración se crean en una subcarpeta Demo.Sí, cada carpeta de integración por separado. Lee primero los pasos para Mirror.
ContentTexturas, materiales, meshes, prefabs, tilesets, bibliotecas, formas de pincel, mapas y configuración de renderizado de demostración. Todo lo que hay aquí es generado.Sí, puedes volver a generarla en cualquier momento. En ese caso, tus propios mapas no deben usarla.
Resources/RTSTerrainStudioUI/RTSRuntimeEditor.uss y UI/RTSRuntimeTheme.tss (estilo de la interfaz en el juego). Aquí se añaden el asset de referencias de shaders (que el editor crea y actualiza automáticamente) y UI/RTSRuntimePanelSettings.asset (creado por Create Demo Content). Garantizan que los shaders del paquete y los shaders de tiempo de ejecución de UI Toolkit se incluyan en las builds.No.
DocumentationEste manual. Lo abren Tools ▸ RTS Terrain Studio ▸ Documentation y todos los botones ?.Sí, pero entonces los botones de ayuda no tendrán nada que abrir.
TestsPruebas EditMode para los datos del mapa, la geometría, los pinceles, la simetría, la acción de deshacer, el serializador, el pathing, el generador, las consultas al terreno, el registro de herramientas y los iconos. Ejecútalas en Window ▸ General ▸ Test Runner.Sí.

Dentro de Runtime

CarpetaContenido
CoreRTSTerrain (componente principal, ciclo de vida del mapa, seguimiento de cambios, consultas), MapData, MapTypes, MapGeometryView (la superficie de referencia que usan los meshes, las consultas y el pathing), PathingMap, RTSTerrainSettings, los ids de propiedades de shader, RTSShaderReferences, RTSRuntimeEditorEvents.
DataTipos de asset: RTSTileset, RTSVegetationLibrary, RTSDecorLibrary, RTSMapAsset.
SerializationRTSMapSerializer: el formato binario .rtsmap (secciones, compresión deflate) y la lectura y escritura de archivos.
BrushesBrushSettings, RTSBrushShape, SymmetrySettings, BrushUtility.
ToolsRTSTool (clase base), RTSEditSession, MapUndo y una subcarpeta por categoría: Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers.
MeshGeneración de la geometría de los chunks y sus jobs de Burst.
RenderingLa parte de renderizado de RTSTerrain, RTSTerrainChunk, las texturas de splat, los datos de capas y RTSTextureArrayBuilder.
VegetationRTSVegetationRenderer, RTSTreeInstance.
WaterRTSWaterRenderer, RTSWaterChunk, los jobs del mesh de agua.
DecorRTSDecorManager, RTSDecorInstance.
FogOfWarRTSFogOfWarManager, RTSFogOfWarRevealer, RTSFogOfWarHideable, RTSFogOfWarRendererFeature.
PathingLa parte de pathing de RTSTerrain, RTSPathingSettings, PathingMapBuilder, ClearanceMap, RTSPathfinder, RTSPathfindingService, las superposiciones de pathing.
GenerationRTSMapGenerator, MapGeneratorSettings, RTSMapGeneratorPreset, RTSAutoPaintProfile, RTSAutoPaint, HeightmapIO y la herramienta Auto Paint.
MinimapRTSMinimapBaker.
EnvironmentRTSEnvironment (aplica el sol, la luz ambiental y la niebla atmosférica del mapa).
ValidationMapValidator (busca problemas en un mapa, como posiciones iniciales inalcanzables).
RuntimeEditorEl editor en el juego: RTSRuntimeEditor, RTSRuntimeEditorCamera, RuntimeToolGizmos y su interfaz de UI Toolkit.
InputRTSInput y RTSKey.
UIRTSIcons: iconos procedurales compartidos por el editor y la interfaz en el juego.
Plugins/WebGLPlugins de JavaScript (.jslib) que solo usan las builds para Web: el almacenamiento del navegador, las descargas, las importaciones y el aviso al salir de la página (RTSWebFileSystem.jslib), las distribuciones de teclado y los atajos del navegador (RTSWebKeyboard.jslib), y la detección del puntero cuando sale de la página (RTSPointer.jslib).
link.xmlConserva las herramientas de edición en las builds que usan eliminación de código (consulta Eliminación de código y herramientas personalizadas).

Dentro de Editor

CarpetaContenido
CoreRTSEditorContext (la sesión de edición compartida y las opciones de vista), RTSEditorPaths, la persistencia de los ajustes de las herramientas.
WindowLa ventana RTS Terrain Studio (RTSTerrainStudioWindow) y sus páginas.
SceneViewLa pintura en la Scene View, el dibujo del pincel y de los gizmos, los paneles superpuestos (overlays), los atajos y el puente con el sistema de deshacer de Unity.
InspectorsInspectors para el tileset, las bibliotecas, las formas de pincel, los perfiles de pintado automático y los componentes.
ImportersEl importador de .rtsmap, su vista previa y Assets ▸ Create ▸ RTS Terrain Studio ▸ Map (.rtsmap).
BuildingEl proceso de precálculo de los arrays de texturas comprimidos, la comprobación que reconstruye los arrays de texturas obsoletos antes de las builds del juego, el generador de miniaturas y la fábrica de assets.
SetupLa ventana Welcome, el New Map Wizard, el validador de proyecto, el menú de creación de GameObject, el actualizador de referencias de shaders y la plantilla Web (WebGLTemplate) con su instalador.
RequirementsEl aviso que se muestra cuando el proyecto no tiene URP 17.3 (consulta Unity y pipeline de renderizado). Aparte de la limpieza de los símbolos de compilación de Mirror, es la única parte del paquete que se compila sin URP.
RuntimeEditorSupportEl menú GameObject ▸ RTS Terrain Studio ▸ In-Game Editor.
DemoContentEl generador procedural de contenido de demostración (RTSDemoContent).

Ensamblados

El código está dividido en definiciones de ensamblado para que compile rápido y las partes opcionales se puedan eliminar. Cada ensamblado tiene un único espacio de nombres.

Ensamblado (asmdef)CarpetaEspacio de nombresPlataformasSe compila cuando
RTSTerrainStudio.RuntimeRuntimeRTSTerrainStudioTodasURP 17.3 o posterior está instalado (RTS_URP). Define RTS_INPUT_SYSTEM cuando el paquete Input System está instalado.
RTSTerrainStudio.SamplesSamples/ScriptsRTSTerrainStudio.SamplesTodasURP 17.3 o posterior está instalado.
RTSTerrainStudio.EditorEditorRTSTerrainStudio.EditorEditorURP 17.3 o posterior está instalado.
RTSTerrainStudio.Requirements.EditorEditor/RequirementsRTSTerrainStudio.RequirementsEditorSolo mientras falte URP 17.3 o posterior. Muestra el aviso de que el paquete necesita URP.
RTSTerrainStudio.Tests.EditorTests/EditorRTSTerrainStudio.TestsEditorUNITY_INCLUDE_TESTS está definido (Unity Test Framework).
RTSTerrainStudio.Integrations.Mirror
RTSTerrainStudio.Integrations.Mirror.Editor
Integrations/MirrorRTSTerrainStudio.Integrations.MirrorTodas / EditorMIRROR está definido.
RTSTerrainStudio.Integrations.Mirror.DefineCleanup.EditorIntegrations/Mirror/Editor/DefineCleanupRTSTerrainStudio.Integrations.MirrorEditorSiempre. No depende de Mirror y elimina los símbolos de compilación obsoletos de Mirror una vez que Mirror se ha borrado.
RTSTerrainStudio.Integrations.AStar
RTSTerrainStudio.Integrations.AStar.Editor
Integrations/AStarPathfindingRTSTerrainStudio.Integrations.AStarTodas / EditorRTS_ASTAR (A* instalado en una versión desde 5.4.4 e inferior a 6.0) y sin RTS_ASTAR_DISABLED.
RTSTerrainStudio.Integrations.NavMesh
RTSTerrainStudio.Integrations.NavMesh.Editor
Integrations/UnityNavMeshRTSTerrainStudio.Integrations.NavMeshTodas / EditorRTS_AI_NAVIGATION (AI Navigation 2.0 o posterior instalado).

Los ensamblados de pruebas y de integraciones también necesitan URP 17.3 o posterior, además de la condición indicada. Sin URP, solo se compilan la limpieza de los símbolos de compilación de Mirror y el aviso de requisitos, así que el paquete no provoca errores de compilación.

RTSTerrainStudio.Runtime, RTSTerrainStudio.Samples y RTSTerrainStudio.Editor se referencian automáticamente. Los scripts del ensamblado predeterminado de tu proyecto (Assembly-CSharp) pueden usarlos sin ninguna configuración. Si tu código tiene su propia definición de ensamblado, añade una referencia a RTSTerrainStudio.Runtime (y a RTSTerrainStudio.Samples si usas las clases de jugabilidad de ejemplo).

El editor en el juego está en Runtime/RuntimeEditor. Si tu juego no lo necesita, puedes eliminar esa carpeta: las herramientas del Unity Editor y el generador de contenido de demostración se siguen compilando, y la escena In-Game Editor Demo se crea entonces sin editor.

Qué incluye

El paquete se distribuye como código fuente, shaders y este manual. Todo lo visual (texturas, modelos, tilesets, mapas y escenas) lo genera un script en tu equipo. Así la descarga es pequeña y el contenido tiene exactamente la misma licencia que el código.

Generador de contenido de demostración

Tools ▸ RTS Terrain Studio ▸ Create Demo Content ejecuta RTSDemoContent.CreateAll. Genera todos los assets de demostración de forma procedural, con semillas fijas, así que cada ejecución produce el mismo resultado:

  • Solo escribe en sus propias carpetas: Content, Samples/Scenes y dos assets en Resources/RTSTerrainStudio. Nunca toca tus propios assets.
  • Si lo vuelves a ejecutar, regenera los assets en su sitio, de modo que las referencias a ellos siguen siendo válidas. Los archivos de mapa cuyo contenido no ha cambiado no se reescriben, así que el control de versiones no muestra cambios.
  • Nunca cierra tus escenas abiertas. Las escenas de demostración se abren de forma aditiva mientras se construyen.
  • Al final ofrece añadir las escenas de demostración a la configuración de build. No se añade nada a menos que hagas clic en Add Scenes.
GeneradoUbicación (bajo la raíz del paquete)Contenido
TexturasContent/Textures/ (Ground, Cliffs, Water, Details, Props, Brushes)Texturas repetibles de suelo, acantilados, agua y props (512 px), sprites de hierba y flores y sellos de pincel (256 px), con sus mapas de normales correspondientes.
Materiales, meshes, prefabsContent/Materials, Content/Meshes, Content/PrefabsÁrboles, rocas, ruinas y props low-poly con sus materiales.
TilesetsContent/Tilesets/Summer.asset, Winter.assetDos tilesets, cada uno con 10 capas de suelo y 2 tipos de acantilado. Summer: Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow y Blight, con Rock Cliff y Grassy Cliff. Winter: las versiones heladas, con Ice Cliff y Snowy Cliff.
Perfil de pintado automáticoContent/Tilesets/Summer Auto Paint.assetReglas para el tileset Summer, por ejemplo, manchas de tierra en pendientes suaves y arena a lo largo de las orillas.
Biblioteca de vegetaciónContent/Libraries/Demo Vegetation.assetDetalles: Grass, Tall Grass, Flowers Yellow, Flowers Purple, Fern. Árboles: Pine, Broadleaf, Dead Tree, Bush.
Biblioteca de decoraciónContent/Libraries/Demo Decor.assetRock A, Rock B, Rock C, Boulder, Ruined Pillar, Crate, Barrel, Fence, Tree Stump, Mushrooms. Combinan prototipos instanciados y de GameObject, y tienen huellas de pathing.
Formas de pincelContent/BrushesSellos personalizados: Noise Blob, Splatter, Star, Soft Square.
MapasContent/MapsTres archivos .rtsmap (consulta más abajo).
Configuración de renderizadoContent/SettingsUn perfil de volumen de posprocesado que usan las escenas de demostración.
EscenasSamples/ScenesTres escenas de demostración (consulta más abajo).
RecursosResources/RTSTerrainStudioEl asset de referencias de shaders y UI/RTSRuntimePanelSettings.asset, para que los shaders y la UI se incluyan en las builds.

Mapas de demostración

Los tres mapas usan celdas de 2 m, una altura de nivel de acantilado de 2 m y el tileset Summer.

MapaTamañoSimetríaDescripción
Two Rivers (1v1).rtsmap128 × 128 celdasRotate 2Dos jugadores, dos ríos. Los ríos se pueden cruzar por vados poco profundos en cualquier punto, y el lago central es profundo. Las rampas suben a mesetas boscosas con ruinas y campamentos abandonados.
Four Kingdoms (4p).rtsmap192 × 192 celdasRotate 4Cuatro reinos, cada uno en sus propias tierras altas. Las rampas bajan a las tierras bajas compartidas, y el centro es tierra corrupta con las ruinas de un antiguo templo.
Sandbox.rtsmap96 × 96 celdasNoneUn mapa pequeño y mayormente llano para probar las herramientas.
Vista general cenital de un mapa de demostración con mesetas, ríos, bosques y posiciones iniciales
Vista cenital de un mapa de demostración.

Escenas de demostración

EscenaMapaQué muestra
RTS Terrain Studio - Gameplay DemoTwo Rivers (1v1)Presiona Play para jugar: unidades en las posiciones iniciales (las enemigas deambulan por el mapa), niebla de guerra, cámara RTS, selección, órdenes, minimapa, HUD, colocación de edificios (B) y tala de árboles (T).
RTS Terrain Studio - In-Game Editor DemoSandboxEl editor en el juego en el modo Play. Edita el mapa y luego presiona F5 para probarlo en el juego con unidades.
RTS Terrain Studio - Editor PlaygroundFour Kingdoms (4p)Un mapa sin jugabilidad, para probar las herramientas del Unity Editor en la Scene View.

Cada escena de demostración contiene un sol, un volumen de posprocesado, una cámara que solicita la textura de profundidad y la textura opaca, y un terreno con RTSTerrain, RTSVegetationRenderer, RTSWaterRenderer, RTSDecorManager y RTSEnvironment.

Cada integración tiene su propio generador de escenas de demostración. Necesita el contenido de demostración y crea su escena en la carpeta Demo de la integración:

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

Scripts de ejemplo

La carpeta Samples/Scripts (RTSTerrainStudio.Samples) es un pequeño framework RTS, fácil de leer, del que puedes aprender o sobre el que puedes construir:

ÁreaClases
ArranqueRTSDemoGame, RTSUnitSpawner
Unidades y movimientoRTSUnit, RTSUnitFactory, RTSUnitRegistry, RTSFormation, RTSWanderAI
Proveedores de rutasIRTSUnitPathProvider, RTSBuiltinPathProvider, RTSUnitPathProviderComponent, RTSUnitPathProviderRegistry
Cámara, entrada, selección, órdenesRTSCameraController, RTSGameInput, RTSSelectionManager, RTSCommandController
Interfaz de usuarioRTSMinimapUI, RTSDemoHUD, RTSRuntimeUI
Demos de jugabilidadRTSBuildingPlacementDemo, RTSDemoBuilding, RTSTreeHarvestDemo, RTSFallingTreeEffect
Utilidades auxiliaresRTSMoveMarker, RTSGroundRing, RTSSampleMaterials, RTSSampleMeshes, RTSSampleUtility

Iconos

RTSIcons dibuja de forma procedural todos los iconos de la ventana del editor, de los inspectores y del editor en el juego, así que se mantienen nítidos a cualquier tamaño y funcionan en las builds sin assets de textura.

Hoja con todos los iconos de RTS Terrain Studio: categorías de herramientas, herramientas y acciones
El conjunto de iconos que usan el editor y la UI del juego.

Menús de un vistazo

Elemento de menúQué hace
Tools ▸ RTS Terrain Studio ▸ Editor WindowAbre la ventana principal de edición.
Tools ▸ RTS Terrain Studio ▸ New Map Wizard...Crea un mapa nuevo paso a paso (tamaño, tileset, plano o generado).
Tools ▸ RTS Terrain Studio ▸ Create Demo ContentGenera el contenido de demostración descrito más arriba.
Tools ▸ RTS Terrain Studio ▸ Validate ProjectComprueba URP, la configuración del proyecto, los paquetes, el contenido y la escena, y ofrece correcciones.
Tools ▸ RTS Terrain Studio ▸ Install WebGL TemplateInstala y selecciona la plantilla web que ocupa toda la ventana del navegador (consulta La plantilla de ventana completa).
Tools ▸ RTS Terrain Studio ▸ WelcomePasos de inicio rápido, enlaces y el estado de las integraciones.
Tools ▸ RTS Terrain Studio ▸ DocumentationAbre este manual.
Tools ▸ RTS Terrain Studio ▸ Integrations ▸ …Generadores de escenas de demostración y utilidades de las integraciones instaladas.
GameObject ▸ RTS Terrain Studio ▸ RTS TerrainCrea un GameObject RTS Terrain con RTSTerrain, Vegetation Renderer, Water Renderer y Decor Manager, el tileset y las bibliotecas predeterminados del proyecto, y un archivo .rtsmap nuevo con un mapa plano.
GameObject ▸ RTS Terrain Studio ▸ Fog of War ManagerCrea el gestor de niebla de guerra y ofrece añadir la renderer feature.
GameObject ▸ RTS Terrain Studio ▸ EnvironmentCrea un RTSEnvironment (y un sol si la escena no tiene ninguno).
GameObject ▸ RTS Terrain Studio ▸ In-Game EditorAñade el editor en el juego a la escena.
GameObject ▸ RTS Terrain Studio ▸ Demo GameAñade RTSDemoGame, que convierte cualquier terreno con posiciones iniciales en una demo jugable.
Assets ▸ Create ▸ RTS Terrain Studio ▸ …Map (.rtsmap), Tileset, Vegetation Library, Decor Library, Brush Shape, Auto Paint Profile, Map Generator Preset y Advanced ▸ Shader References. Rara vez necesitarás el último: el validador y las builds del juego crean y actualizan RTSShaderReferences.asset automáticamente.

Los elementos del menú GameObject están desactivados en el modo Play, porque crean assets y objetos de escena que hay que guardar.

Inicio rápido

Este capítulo te lleva desde un proyecto de Unity vacío hasta un mapa creado por ti y que ya has jugado con unidades, niebla de guerra y minimapa. No necesitas conocimientos de programación. Sigue los pasos numerados en orden. Cada paso indica exactamente el menú, la ventana o el botón que debes usar.

ParteQué hacesTiempo
Instalación y configuraciónImporta el paquete, deja que el validador de proyecto corrija la configuración de URP y genera el contenido de demostración.unos 5 min
Escenas de demostraciónJuega un mapa terminado, prueba el editor en el juego y explora la escena de pruebas del editor.5 min
Tu primer mapaCrea un terreno, pinta alturas, acantilados, texturas, árboles y agua, coloca las posiciones iniciales y guarda.10 min
Tu primera prueba de juegoJuega tu mapa con el juego de demostración, o edítalo y pruébalo dentro del juego en ejecución.5 min
Tutorial de 10 minutosCrea un mapa 1v1 equilibrado con pintado simétrico, de principio a fin.10 min

Puedes abrir este capítulo en cualquier momento desde el enlace Quick Start Guide de la ventana Welcome (Tools ▸ RTS Terrain Studio ▸ Welcome). El manual completo se abre con Tools ▸ RTS Terrain Studio ▸ Documentation.

Instalación y configuración

Requisitos

ElementoVersiónNotas
Unity6.3 LTS (6000.3)Usa la versión del editor para la que se creó el paquete.
Universal Render Pipeline17.3Obligatorio. Todos los shaders de terreno, agua, vegetación y niebla de guerra son shaders de URP. Si creas un proyecto nuevo, parte de la plantilla de proyecto Universal 3D. Sin URP, el paquete permanece inactivo y muestra un aviso (consulta Requisitos).
Burst, Collections, Mathematicsdel Unity RegistryObligatorio. El trabajo pesado (construcción de meshes, pinceles, pathing, niebla de guerra, búsqueda de rutas) se ejecuta en jobs de Burst.
Input Systemdel Unity RegistryRecomendado. Lo usan el editor en el juego, la cámara RTS y los ejemplos. El Input Manager antiguo funciona como alternativa.
Mirror, A* Pathfinding Project, AI NavigationopcionalSolo se necesitan para la integración correspondiente. Las integraciones se compilan automáticamente en cuanto el paquete correspondiente está instalado. Consulta Integraciones.

Importar el paquete

  1. Abre tu proyecto en Unity 6.3. Asegúrate de que usa URP (los proyectos nuevos creados con la plantilla Universal 3D ya lo usan).
  2. Abre Window ▸ Package Manager, selecciona My Assets, busca RTS Terrain Studio, haz clic en Download y después en Import.
  3. En el cuadro de diálogo de importación, deja todo marcado y haz clic en Import. El paquete se instala en Assets/RTSTerrainStudio.
  4. Espera a que Unity termine de compilar. La ventana Welcome se abre sola (consulta más abajo).

Puedes mover la carpeta RTSTerrainStudio a cualquier lugar dentro de Assets. El paquete encuentra sus propios archivos esté donde esté la carpeta, así que nada se rompe cuando reorganizas tu proyecto.

Si la ventana Console muestra errores de compilación justo después de importar, lo más probable es que falte el paquete Burst, Collections o Mathematics. Instálalos con Window ▸ Package Manager ▸ Unity Registry. Los errores desaparecen en cuanto están instalados.

Si en lugar de la ventana Welcome aparece el cuadro de diálogo RTS Terrain Studio requires URP, el proyecto no tiene URP 17.3. En ese caso, el paquete permanece inactivo y no cambia nada. Sigue los pasos del cuadro de diálogo o lee Unity y pipeline de renderizado.

La ventana Welcome

La ventana Welcome se abre automáticamente la primera vez que el paquete se carga en un proyecto. Puedes volver a abrirla en cualquier momento con Tools ▸ RTS Terrain Studio ▸ Welcome. Marca Show at startup en la parte inferior si quieres que aparezca cada vez que se inicia Unity. Este ajuste se guarda por usuario y por proyecto, así que nunca acaba en el control de versiones.

La columna izquierda es una lista de inicio rápido con un botón por paso:

PasoBotónQué hace
1. Validar y corregir el proyectoValidateAbre el Project Validator. Debajo del paso, una línea de estado muestra «Project is ready» o el número de errores y advertencias encontrados.
2. Crear el contenido de demostraciónCreateEjecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content.
3. Abrir una escena de demostraciónlista de escenas + OpenMuestra las escenas de demostración en cuanto existen y abre la que elijas (antes te pide que guardes las escenas abiertas).
4. Crear tu propio mapaNew MapAbre el New Map Wizard.
5. Empezar a editarEditor WindowAbre la ventana RTS Terrain Studio.

La columna derecha tiene enlaces Learn a este manual (Documentation, Quick Start Guide, Scripting API, Integrations, Troubleshooting) y una lista Integrations que muestra si Mirror, el A* Pathfinding Project y Unity AI Navigation están instalados en el proyecto.

Validar el proyecto

El Project Validator comprueba todo lo que necesita el paquete y corrige la mayoría de los problemas con un clic. Ejecútalo una vez después de importar, y de nuevo siempre que algo se vea mal (materiales rosas, agua plana, unidades visibles a través de la niebla de guerra).

  1. Abre Tools ▸ RTS Terrain Studio ▸ Validate Project, o haz clic en Validate en la ventana Welcome.
  2. Lee el resumen de la parte superior. Cada comprobación es una tarjeta con una insignia de estado (OK, Info, Warning o Error). Abre Details en una tarjeta para ver exactamente qué se ha encontrado.
  3. Haz clic en Fix All. Un cuadro de diálogo enumera todos los cambios antes de modificar nada. Haz clic en Apply Fixes.
  4. Haz clic en Run Again y comprueba que no queda ningún error ni ninguna advertencia. Las tarjetas Info no son un problema: solo te informan de algo.

Fix All trabaja en varias pasadas. Si URP aún no estaba activo, la primera pasada cambia el proyecto a URP, y las correcciones de las texturas de cámara y de la niebla de guerra, que necesitan URP, se aplican con el mismo clic. Cada tarjeta tiene además su propio botón de corrección por si prefieres corregir los problemas uno a uno.

ComprobaciónNivel del problemaBotón de correcciónQué hace la corrección y por qué importa
Rendering
Universal Render Pipeline is activeErrorUse URPAsigna un URP Asset del proyecto (o crea uno) como pipeline de renderizado predeterminado y a cada nivel de calidad que use otro pipeline. Sin URP, el terreno, el agua y la vegetación se ven rosas o no se renderizan.
Depth and Opaque Texture enabledWarningEnable BothActiva Depth Texture y Opaque Texture en cada URP Asset que use un nivel de calidad. El agua usa la textura de profundidad para que su color de profundidad, la espuma y la línea de la orilla se adapten a los objetos que hay dentro del agua, y la textura opaca para la refracción. Sin ellas, el agua se ve más simple. Aun así, las cámaras pueden sobrescribir estos ajustes en su sección Rendering.
Fog of War renderer featureWarningAdd FeatureAñade la renderer feature RTS Fog Of War al renderer predeterminado de cada URP Asset que usa el proyecto y le asigna su shader. Sin ella, solo los shaders del propio paquete muestran la niebla de guerra; las unidades y los props con otros materiales siguen visibles.
Fog kept in player buildsWarningKeep Fog ModesAparece cuando un RTSEnvironment de una escena incluida en la build o de una escena abierta aplica niebla atmosférica. Establece Project Settings ▸ Graphics ▸ Fog Modes en Custom y conserva los modos de niebla que se usan. De lo contrario, Unity elimina las variantes de shader de la niebla, y los mapas con niebla no la muestran en las builds. Consulta Niebla en las builds del juego.
Project
Linear color spaceWarningSwitch to LinearEstablece Player Settings ▸ Other Settings ▸ Color Space en Linear. En el espacio Gamma, los colores, la iluminación y el agua se ven desvaídos. El cambio vuelve a importar todas las texturas.
Burst, Collections and MathematicsErrorInstallAñade los paquetes que faltan a través del Package Manager. Los scripts del paquete no compilan sin ellos.
Input handlingInfoningunoInforma de si el paquete Input System está instalado y de qué gestión de entrada está activa. El Input Manager antiguo también funciona.
WebGL (solo mientras Web es la plataforma de build activa)
Web quality levels have Depth and Opaque TextureWarningEnable BothActiva Depth Texture y Opaque Texture en los URP Assets de los niveles de calidad que usan las builds web. El editor suele renderizar con un nivel de calidad de escritorio, así que sin esta comprobación el agua más simple solo se vería en el navegador.
MSAA is off for Web buildsErrorTurn MSAA OffDesactiva MSAA en los URP Assets de los niveles de calidad web que también usan Depth Texture u Opaque Texture. URP no puede renderizar esta combinación en WebGL 2, por lo que el juego se quedaría en negro en el navegador.
Web template fills the browser windowWarningInstall TemplateInstala la plantilla web del paquete y la selecciona. Las plantillas Default y Minimal de Unity muestran el juego en un recuadro pequeño de tamaño fijo, donde el editor en el juego, el HUD y el minimapa son difíciles de leer. Consulta La plantilla de ventana completa.
Content
Package shaders compileErrorReimportVuelve a importar los shaders del paquete que tienen errores de compilación (esto ayuda cuando se importaron antes de que URP terminara de cargarse). Si faltan shaders por completo, vuelve a importar el paquete desde la Asset Store.
Shaders included in buildsErrorUpdateCrea o actualiza RTSShaderReferences.asset y el material DecorPlaceholder en la carpeta Resources del paquete. Los shaders que solo se crean desde código se eliminan de las builds del juego a menos que algo haga referencia a ellos, y el terreno se vería rosa en tu build. Este asset también se actualiza automáticamente tras recargar los scripts, cuando cambian los shaders y antes de cada build del juego.
Tileset texture arrays builtInfoBuild ArraysGenera los arrays de texturas comprimidos de los tilesets que no los tienen o que los tienen desactualizados. Los tilesets funcionan sin ellos, pero entonces el editor crea arrays sin comprimir al cargar el mapa, lo que cuesta memoria y tiempo de carga. Las builds del juego regeneran automáticamente los arrays desactualizados; los tilesets cuyos arrays nunca se generaron también usan arrays sin comprimir en las builds.
GPU instancing on instanced prototypesWarningEnable InstancingMarca Enable GPU Instancing en los materiales que usan los árboles, detalles u objetos decorativos instanciados.
Scene (solo escenas abiertas)
Terrains have a tilesetWarningAssign '<tileset>'Asigna un tileset a cada terreno que no tenga uno (se puede deshacer). Sin tileset, un terreno muestra un aspecto neutro provisional.
Terrains save to a map fileInfoCreate Map FilesGuarda el mapa de cada terreno que no tiene un asset .rtsmap en un archivo nuevo junto a su escena y se lo asigna. Sin archivo de mapa, las ediciones se pierden al cerrar la escena.

Las correcciones de escena no están disponibles en el modo Play. Sal primero del modo Play.

Crear el contenido de demostración

El paquete no incluye arte binario de gran tamaño. En su lugar, genera un conjunto completo de assets de demostración en tu proyecto, así que todo funciona desde el primer momento y puedes empezar a pintar enseguida.

  1. Elige Tools ▸ RTS Terrain Studio ▸ Create Demo Content, o haz clic en Create en la ventana Welcome.
  2. Una barra de progreso recorre los pasos: texturas; materiales; meshes y prefabs; tilesets, bibliotecas y pinceles; referencias de shaders y configuración de la UI; mapas de demostración; escenas de demostración. La primera vez puede tardar un poco.
  3. Al final, un cuadro de diálogo pregunta si se deben añadir las escenas de demostración a la configuración de build. Haz clic en Not Now a menos que quieras compilar el juego con las demos (puedes añadirlas más tarde en File ▸ Build Profiles). Solo Add Scenes cambia la configuración de build.
  4. La escena de la demo de juego queda seleccionada en la ventana Project. Tus escenas abiertas siguen abiertas; no se reemplaza nada.

Qué se genera:

Carpeta (dentro de la carpeta del paquete)Contenido
Content/TexturesTexturas de suelo, acantilados, agua, detalles, props y pinceles.
Content/Materials, Content/Meshes, Content/PrefabsÁrboles, arbustos, rocas y props low-poly, y sus materiales.
Content/TilesetsLos tilesets Summer y Winter, y el perfil Summer Auto Paint. Summer tiene las capas de suelo Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow y Blight, y los tipos de acantilado Rock Cliff y Grassy Cliff.
Content/LibrariesDemo Vegetation (hierba, hierba alta, flores, helecho; los árboles Pine, Broadleaf, Dead Tree y Bush) y Demo Decor (rocas, cantos rodados, tocones y otros props).
Content/BrushesSellos de formas de pincel.
Content/MapsTres mapas: Two Rivers (1v1).rtsmap (128 × 128, simetría central), Four Kingdoms (4p).rtsmap (192 × 192, simetría rotacional de orden cuatro) y Sandbox.rtsmap (96 × 96, mayormente llano, con una meseta y un estanque).
Samples/ScenesLas tres escenas de demostración que se describen más abajo.
Resources/RTSTerrainStudioReferencias de shaders y la configuración del panel de la UI en tiempo de ejecución.

Si vuelves a ejecutar el generador, aparece una pregunta con las opciones Regenerate / Cancel. Solo se sobrescriben los assets de demostración generados y las escenas de demostración; tus propios assets nunca se tocan. Los archivos cuyo contenido no ha cambiado no se reescriben.

El generador también añade la renderer feature de niebla de guerra a tu renderer de URP si falta. No cambia los ajustes de textura de profundidad y textura opaca de tu URP Asset: las cámaras de demostración solicitan esas texturas por sí mismas. Para el agua en tus propias escenas, ejecuta Validate Project como se describe más arriba.

Abrir las escenas de demostración

Las escenas de demostración están en RTSTerrainStudio/Samples/Scenes. Abre una haciendo doble clic en ella en la ventana Project, o elígela en el paso 3 de la ventana Welcome y haz clic en Open.

La escena Gameplay Demo: unidades en el mapa Two Rivers con niebla de guerra y minimapa
La escena Gameplay Demo en el mapa Two Rivers (1v1).
EscenaMapaQué contienePrueba esto
RTS Terrain Studio - Gameplay Demo Two Rivers (1v1) Cámara RTS, niebla de guerra y un objeto RTS Demo Game que inicia la partida automáticamente. Presiona Play. Tus unidades (jugador 1) aparecen en tu posición inicial; las unidades enemigas deambulan por el mapa y aparecen y desaparecen en la niebla de guerra.
RTS Terrain Studio - In-Game Editor Demo Sandbox El editor de mapas en el juego (objeto RTS In-Game Editor), además de los mismos sistemas de juego, que permanecen desactivados hasta que pruebas el mapa en el juego. Presiona Play, pinta con el editor en el juego y luego presiona Play Test (F5). Presiona Esc para volver al editor.
RTS Terrain Studio - Editor Playground Four Kingdoms (4p) Solo el terreno, la luz y la cámara, sin jugabilidad. Quédate en el modo Edit, abre Tools ▸ RTS Terrain Studio ▸ Editor Window y prueba las herramientas en un mapa terminado.

Controles de la demo de juego

EntradaAcción
W A S D / teclas de flecha, borde de la pantalla, arrastrar con el botón central del ratónDesplazar la cámara. Con unidades seleccionadas, S las detiene en lugar de desplazar la cámara; las teclas de flecha siempre la desplazan.
Rueda del ratónAcercar y alejar (zoom)
Q / E, Alt + arrastrar con el botón derechoGirar la vista
Clic izquierdo / arrastrar con el botón izquierdoSeleccionar una unidad / dibujar un rectángulo de selección
Shift + clicAñadir o quitar unidades
Doble clic, Ctrl + clicSeleccionar todas las unidades de ese tipo que hay en pantalla
Clic derechoMover en formación (Shift pone la orden en cola)
S / H / PDetener / mantener la posición / patrullar
Ctrl + 0…9, Shift + 0…9, 0…9Guardar un grupo de control / añadirle la selección / recuperarlo (presiona dos veces para centrar la cámara)
MinimapaEl clic izquierdo mueve la cámara; el clic derecho mueve las unidades seleccionadas
B, DeleteColocar un edificio / eliminar el edificio que hay bajo el cursor
T, clic derecho en un árbolTalar el árbol que hay bajo el cursor / hacer que los trabajadores seleccionados lo talen
EscCancelar el modo actual o anular la selección
F1Mostrar u ocultar el panel de ayuda

Tu primer mapa

Un mapa se aloja en un GameObject RTS Terrain de tu escena y se guarda en un archivo .rtsmap de tu proyecto. La escena solo hace referencia al archivo; tus ediciones se guardan en el archivo. Empieza con una escena nueva para poder experimentar libremente:

  1. Elige File ▸ New Scene y selecciona una plantilla de escena de URP (por ejemplo, Basic (URP)).
  2. Guarda la escena enseguida con Ctrl+S, por ejemplo, como Assets/MyMaps/FirstMap.unity. Los archivos de mapa nuevos se crean junto a la escena guardada, así tu mapa y tu escena quedan juntos.

Crear un terreno

Hay dos formas. Ambas crean el mismo objeto RTS Terrain, con los componentes RTSTerrain, RTSVegetationRenderer, RTSWaterRenderer y RTSDecorManager ya configurados.

FormaMenúÚsala cuando
Un clicGameObject ▸ RTS Terrain Studio ▸ RTS TerrainQuieres empezar a pintar de inmediato. Obtienes un mapa plano de 128 × 128 celdas (celdas de 2 m, nivel de acantilado 2) con el tileset y las bibliotecas predeterminados del proyecto, colocado en el pivote de la Scene View y ya seleccionado. Se crea un nuevo archivo de mapa llamado <Scene name> Map.rtsmap junto a la escena (o Assets/Maps/New Map.rtsmap si la escena no está guardada).
GuiadaTools ▸ RTS Terrain Studio ▸ New Map Wizard...Quieres elegir el nombre, el tamaño, el tileset y la textura base, o generar un mapa jugable aleatorio.

Si el proyecto aún no tiene ningún tileset, el menú de un clic ofrece primero Create Demo Content. Con Continue Without, el terreno muestra un patrón de damero neutro hasta que asignes un tileset.

El New Map Wizard tiene cinco pasos. Usa Next y Back, o haz clic en un paso de la barra superior:

PasoAjustes
1. BasicsMap Name (también es el nombre del archivo), Author (se recuerda para el siguiente mapa), Description, Players (1–16; los mapas competitivos suelen diseñarse para 2, 4 u 8 jugadores).
2. SizeTarjetas de tamaño: Tiny 64 (duelos 1v1 rápidos y pruebas), Small 96 (1v1 y 2v2), Medium 128 (de 2 a 4 jugadores), Large 192 (de 4 a 6 jugadores), Huge 256 (de 6 a 8 jugadores), Epic 384 (8 jugadores o más, campañas), Epic XL 512 (mundos muy grandes) o Custom (múltiplos de 16). Además, Cell Size (m) (deja 2 m si no estás seguro), Cliff Level Height (m), Base Cliff Level (predeterminado: 2, para que puedas excavar hacia abajo y crear lagos, como en los mapas de los RTS clásicos) y una sección plegable Advanced con las resoluciones de textura, de detalles y de pathing.
3. LookTileset (haz clic en una textura de suelo para convertirla en la capa BASE, que cubre el mapa al principio), Vegetation Library, Decor Library. Sin tileset se muestra un botón Create Demo Content.
4. TerrainFlat (un mapa vacío y plano en el nivel de acantilado base, perfecto para aprender las herramientas) o Generate (mesetas, rampas, agua, posiciones iniciales, vegetación y decoración a partir de un Preset y una Seed, con Symmetry, Start Locations, Scatter Vegetation, Scatter Decor y un botón Preview).
5. CreateNew terrain object o Replace map of selected terrain, la ruta de Map File (Browse...), Add Environment (sol, luz ambiental y niebla del mapa; crea una luz Sun si la escena no tiene ninguna), Open Editor Window y un resumen. Haz clic en Create Map.

El asistente guarda el archivo de mapa, crea el terreno, lo encuadra en la Scene View y abre la ventana del editor. Todos los ajustes se pueden cambiar más tarde en la pestaña Map de la ventana del editor.

Abrir la ventana del editor

Abre Tools ▸ RTS Terrain Studio ▸ Editor Window (el asistente y la ventana Welcome pueden abrirla por ti, y el Inspector de RTS Terrain tiene un botón Open RTS Terrain Studio). Acóplala junto al Inspector para ver la Scene View al mismo tiempo.

La ventana del editor de RTS Terrain Studio con la pestaña Cliffs abierta
La ventana del editor con la pestaña Cliffs abierta: herramientas de acantilado, la descripción de la herramienta, los ajustes del pincel y la simetría.
ÁreaQué muestra
CabeceraSelector de terreno, nombre del mapa (un punto indica cambios sin guardar), Save, Save As…, Open…, Reload, Undo / Redo con el nombre del paso, un botón de ayuda y el interruptor Editing On / Editing Off.
Barra de pestañasOcho categorías de herramientas (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers), seguidas de Map y Settings. Un punto marca la categoría de la herramienta activa.
Página de herramientasBotones grandes de herramientas, una tarjeta que describe la herramienta activa con su estado en tiempo real, las tarjetas Brush y Symmetry, y los ajustes y paletas propios de la herramienta (capas, niveles de acantilado, jugadores, árboles, decoración).
Barra de estadoLa celda, el nivel de acantilado, la altura y el pathing bajo el cursor, y la memoria que ocupa el historial de deshacer.

Las ocho categorías y sus herramientas:

TeclaCategoríaHerramientas
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

Cada herramienta se describe en detalle en su propio capítulo (por ejemplo, Herramientas de altura y Herramientas de acantilado); la ventana en sí se describe en Ventana del editor.

Nociones básicas de pintura

  1. Haz clic en un botón de herramienta en la ventana del editor. Así se activa la edición (la cabecera muestra Editing On). Si vuelves a hacer clic en la herramienta activa, la edición se desactiva.
  2. Mueve el ratón sobre el terreno en la Scene View. El círculo del pincel se dibuja directamente sobre el suelo, exactamente donde vas a pintar.
  3. Mantén presionado el botón izquierdo del ratón y arrastra para pintar.
Entrada (Scene View)Acción
Botón izquierdo del ratónPintar un trazo (o hacer clic, en las herramientas de clic como Start Locations)
Shift + botón izquierdo del ratónAcción inversa: bajar, borrar, quitar
Ctrl + clicTomar un valor bajo el cursor (altura, capa de suelo, altura del agua…) en las herramientas que lo admiten
[ / ]Pincel más pequeño / más grande
- / =Bajar / subir la intensidad
1 … 8Cambiar a una categoría de herramientas (presiona de nuevo para recorrer sus herramientas)
G / PActivar o desactivar la cuadrícula / recorrer los modos de la superposición de pathing (Off, Walkability, Buildability, Combined)
RActivar o desactivar la rotación aleatoria del pincel
Ctrl+Z / Ctrl+YDeshacer / rehacer las ediciones del terreno (el menú Edit muestra los nombres de los pasos)
EscCancelar el trazo en curso o dejar de editar

La Scene View también muestra un panel superpuesto (overlay) RTS Terrain Studio con el interruptor de edición, los botones de categoría y de herramienta, los controles deslizantes Size y Strength y el ajuste Symmetry, además de una línea de estado en la parte inferior con la celda bajo el cursor, el nivel de acantilado y la altura. Puedes hacer la mayor parte del trabajo sin salir de la Scene View.

La navegación de la cámara en la Scene View funciona como siempre (arrastrar con el botón derecho para volar, Alt + arrastrar con el botón izquierdo para orbitar, arrastrar con el botón central para desplazar la vista). Si la herramienta Hand de Unity (Q) está activa, al arrastrar con el botón izquierdo se desplaza la vista en lugar de pintar; presiona W para volver a pintar. Todos los atajos de RTS Terrain Studio se pueden reasignar en Edit ▸ Shortcuts... (en macOS, Unity ▸ Shortcuts...) dentro de la categoría RTS Terrain Studio (útil si 2 entra en conflicto con el atajo del modo 2D de Unity en tu configuración).

Pintar alturas

  1. Abre la pestaña Height (1) y haz clic en Raise / Lower.
  2. Arrastra sobre el terreno para crear colinas. Mantén presionado el botón para seguir elevando el terreno. Mantén presionada Shift para bajarlo.
  3. Cambia a Smooth y arrastra sobre las zonas abruptas para suavizarlas.
  4. Prueba Noise para crear terreno irregular y Flatten (Ctrl+clic para tomar la altura) para las zonas de construcción.

Las alturas añaden detalle fino sobre los niveles de acantilado: una colina sobre una meseta permanece en esa meseta.

Pintar acantilados y rampas

Los acantilados son el corazón de un mapa de RTS clásico. El mapa se compone de niveles de acantilado del 0 al 15 y empieza en el nivel base (2 de forma predeterminada). Cada nivel equivale a un escalón de acantilado vertical y bien definido.

  1. Abre la pestaña Cliffs (2) y haz clic en Raise / Lower Level.
  2. Haz clic en el terreno: el suelo sube un nivel de acantilado. Mantén presionado el botón y arrastra para ampliar la meseta en la que empezaste, al mismo nivel.
  3. Haz clic dentro de la meseta y vuelve a arrastrar para apilar un segundo nivel encima.
  4. Mantén presionada Shift para bajar un nivel. Al bajar por debajo del nivel base se excavan cuencas para lagos y ríos.
  5. Haz clic en Ramp y pinta de un lado a otro de un borde de acantilado de un nivel para convertirlo en una pendiente transitable. Las cuatro esquinas de una celda deben ser rampa, así que pinta con generosidad a ambos lados del borde. Shift quita una rampa.
  6. Usa Clean Cliffs sobre los bordes irregulares para eliminar pilares y hoyos de un solo vértice. Cliff Type pinta otro material de acantilado (por ejemplo, Grassy Cliff en lugar de Rock Cliff).

Pintar texturas

  1. Abre la pestaña Paint (3) y haz clic en Paint Layer.
  2. Haz clic en una capa de suelo de la paleta de capas, por ejemplo, Dirt.
  3. Arrastra sobre el terreno para pintar caminos y claros. Shift borra la capa; Ctrl+clic toma la capa que hay bajo el cursor.
  4. Para baldosas con bordes nítidos al estilo de los RTS clásicos, usa Tile Paint. Para zonas grandes, usa Fill / Replace.
  5. Para conseguir rápidamente un aspecto natural, usa la tarjeta Auto Paint Whole Map de la pestaña Paint, con Profile en Summer Auto Paint: pinta tierra cerca de los acantilados, arena en las orillas, roca en las pendientes empinadas, etc.

Colocar árboles y hierba

  1. Abre la pestaña Vegetation (4) y haz clic en Place Trees.
  2. En Trees, elige uno o varios tipos de árbol, por ejemplo, Pine y Broadleaf. Si eliges varios tipos, se mezclan al azar.
  3. Deja Mode en Scatter y arrastra a lo largo de los bordes del mapa para plantar bosques. Density controla cuántos árboles añade cada toque de pincel. No se colocan árboles en pendientes más empinadas que Max Slope ni bajo el agua (Avoid Water).
  4. Establece Mode en Single para colocar un árbol por clic. Shift quita árboles.
  5. Haz clic en Paint Details, elige Grass o Flowers Yellow en Details y pinta praderas. Target Density define la densidad con la que pinta el pincel.

Las herramientas de vegetación necesitan una biblioteca de vegetación en el terreno. Los terrenos creados después de Create Demo Content reciben Demo Vegetation automáticamente. La decoración funciona igual, con Place Decor en la pestaña Decor (5) y la biblioteca Demo Decor.

Añadir agua

El agua llena el terreno que queda por debajo de una altura de superficie objetivo. La forma más sencilla es excavar una cuenca con las herramientas de acantilado y llenarla.

  1. En la pestaña Cliffs, usa Raise / Lower Level con Shift para excavar un lago o un río un nivel por debajo del nivel base (nivel 1 en un mapa predeterminado).
  2. Abre la pestaña Water (6) y haz clic en Flood Fill.
  3. Deja Water Height en su modo predeterminado (nivel de acantilado más desplazamiento) y Level en 1. El desplazamiento predeterminado sitúa la superficie del agua justo por debajo del nivel base, de modo que la cuenca se llena y el terreno de alrededor se mantiene seco.
  4. Haz clic dentro de la cuenca. Se llena con un solo clic. Shift+clic drena el agua conectada.
  5. Usa Paint Water para pintar agua a mano y Erase Water para quitarla.

El agua poco profunda y el agua profunda se determinan automáticamente a partir de la profundidad y afectan al pathing: las unidades terrestres pueden atravesar el agua poco profunda (allí no se puede construir nada, porque para construir hace falta suelo seco), mientras que el agua profunda les bloquea el paso.

Si el agua no tiene refracción y su orilla ignora los objetos que hay en el agua, las opciones Depth Texture y Opaque Texture de URP están desactivadas. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y aplica Enable Both.

Colocar posiciones iniciales

Las posiciones iniciales son los puntos de aparición de los jugadores. El juego de demostración hace aparecer allí las unidades de cada jugador.

  1. Abre la pestaña Markers (8) y haz clic en Start Locations.
  2. En la paleta de jugadores, elige P1 (jugador 1, rojo).
  3. Haz clic en el terreno donde debe empezar el jugador 1. Cada jugador tiene una sola posición inicial, así que al volver a hacer clic la mueves. Usa Yaw para definir la orientación.
  4. Elige el siguiente jugador y vuelve a hacer clic. Shift+clic quita la posición inicial más cercana.

Con la simetría activada, un solo clic coloca a todos los jugadores a la vez: las copias simétricas se asignan a los jugadores siguientes (P1, P2, …) siempre que la casilla Symmetry Assigns Next Players esté marcada. Consulta el tutorial de 10 minutos.

Coloca las posiciones iniciales en suelo llano y seco, lejos de los bordes de acantilado. Validate Map, en la pestaña Map (tarjeta Validation), informa de posiciones iniciales que faltan o que están en bordes de acantilado o en agua profunda, entre otros problemas.

Guardar el mapa

Tus cambios permanecen en memoria hasta que los guardas en el archivo .rtsmap:

  • Haz clic en Save en la cabecera de la ventana del editor. El botón se resalta mientras haya cambios sin guardar, y aparece un punto junto al nombre del mapa.
  • Save As… guarda un nuevo archivo .rtsmap en cualquier lugar de Assets y lo asigna al terreno.
  • Al guardar la escena (Ctrl+S) también se guardan los mapas de sus terrenos. Puedes desactivarlo en el menú de opciones del panel superpuesto de la Scene View (Save Maps When Saving the Scene).
  • El Inspector de RTS Terrain y el menú del panel superpuesto de la Scene View también tienen las entradas Save y Save As.

Si entras en el modo Play, cierras una escena o sales de Unity con cambios sin guardar en el mapa, un cuadro de diálogo te pregunta si quieres guardarlos. Selecciona un archivo .rtsmap en la ventana Project para ver la vista previa de su minimapa y un botón Open In Scene.

Un terreno sin archivo de mapa solo conserva su mapa en memoria. Si ves el cuadro de diálogo «The map of '…' is not saved as a map asset yet», elige Save As… para crear el archivo.

Tu primera prueba de juego

Hay dos formas de jugar tu mapa: con el Demo Game en el modo Play, o dentro del juego en ejecución con el editor en el juego y su botón Play Test.

Jugar con el Demo Game

  1. Abre la escena con tu terreno. Asegúrate de que tenga al menos una posición inicial (si no, las unidades aparecen en dos esquinas opuestas).
  2. Elige GameObject ▸ RTS Terrain Studio ▸ Demo Game. Se crea y se selecciona un objeto RTS Demo Game.
  3. Presiona Play. Guarda el mapa cuando se te pida.
  4. El juego de demostración configura todo lo que necesita: niebla de guerra, cámara RTS, selección, órdenes de movimiento, minimapa, HUD, colocación de edificios y tala de árboles. Tus unidades (jugador 1) aparecen en tu posición inicial; las unidades de los demás jugadores deambulan por el mapa.
  5. Presiona F1 para abrir el panel de ayuda. Los controles se enumeran en Controles de la demo de juego.
  6. Sal del modo Play. Los árboles talados durante la partida se restauran, así que el mapa queda tal como lo hiciste.

El componente RTS Demo Game tiene opciones como Local Team, Units Per Player, Unit Prefabs (si está vacío, se usan unidades sustitutas), Fog Of War, Minimap y Hud. Se usa la cámara principal de la escena.

Si las unidades siguen visibles dentro de la niebla de guerra, falta la renderer feature RTS Fog Of War. Ejecuta Validate Project y aplica Add Feature.

Editar y probar el mapa en el juego

El editor en el juego es el mismo conjunto de herramientas dentro de tu juego en ejecución, con su propia interfaz. Con él, los jugadores y los modders pueden crear y jugar mapas en un juego publicado.

  1. Abre la escena con tu terreno y una cámara. Si aún no tiene un RTS Demo Game, añade uno como se describe más arriba.
  2. Elige GameObject ▸ RTS Terrain Studio ▸ In-Game Editor.
  3. Presiona Play. El editor se abre con una barra superior (File, Map, deshacer y rehacer, Symmetry, Grid, Pathing, Markers, Play Test), una barra de categorías y herramientas a la izquierda, el panel de herramientas a la derecha y una barra de estado. El juego de demostración espera hasta que inicies una prueba de juego.
  4. Pinta como en el Unity Editor: botón izquierdo del ratón, Shift para invertir, Ctrl+clic para tomar un valor, [ ] y - = para el pincel, 1…8 para las categorías. Mueve la cámara con W A S D, arrastrando con el botón derecho o central, con Q/E y con la rueda del ratón.
  5. Haz clic en Play Test o presiona F5. Aparecen las unidades y se activan la niebla de guerra y la cámara RTS.
  6. Presiona Esc para volver al editor. El mapa se restaura al estado que tenía antes de la prueba de juego.

Teclas útiles en el editor en el juego: F1 ayuda, F2 mostrar u ocultar todo el editor, H ocultar los paneles, G cuadrícula, P superposición de pathing, M marcadores, F centrar la vista en el punto bajo el cursor, Ctrl+Z / Ctrl+Y deshacer y rehacer.

En el editor en el juego, File ▸ Save (Ctrl+S) y File ▸ Save As... (Ctrl+Shift+S) guardan los archivos de mapa en la carpeta de mapas del juego (de forma predeterminada, una carpeta Maps en Application.persistentDataPath), igual que lo hace un juego publicado. Cuando sales del modo Play en el Unity Editor después de editar, un cuadro de diálogo pregunta «Save map changes made in Play Mode?»: Save los guarda en el asset .rtsmap del terreno y Discard los descarta.

El editor en el juego se describe por completo en Editor en el juego.

Para programadores: reaccionar a una prueba de juego

Tu propio código de juego puede escuchar el mismo evento que usa el juego de demostración. Este ejemplo registra en el log dónde empieza cada jugador cuando comienza una prueba de juego:

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");
        }
    }
}

En la Guía de scripting encontrarás las consultas al terreno, la carga de mapas y la API de herramientas.

Tutorial de 10 minutos: tu primer mapa 1v1

En este tutorial crearás un mapa pequeño y equilibrado para dos jugadores: cada jugador tiene una base sobre una meseta con una rampa, un río separa las dos mitades y los bosques enmarcan el mapa. El pintado simétrico garantiza que ambos lados sean idénticos, así que solo tienes que construir una mitad.

Vista superior de un mapa simétrico para dos jugadores
Un mapa 1v1 con simetría central visto desde arriba: todo lo que pintas en un lado aparece girado 180° en el otro.

Antes de empezar: el proyecto debe estar validado y debe existir el contenido de demostración (consulta Instalación y configuración).

1. Crear el mapa (1 minuto)

  1. Elige File ▸ New Scene, selecciona una plantilla de URP y guarda la escena como Assets/MyMaps/Duel.unity.
  2. Abre Tools ▸ RTS Terrain Studio ▸ New Map Wizard....
  3. Basics: Map Name Duel, tu nombre como Author, Players 2. Haz clic en Next.
  4. Size: haz clic en Small (96 × 96, 1v1 y 2v2). Deja Cell Size en 2 m y Base Cliff Level en 2. Haz clic en Next.
  5. Look: Tileset Summer, haz clic en Grass como capa base, Vegetation Library Demo Vegetation, Decor Library Demo Decor. Haz clic en Next.
  6. Terrain: elige Flat. Haz clic en Next.
  7. Create: deja New terrain object, marca Add Environment y Open Editor Window y, a continuación, haz clic en Create Map.

2. Activar la simetría (10 segundos)

  1. En la ventana del editor, abre la pestaña Cliffs. Busca la tarjeta Symmetry debajo de la tarjeta Brush.
  2. Elige Rotate 180°. Deja el centro de simetría en el centro del mapa (0.5, 0.5).
  3. Mueve el ratón sobre el terreno: ahora ves un segundo pincel en el lado opuesto del mapa, y las guías de simetría se dibujan en la Scene View.

Todas las herramientas comparten la simetría, así que sigue activada cuando cambias de herramienta. Los modos:

Modo (ventana)CopiasUso típico
Off1Mapas de campaña o de historia
Mirror X2Reflejo izquierda-derecha
Mirror Z2Reflejo arriba-abajo
Mirror XZ4Reflejo en ambos ejes (4 jugadores)
Rotate 180°2Simetría central, el diseño 1v1 clásico (esquina contra esquina)
Rotate 90°4Simetría rotacional de orden 4 para 4 jugadores; requiere un mapa cuadrado
Diagonal2Reflejo respecto a la diagonal; requiere un mapa cuadrado

El panel superpuesto de la Scene View y el editor en el juego ofrecen los mismos modos (el panel superpuesto muestra los nombres del enum, por ejemplo, Rotate 2 para Rotate 180°).

3. Construir las bases (2 minutos)

  1. Haz clic en Raise / Lower Level. Con [ y ], elige un pincel grande.
  2. En el cuadrante inferior izquierdo del mapa, haz clic y arrastra para pintar una meseta de aproximadamente un cuarto del ancho del mapa. La misma meseta aparece en el cuadrante superior derecho.
  3. Haz clic en Ramp y pinta de un lado a otro del borde de la meseta que da al centro del mapa. Haz la rampa de al menos dos o tres celdas de ancho para que puedan pasar grupos de unidades.
  4. Haz clic en Clean Cliffs y pinta una vez sobre el contorno de la meseta para eliminar protuberancias sueltas de un solo vértice.

4. Excavar el río (2 minutos)

  1. Todavía con Raise / Lower Level, mantén presionada Shift y arrastra un río desde la esquina superior izquierda hacia el centro del mapa. La copia simétrica lo continúa desde la esquina inferior derecha, de modo que el río cruza todo el mapa.
  2. Deja uno o dos puentes de tierra cerca del centro para que cada jugador pueda llegar hasta el otro.
  3. Abre la pestaña Water, haz clic en Flood Fill, deja Level en 1 y haz clic en el cauce del río. Vuelve a hacer clic en cualquier parte que aún no se haya llenado.

5. Pintar el suelo (2 minutos)

  1. Abre la pestaña Paint. En la tarjeta Auto Paint Whole Map, establece Profile en Summer Auto Paint (si se deja vacío, se usan reglas integradas que se asocian a los nombres de las capas del tileset) y haz clic en Auto Paint Whole Map. Confirma el cuadro de diálogo; todo el repintado cuenta como un solo paso de deshacer. Aparece tierra cerca de los acantilados, arena en las orillas del río y roca en las pendientes empinadas.
  2. Haz clic en Paint Layer, elige Cobblestone y pinta un cuadrado en cada meseta, donde se levantará el edificio principal (basta con un toque: la copia pinta la otra base).
  3. Elige Dirt y pinta un camino desde la rampa de la base hasta el puente de tierra.
  4. Opcional: en la pestaña Height, usa Raise / Lower con poca intensidad (-) para añadir algunas colinas suaves entre las bases, y Smooth para suavizarlas.

6. Bosques, hierba y rocas (2 minutos)

  1. Abre la pestaña Vegetation y haz clic en Place Trees. Elige Pine y Broadleaf y deja Scatter.
  2. Pinta bosques a lo largo del borde del mapa y detrás de cada base. Deja libres las rampas, los caminos y los puentes de tierra; Shift quita árboles.
  3. Haz clic en Paint Details, elige Grass y Flowers Yellow, y pinta algunas praderas.
  4. Abre la pestaña Decor, haz clic en Place Decor, elige algunas rocas y haz clic cerca de las orillas del río.

7. Colocar las posiciones iniciales (30 segundos)

  1. Abre la pestaña Markers y haz clic en Start Locations.
  2. Elige el jugador P1 y asegúrate de que la casilla Symmetry Assigns Next Players esté marcada.
  3. Haz clic una vez en el centro de la base inferior izquierda. P1 se coloca allí y P2 aparece en la base opuesta; ambos miran hacia el centro si estableces un Yaw que apunte hacia él.

8. Validar, guardar y jugar (1 minuto)

  1. Abre la pestaña Map y haz clic en Validate Map en la tarjeta Validation. Corrige los errores que indique (por ejemplo, una posición inicial en un borde de acantilado).
  2. Haz clic en Save en la cabecera de la ventana. El mapa se guarda en Assets/MyMaps/Duel.rtsmap.
  3. Elige GameObject ▸ RTS Terrain Studio ▸ Demo Game y presiona Play.
  4. Selecciona tus unidades, haz clic con el botón derecho para enviarlas a través del puente de tierra y observa cómo el enemigo surge de la niebla de guerra.

¿Quieres cuatro jugadores? Crea un mapa cuadrado (por ejemplo, Medium 128 × 128), elige Rotate 90° y establece Players en 4. Así, un solo clic con la herramienta Start Locations coloca de P1 a P4. O deja que lo haga el generador: en el paso Terrain del asistente, elige Generate, configura Symmetry y Start Locations, haz clic en Preview y luego perfecciona el resultado a mano.

Próximos pasos

  • Ventana del editor: cada pestaña, tarjeta y ajuste en detalle, incluida la pestaña Map (cambio de tamaño, generador, importación y exportación de mapas de alturas, minimapa, entorno).
  • Los capítulos de herramientas, uno por categoría: Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers.
  • Editor en el juego: publica el editor con tu juego y deja que los jugadores creen mapas.
  • Scripting: consulta alturas, niveles de acantilado y pathing desde código, carga mapas en tiempo de ejecución y reacciona a las ediciones.
  • Integraciones: multijugador con Mirror, búsqueda de rutas con el A* Pathfinding Project o Unity NavMesh.
  • Solución de problemas: materiales rosas, agua plana, desfases al pintar y otros problemas comunes.

Configuración del proyecto

RTS Terrain Studio necesita algunos ajustes del proyecto: el Universal Render Pipeline con dos texturas de cámara, la renderer feature de niebla de guerra, el espacio de color Linear y tres paquetes de Unity. La ventana Tools ▸ RTS Terrain Studio ▸ Validate Project los comprueba todos y corrige casi todos con un clic, así que la mayoría de los usuarios nunca necesita los pasos manuales de este capítulo. Sigue leyendo si una comprobación se queda en rojo, si quieres saber qué cambia una corrección o si configuras un proyecto a mano.

El botón de ayuda de cada tarjeta del Project Validator abre la sección correspondiente de este capítulo.

El Project Validator

Abre Tools ▸ RTS Terrain Studio ▸ Validate Project o haz clic en Validate en la ventana Welcome. Las comprobaciones se ejecutan en cuanto se abre la ventana. Se agrupan en cinco categorías:

CategoríaQué se comprueba
RenderingURP está activo, Depth Texture y Opaque Texture están activadas, la renderer feature de niebla de guerra está instalada, las builds del juego conservan los modos de niebla atmosférica que usa RTS Environment.
ProjectEspacio de color Linear; Burst, Collections y Mathematics están instalados; qué gestión de entrada está activa.
WebGLSolo mientras Web es la plataforma de build activa: los niveles de calidad de las builds web tienen Depth Texture y Opaque Texture, y la plantilla web ocupa toda la ventana del navegador (consulta Comprobaciones para Web en el Project Validator).
ContentLos shaders del paquete compilan y se incluyen en las builds, los arrays de texturas de los tilesets están generados, los prototipos instanciados tienen activada la instanciación por GPU.
SceneCada RTS Terrain de las escenas abiertas tiene un tileset y un archivo de mapa.

Cada resultado tiene uno de estos cuatro estados:

EstadoSignificado
OKTodo está configurado correctamente.
InfoUna sugerencia o recomendación. No hay nada roto.
WarningUna característica funciona de forma limitada, por ejemplo, el agua sin refracción. Se recomienda corregirlo.
ErrorFalta un ajuste obligatorio. Partes del paquete no funcionarán.

Fix All aplica todas las correcciones disponibles. Primero, un cuadro de diálogo enumera todos los cambios, y no se modifica nada hasta que haces clic en Apply Fixes. Las correcciones que necesitan URP (las texturas de cámara y la renderer feature de niebla de guerra) se ejecutan automáticamente después de asignar el pipeline, así que un solo clic configura un proyecto nuevo. Run Again repite las comprobaciones. La lista completa de comprobaciones, con sus botones de corrección, está en Validar el proyecto.

El Project Validator comprueba tu proyecto. Para comprobar un solo mapa (posiciones iniciales, accesibilidad, prototipos que faltan, límites de rendimiento), usa el validador de mapas de la pestaña Map; consulta Validación de mapas.

Ejecutar las comprobaciones desde código

RTSProjectValidator.RunChecks() devuelve los resultados sin cambiar nada, y RTSProjectValidator.FixAll() aplica las correcciones. Puedes usarlos en un script de compilación o en una tarea de integración continua. El ejemplo debe colocarse en una carpeta Editor.

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);
    }
}

La corrección Install para los paquetes que faltan solo inicia una solicitud del Package Manager, y los scripts del paquete no se pueden compilar hasta que esta termine. Instala Burst, Collections y Mathematics antes de ejecutar el paquete en modo por lotes (batch mode).

Universal Render Pipeline

Todos los shaders del paquete están escritos para URP. Sin él, el terreno, el agua y la vegetación se renderizan en rosa o no se renderizan en absoluto. La corrección Use URP toma un asset de URP del proyecto (o crea Assets/Settings/RTS URP.asset con un renderer) y lo asigna como pipeline de renderizado predeterminado y a cada nivel de calidad que use otro pipeline. Para hacer lo mismo a mano:

  1. Si el proyecto aún no tiene un asset de URP, crea uno en la ventana Project con Create ▸ Rendering ▸ URP Asset (with Universal Renderer).
  2. Asígnalo en Edit ▸ Project Settings ▸ Graphics como Default Render Pipeline.
  3. Abre Edit ▸ Project Settings ▸ Quality. Para cada nivel de calidad que sobrescriba el pipeline con su propio Render Pipeline Asset, comprueba que también sea un asset de URP.

Depth Texture y Opaque Texture

El agua usa la textura de profundidad de la cámara para que su color de profundidad, la espuma y la orilla suave se adapten a los objetos que hay dentro del agua, y la textura opaca, para la refracción. Sin ellas, el agua se ve más simple. Selecciona cada asset de URP que usen tus niveles de calidad y marca Depth Texture y Opaque Texture en su sección Rendering, o haz clic en Enable Both en el validador.

Una cámara puede sobrescribir ambos ajustes en la sección Rendering de su propio Inspector, y el agua sigue el ajuste de cada cámara que la renderiza. Si el agua de la Game View sigue viéndose más simple de lo esperado, comprueba que la cámara de tu juego no los desactive. La Scene View siempre usa el asset de URP. Consulta también los requisitos de URP del agua.

La renderer feature de niebla de guerra

La renderer feature RTS Fog Of War (RTSFogOfWarRendererFeature) dibuja la niebla de guerra sobre todo lo que hay en pantalla, incluidas las unidades y los props que usan tus propios materiales. Sin ella, solo los shaders propios del paquete muestran la niebla de guerra. Add Feature la añade al renderer predeterminado de cada asset de URP en uso. Para añadirla a mano, selecciona el asset Universal Renderer Data, haz clic en Add Renderer Feature y elige RTS Fog Of War. Encontrarás los detalles en Renderizado: renderer feature y respaldo en los shaders.

Espacio de color

El paquete está diseñado para el espacio de color Linear, el predeterminado de URP. En el espacio Gamma, los colores, la iluminación y el agua se ven desvaídos en comparación con las capturas de pantalla. Establece Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Color Space en Linear o haz clic en Switch to Linear en el validador.

Al cambiar el espacio de color, se vuelven a importar todas las texturas. En un proyecto grande, esto puede tardar un rato.

Paquetes necesarios

El ensamblado de tiempo de ejecución usa Burst (com.unity.burst), Collections (com.unity.collections) y Mathematics (com.unity.mathematics) para sus jobs y su modelo de datos. Los scripts del paquete no compilan mientras falte alguno de ellos. Instálalos desde Window ▸ Package Manager ▸ Unity Registry o haz clic en Install en el validador, que añade todos los paquetes que faltan con una sola solicitud del Package Manager. Las versiones probadas se indican en Paquetes necesarios.

Si la importación muestra errores de compilación y falta el menú Tools ▸ RTS Terrain Studio, el validador tampoco puede ejecutarse, porque forma parte del paquete. Instala los tres paquetes a mano y luego abre el validador.

Gestión de entrada

Toda la entrada en tiempo de ejecución (el editor en el juego, la cámara y las unidades de ejemplo) pasa por la clase estática RTSInput. Cuando el paquete Input System (com.unity.inputsystem) está instalado, el ensamblado de tiempo de ejecución se compila con el símbolo de compilación RTS_INPUT_SYSTEM y usa el Input System. En caso contrario, o cuando el proyecto solo usa el antiguo Input Manager, RTSInput recurre a la antigua API UnityEngine.Input.

Active Input HandlingInput System instaladoResultado
Input System Package (new)síOK. Se usa el Input System.
BothsíOK. Se usa el Input System.
Input Manager (old)síInfo. Se usa el modo de respaldo.
cualquieranoInfo. Se usa el modo de respaldo. Funciona, pero no se recomienda para proyectos nuevos.

El ajuste está en Edit ▸ Project Settings ▸ Player ▸ Other Settings ▸ Active Input Handling. Unity pide reiniciar el editor cuando lo cambias. La comprobación Input handling nunca cambia el ajuste por ti, porque depende de tu juego. Consulta también Gestión de entrada en los requisitos.

Shaders y contenido

  • Package shaders compile. Si un shader del paquete tiene errores de compilación (los objetos afectados se renderizan en rosa), Reimport lo vuelve a importar. Esto ayuda cuando los shaders se importaron antes de que URP terminara de cargarse. Si faltan por completo algunos shaders necesarios, vuelve a importar el paquete desde la Asset Store. Consulta Solución de problemas.
  • Shaders included in builds. Consulta Compilar tu juego.
  • Tileset texture arrays built. Los tilesets sin arrays generados y actualizados siguen funcionando, pero hasta que se vuelvan a generar los arrays, el editor genera arrays sin comprimir al cargar el mapa, lo que consume memoria y alarga el tiempo de carga. Build Arrays los genera para cada tileset desactualizado. Al compilar el juego, los arrays desactualizados se vuelven a generar automáticamente; los tilesets cuyos arrays nunca se generaron también usan arrays sin comprimir en el juego compilado. Consulta Arrays de texturas y Arrays de texturas en las builds del juego.
  • GPU instancing on instanced prototypes. Los materiales de los árboles instanciados, de los detalles en modo Mesh y de la decoración instanciada deberían tener marcada la opción Enable GPU Instancing. De lo contrario, se dibujan mediante copias temporales, y sus variantes de instanciación pueden eliminarse de las builds del juego. Enable Instancing la marca en cada material de la lista que se pueda editar. Los materiales que están dentro de un modelo o de un paquete son de solo lectura: extráelos primero. Consulta Instanciación por GPU.
  • Terrains have a tileset. Un terreno sin tileset muestra un aspecto neutro provisional. La corrección asigna un tileset del proyecto a cada terreno que no tenga uno (se puede deshacer). Consulta Tilesets.

Las correcciones de escena no se ofrecen en el modo Play, porque los cambios en la escena hechos en el modo Play se pierden al salir de él.

Archivos de mapa para los terrenos de la escena

Un RTS Terrain guarda su mapa en un asset .rtsmap (su campo Map Asset). Un terreno sin él tiene su mapa solo en memoria, y tus ediciones se pierden al cerrar la escena. La comprobación Terrains save to a map file enumera estos terrenos. Create Map Files guarda el mapa actual de cada terreno de la lista en un nuevo archivo .rtsmap en la carpeta de su escena (o en Assets/Maps si la escena aún no se ha guardado) y se lo asigna al terreno.

Los terrenos creados con el New Map Wizard o con el contenido de demostración ya tienen un archivo de mapa. Para saber cómo funcionan los assets de mapa, Save y Save As, lee Assets de mapa y archivos de mapa.

Compilar tu juego

Unity solo incluye shaders en una build del juego cuando un material de la build los usa. RTS Terrain Studio crea los materiales del terreno, el agua y la niebla de guerra desde código en tiempo de ejecución, por lo que los shaders se referencian desde el asset Resources/RTSTerrainStudio/RTSShaderReferences.asset dentro de la carpeta del paquete. El asset también referencia el material DecorPlaceholder, que mantiene en la build las variantes de instanciación de URP Lit. Este asset se comprueba y se actualiza automáticamente después de cada recarga de scripts, cada vez que se importan shaders y antes de cada build del juego. Si la comprobación Shaders included in builds está en rojo, haz clic en Update.

Antes de compilar, repasa esta lista:

  1. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y corrige todos los errores y advertencias.
  2. Comprueba que cada nivel de calidad de tu plataforma de destino use un asset de URP con Depth Texture y Opaque Texture, y que su renderer tenga la feature RTS Fog Of War si usas niebla de guerra.
  3. Genera una vez los arrays de texturas de tus tilesets (Build Arrays en el validador o Build Texture Arrays en el Inspector del tileset), para que el terreno no los genere sin comprimir durante la carga. Los arrays desactualizados se vuelven a generar automáticamente antes de cada build del juego (consulta más abajo).
  4. Si tus mapas usan niebla atmosférica, asegúrate de que la comprobación Fog kept in player builds esté en OK (consulta Niebla en las builds del juego).
  5. Valida tus mapas en la pestaña Map (consulta Validación de mapas).
  6. Añade tus escenas a la lista de escenas de tu perfil de build. Los mapas asignados a un RTS Terrain de una escena se incluyen como cualquier otro asset referenciado.
  7. Si publicas el editor en el juego, lee Publicar el editor en el juego. Los mapas que los jugadores guardan en tiempo de ejecución van a la carpeta Maps de RTSMapSerializer.StorageRoot (Application.persistentDataPath y, en las builds web, el almacenamiento del navegador); consulta Archivos de mapa en tiempo de ejecución.
  8. Si subes el Managed Stripping Level por encima de Minimal y has escrito tus propias herramientas de edición, lee Eliminación de código y herramientas personalizadas.
  9. Para una build web, lee también Builds web (WebGL).

No muevas ni renombres RTSShaderReferences.asset ni la carpeta Resources del paquete: el paquete carga estos assets con Resources.Load en tiempo de ejecución.

Arrays de texturas en las builds del juego

Una build del juego usa los arrays de texturas generados de un tileset tal como están, porque solo el editor puede compararlos con las texturas de origen. Para que una build nunca se publique con texturas de suelo antiguas, justo antes de que empiece una build del juego se comprueba cada tileset cuyos arrays ya se generaron alguna vez:

  • Los arrays que ya no coinciden con las texturas, la resolución o la compresión, o que se generaron para otra plataforma u otra compresión de texturas, se vuelven a generar con calidad de compresión normal. Una barra de progreso muestra el avance.
  • Los tilesets cuyos arrays nunca se generaron no se tocan. El juego compilado genera para ellos arrays sin comprimir al cargar un mapa.
  • Si se cancela o falla una regeneración y las texturas o los ajustes han cambiado, un cuadro de diálogo ofrece Try Again, Cancel Build y Build Anyway. Los arrays que solo usan otro formato siguen mostrando las texturas correctas, así que para ellos solo se registra una advertencia.
  • En ese caso, una build en modo batch se detiene. Añade -rtsAllowOutdatedTextureArrays a la línea de comandos para compilar de todos modos, con una advertencia por tileset.
  • Un editor sin dispositivo gráfico (-nographics, habitual en los servidores de compilación) no puede generar arrays. En ese caso, la build continúa con una advertencia por cada tileset desactualizado, y el juego compilado usa los arrays tal como se generaron la última vez. Genera los arrays en el editor antes de una build así.
  • Los arrays solo se vuelven a generar para la plataforma de build activa. Si compilas para otra plataforma, la build registra una advertencia en su lugar: cambia primero a esa plataforma.
  • Los asset bundles y el contenido de Addressables que compilas por separado no ejecutan esta comprobación. Genera antes los arrays.

La comprobación de arrays desactualizados usa el estado de importación de las texturas de origen, que puede cambiar al actualizar Unity. Por eso, la primera build del juego después de una actualización puede volver a generar los arrays de todos los tilesets ya generados, incluidos los tilesets de demostración.

Los arrays generados se guardan en el asset del tileset con lectura habilitada (readable). El juego compilado libera esta copia en la CPU la primera vez que se usan los arrays, lo que ahorra unos 14 MB por tileset a 512 px y unos 56 MB a 1024 px.

Niebla en las builds del juego

RTSEnvironment activa la niebla atmosférica guardada en un mapa mientras el juego se ejecuta. Unity elimina las variantes de shader de los modos de niebla que cree que no se usan: con Project Settings ▸ Graphics ▸ Shader Stripping ▸ Fog Modes en Automatic, solo conserva la niebla de las escenas de la build que se guardaron con la niebla activada. RTS Environment restaura la niebla propia de la escena antes de cada guardado, así que su niebla no cuenta, y los mapas con niebla no mostrarían ninguna niebla en una build.

La comprobación Fog kept in player builds lee las escenas de la build del perfil de build activo y las escenas abiertas, y enumera los modos de niebla que RTS Environment usa en ellas. Keep Fog Modes (que también forma parte de Fix All) establece Fog Modes en Custom y conserva exactamente esos modos, además de los que ya se conservaban. Cada modo conservado añade variantes de shader a la build, por lo que no se añaden los modos que no se usan. Si el perfil de build activo sobrescribe los ajustes de Graphics, comprueba también su ajuste Fog Modes.

Builds web (WebGL)

RTS Terrain Studio funciona en un navegador web: el terreno, la vegetación, el agua, la decoración, la niebla de guerra, el editor en el juego y los ejemplos. Sin embargo, un navegador funciona de forma distinta a un equipo de escritorio: no hay ninguna carpeta que el jugador pueda abrir, algunas teclas pertenecen al navegador, todo el código se ejecuta en un solo hilo y todo el juego comparte un único heap de memoria de tamaño limitado. Esta sección explica qué hace el paquete al respecto y cómo compilar, probar y publicar una build web.

En resumen:

  1. Abre File ▸ Build Profiles, selecciona Web y haz clic en Switch Platform (o convierte tu perfil de build para Web en el perfil activo).
  2. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y haz clic en Fix All. Con Web activa, el validador también instala la plantilla de ventana completa y comprueba los niveles de calidad de las builds web.
  3. Si tus mapas usan niebla atmosférica, asegúrate de que la comprobación Fog kept in player builds esté en OK.
  4. Haz clic en Build And Run. Unity compila el juego y lo abre en tu navegador.

Qué funciona en el navegador

TemaCompatibilidad
API gráficaWebGL 2.0, la opción predeterminada de las builds web (Auto Graphics API). Todos los shaders del paquete funcionan con ella. Esta sección describe WebGL 2.0; WebGPU, que Unity ofrece como opción, no se trata aquí.
NavegadoresLos navegadores de escritorio actuales compatibles con el reproductor web de Unity, usados con ratón y teclado.
Teléfonos y tabletasLa página se carga, pero el editor en el juego, la cámara RTS y las unidades de ejemplo están pensados para ratón y teclado. La entrada táctil no es compatible. La plantilla del paquete muestra un aviso en los dispositivos móviles.
RenderizadoEl terreno, la vegetación, el agua, la decoración y la niebla de guerra se ven igual que en escritorio. La hierba se guarda en una textura de coma flotante en lugar de en un structured buffer (consulta Renderizado y rendimiento).
Editor en el juegoTodas las herramientas, el explorador de mapas y la prueba de juego. Los mapas se guardan en el almacenamiento del navegador y se pueden descargar e importar (consulta Mapas en el navegador). El tamaño del mapa y la memoria de deshacer tienen límites más bajos (consulta El editor en el juego en el navegador).
HilosLas builds web no tienen hilos de trabajo (worker threads), así que todos los jobs se ejecutan uno tras otro en el hilo principal.

La plantilla de ventana completa

La página que rodea una build web proviene de una plantilla web. Las plantillas propias de Unity, Default y Minimal, muestran el juego en un recuadro fijo en los navegadores de escritorio (960 × 600 píxeles, salvo que lo cambies). El editor en el juego, el HUD y el minimapa se escalan con ese recuadro, por lo que su texto se vuelve difícil de leer. Por eso, el paquete incluye su propia plantilla:

  • El juego ocupa toda la ventana del navegador, sobre un pie de página estrecho con el nombre del juego y el botón de pantalla completa, y cambia de tamaño con la ventana. En teléfonos y tabletas ocupa toda la página.
  • Se conservan la barra de carga (con un porcentaje), el botón de pantalla completa y los banners de mensajes de Unity.
  • En pantallas de alta resolución, la resolución de renderizado se limita a 1.5 veces el tamaño en píxeles CSS, lo que mantiene alta la tasa de fotogramas. Para ajustarlo, cambia maxDevicePixelRatio en el index.html de la plantilla instalada (los valores más bajos, hasta 1, son más rápidos; los más altos, más nítidos).
  • Los archivos que guarda el juego, como los mapas del editor en el juego, se escriben de inmediato en el almacenamiento del navegador.
  • La página no hace solicitudes a otros servidores; sus iconos forman parte de la propia página.

Para instalarla:

  1. Elige Tools ▸ RTS Terrain Studio ▸ Install WebGL Template o haz clic en Install Template en la tarjeta Web template fills the browser window del Project Validator.
  2. La plantilla se copia en Assets/WebGLTemplates/RTSTerrainStudio (Unity solo encuentra plantillas en Assets/WebGLTemplates) y se selecciona en Player Settings ▸ Resolution and Presentation ▸ Web Template.
  3. Si la plantilla ya está instalada, el menú pregunta si quieres conservar y seleccionar tu copia (Keep and Select) o sustituirla por la versión del paquete (Replace). La copia pertenece a tu proyecto: edítala libremente; el paquete nunca la sobrescribe sin preguntar.

Perfiles de build: un perfil de build puede sobrescribir los Player Settings y, en ese caso, conserva su propia elección de plantilla. Activa tu perfil de build para Web antes de instalar la plantilla, para que el ajuste se guarde en ese perfil. Si la instalaste mientras otra plataforma estaba activa, selecciona a mano la plantilla RTSTerrainStudio en los Player Settings sobrescritos de tu perfil de build para Web.

La plantilla se basa en la plantilla Default de Unity y solo usa las variables de plantilla propias de Unity, así que no aparece nada nuevo en Player Settings. En teléfonos y tabletas muestra durante unos segundos el aviso This game is best played on a computer with a mouse and keyboard. Si tu juego admite entrada táctil, elimina esa línea de index.html.

Desde un script de editor (por ejemplo, un script de compilación), RTSWebGLTemplateInstaller.InstallAndSelect(false) instala la plantilla sin cuadros de diálogo, conserva una copia existente y la selecciona. IsInstalled e IsSelected indican el estado actual.

Comprobaciones para Web en el Project Validator

Mientras Web es la plataforma de build activa, el validador muestra un grupo WebGL con estas tres comprobaciones.

ComprobaciónCorrecciónQué comprueba
Web quality levels have Depth and Opaque TextureEnable BothLos niveles de calidad marcados en la columna Web de Project Settings ▸ Quality (el nivel predeterminado de Web aparece primero) y el asset de URP con el que renderiza cada uno de ellos. El editor suele renderizar con un nivel de calidad de escritorio, así que una build web puede tener un agua más simple que nunca llegas a ver en el editor. Cada textura cuesta una copia a pantalla completa por fotograma. La comprobación general Depth and Opaque Texture enabled puede indicar el mismo asset; Fix All corrige ambas.
MSAA is off for Web buildsTurn MSAA OffUn error cuando un nivel de calidad para Web renderiza con un asset de URP que combina MSAA con la Depth Texture o la Opaque Texture. URP no puede renderizar esta combinación en WebGL 2: el lienzo se queda en negro y la consola del navegador muestra Render Graph Execution error. La corrección desactiva MSAA en estos assets. Los niveles de calidad de escritorio que comparten uno de estos assets también pierden MSAA, así que asigna a la plataforma Web un nivel de calidad con su propio asset de URP.
Web template fills the browser windowInstall Template / ReinstallUna advertencia para las plantillas Default y Minimal de Unity, un error cuando la plantilla del paquete está seleccionada pero falta su carpeta (la build fallaría), y OK para la plantilla del paquete. La plantilla PWA también ocupa toda la ventana y da OK, pero su service worker guarda la build en caché: aumenta Player Settings ▸ Version con cada subida. Cualquier otra plantilla personalizada recibe un estado Info sin corrección.

La comprobación de Rendering Fog kept in player builds se ejecuta para todas las plataformas y también es importante para las builds web (consulta Niebla en las builds del juego).

Ajustes de build recomendados

AjusteDóndeRecomendación
Texture CompressionTu perfil de build para WebUse Player Settings o DXT para navegadores de escritorio. Para una build pensada para navegadores móviles, elige ASTC o ETC2. Los arrays de texturas de los tilesets siguen este ajuste (DXT5, ASTC 6×6 o ETC2 RGBA8) y se vuelven a generar antes de la build. Si un navegador no puede usar el formato elegido, Unity descomprime las texturas, lo que necesita unas cuatro veces más memoria.
Maximum Memory SizePlayer Settings ▸ Publishing SettingsMantén el valor predeterminado de 2048 MB. Los límites del editor en el juego en el navegador están pensados para este heap.
Compression FormatPlayer Settings ▸ Publishing SettingsBrotli ofrece la descarga más pequeña, pero solo funciona con HTTPS. Gzip también funciona con HTTP. Ambos necesitan las cabeceras correctas en el servidor (consulta Publicar en un sitio web).
Decompression FallbackPlayer Settings ▸ Publishing SettingsActívalo cuando no puedas configurar las cabeceras del servidor, por ejemplo, en portales de juegos. Así el juego se carga en cualquier sitio, con un inicio algo más lento.
Data CachingPlayer Settings ▸ Publishing SettingsActivado. El navegador conserva los datos del juego descargados, así que la siguiente visita empieza más rápido.
Enable ExceptionsPlayer Settings ▸ Publishing SettingsExplicitly Thrown Exceptions Only (el valor predeterminado) para las builds de lanzamiento. En este modo, una referencia nula no se notifica como excepción, así que usa una build de desarrollo con Full With Stacktrace mientras buscas un error.
Managed Stripping LevelPlayer Settings ▸ Other Settings ▸ OptimizationCualquier nivel funciona con el paquete. Los niveles más altos reducen el tamaño de la descarga; en ese caso, las herramientas de edición personalizadas necesitan [Preserve] (consulta Eliminación de código y herramientas personalizadas).
Niveles de calidad y asset de URPProject Settings ▸ QualityDesmarca la columna Web de los niveles de calidad de escritorio que no uses en el navegador, para que sus assets de URP no añadan variantes de shader a la build. En el asset de URP que se usa para Web, mantén desactivados los light cookies y la mezcla de reflection probes, salvo que tus escenas los necesiten: WebGL 2.0 solo permite 16 texturas por draw call (consulta Notas para móviles). Mantén desactivado el ajuste Anti Aliasing (MSAA) en ese asset: junto con la Depth Texture o la Opaque Texture, impide que URP renderice en WebGL 2.
Web TemplatePlayer Settings ▸ Resolution and PresentationRTSTerrainStudio (consulta La plantilla de ventana completa).

Probar una build web en tu equipo

  • Build And Run es la forma más sencilla. Unity inicia un pequeño servidor web local y abre el juego en tu navegador predeterminado.
  • Hacer doble clic en el index.html compilado no funciona. Los navegadores no ejecutan builds web desde direcciones file://, y la página solo muestra un mensaje de error.
  • Para volver a abrir más tarde una carpeta de build terminada, sírvela con cualquier servidor web local. Un servidor sencillo que no establece cabeceras de compresión solo puede servir builds con Decompression Fallback activado o con la compresión desactivada.
  • Prueba una vez las teclas en el navegador de destino: F1, F5, Ctrl+S, Alt+N y Esc en pantalla completa (consulta El editor en el juego en el navegador).

Publicar en un sitio web

  • Sube la carpeta de build completa: index.html, Build y TemplateData.
  • Las builds comprimidas necesitan dos cabeceras en los archivos de Build: Content-Encoding: br para Brotli o Content-Encoding: gzip para Gzip, y el Content-Type correcto, por ejemplo, application/wasm para el archivo .wasm. Sin Content-Encoding, la página muestra un mensaje de error, y un Content-Type incorrecto hace que el inicio sea más lento. Si no puedes configurar el servidor, activa Decompression Fallback.
  • Los portales de juegos como itch.io muestran el juego dentro de un iframe. Sube un archivo ZIP con index.html en el nivel superior. Un iframe con sandbox necesita allow-modals para la pregunta Leave site? y allow-downloads para Download Map. Comprueba ambos en la página real.
  • Los mapas guardados pertenecen al sitio web. El navegador los guarda por dirección del sitio web (protocolo, host y puerto). Una nueva subida al mismo sitio web los conserva; el mismo juego en otro sitio web empieza sin mapas.
  • Safari puede borrar los datos almacenados de un sitio web que no se haya usado durante siete días, y Safari en iPhone y iPad ignora la pregunta Leave site?; por eso, pide a tus jugadores que descarguen los mapas que quieran conservar.
  • Dentro de un iframe, el juego solo recibe las teclas después de que el jugador haya hecho clic en él, y Esc siempre sale de la pantalla completa del navegador (consulta más abajo).

Mapas en el navegador

En un navegador, Application.persistentDataPath es un sistema de archivos en memoria. Solo se copia a la base de datos del navegador (IndexedDB) cuando se solicita. La plantilla del paquete hace esta solicitud después de cada cambio. Con otras plantillas, el paquete la hace por sí mismo después de cada guardado, borrado e importación, así que los mapas guardados siguen ahí después de recargar la página. Tu propio código puede hacer lo mismo con RTSWebFileSystem.Flush() (consulta API de scripting para builds web).

persistentDataPath depende de la dirección de la página, así que una nueva subida con otra dirección (los portales de juegos suelen usar una nueva en cada subida) empezaría sin mapas. Por eso, en las builds web los mapas se guardan en RTSMapSerializer.StorageRoot, una carpeta con el nombre de la empresa y del producto de los Player Settings, dentro del mismo almacenamiento del navegador. En todas las demás plataformas, StorageRoot es persistentDataPath. La carpeta de mapas del editor en el juego es Maps, dentro de ella.

Los jugadores no pueden abrir este almacenamiento como una carpeta, así que el propio editor en el juego mueve los mapas entre el navegador y el equipo:

ComandoDóndeQué hace
Download MapMenú FileHace que el navegador descargue el mapa actual, incluidos los cambios sin guardar, como archivo .rtsmap (normalmente en la carpeta de descargas).
Import Map...Menú FileAbre el selector de archivos del navegador. El archivo .rtsmap elegido se añade a los mapas guardados en este navegador y se abre, después de la pregunta habitual sobre los cambios sin guardar.
Import...Explorador de mapas (Open)Lo mismo que Import Map, junto a la línea de ubicación.
DownloadExplorador de mapas (Open)Descarga el archivo de mapa seleccionado.
Arrastrar y soltarLienzo del juegoUn archivo .rtsmap que se suelta sobre el juego se importa y se abre. Los demás archivos se rechazan con un mensaje, para que el navegador no abandone la página para mostrarlos.
  • Todo el archivo se comprueba antes de guardarse, y se aceptan archivos de hasta 128 MB.
  • Una importación nunca sustituye a otro mapa. Si vuelves a importar el mismo archivo, se reutiliza; un archivo distinto con el mismo nombre se guarda como Nombre (2), Nombre (3), etc. Al abrir el mapa se escribe una imagen de vista previa.
  • En lugar de una ruta de carpeta, el explorador de mapas muestra el texto Saved in this browser. Use Download to keep a copy on your computer. Show Maps Folder y Quit no existen en el navegador.
  • Si se borran los datos de navegación del sitio web, también se borran los mapas guardados.

El editor en el juego en el navegador

Cerrar la pestaña con cambios sin guardar

Mientras el mapa tenga cambios sin guardar y Confirm Quit With Unsaved Changes esté activado, el navegador pregunta Leave site? antes de cerrar o recargar la pestaña. El navegador elige el texto de esta pregunta. Con las plantillas propias de Unity, que no guardan los archivos de inmediato, también pregunta mientras un guardado aún se está escribiendo en el almacenamiento del navegador, independientemente de esta opción.

Volver a abrir el editor después de cerrarlo

En los navegadores, las teclas de función suelen ser teclas multimedia (en los Mac y en muchos portátiles), y los teléfonos no tienen ninguna. Por eso, cuando el jugador cierra el editor con su botón de cerrar (×), aparece abajo a la izquierda un botón Open Map Editor (F2) que lo vuelve a abrir. Sin teclado, o cuando Toggle Key es None, el botón dice Open Map Editor. El botón solo aparece después de usar el botón de cerrar; cuando el código de tu juego oculta el editor, es tu juego el que decide cómo vuelve a aparecer. En los navegadores y en los teléfonos, el botón de cerrar está disponible incluso cuando Toggle Key es None.

Teclado
  • New Map es Alt+N. Los navegadores reservan Ctrl+N para abrir una ventana nueva y nunca lo pasan a la página. Alt+N también funciona en las plataformas de escritorio. Ctrl+T y Ctrl+W tampoco llegan nunca a una página.
  • Se retienen los atajos del navegador. Mientras el editor está activado, el navegador no reacciona a las teclas de F1 a F10 ni a Ctrl (o Cmd) + S, O, D y A, así que funcionan como atajos del editor. F11, F12, Ctrl+F5, Ctrl+R, copiar, pegar y las teclas que se escriben en los campos de texto de la página siguen correspondiendo al navegador.
  • F5 inicia y termina la prueba de juego, así que no dependes de Esc.
  • Esc en pantalla completa: los navegadores usan Esc para salir de la pantalla completa. En Chrome y Edge, en una página segura (HTTPS o localhost) que no esté dentro de un iframe, una pulsación corta de Esc llega al juego, y mantener presionada Esc sale de la pantalla completa. En los demás casos, la primera pulsación de Esc sale de la pantalla completa. Las alternativas: F5 o Back to Editor terminan una prueba de juego, el botón × cierra un cuadro de diálogo y Ctrl+Z deshace un trazo. La ventana de ayuda (F1) también lo indica.
  • Distribuciones de teclado: los atajos de letras (Ctrl+Z, G, P …) siguen la letra impresa en la tecla, también en los teclados alemanes (QWERTZ) y franceses (AZERTY). Las teclas del pincel siguen el carácter escrito, así que [ también funciona donde se escribe con AltGr+8. Esto es igual en las plataformas de escritorio.
  • Escena Gameplay Demo: en la mayoría de los navegadores, de Ctrl+1 a Ctrl+9 (grupos de control) cambian de pestaña del navegador. Elige otras teclas para un juego web.
Límites

El mapa, sus texturas y el historial de deshacer comparten un único heap de memoria de 2 GB como máximo (el valor predeterminado de Maximum Memory Size), y ese heap nunca se reduce. Por eso, el editor usa límites más bajos en el navegador:

LímiteNavegadorOtras plataformas
Mapa más grande en New Map y Resize2048 ÷ resolución de splat, en celdas por lado: 512 con la resolución de splat predeterminada 4, 256 con 8. Los presets más grandes no aparecen, y los cuadros de diálogo indican In the browser, maps can be up to N x N cells. Resize conserva el tamaño de un mapa más grande que se abrió desde un archivo, pero no lo amplía.1024
Memoria de deshacerComo máximo 192 MB. Si Undo Memory MB tiene un valor más bajo, se mantiene.Undo Memory MB (predeterminado: 512)
Archivo de mapa importado128 MBSin comando de importación
Otras diferencias
  • La interfaz nunca se reduce por debajo del 85 %, medido en píxeles CSS, así que sigue siendo legible en pantallas de alta resolución y en ventanas pequeñas.
  • El desplazamiento por bordes de la cámara del editor y de la cámara RTS de ejemplo se detiene cuando el ratón sale de la página o del iframe del juego.

Renderizado y rendimiento

  • Hierba: WebGL 2.0 no tiene structured buffers. En su lugar, el renderizador de detalles guarda las instancias de cada parche en una textura de coma flotante (dos texels por instancia). Elige esta vía en tiempo de ejecución, y la hierba se ve igual.
  • Terreno: el pase forward usa 10 de las 16 texturas que WebGL 2.0 permite por draw call. Mantén ligero el asset de URP de las builds web (consulta Notas para móviles).
  • Un solo hilo: sin hilos de trabajo, las operaciones sobre todo el mapa tardan bastante más que en escritorio, y mientras tanto la página no responde: Generate, New Map, Resize, abrir un mapa grande y Apply to Whole Map de Auto Paint. Los mapas de 128 a 256 celdas por lado siguen siendo cómodos.
  • Arrays de texturas: usa arrays generados en el formato de tu build web (consulta Ajustes de build recomendados). El juego compilado libera la copia en la CPU de los arrays generados, algo importante con la memoria limitada de una página.
  • Resolución de renderizado: la plantilla limita la relación de píxeles a 1.5; redúcela para obtener una tasa de fotogramas más alta.

Eliminación de código y herramientas personalizadas

Las herramientas de edición se encuentran y se crean mediante reflexión (RTSToolRegistry), así que ningún código las referencia directamente. Con un Managed Stripping Level por encima de Minimal, habitual para reducir el tamaño de las builds web e IL2CPP, Unity no puede ver que se usan y las eliminaría. El archivo Runtime/link.xml del paquete conserva todas las clases del espacio de nombres RTSTerrainStudio cuyo nombre termina en Tool, así que las herramientas integradas se mantienen siempre.

Marca tus propias clases de herramientas con [UnityEngine.Scripting.Preserve], que conserva la clase y su constructor sin parámetros, o enuméralas en un archivo link.xml de tu proyecto. Una build del juego que no encuentra ninguna herramienta registra No editing tools were found in this build.

using RTSTerrainStudio;
using UnityEngine.Scripting;

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

API de scripting para builds web

MiembroDescripción
RTSWebFileSystem.IsSupportedTrue en una build de WebGL. En otras plataformas y en el Unity Editor, la clase no hace nada.
RTSWebFileSystem.Flush()Copia el sistema de archivos de la página al almacenamiento del navegador, para que los archivos escritos o borrados con System.IO sobrevivan a una recarga. Todas las llamadas de un mismo fotograma se combinan en una sola escritura. RTSMapSerializer.SaveToFile lo llama por ti.
RTSWebFileSystem.DownloadFile(fileName, data, mimeType)Hace que el navegador descargue bytes como un archivo. Devuelve false en otras plataformas o cuando el navegador rechaza la descarga.
RTSMapSerializer.StorageRootCarpeta para los archivos que se conservan entre sesiones: Application.persistentDataPath y, en las builds web, una carpeta del almacenamiento del navegador que no depende de la dirección de la página.
RTSMapSerializer.DefaultMapsFolderMaps dentro de StorageRoot.
RTSRuntimeEditor.RunsInBrowserTrue en las builds web, donde el editor usa los límites del navegador y Alt+N para New Map.
RTSRuntimeEditor.StoresMapsInBrowserTrue cuando los mapas se guardan en el almacenamiento del navegador (builds de WebGL).
RTSRuntimeEditor.HasReliableFunctionKeysFalse en los navegadores web y en las plataformas móviles, donde el botón de cerrar muestra el botón para volver a abrir el editor.
editor.DownloadMap(), editor.ImportMap()Los comandos del menú File. Los navegadores solo muestran el selector de archivos en respuesta a un clic o a una pulsación de tecla, así que llama a ImportMap desde un controlador de clic.
RTSInput.SetBrowserShortcutGuard(bool)Retiene la acción propia del navegador para las teclas de F1 a F10 y Ctrl+S/O/D/A, y deja que una pulsación corta de Esc llegue al juego en pantalla completa (Chrome, Edge). Cada llamada con true necesita su llamada correspondiente con false. El editor en el juego y RTSDemoHUD lo activan mientras están habilitados. No hace nada en otras plataformas.
RTSInput.GetKeyDownByLabel(RTSKey)Como GetKeyDown, pero para la tecla que muestra esta letra en la distribución de teclado del jugador. Úsalo para atajos de letras como Ctrl+Z.
RTSInput.GetCharacterDown(char)True cuando el carácter se escribió en este fotograma, con cualquier distribución de teclado. Úsalo para teclas de símbolos como [ ] - +. Los caracteres escritos en un campo de texto con el foco también llegan aquí, así que comprueba primero el foco de la interfaz.
RTSInput.PointerInsideScreenTrue mientras el puntero está dentro de la ventana del juego. En las builds web, el ratón también debe seguir sobre la página.
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);
    }
}

Conceptos básicos

Este capítulo explica cómo entiende RTS Terrain Studio un mapa: qué se guarda, en qué unidades, cómo se construye la superficie visible a partir de esos datos y qué assets (tilesets, bibliotecas, archivos de mapa) forman parte de él. No necesitas leerlo antes de crear tu primer mapa, pero vuelve a él cuando quieras saber por qué algo se comporta como lo hace, o cuando empieces a escribir código que trabaje con los datos del mapa.

En resumen:

  • Un mapa es una cuadrícula de celdas cuadradas. Las alturas, los niveles de acantilado y el agua están en las esquinas (vértices) de las celdas.
  • Los niveles de acantilado son números enteros de 0 a 15. Donde las esquinas vecinas tienen niveles distintos, aparece una pared de acantilado vertical, salvo que las cuatro esquinas de la celda estén marcadas como rampa.
  • Las texturas de suelo, la densidad de la hierba y los ajustes manuales de pathing (dónde pueden caminar, construir y volar las unidades) son capas ráster con varios texels por celda.
  • Los árboles, la decoración, las regiones, las posiciones iniciales y los marcadores son listas de objetos en coordenadas locales del mapa.
  • El aspecto proviene de un tileset (texturas de suelo, acantilado y agua); los objetos provienen de una biblioteca de vegetación y de una biblioteca de decoración. Los mapas solo guardan índices e ids, así que puedes cambiar el aspecto sin tocar el mapa.
  • Todo se guarda en un único archivo binario compacto .rtsmap, que usan tanto el Unity Editor como el editor en el juego.

El modelo de mapa

Todos los datos del mapa residen en un único objeto de la clase MapData (espacio de nombres RTSTerrainStudio). Un componente RTSTerrain posee un MapData (terrain.Map), lo renderiza como meshes de chunks, mantiene actualizado el mapa de pathing derivado y responde a consultas como «¿a qué altura está el suelo aquí?».

Celdas y vértices

El mapa es una cuadrícula de Width × Height celdas. Cada celda tiene cuatro vértices en sus esquinas, y las celdas vecinas comparten esquinas. Por eso, un mapa de 4 × 2 celdas tiene 5 × 3 vértices:

 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)
  • Las capas por vértice (altura fina, nivel de acantilado, indicadores, tipo de acantilado, altura del agua) tienen (Width + 1) × (Height + 1) entradas, guardadas fila a fila. Usa map.VertexIndex(x, z).
  • El tamaño de celda (map.CellSize, predeterminado: 2 m) es el tamaño de una celda en el mundo. Un mapa de 128 × 128 con celdas de 2 m cubre 256 × 256 m (map.WorldSize).
  • Una posición pertenece a la celda floor(localXZ / CellSize); map.LocalToCell(localXZ) hace este cálculo y limita el resultado al mapa.
  • Para el renderizado, las celdas se agrupan en chunks (bloques de 16, 32 o 64 celdas por lado; ajuste Chunk Size del componente RTSTerrain, predeterminado: 32). Solo se reconstruyen los chunks afectados por una edición. El tamaño de chunk no cambia los datos del mapa.

Dimensiones del mapa

Las dimensiones se eligen al crear un mapa (con el New Map Wizard, el cuadro de diálogo New Map del editor en el juego o MapCreationSettings desde código). MapCreationSettings.Validate() corrige los valores que quedan fuera del rango válido.

AjustePredeterminadoValores válidosDescripción
width, height12816–1024, redondeado a un múltiplo de 16Número de celdas en X y Z. El New Map Wizard ofrece presets desde Tiny (64) hasta Epic XL (512).
cellSize2≥ 0.1Tamaño de una celda en el mundo, en metros.
cliffLevelHeight2≥ 0.05Altura de un nivel de acantilado en el mundo, en metros.
baseCliffLevel20–15Nivel de acantilado inicial de cada vértice. El nivel 2 deja margen para excavar hacia abajo y crear lagos y ríos.
baseGroundLayer00–15Capa de suelo que cubre inicialmente todo el mapa.
splatResolution41, 2, 4, 8Texels de textura de suelo (splat) por lado de celda.
detailResolution21, 2, 4Texels de densidad de detalles (hierba) por lado de celda.
pathingResolution21, 2, 4Celdas de pathing por lado de celda del mapa.
name«New Map»cualquier textoNombre del mapa (se guarda en la información del mapa).

Las resoluciones que no son potencia de dos se redondean hacia arriba (3 pasa a ser 4). El tamaño de celda, la altura de nivel y las tres resoluciones ráster quedan fijos una vez creado el mapa. La función Resize (RTSTerrain.ResizeMap) solo cambia el número de celdas: conserva el contenido, lo desplaza mediante un offset y rellena las zonas nuevas con el nivel de acantilado y la capa de suelo que elijas.

MapData.Resize(newWidth, newHeight, offsetCells, fillCliffLevel, fillGroundLayer) devuelve un mapa nuevo (el original no se modifica). Se eliminan los árboles, la decoración y las posiciones iniciales que quedan fuera de los nuevos límites. Los marcadores y las regiones se desplazan, pero se conservan aunque queden fuera. Los tamaños se limitan a 16–1024 (MapData.MinSize–MaxSize), pero no se redondean a un múltiplo de 16 cuando lo llamas desde código. RTSTerrain.ResizeMap sustituye el mapa (se lanza MapLoaded), lo marca como no guardado y borra el historial de deshacer de las sesiones de edición.

Qué contiene un mapa

Cada tipo de datos tiene un indicador correspondiente en el enum MapLayers. Los indicadores se usan para las notificaciones de cambios, el registro de los pasos de deshacer y la serialización.

DatosSe guarda porMiembro de MapDataTipoIndicador de MapLayers
Altura finavérticeHeightsNativeArray<float>, metrosHeights
Nivel de acantiladovérticeCliffLevelsNativeArray<byte>, 0–15CliffLevels
Indicadores de vértice (rampa, agua)vérticeVertexFlagsNativeArray<byte> de VertexFlagsVertexFlags
Tipo de acantiladovérticeCliffTypesNativeArray<byte>, 0–3CliffTypes
Altura de la superficie del aguavérticeWaterHeightsNativeArray<float>, Y local del mapaWater
Pesos de las capas de suelotexel de splatSplatMaps[0..3]NativeArray<Color32> × 4Splat
Densidad de hierba / plantastexel de detalleDetailLayersun DetailLayer (un byte por texel) por prototipoDetails
ÁrbolesinstanciaTrees, TreePrototypeIdsList<TreeInstanceData>Trees
Decoración (doodads)instanciaDecor, DecorPrototypeIdsList<DecorInstanceData>Decor
Ajustes manuales de pathingcelda de pathingPathingOverridesNativeArray<byte> de PathingOverridePathingOverrides
RegioneslistaRegionsList<MapRegion>Regions
Posiciones inicialeslistaStartLocationsList<StartLocation>StartLocations
MarcadoreslistaMarkersList<MapMarker>Markers
Información del mapamapaInfoMapInfoInfo
Entorno (iluminación)mapaEnvironmentEnvironmentSettingsEnvironment

Hay dos indicadores combinados muy útiles: MapLayers.Geometry (alturas, niveles, indicadores de vértice, tipos de acantilado) y MapLayers.PathingInputs (todo lo que influye en el mapa de pathing). MapLayers.All lo abarca todo.

El mapa de pathing (terrain.Pathing, clase PathingMap) está formado por datos derivados: se calcula a partir de las capas anteriores y nunca se guarda. Consulta Mapa de pathing.

VertexFlags.User0 y VertexFlags.User1 están reservados para tu propio código. El paquete nunca los usa y se guardan con el mapa, así que puedes marcar vértices especiales (por ejemplo, «zona de aparición de creeps») sin necesidad de una sección personalizada en el archivo.

MapData contiene memoria nativa. Un mapa que creas o cargas tú mismo (MapData.Create, RTSMapSerializer.LoadFromFile, RTSMapAsset.LoadMap, map.Clone()) debe liberarse con Dispose(), salvo que se lo pases a un terreno con terrain.SetMap(map): entonces el terreno pasa a ser su propietario y lo libera cuando se sustituye el mapa o se desactiva el componente.

Niveles de acantilado, alturas finas y tipos de acantilado

El terreno de los RTS clásicos tiene dos tipos de altura: grandes escalones discretos (acantilados) y pequeñas variaciones suaves (colinas). RTS Terrain Studio guarda ambos por separado en cada vértice.

Niveles de acantilado

  • Cada vértice tiene un nivel de acantilado de 0 a 15 (MapData.MaxCliffLevel = 15).
  • Un nivel mide CliffLevelHeight metros de alto (predeterminado: 2 m).
  • Los mapas nuevos empiezan en el nivel 2, así que hay dos niveles por debajo del suelo para lagos, ríos y cañones, y trece por encima para mesetas.
  • Edita los niveles con las herramientas de acantilado: Raise / Lower Level, Set Level, Ramp, Cliff Type y Clean Cliffs.
  • La niebla de guerra usa los niveles de acantilado para la línea de visión: las unidades ven su propio nivel y los inferiores; el terreno más alto les bloquea la visión.

Alturas finas

Además del nivel, cada vértice tiene una altura fina en metros (Heights), positiva o negativa. Las herramientas de altura (Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode) solo cambian la altura fina, así que puedes crear colinas sobre una meseta sin convertirlas en acantilados. La altura del suelo de un vértice es:

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

Ejemplo: el nivel 3 con una altura de nivel de 2 m y una altura fina de 0.4 m da un vértice situado 6.4 m por encima del origen del mapa. map.GetVertexHeight(x, z) y map.GetVertexPosition(x, z) lo calculan por ti.

Tipos de acantilado

Un tileset puede tener hasta 4 capas de acantilado (por ejemplo, «Rock» y «Grassy Cliff»). Cada vértice guarda cuál debe usar (CliffTypes, 0–3; se pinta con la herramienta Cliff Type). Una pared de acantilado usa el tipo de acantilado de la esquina más alta de su celda. Si un índice de tipo de acantilado no tiene capa en el tileset, la pared recurre a la capa de acantilado 0.

Cómo se construye la superficie (la superficie de referencia)

El struct MapGeometryView define la única superficie real del terreno. Los meshes de los chunks, las consultas de jugabilidad (GetHeight, Raycast, …), el mapa de pathing y la niebla de guerra siguen las mismas reglas, así que lo que ves es exactamente por donde caminan las unidades. Cada celda se clasifica según sus cuatro esquinas:

Tipo de celdaCondiciónSuperficie
Celda suaveLas cuatro esquinas tienen el mismo nivel de acantilado.Parche bilineal de las alturas de las cuatro esquinas.
Celda de rampaLas cuatro esquinas tienen el indicador VertexFlags.Ramp (los niveles pueden ser distintos).Parche bilineal: los niveles se mezclan suavemente formando una pendiente. Consulta Rampas.
Celda de acantiladoLas esquinas tienen niveles distintos y la celda no es una celda de rampa.Terrazas planas separadas por una pared vertical que sigue un contorno redondeado.

Contornos redondeados. Dentro de una celda de acantilado, para cada umbral de nivel t se interpola bilinealmente sobre toda la celda la función indicadora «nivel de la esquina ≥ t» (1 o 0). Un punto pertenece al nivel más alto cuyo valor indicador interpolado sea al menos 0.5 (MapGeometryView.LevelAt). Es una regla de tipo marching squares: una sola esquina elevada produce un cuarto de círculo en lugar de una diagonal, y las líneas de acantilado largas se mantienen rectas. La pared en sí es vertical en el contorno. Las alturas finas siempre se interpolan bilinealmente y se suman encima, también dentro de las celdas de acantilado.

El aspecto de las paredes renderizadas se ajusta en el componente RTSTerrain (Settings):

AjustePredeterminadoDescripción
Surface Subdivisions2Quads por lado de celda en terreno suave (1 = aspecto de RTS clásico, 2–4 = colinas más suaves).
Cliff Subdivisions4Resolución de la subcuadrícula con la que se traza el contorno redondeado dentro de una celda de acantilado.
Cliff Noise0.18Desplazamiento horizontal aleatorio de los vértices de la pared (fracción de una celda) para dar un aspecto de roca natural.
Cliff Noise Scale1.6Escala del ruido de acantilado en unidades del mundo.
Cliff Slant0.12Cuánto se retrae el borde superior de una pared, de modo que las paredes quedan ligeramente inclinadas (fracción de una celda).
Cliff Wall Segments3Segmentos verticales de la geometría de la pared por nivel de acantilado.

Obtén una vista con map.GetGeometryView(). Es un struct de arrays nativos, así que también puedes pasarlo a jobs de Burst. Todos los miembros reciben posiciones en espacio local del mapa:

MiembroDescripción
SampleHeight(localXZ)Altura del suelo (Y local del mapa).
SampleHeightInCell(cell, uv), SampleLevelHeightInCell(cell, uv)Lo mismo dentro de una celda conocida (uv 0–1), con o sin la altura fina.
SampleNormal(localXZ), SampleSlope(localXZ)Normal de la superficie; pendiente en grados.
SampleCliffLevel(localXZ)Nivel de acantilado en una posición.
SampleWater(localXZ, out waterHeight), SampleWaterDepth(localXZ)Si hay agua y la altura de su superficie; profundidad del agua, o 0 si el punto está seco.
IsCliffCell(cell), IsRampCell(cell)Clasifican una celda (consulta la tabla anterior).
LocateCell(localXZ, out cell, out uv)La celda de una posición y la posición 0–1 dentro de ella.
Raycast(origin, direction, maxDistance, out hitPoint, out hitDistance)Raycast analítico contra la superficie de referencia, sin necesidad de colliders.
static LevelAt(l00, l10, l01, l11, uv), static Bilerp(v00, v10, v01, v11, uv)La regla de nivel dentro de una celda de acantilado y la interpolación bilineal.
width, height, cellSize, cliffLevelHeight, WorldSizeTamaño del mapa en celdas, tamaño de celda, altura de nivel y tamaño en el mundo.

Cliff Noise y Cliff Slant solo desplazan los vértices renderizados de la pared una fracción de celda. Las consultas usan el contorno exacto, así que justo en el borde de un acantilado la roca visible y la superficie de referencia pueden diferir ligeramente. El mapa de pathing lo compensa con su ajuste Cliff Margin, que mantiene una franja estrecha no transitable a lo largo de cada pared.

Rampas

Una rampa permite a las unidades caminar de un nivel de acantilado al siguiente. La regla es sencilla:

Una celda es una rampa cuando sus cuatro esquinas están marcadas como rampa. Si falta el indicador aunque sea en una sola esquina, la celda sigue siendo una celda de acantilado con una pared vertical.

  • El indicador es VertexFlags.Ramp en cada vértice. La herramienta Ramp (id cliff.ramp) lo pinta; mantén presionada Shift para quitarlo.
  • Una celda de rampa es una pendiente bilineal suave entre las alturas de sus esquinas (nivel más altura fina).
  • Las rampas funcionan mejor cuando salvan exactamente un nivel. Se permiten rampas más empinadas, pero la barra de estado avisa de las celdas de rampa que abarcan más de un nivel.
  • Pathing: en una rampa válida de un nivel, la transitabilidad solo se comprueba con la pendiente de su altura fina (el desnivel entre niveles que salva no la vuelve «demasiado empinada»). Las rampas nunca son edificables.
  • En una rampa, GetCliffLevel devuelve el nivel interpolado y redondeado: el nivel inferior en la mitad inferior y el superior en la mitad superior.
  1. Eleva una meseta un nivel con Raise / Lower Level.
  2. Selecciona la herramienta Ramp y pinta a través del borde del acantilado donde quieras la rampa. Haz que el pincel cubra al menos los vértices de ambos lados de la pared, para que todas las esquinas de las celdas intermedias queden marcadas.
  3. La pared desaparece y aparece una pendiente. Ensancha la rampa pintando más vértices a lo largo del borde.

Capas de suelo y splat maps

Las texturas de suelo se pintan en splat maps: cuatro texturas RGBA (MapData.SplatMapCount = 4) de SplatWidth × SplatHeight texels. Cada uno de los 16 canales contiene el peso de una capa de suelo del tileset:

Splat mapRGBA
SplatMaps[0]capa 0capa 1capa 2capa 3
SplatMaps[1]capa 4capa 5capa 6capa 7
SplatMaps[2]capa 8capa 9capa 10capa 11
SplatMaps[3]capa 12capa 13capa 14capa 15
  • La capa i se guarda en el splat map i / 4, canal i % 4. Por eso un tileset tiene exactamente 16 capas de suelo: 4 splat maps × 4 canales.
  • Los pesos de un texel siempre suman 255 entre los 16 canales. Todas las herramientas de pintura mantienen esta invariante; el código que escriba datos de splat también debe mantenerla.
  • La resolución de splat es el número de texels por lado de celda (predeterminado: 4). Con celdas de 2 m, eso equivale a un texel cada 0.5 m.
  • El centro del texel (x, z) está en ((x + 0.5), (z + 0.5)) × CellSize / SplatResolution en coordenadas XZ locales del mapa. La misma fórmula se aplica a los texels de detalle y de pathing, con su propia resolución.
  • Funciones auxiliares: map.SplatIndex(x, z), map.GetSplatWeight(texelIndex, layer), map.GetDominantLayer(texelIndex), MapData.GetChannel / SetChannel y map.FillSplat(layer).

Los mapas guardan las capas de suelo por índice (0–15), no por nombre. Reordenar o eliminar capas de suelo en el tileset cambia lo que muestran los mapas ya pintados. Si ya hay mapas que usan el tileset, añade las capas nuevas al final.

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));
    }
}

Agua

  • Un vértice está bajo el agua cuando tiene el indicador VertexFlags.Water. La altura de la superficie del agua (Y local del mapa, en metros) se guarda en WaterHeights; el valor solo tiene sentido donde el indicador está activo.
  • Un punto está mojado cuando la cobertura bilineal de los indicadores de agua de las cuatro esquinas de su celda es de al menos 0.5. Las esquinas sin agua dentro de una celda mojada usan la mayor altura de agua de la celda, así que las orillas se mantienen niveladas.
  • Profundidad del agua = superficie del agua − altura del suelo. La profundidad determina el pathing: el agua cuya profundidad supera el Shallow Depth Limit del tileset (predeterminado: 0.9 m) es agua profunda (no transitable); el agua menos profunda es agua poco profunda (transitable y, de forma predeterminada, no edificable).
  • Las herramientas de agua son Paint Water, Erase Water y Flood Fill. Las ediciones de agua solo notifican MapLayers.Water, así que los chunks del terreno no se reconstruyen mientras pintas agua.
  • Consultas: terrain.IsWater(world, out depth) y terrain.TryGetWaterHeight(world, out waterWorldY).

El aspecto del agua (colores, olas, espuma) procede del perfil de agua del tileset; consulta Perfil de agua.

Densidad de detalles (hierba y plantas pequeñas)

  • Los detalles no son objetos individuales. Cada prototipo de detalle que se usa en el mapa tiene un DetailLayer en map.DetailLayers: un byte por texel de detalle (DetailWidth × DetailHeight, predeterminado: 2 texels por lado de celda).
  • Una densidad de 0 significa vacío; 255 significa Max Density instancias por texel (un campo del prototipo de detalle, predeterminado: 6). Cada texel genera density / 255 × maxDensity instancias; el renderizador las distribuye de forma determinista, así que el mismo mapa siempre se ve igual.
  • Las capas se asocian con la biblioteca de vegetación mediante prototypeId. map.GetOrAddDetailLayer(id) crea una capa cuando hace falta; map.FindDetailLayer(id) devuelve null si el prototipo no se usa.
  • Pinta con Paint Details y borra con Erase Details.

Instancias de árboles y decoración

Los árboles (TreeInstanceData) y los objetos decorativos (DecorInstanceData) se guardan como listas simples. En lugar del nombre de un prototipo, cada instancia guarda un pequeño índice de paleta que apunta a una lista de ids de biblioteca del mapa:

map.TreePrototypeIds = [ "a3f0…", "77c1…" ]        <- ids of TreePrototype entries in the vegetation library
map.Trees[n].prototype = 1                          <- this tree uses "77c1…"
  • Usa map.GetOrAddTreePrototype(id) / map.GetOrAddDecorPrototype(id) para obtener el índice de paleta de un id de biblioteca (si falta, se añade).
  • Como los mapas hacen referencia a los prototipos por su id, puedes renombrar, reordenar o ampliar una biblioteca sin riesgo. Si a la biblioteca le falta un prototipo, el renderizador de vegetación lo omite y el gestor de decoración lo muestra como un cubo sustituto magenta; al añadir un prototipo con el mismo id, vuelve a aparecer. En cuanto al pathing: los árboles cuyo prototipo falta no se dibujan, pero siguen obstruyendo un círculo de PathingMapBuilder.DefaultTreeFootprintRadius (0.8 m) × escala. Los objetos sustitutos de decoración no escriben ninguna huella de pathing hasta que el prototipo vuelve a estar disponible.
  • Cada instancia tiene un id único y estable dentro del mapa. Obtén ids nuevos con map.AllocateInstanceId().
  • Las posiciones están en coordenadas locales del mapa. Las rotaciones siguen la misma convención en todas partes: grados, en sentido horario visto desde arriba, 0 = +Z, 90 = +X (igual que la rotación Y de Unity).

La convención de position.y

position.y es un desplazamiento adicional sobre el suelo, normalmente 0. No es la altura absoluta. Los renderizadores muestrean el suelo cada vez que se actualizan, así que los árboles y la decoración permanecen sobre el suelo cuando editas el terreno que tienen debajo.

ObjetoSe renderiza en (Y local del mapa)
Árbolaltura del suelo + position.y
Objeto decorativoaltura del suelo + position.y + el Y Offset del prototipo
Objeto decorativo con DecorFlags.KeepHeightposition.y como altura absoluta local del mapa (no sigue las ediciones del terreno)

Nunca copies el yOffset del prototipo en position.y: se suma en el momento del renderizado. DecorMath.ComputeLocalPosition y DecorMath.ToStoredHeight convierten entre las posiciones guardadas y las renderizadas.

CampoTreeInstanceDataDecorInstanceData
iduint, único en el mapauint, único en el mapa
prototypeushort, índice en TreePrototypeIdsushort, índice en DecorPrototypeIds
variant–byte, 0 = prefab principal, 1… = entradas de variants
flags–DecorFlags: IgnorePathing, Locked (las herramientas no pueden seleccionarlo ni borrarlo), KeepHeight
positionfloat3, local del mapa, y = desplazamientofloat3, local del mapa, y = desplazamiento (o altura absoluta con KeepHeight)
rotaciónyaw en gradosrotation, un quaternion local del mapa
escalascale, float uniformescale, float3
tintColor32 multiplicado por el color del materialColor32 multiplicado por el color del material
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;
    }
}

Los cambios que el código hace directamente en MapData no quedan registrados en el historial de deshacer. Para que las ediciones hechas desde scripts del editor se puedan deshacer, ejecútalas a través de la sesión de edición con RTSEditSession.RunOperation(name, layers, operation), que registra las capas indicadas como un solo paso de deshacer.

Mapa de pathing

El mapa de pathing responde a las preguntas «¿puede una unidad caminar aquí?» y «¿se puede colocar un edificio aquí?». Es una cuadrícula de celdas de pathing (PathingResolution por lado de celda del mapa; predeterminado: 2, es decir, celdas de pathing de 1 m en un mapa con celdas de 2 m) con un byte PathingFlags por celda. Se deriva automáticamente y se reconstruye de forma incremental cada vez que cambia alguno de sus datos de entrada (geometría, agua, capas de suelo, árboles, decoración, ajustes manuales). Nunca se guarda: solo se guardan los ajustes manuales.

PathingFlagsSignificado
WalkableLas unidades terrestres pueden caminar aquí.
BuildableAquí se pueden colocar edificios.
FlyableLas unidades aéreas pueden volar aquí. Se establece en todas partes, salvo que lo elimine un efecto Unflyable: un ajuste manual de pathing pintado, la regla de capa de suelo del tileset o el Footprint Effect de un prototipo de decoración.
ShallowWaterAgua poco profunda (de forma predeterminada, transitable pero no edificable).
DeepWaterAgua profunda (solo para unidades navales).
CliffLa celda toca una pared de acantilado.
BlockedObstruida por la huella de un árbol o de un objeto decorativo.
SteepDemasiado empinada para las unidades terrestres.

Cómo se derivan los indicadores

  1. Terreno: las paredes de acantilado (más el Cliff Margin) establecen Cliff; las pendientes por encima de Max Walkable Slope (predeterminado: 42°) establecen Steep; el agua establece ShallowWater o DeepWater según su profundidad; Flyable se establece en todas partes; Buildable = transitable, pendiente ≤ Max Buildable Slope (predeterminado: 18°) y sin agua (o con agua poco profunda si Shallow Water Buildable está activado). Después se aplica el Pathing Effect de la capa de suelo dominante (por ejemplo, «Unbuildable» en roca rugosa).
  2. Huellas: los árboles obstruyen un círculo (Footprint Radius × escala del árbol × Tree Footprint Scale); la decoración aplica su Footprint Effect dentro de un círculo o de un rectángulo girado, salvo que la instancia tenga DecorFlags.IgnorePathing.
  3. Los ajustes manuales pintados con Paint Pathing / Clear Pathing se aplican en último lugar.

Los ajustes de las reglas están en el componente RTSTerrain (RTSPathingSettings, propiedad terrain.PathingSettings).

PathingOverrideEfecto
UnwalkableQuita Walkable (y Buildable) y establece Blocked.
UnbuildableQuita Buildable.
UnflyableQuita Flyable.
ForceWalkableEstablece Walkable aunque las reglas derivadas indiquen lo contrario.
ForceBuildableEstablece Buildable aunque las reglas derivadas indiquen lo contrario.

Los mismos indicadores PathingOverride se usan para los efectos de pathing de las capas de suelo, los efectos de huella de la decoración y los ajustes manuales pintados. Consulta el resultado con terrain.GetPathing(world), terrain.IsWalkable(world) y terrain.IsBuildable(world), o lee terrain.Pathing.Flags directamente.

Regiones, posiciones iniciales y marcadores

Son objetos ligeros para la lógica de tu juego. No se renderizan en el juego; los editores los dibujan como gizmos.

TipoCamposUso típico
MapRegion (herramienta Regions)name, rect (rectángulo XZ local del mapa en metros: rect.x = X, rect.y = Z), color, properties (lista de pares clave/valor)Disparadores, zonas de aparición, campamentos de creeps. Contains(localXZ), GetProperty(key, fallback).
StartLocation (herramienta Start Locations)player (basado en cero), position, yawPuntos de aparición de los jugadores. El generador de unidades de ejemplo utiliza estas posiciones.
MapMarker (herramienta Markers)name, tag (texto libre, como «camera» o «goldmine»), position, rotation, propertiesCámaras cinemáticas, puntos de interés, puntos de aparición controlados por script.

Las herramientas guardan position.y = 0 para las posiciones iniciales y los marcadores, y lo tratan como un desplazamiento sobre el suelo, igual que con los árboles.

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;
    }
}

Información del mapa y entorno

Campo de MapInfoPredeterminadoDescripción
name"New Map"Nombre visible (también se guarda en la cabecera sin comprimir del archivo).
author""Nombre del autor.
description""Texto libre que se muestra en los exploradores de mapas.
maxPlayers2Número de jugadores recomendado.
tilesetId""Id del tileset para el que se creó el mapa (RTSTileset.tilesetId). Se rellena automáticamente cuando el terreno tiene un tileset. El Inspector de RTSTerrain muestra una advertencia cuando el tileset asignado no coincide.
createdUtcTicks, modifiedUtcTicksse establecen automáticamenteFecha y hora de creación y del último guardado (ticks UTC). RTSMapSerializer.Write establece la fecha de modificación cada vez que se serializa el mapa: SaveToFile, el comando Save del editor, Serialize(map) y RTSMapAsset.CreateFromMap.

EnvironmentSettings guarda la iluminación de un mapa. La aplica a la escena un componente RTSEnvironment (GameObject ▸ RTS Terrain Studio ▸ Environment); sin él, los valores se guardan, pero no se usan.

Campo de EnvironmentSettingsPredeterminadoDescripción
sunColorblanco cálido (1, 0.956, 0.84)Color de la luz del sol.
sunIntensity1.2Intensidad de la luz del sol.
sunRotation(50, −30)Rotación del sol en grados (x = inclinación, y = giro horizontal).
ambientColor(0.45, 0.5, 0.58)Color de la luz ambiental.
fogdesactivadoActiva la niebla por distancia.
fogColor(0.6, 0.68, 0.75)Color de la niebla.
fogDensity0.004Densidad de la niebla.

Notificaciones de cambios

Cuando cambies MapData desde código, indica al terreno qué ha cambiado y dónde, en celdas:

  • terrain.NotifyChanged(MapLayers layers, RectInt cellRect) lanza MapChanged inmediatamente y pone en cola la reconstrucción.
  • terrain.NotifyChangedAll(layers) marca todo el mapa (tras ejecutar un generador o una importación).
  • Los cambios pendientes se procesan en LateUpdate (y en cada tick del editor fuera del modo Play) o inmediatamente con terrain.ProcessPendingChanges(). Después se lanza ChangesProcessed: los meshes, las texturas y el pathing ya están actualizados.
  • Un vértice toca hasta cuatro celdas. Convierte un rectángulo de vértices con BrushUtility.VertexRectToCellRect(map, vertexRect) y los rectángulos de texels con 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);
    }
}

Coordenadas y varios terrenos

Todos los datos del mapa se guardan en el espacio local del mapa. El transform del GameObject de RTSTerrain sitúa ese espacio en el mundo.

Espacio local del mapa

  • El origen es el vértice (0, 0), la esquina de la celda (0, 0). X y Z van de 0 a map.WorldSize en metros; Y apunta hacia arriba.
  • El vértice (x, z) está en la posición local (x × CellSize, height, z × CellSize); el centro de la celda (x, z) está en ((x + 0.5) × CellSize, …, (z + 0.5) × CellSize).
  • La Y local = 0 corresponde al nivel 0 con una altura fina de 0. Un mapa nuevo con nivel base 2 y niveles de 2 m tiene el suelo en la Y local = 4 m.
  • Los GameObjects de los chunks cuelgan de un hijo oculto del terreno llamado RTS Chunks (RTSTerrain.ChunkRoot). Esa raíz no tiene desplazamiento ni rotación locales y compensa la escala del terreno, y cada chunk tiene un transform local de identidad, de modo que el espacio de objeto de los meshes de los chunks es el espacio local del mapa. Esto es importante cuando escribes shaders para el terreno.

Conversión entre el espacio del mundo y el del mapa

Método (RTSTerrain)Descripción
WorldToLocal(Vector3) / LocalToWorld(Vector3)Posiciones entre el espacio del mundo y el espacio local del mapa.
WorldToLocalDirection / LocalToWorldDirectionDirecciones (rayos, normales).
WorldToCell(Vector3)Celda que contiene una posición del mundo (limitada al mapa).
CellCenterToWorld(int2)Posición en el mundo del centro de una celda, sobre el suelo.
VertexToWorld(int2)Posición en el mundo de un vértice.
Contains(Vector3)Devuelve true si la posición del mundo está dentro del área XZ del mapa.
WorldBoundsLímites aproximados en el mundo de todo el mapa, incluido el nivel de acantilado más alto posible.
GetHeight, SnapToGround, GetNormal, GetCliffLevel, IsWater, TryGetWaterHeight, GetPathing, RaycastConsultas que reciben y devuelven valores del mundo y hacen la conversión internamente.
GetGeometry()Un MapGeometryView para consultas rápidas en el espacio local del mapa (también dentro de jobs de Burst).
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)}.");
    }
}

Reglas de posición, rotación y escala

Parte del transformCompatibleNotas
PosiciónSíMueve todo el mapa. Úsala para colocar varios terrenos uno al lado de otro o a distintas alturas.
Rotación YSíGira el mapa alrededor de su origen (vértice 0, 0). Las consultas, las herramientas, el cursor del pincel y las rotaciones de las instancias tienen en cuenta esta rotación.
Rotación X / ZNoSolo se debe usar la rotación Y. El Inspector de RTSTerrain muestra una advertencia y ofrece el botón Fix, que conserva Y y restablece X y Z.
EscalaSe ignoraLos chunks compensan cualquier escala del terreno o de sus objetos padre. El Inspector muestra una advertencia y ofrece el botón Reset Scale. Para hacer un mapa más grande, usa más celdas o un tamaño de celda mayor.

Varios terrenos

Una escena puede contener cualquier número de componentes RTSTerrain. Cada uno tiene su propio mapa, tileset, bibliotecas, chunks y mapa de pathing.

  • RTSTerrain.All enumera todos los terrenos activados; RTSTerrain.Main devuelve el primero (práctico para juegos de un solo mapa; los terrenos temporales de vista previa se omiten).
  • RTSTerrain.FindAt(worldPosition) devuelve el terreno cuya área XZ contiene una posición.
  • RTSTerrain.RaycastAll(ray, out hit) lanza un rayo contra todos los terrenos y devuelve el impacto más cercano (hit.terrain te indica cuál).
  • RTSTerrain.TerrainEnabled / TerrainDisabled son eventos estáticos para los sistemas que hacen un seguimiento de los terrenos.
  • Los terrenos son independientes: los mapas no se unen por sus bordes y la búsqueda de rutas (RTSPathfinder.FindPath(terrain, …)) funciona en un solo terreno a la vez. Para que dos mapas encajen sin costuras, da a sus vértices de borde las mismas alturas.
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);
    }
}

Assets de mapa y archivos de mapa

Un mapa se guarda como un archivo .rtsmap. El mismo formato binario se usa en el Unity Editor (como asset de tu proyecto) y en el editor en el juego (como archivo en disco), así que los mapas creados por los jugadores se pueden abrir en Unity, y viceversa.

Archivos .rtsmap en el proyecto

Cuando Unity encuentra un archivo .rtsmap en la carpeta Assets, el importador RTSMapImporter lo convierte en un RTSMapAsset. El asset guarda los bytes comprimidos del archivo, una pequeña cabecera (nombre, autor, tamaño, jugadores, id del tileset) y una vista previa del minimapa que también se usa como miniatura en la ventana Project.

Crear un asset de mapa

  • Tools ▸ RTS Terrain Studio ▸ New Map Wizard...: configuración guiada (datos básicos, tamaño, aspecto, terreno); escribe el archivo y crea un terreno para él, o lo asigna al terreno seleccionado.
  • Assets ▸ Create ▸ RTS Terrain Studio ▸ Map (.rtsmap): crea en la carpeta seleccionada un mapa plano de 128 × 128 con la configuración predeterminada.
  • Save As… en la ventana RTS Terrain Studio: escribe el mapa actual de un terreno en un archivo nuevo.
  • Copia en la carpeta Assets un archivo .rtsmap creado con el editor en el juego; se importa como cualquier otro archivo.

El Inspector del asset de mapa

Selecciona un asset de mapa para ver su vista previa, nombre, autor, descripción, tamaño, jugadores, tileset y versión de formato, además de estadísticas (celdas, tamaño en el mundo, tamaño de celda, altura de nivel, posiciones iniciales, árboles, decoración, regiones, marcadores, capas de detalle, tamaño del archivo, memoria, última modificación).

ControlDescripción
Open In SceneAsigna el mapa al terreno seleccionado (o al único terreno; pregunta si hay varios). Crea un terreno si la escena no tiene ninguno. Desactivado en el modo Play.
Menú ▾Open In New Terrain, o bien Open On para abrirlo en un terreno concreto de la escena.
Show In Explorer (Reveal In Finder en macOS)Muestra el archivo en el explorador de archivos del sistema operativo.
Tileset Id (Copy, Select)El id del tileset guardado en el mapa. Select selecciona el asset de tileset correspondiente.
Preview SizeLado más largo de la vista previa precalculada: 128, 256 (predeterminado) o 512 píxeles.
Preview TilesetTileset que se usa para los colores de la vista previa. Vacío = el tileset cuyo id guarda el mapa (se encuentra automáticamente).
Re-bake Preview, Apply / RevertVuelve a precalcular la vista previa / aplica los ajustes de importación modificados.

La vista previa se vuelve a precalcular automáticamente cuando el tileset con el id del mapa se añade, se elimina o cambia sus colores del minimapa. Regenerar los arrays de texturas no vuelve a importar los mapas. Un archivo dañado se importa como un asset vacío con un icono de error y un error de importación en la consola.

Edición, Save y Save As

Asigna un asset de mapa al campo Map Asset de un RTSTerrain. Cuando el terreno se activa, carga el mapa en memoria, y todas las ediciones se realizan sobre esa copia en memoria. El archivo de la escena solo guarda la referencia al asset, nunca los datos del mapa en sí.

  • Save (en la cabecera de la ventana RTS Terrain Studio, o Save Map en el menú de opciones del panel superpuesto RTS Terrain Studio de la Scene View) vuelve a escribir el mapa en su archivo .rtsmap y lo reimporta. Save aparece resaltado mientras haya cambios sin guardar.
  • Save As… escribe un archivo nuevo y lo asigna al terreno sin volver a cargar el mapa, así que se conserva tu historial de deshacer.
  • Open… carga otro asset de mapa; Reload vuelve a cargar el mapa desde su asset y descarta los cambios sin guardar.
  • Un terreno sin asset de mapa crea un mapa a partir de sus Default Map Settings. Ese mapa solo existe en memoria hasta que uses Save As.
  • Al guardar la escena (Ctrl+S) también se guardan los mapas editados de los terrenos de esa escena, y se ofrece Save As para los mapas nuevos que aún no tienen archivo. Ambos comportamientos se pueden desactivar en el menú de opciones del panel superpuesto de la Scene View (Save Maps When Saving the Scene, Offer Save As for New Maps).
  • Si hay ediciones de mapa sin guardar, se te pregunta si quieres guardarlas antes de entrar en el modo Play, cerrar una escena o salir de Unity. Las ediciones hechas en el modo Play (con las herramientas de la Scene View o con el editor en el juego) también se ofrecen para guardar al salir del modo Play.

Las ediciones de mapa no forman parte de la escena. El propio cuadro de diálogo «Save scene?» de Unity no las tiene en cuenta; los avisos de RTS Terrain Studio sí. Si un archivo .rtsmap es de solo lectura o está bloqueado por el control de versiones, Save muestra un cuadro de diálogo en lugar de escribir el archivo.

Archivos de mapa en tiempo de ejecución

En un juego compilado, los mapas son archivos normales en disco:

  • RTSMapSerializer.DefaultMapsFolder es la carpeta Maps dentro de RTSMapSerializer.StorageRoot, que es Application.persistentDataPath, así que en Windows la carpeta suele ser %USERPROFILE%\AppData\LocalLow\<Company>\<Product>\Maps. En las builds para Web, StorageRoot es una carpeta del almacenamiento del navegador (consulta Mapas en el navegador).
  • El editor en el juego (RTSRuntimeEditor) guarda y abre los mapas en su Maps Folder (vacío = la carpeta predeterminada indicada arriba; las rutas relativas se interpretan respecto a RTSMapSerializer.StorageRoot). Con Write Preview Images activado, escribe una pequeña imagen del minimapa, Name.preview.png, junto a cada mapa guardado para su explorador de mapas.
  • Para compartir un mapa, copia el archivo .rtsmap en la carpeta de mapas de otro jugador o en un proyecto de Unity. En un navegador web, usa en su lugar los comandos Download Map e Import Map del editor en el juego.
  • Los mapas que publiques con el juego pueden ser simplemente assets de mapa referenciados por un terreno o un script; se incluyen en la build como cualquier otro asset.
APIDescripción
terrain.LoadFromFile(path) / terrain.SaveToFile(path)Carga un archivo en un terreno / guarda el mapa del terreno. Ambos establecen terrain.CurrentFilePath.
terrain.MapAsset = assetAsigna un asset de mapa y lo carga de inmediato si el terreno está activado.
terrain.SetMap(mapData)Muestra un MapData que hayas creado o cargado; el terreno pasa a ser su propietario.
terrain.HasUnsavedChangesEs true si ha habido ediciones desde la última carga o el último guardado.
RTSMapAsset.LoadMap()Deserializa el asset en un nuevo MapData (tú eres su propietario).
RTSMapAsset.CreateFromMap(map, preview)Crea un asset de mapa en memoria, por ejemplo, para pasar un mapa de una escena a otra.
RTSMapSerializer.Serialize(map) / Deserialize(bytes)Bytes, por ejemplo, para enviar un mapa por la red.
RTSMapSerializer.TryReadHeaderFromFile(path, out header)Lee solo la cabecera sin comprimir (rápido, para listas de mapas).
RTSMapSerializer.TryReadHeader(bytes, out header)Lo mismo para bytes de mapa en memoria (por ejemplo, recibidos por la red).
terrain.CreateNewMap(MapCreationSettings)Crea y muestra un mapa plano nuevo; es el equivalente en código del cuadro de diálogo New Map. Rellena Info.tilesetId a partir del tileset del terreno, borra CurrentFilePath y marca el mapa como no guardado.
terrain.LoadFromAsset()Vuelve a cargar el mapa desde MapAsset y descarta las ediciones sin guardar. Si el asset no se puede leer, registra un error y recurre a un mapa creado a partir de DefaultMapSettings.
terrain.SaveToMapAsset()Solo en el editor: vuelve a escribir el mapa en su asset .rtsmap. Devuelve false si el terreno no tiene asset de mapa.
terrain.PendingLayersCapas con cambios que se notificaron pero que aún no se han procesado.
RTSMapAsset.Header / Preview / DataLa cabecera del mapa (se lee sin descomprimir el mapa), la vista previa del minimapa (puede ser null) y los bytes sin procesar del archivo.
RTSMapAsset.SetData(bytes, preview)Reemplaza el contenido de un asset de mapa (la cabecera se lee de los bytes).
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

Un tileset (RTSTileset) define el aspecto de un mapa: hasta 16 capas de suelo, hasta 4 capas de acantilado y el estilo del agua, de forma comparable a los tilesets de verano, invierno o desierto de los editores de mapas de los RTS clásicos. Puedes tener tantos tilesets como quieras y cambiar el tileset de un terreno en cualquier momento: los mapas pintados solo guardan índices de capa, así que el mismo mapa se puede mostrar en verano y en invierno.

  1. Crea un tileset con Assets ▸ Create ▸ RTS Terrain Studio ▸ Tileset.
  2. Arrastra tus texturas de suelo a la sección Ground Layers del Inspector. Se crea una capa por cada textura de albedo. Los mapas de normales y de relieve correspondientes se detectan por el sufijo del nombre de archivo (por ejemplo, _n, _normal, _nrm, _normalgl, _h, _height, _disp); los mapas de rugosidad, AO y metálico se ignoran, así que puedes soltar una carpeta entera de texturas descargada.
  3. Añade las capas de acantilado en Cliff Layers de la misma manera.
  4. Asigna el tileset al campo Tileset de tu RTSTerrain.
  5. Haz clic una vez en Build Texture Arrays, en la sección Texture Arrays (consulta Arrays de texturas).

Campos del tileset

AjustePredeterminadoDescripción
Tileset Id (tilesetId)generadoIdentificador estable que se guarda en los archivos de mapa, para que los mapas recuerden para qué tileset se crearon. Se muestra como solo lectura, con un botón Copy.
Display Name (displayName)«New Tileset»Nombre que se muestra en los editores.
Ground Layers (groundLayers)vacíoHasta 16 entradas GroundLayer. Las entradas sobrantes se eliminan.
Cliff Layers (cliffLayers)vacíoHasta 4 entradas CliffLayer.
Water (water)verde azuladoEl WaterProfile; consulta más abajo.
Resolution (arrayResolution)1024Tamaño de cada slice de los arrays de texturas: 256, 512, 1024 o 2048. Las texturas de origen se redimensionan automáticamente.
Compress (compressArrays)activadoComprime los arrays generados (BC7 / ASTC). Desactívalo para iterar lo más rápido posible.
Material Override (materialOverride)ningunoMaterial opcional en lugar del material de terreno predeterminado. Debe usar el shader RTS Terrain Studio/Terrain u otro compatible.
Macro Variation (macroVariation)0.25Variación a gran escala que rompe la repetición de la textura en zonas grandes.
Triplanar Sharpness (triplanarSharpness)6Nitidez de la mezcla triplanar en las paredes de acantilado (1–16).

Campos de las capas de suelo

AjustePredeterminadoDescripción
Name«Layer»Se muestra en las paletas.
AlbedoningunoColor base (RGB). Cualquier tamaño y formato.
NormalningunoMapa de normales en espacio tangente (impórtalo como Normal Map).
Height MapningunoAltura opcional (canal R) para la mezcla por altura. Tiene prioridad sobre el alfa del albedo.
Height From Albedo AlphadesactivadoUsa el alfa del albedo como altura cuando no hay ningún mapa de relieve asignado.
TintblancoColor que se multiplica por el albedo.
Tile Size6Tamaño en el mundo de una repetición de la textura, en metros (≥ 0.1).
Normal Strength10–2.
Smoothness0.10–1.
Metallic00–1.
Height Blend0.5Intensidad de la mezcla por altura con las capas vecinas (0 = mezcla lineal). Requiere una fuente de altura.
Anti Tiling0.3Rompe la repetición visible con una segunda muestra variada (0–1).
Pathing EffectNoneReglas de pathing que se aplican donde domina esta capa, por ejemplo, Unbuildable para roca irregular o Unwalkable para lava.
Minimap ColornegroColor en el minimapa y en las vistas previas de mapas. Negro = calculado a partir del albedo.

Campos de las capas de acantilado

AjustePredeterminadoDescripción
Name«Cliff»Se muestra en la paleta Cliff Type.
AlbedoningunoColor de la pared. Sin él, los acantilados de este tipo se muestran con un gris oscuro como sustituto.
NormalningunoMapa de normales en espacio tangente.
TintblancoColor que se multiplica por el albedo.
Tile Size4Tamaño en el mundo de una repetición de la textura, en metros.
Normal Strength10–2.
Smoothness0.150–1.
Top Ground Layer−1 (ninguna)Capa de suelo que se pinta automáticamente sobre los acantilados de este tipo. La usa Auto Paint.
Edge Ground Layer−1 (ninguna)Capa de suelo que las herramientas de acantilado pintan a lo largo de los bordes de los nuevos acantilados de este tipo, por ejemplo, un reborde pedregoso (consulta Bordes de acantilado automáticos). Aparece como Edge Layer en la tarjeta del acantilado. −1 = sin borde automático.
Minimap ColornegroColor en el minimapa. Negro = calculado a partir del albedo.

Las paredes de acantilado se texturizan con proyección triplanar en el espacio local del mapa, por lo que las texturas de acantilado no necesitan una distribución especial de UV.

Perfil de agua

El perfil de agua forma parte del tileset y lo usan el renderizador de agua, el mapa de pathing y el minimapa.

AjustePredeterminadoDescripción
Shallow Color(0.22, 0.55, 0.6, 0.55)Color del agua poco profunda; alfa = opacidad.
Deep Color(0.03, 0.16, 0.27, 0.95)Color del agua profunda; alfa = opacidad.
Color Depth3Profundidad en metros a la que se alcanza por completo el color profundo.
Shallow Depth Limit0.9El agua más profunda que este valor es agua profunda (no transitable). El agua menos profunda es transitable, como en los RTS clásicos.
Normal MapningunoMapa de normales de las olas.
Normal Strength0.60–2.
Normal Tile Size8Tamaño en el mundo de una repetición del mapa de normales, en metros.
Flow Speed(0.03, 0.02)Velocidad de desplazamiento del mapa de normales.
Wave Height0.050–1.
Smoothness0.920–1.
Refraction0.030–0.2.
Foam Colorblanco, alfa 0.8Color de la espuma de la orilla.
Foam Distance0.6Distancia de la espuma de la orilla, en metros.
Foam TextureningunoPatrón de espuma opcional.
Minimap Color(0.1, 0.3, 0.55)Color del agua en el minimapa.

Editar tilesets desde código

Los tilesets son ScriptableObjects. Después de cambiar campos desde código, llama a tileset.NotifyChanged() para que los terrenos actualicen sus materiales. tileset.GetGroundLayer(index) y GetCliffLayer(index) devuelven null para las ranuras sin usar.

Arrays de texturas

El shader del terreno dibuja las 20 capas en una sola pasada, por lo que todas las texturas de las capas se empaquetan en dos arrays de texturas de GPU. Nunca tienes que crear estos arrays tú mismo: entran texturas normales de Unity de cualquier tamaño y formato, y salen los arrays.

Estructura

ArraySlicesCanalesEspacio de color
_RTS_AlbedoArray0–15: capas de suelo; 16–19: capas de acantiladoRGB = albedo, A = altura para la mezcla por alturasRGB
_RTS_NormalArray0–15: capas de suelo; 16–19: capas de acantiladoRGB = normal en espacio tangente reasignada a 0–1 (n × 0.5 + 0.5)lineal
  • El primer slice de acantilado es RTSTileset.CliffSliceOffset (16); el total es RTSTileset.TotalSlices (20).
  • La altura (alfa) proviene del Height Map de la capa (canal R); si no hay ninguno, del alfa del albedo cuando Height From Albedo Alpha está activado; en caso contrario, se usa una constante de 0.5.
  • Las normales se desempaquetan durante la generación, así que tus mapas de normales pueden usar la importación de mapas de normales de Unity (DXT5nm / BC5) como siempre. Las capas sin mapa de normales reciben una normal plana.
  • Las capas sin albedo reciben un slice gris neutro con un patrón sutil; el shader sigue aplicando el tinte.
  • Cada slice tiene una cadena de mipmaps completa y usa filtrado trilineal. Las texturas de origen de cualquier tamaño se redimensionan a la Resolution elegida.

Arrays generados y alternativa en tiempo de ejecución

Los arrays pueden crearse de dos maneras:

Arrays generados (recomendado)Alternativa en tiempo de ejecución
Los creaEl botón Build Texture Arrays del Inspector del tilesetEl renderizador, automáticamente, cuando no existen arrays generados actualizados
Se guardanComo sub-assets dentro del asset del tileset (se incluyen en las builds del juego)Solo en la memoria de la GPU; se regeneran al cargar
FormatoBC7 (escritorio, consolas), ASTC 6×6 (Android, iOS, tvOS, visionOS), la compresión de texturas de la build para Web (DXT5, ASTC 6×6 o ETC2 RGBA8), DXT5 (otras plataformas); RGBA32 cuando Compress está desactivadoSin comprimir (4 bytes por píxel)
Memoria a 1024 pxunos 56 MB con BC7 para ambos arraysunos 224 MB para ambos arrays

La sección Texture Arrays muestra una insignia de estado:

  • Not built: el terreno usa arrays generados en tiempo de ejecución. Haz clic una vez en Build Texture Arrays.
  • Up to date: se muestran el formato, el tamaño, el número de mipmaps y la memoria.
  • Outdated: han cambiado las texturas, la resolución o la compresión, o los arrays se generaron para otra plataforma o compresión de texturas. Vuelve a generarlos o deja que lo haga la próxima build del juego.
  • Building: hay una generación en curso o programada.

La sección también muestra el formato de destino para la plataforma de compilación activa, el tamaño y el tiempo de generación estimados, y pide confirmación antes de generar arrays de más de 256 MB. Clear elimina los arrays generados.

Builds del juego

Las builds del juego usan los arrays generados tal como están, porque solo el editor puede compararlos con las texturas de origen. Por eso, los arrays desactualizados se regeneran automáticamente justo antes de cada build del juego, y los tilesets cuyos arrays nunca se generaron siguen usando arrays generados en tiempo de ejecución. Los detalles, incluidos el modo batch y -rtsAllowOutdatedTextureArrays, están en Arrays de texturas en las builds del juego. Además, las builds del juego liberan la copia en la CPU de los arrays generados la primera vez que los usan, lo que ahorra aproximadamente tanta memoria como la que ocupan los arrays en la GPU.

Desde código: tileset.HasValidBuiltArrays indica si se usarán los arrays comprimidos. En el editor, deben existir y coincidir con ComputeContentHash(). Una build del juego confía en los arrays generados hasta que tu código cambie en tiempo de ejecución las texturas de las capas, la resolución o la compresión; a partir de ese momento, pasa a usar arrays generados en tiempo de ejecución. Los valores de ComputeContentHash() solo son comparables dentro del editor o dentro de una misma sesión del juego, porque el editor calcula el hash a partir del estado de importación de las texturas. Si HasValidBuiltArrays es false, RTSTerrain genera arrays sin comprimir en tiempo de ejecución (RTSTextureArrayBuilder.Acquire). tileset.BuiltAlbedoArray / BuiltNormalArray dan acceso a los arrays generados. En tu propio código de tiempo de ejecución, usa RTSShaderReferences.FindShader(name) (y FindMaterial(name)) en lugar de Shader.Find, para que la búsqueda también funcione en las builds del juego.

Auto Build

Con Auto Build activado (la opción predeterminada; es una preferencia del editor que se aplica a todos los tilesets), los arrays que ya se habían generado se regeneran con compresión rápida un segundo después de que cambien las texturas, la resolución o la compresión. La primera generación siempre se inicia a mano. Una generación automática cancelada o fallida no se reintenta hasta que cambie el contenido o el formato de destino. En cambio, al hacer clic en el botón Build Texture Arrays se usa la calidad de compresión normal, que se ve algo mejor, así que hazlo antes de publicar el juego.

La estimación de tamaño es fácil de calcular: resolución² × 20 slices × 2 arrays × 4/3 por los mipmaps × bytes por píxel (1 para BC7, unos 0.44 para ASTC 6×6, 4 sin comprimir). Para móviles, baja la resolución a 512: ocupa una cuarta parte de la memoria.

En las builds del juego deben incluirse los shaders RTS Terrain Studio/Terrain y Hidden/RTS Terrain Studio/TextureArrayBlit (este último lo usa la alternativa en tiempo de ejecución). Tools ▸ RTS Terrain Studio ▸ Validate Project crea el asset RTSShaderReferences en Resources/RTSTerrainStudio, que mantiene todos los shaders del paquete en las builds.

Biblioteca de vegetación

Una biblioteca de vegetación (RTSVegetationLibrary) enumera la hierba, las plantas y los árboles disponibles para pintar. Crea una con Assets ▸ Create ▸ RTS Terrain Studio ▸ Vegetation Library y asígnala al campo Vegetation Library del RTSTerrain. Para mostrar la vegetación, el terreno también necesita un componente RTSVegetationRenderer (el Inspector de RTSTerrain ofrece añadirlo).

El Inspector tiene dos pestañas, Details y Trees, con una cuadrícula de miniaturas. Suelta en ella prefabs (árboles), texturas (planos de hierba) o meshes (meshes de detalle) para añadir entradas. Cada entrada recibe un id estable que usan los mapas; las entradas duplicadas reciben automáticamente ids nuevos.

Prototipos de detalle

Los detalles son plantas pequeñas y numerosas que se pintan por densidad (consulta Densidad de detalles). Se generan en parches de 16 × 16 celdas y se dibujan con el shader procedural interno Hidden/RTS Terrain Studio/Detail, que se encarga del viento, la niebla de guerra y la variación de color.

AjustePredeterminadoDescripción
IdgeneradoIdentificador estable que se guarda en los archivos de mapa.
Name«Grass»Se muestra en la paleta.
Render ModeCrossed QuadsCrossedQuads: quads cruzados texturizados con Texture (hierba clásica). Mesh: usa Mesh, texturizado con la textura principal y el color de Material.
MeshningunoMesh para el modo Mesh.
MaterialningunoMaterial para el modo Mesh. Su textura principal, su color y su umbral de recorte alfa se usan con el shader de detalles.
TextureningunoTextura con alfa para el modo Crossed Quads.
Healthy Color / Dry Colorverde claro / pajizoCada instancia toma un color intermedio entre ambos según un ruido.
Min / Max Width0.6 / 1.0Rango de anchura aleatoria, en metros.
Min / Max Height0.4 / 0.8Rango de altura aleatoria, en metros.
Max Density6Instancias por texel de detalle con la densidad máxima (255). Rango: 1–32.
Color Noise Scale12Escala del ruido que mezcla los colores sano y seco.
Wind Strength0.50–1. La dirección y la velocidad del viento se ajustan en el RTSVegetationRenderer.
Render Distance70Las instancias situadas más allá de esta distancia a la cámara se descartan por culling (antes se encogen suavemente hasta desaparecer).
Cast ShadowsdesactivadoLas sombras de los detalles son costosas; déjalo desactivado para la hierba.
Align To GroundactivadoInclina las instancias para que sigan la normal del suelo.
Max Slope40No genera instancias en pendientes más empinadas que este valor (en grados).
Avoid WateractivadoNo genera instancias bajo el agua.
IconningunoIcono de la paleta; se genera en el editor si está vacío.

Prototipos de árbol

Los árboles son instancias individuales (consulta Instancias de árboles y de decoración) que se colocan con Place Trees.

AjustePredeterminadoDescripción
IdgeneradoIdentificador estable que se guarda en los archivos de mapa.
Name«Tree»Se muestra en la paleta.
PrefabningunoPrefab con MeshRenderers y un LODGroup opcional (se usan LOD0 y LOD1).
Spawn ModeInstancedInstanced: instanciación por GPU, sin GameObjects; es la opción más rápida. GameObject: una instancia del prefab por árbol, para mecánicas de juego como la tala.
Min / Max Scale0.85 / 1.2Rango de escala uniforme aleatoria.
Random RotationactivadoGiro horizontal aleatorio al colocar.
Tint A / Tint Bblanco / verde pálidoRango de tinte aleatorio por instancia.
Footprint Radius0.8Radio en metros que queda obstruido en el mapa de pathing (se escala con el árbol).
Blocks VisionactivadoLos árboles obstruyen la línea de visión para la niebla de guerra, como en los RTS clásicos.
Min Spacing1.6Distancia mínima a otros árboles al dispersarlos.
CollidersactivadoModo Instanced: se crean colliders de cápsula sustitutos (proxies) tomados de un pool. Modo GameObject: se usan los colliders del propio prefab.
Collider Radius / Height0.4 / 4Tamaño de los proxies de cápsula (modo Instanced).
Render Distance250Los árboles situados más allá de esta distancia a la cámara se descartan por culling.
Cast ShadowsactivadoProyección de sombras.
IconningunoIcono de la paleta; se genera en el editor si está vacío.

Instanciación por GPU

Los árboles en modo Instanced se dibujan con instanciación por GPU, por lo que sus materiales deberían tener activada la opción Enable GPU Instancing. Si un material la tiene desactivada, el renderizador usa automáticamente una copia oculta del material con instanciación (que se mantiene sincronizada con el original mientras editas), y el Inspector de la biblioteca muestra un mensaje informativo con un botón Enable que corrige el propio material. Los detalles no necesitan instanciación en sus materiales, porque siempre se dibujan con el shader procedural de detalles.

Usa el shader RTS Terrain Studio/Vegetation en los materiales de los prefabs de árboles y plantas para obtener viento, translucidez, niebla de guerra y el tinte por instancia. Otros materiales de URP compatibles con instanciación también funcionan, pero sin el tinte.

Desde código, las instancias de árbol se eliminan con RTSVegetationRenderer.RemoveTree(id) (por ejemplo, cuando un trabajador tala un árbol), se buscan con TryFindNearestTree, y GetTreeObject(id) devuelve su GameObject o su proxy de collider.

Editar bibliotecas desde código

Los mapas guardan una paleta de ids de prototipo, así que debes resolver cada índice de la paleta a través de la biblioteca:

  • Árboles: vegetationLibrary.FindTree(map.TreePrototypeIds[tree.prototype]).
  • Capas de detalle: vegetationLibrary.FindDetail(layer.prototypeId).
  • Decoración: decorLibrary.Find(map.DecorPrototypeIds[d.prototype]). Obtén el prefab de la variante con prototype.GetPrefab(d.variant): 0 es el prefab principal, y las variantes no válidas o vacías recurren a él. VariantCount incluye el prefab principal.
  • decorLibrary.GetCategories() enumera las categorías en el orden en que se muestran (los prototipos sin categoría aparecen como Misc).
  • vegetationLibrary.EnsureUniqueIds() asigna ids nuevos a las entradas duplicadas.

Después de cambiar una biblioteca desde código, llama a library.NotifyChanged() (lanza el evento Changed de la biblioteca), para que RTSTerrain lance LibrariesChanged y los renderizadores se actualicen. Es la misma regla que para los tilesets.

Biblioteca de decoración

Una biblioteca de decoración (RTSDecorLibrary) enumera los props (doodads) que puedes colocar: rocas, ruinas, vallas, cajas, edificios o cualquier otro prefab. Crea una con Assets ▸ Create ▸ RTS Terrain Studio ▸ Decor Library y asígnala al campo Decor Library del RTSTerrain. Un componente RTSDecorManager en el terreno mantiene la escena sincronizada con las instancias colocadas.

El Inspector muestra los prototipos en una cuadrícula con miniaturas generadas automáticamente y una pestaña por categoría (además de All). Suelta prefabs sobre ella para añadirlos a la categoría actual; haz clic con el botón derecho en la pestaña de una categoría para cambiarle el nombre. Los prototipos sin categoría aparecen en Misc.

Campos de los prototipos de decoración

AjustePredeterminadoDescripción
IdgeneradoIdentificador estable que se guarda en los archivos de mapa.
Name«Decor»Se muestra en la paleta.
Category«Props»Pestaña de la paleta en la que aparece el prototipo.
PrefabningunoEl objeto que se coloca.
VariantsvacíoPrefabs alternativos opcionales; al colocar se elige una variante al azar. La instancia guarda el índice de la variante (0 = prefab principal).
Spawn ModeGameObjectGameObject: un GameObject por instancia (scripts, colliders, animaciones). Instanced: instanciación por GPU, sin GameObjects, solo props estáticos; es el modo más rápido.
Min / Max Scale0.9 / 1.1Rango de escala uniforme aleatoria.
Random YawactivadoRotación aleatoria alrededor del eje vertical.
Yaw Step0Ajusta el giro horizontal a múltiplos de este ángulo (0 = libre), por ejemplo, 90 para muros y vallas.
Max Random Tilt0Inclinación aleatoria en grados (0–45).
Align To Normal0Alinea el eje hacia arriba del objeto con la normal del suelo (0 = vertical, 1 = totalmente alineado).
Snap To GriddesactivadoAjusta la posición a los centros de las celdas.
Y Offset0Desplazamiento vertical en metros que se suma al renderizar (un valor negativo hunde el objeto en el suelo).
Tint A / Tint Bblanco / blancoRango de tinte aleatorio por instancia.
Footprint ShapeCircleNone, Circle o Box.
Footprint Size(0.75, 0.75)Circle: X es el radio. Box: la mitad del tamaño a lo largo de los ejes X y Z del objeto. En metros; se escala con la instancia.
Footprint EffectUnwalkable, UnbuildableIndicadores PathingOverride que se escriben dentro de la huella.
Blocks VisiondesactivadoBloquea la línea de visión para la niebla de guerra.
Min Spacing1Distancia mínima a otros objetos decorativos al dispersarlos.
Cast ShadowsactivadoProyecta sombras.
IconningunoIcono de la paleta; si está vacío, se genera en el editor.

Huellas

  • Una huella circular usa la X de Footprint Size como radio, multiplicada por la mayor escala horizontal de la instancia.
  • Una huella rectangular sigue la rotación de la instancia (sus ejes X y Z proyectados sobre el suelo) y su escala en X/Z, de modo que un muro girado obstruye exactamente su propia superficie.
  • No se escribe ninguna huella si la forma es None, si el efecto es None o si la instancia tiene el indicador DecorFlags.IgnorePathing.
  • Usa solo Unbuildable para decoraciones planas por encima de las que pueden caminar las unidades (flores, escombros) y Unwalkable para objetos sólidos.

Modos de generación

GameObjectInstanced
Objetos de escenaUno por instancia, bajo un objeto hijo llamado «RTS Decor» (no se guarda con la escena)Ninguno
Scripts, colliders, animaciónSí (la raíz de cada instancia recibe un componente RTSDecorInstance)No
RenderizadoLos renderers normales del prefabGraphics.RenderMeshInstanced con LOD0, con culling por parche
Ideal paraProps interactivos, minas de oro, objetos destructiblesMiles de rocas y plantas estáticas

En cuanto a la instanciación por GPU, la decoración instanciada se comporta igual que los árboles instanciados: los materiales sin Enable GPU Instancing se dibujan mediante una copia oculta con la instanciación activada. Los objetos generados se reutilizan desde un pool; RTSDecorManager.GetInstanceObject(id) devuelve el objeto generado de una instancia, y RTSDecorInstance.FromCollider(collider) devuelve la instancia a la que pertenece un impacto de física.

El formato de archivo .rtsmap

Esta sección está dirigida a programadores que quieran leer mapas con otras herramientas, escribir conversores o añadir sus propios datos. No la necesitas para usar el paquete: RTSMapSerializer se encarga de todo.

Estructura binaria

Todos los números están en little-endian. Las cadenas usan el formato de BinaryWriter de .NET: una longitud con codificación de 7 bits seguida de los bytes en UTF-8.

ParteTipoContenido
Número mágico4 bytesRTSM en ASCII
Versión del formatoint32RTSMapSerializer.CurrentVersion (actualmente 1)
Longitud de la cabeceraint32Número de bytes de cabecera que siguen
Cabecerabytesstring nombre, string autor, string descripción, int32 ancho, int32 alto, float cellSize, int32 maxPlayers, string tilesetId, int64 modifiedUtcTicks
Cuerpoflujo deflate sin procesar (RFC 1951, sin envoltorio zlib ni gzip) hasta el final del archivoUna secuencia de secciones que termina con una etiqueta int32 de valor 0

La cabecera no está comprimida para que los exploradores de mapas puedan mostrar el nombre, el autor, el tamaño y los jugadores sin descomprimir el mapa (RTSMapSerializer.TryReadHeader, TryReadHeaderFromFile, struct RTSMapHeader). Los lectores saltan al final de la cabecera gracias a su longitud, así que las versiones futuras pueden añadirle campos nuevos al final.

Secciones

Cada sección es [int32 tag][int32 length][payload]. Las etiquetas son códigos FourCC guardados en little-endian, de modo que el primer carácter está en el byte menos significativo (DIMS = 0x534D4944). Los arrays de datos ráster se escriben como un int32 con el número de elementos seguido de los elementos sin procesar.

EtiquetaValorCarga útil
DIMS0x534D4944int32 ancho, int32 alto, float cellSize, float cliffLevelHeight, int32 splatResolution, int32 detailResolution, int32 pathingResolution, uint32 nextInstanceId. Debe ser la primera sección.
INFO0x4F464E49string nombre, autor, descripción; int32 maxPlayers; string tilesetId; int64 createdUtcTicks, modifiedUtcTicks
HGHT0x54484748Alturas finas: float por vértice
LEVL0x4C56454CNiveles de acantilado: byte por vértice
VFLG0x474C4656Indicadores de vértice: byte por vértice
CTYP0x50595443Tipos de acantilado: byte por vértice
WATR0x52544157Alturas de la superficie del agua: float por vértice
SPLT0x544C5053int32 cantidad de mapas (4) y, después, por cada splat map, un array de bytes RGBA por texel
DETL0x4C544544int32 cantidad de capas y, después, por cada capa: string prototypeId y un array de bytes con las densidades por texel de detalle
TREE0x45455254Lista de ids de prototipo (int32 cantidad + strings), int32 cantidad de árboles y, después, por cada árbol: uint32 id, uint16 prototipo, float3 posición, float giro, float escala, 4 bytes de tinte (30 bytes)
DECO0x4F434544Lista de ids de prototipo, int32 cantidad y, después, por cada instancia: uint32 id, uint16 prototipo, byte variante, byte indicadores, float3 posición, float4 rotación (x, y, z, w), float3 escala, 4 bytes de tinte (52 bytes)
PATH0x48544150Ajustes manuales de pathing: byte por celda de pathing
REGN0x4E474552int32 cantidad y, después, por cada región: string nombre, float x, z, ancho, profundidad, float4 color, propiedades
STRT0x54525453int32 cantidad y, después, por cada posición inicial: int32 jugador, float3 posición, float giro
MARK0x4B52414Dint32 cantidad y, después, por cada marcador: string nombre, string etiqueta, float3 posición, float4 rotación, propiedades
ENVR0x52564E45float4 sunColor, float sunIntensity, float2 sunRotation, float4 ambientColor, bool fog, float4 fogColor, float fogDensity

Las «propiedades» se escriben como un int32 con la cantidad seguido de pares de cadenas clave/valor. El mapa de pathing derivado no se guarda; solo se guardan los ajustes manuales de pathing.

Secciones desconocidas, archivos dañados y versionado

  • Las secciones desconocidas se omiten gracias a su longitud. Por eso, las versiones anteriores del paquete pueden leer archivos más recientes con secciones adicionales, y tus secciones personalizadas nunca hacen fallar a otros lectores.
  • Si un archivo tiene una versión de formato más reciente que la del lector, se registra una advertencia y se lee igualmente; los datos desconocidos se omiten.
  • Si el número de elementos de una capa ráster no coincide con el tamaño del mapa, se registra una advertencia; se lee lo que cabe y se omite el resto.
  • Los archivos truncados o dañados lanzan un único tipo de excepción, System.IO.InvalidDataException. Si el número mágico de un archivo es incorrecto, se informa con el mensaje «Not an RTS Terrain Studio map file».
  • RTSMapSerializer.SaveToFile escribe primero en <path>.tmp y después reemplaza el archivo, de modo que un cierre inesperado durante el guardado nunca deja un mapa a medio escribir.
  • Regla de compatibilidad del formato: los datos nuevos se añaden como secciones nuevas o como campos añadidos al final de la cabecera, nunca modificando las secciones existentes. Haz lo mismo con tus propias secciones y guarda un número de versión dentro de tu carga útil.

Secciones personalizadas

Puedes guardar tus propios datos en los archivos de mapa, por ejemplo, reglas de juego, disparadores o datos de misiones. Dos hooks estáticos de RTSMapSerializer lo hacen posible:

HookTipoCuándo se llama
CustomSectionWriterAction<MapData, Action<int, byte[]>>Una vez por guardado, después de las secciones integradas. Llama a la acción recibida con tu etiqueta y tu carga útil por cada sección que quieras escribir.
CustomSectionReaderAction<MapData, int, byte[]>Por cada sección que el lector no conoce, con su etiqueta y su carga útil. En ese momento, el MapData ya existe (DIMS va primero).
  • Elige etiquetas que no coincidan con las integradas; haz que empiecen por X (por ejemplo, XGLD). Nunca uses 0, que marca el final de las secciones.
  • Ambos hooks son delegados normales: combina manejadores con += y quítalos con -=, de modo que varios sistemas puedan añadir secciones.
  • Comprueba la etiqueta en tu lector e ignora las secciones que no sean tuyas.
  • MapData no tiene un espacio para tus datos. Guárdalos junto al mapa (el ejemplo usa una ConditionalWeakTable). Ni MapData.Clone() ni ResizeMap copian los datos guardados de esta forma, y tampoco se registran en el historial de deshacer.
  • Registra los hooks antes de cargar o guardar cualquier mapa. En el Unity Editor, el importador también lee los mapas y el comando Save los escribe, así que regístralos también para el editor (el ejemplo usa 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();
        }
    }
}

Úsalo en el código del juego con StartingGoldSection.Get(terrain.Map).startingGold. Después de cambiar el valor en el editor, marca el mapa como modificado (por ejemplo, terrain.NotifyChanged(MapLayers.Info, terrain.Map.CellBounds), o establece terrain.HasUnsavedChanges = true) para que se guarde.

Edición en el Unity Editor

Este capítulo cubre todo lo que usas para crear un mapa dentro del Unity Editor: la ventana de RTS Terrain Studio, cómo pintar en la Scene View, los atajos de teclado, los pinceles, la simetría, la acción de deshacer, cada herramienta de edición con todos sus ajustes, Auto Paint y el propio componente RTS Terrain.

Las mismas herramientas y los mismos ajustes de pincel y simetría son también la base del editor en el juego (consulta Editor en el juego), así que lo que aprendas aquí también te sirve allí.

Tu primera edición en cinco pasos. Si nunca has usado el paquete, prueba esto:

  1. Crea un terreno con GameObject ▸ RTS Terrain Studio ▸ RTS Terrain. Si el proyecto todavía no tiene ningún tileset, se te ofrece crear primero el contenido de demostración; acéptalo.
  2. Abre Tools ▸ RTS Terrain Studio ▸ Editor Window.
  3. En la pestaña Height, haz clic en Raise / Lower. La edición se activa automáticamente.
  4. Mueve el ratón sobre el terreno en la Scene View y mantén presionado el botón izquierdo. Verás crecer una colina. Mantén presionada Shift para excavar en lugar de elevar.
  5. Presiona 2 para cambiar a las herramientas de acantilado, haz clic en la colina para elevar una meseta y presiona Esc cuando termines. Ctrl+Z deshace cualquier paso.

La ventana de RTS Terrain Studio

La ventana es el centro de control del editor. No guarda ningún estado de edición propio: muestra y modifica la sesión de edición compartida que también usan la Scene View, el panel superpuesto (overlay) de la Scene View y el Inspector de RTS Terrain. Todo lo que cambies en un sitio aparece de inmediato en los demás.

Puedes abrirla de cualquiera de estas formas:

  • Tools ▸ RTS Terrain Studio ▸ Editor Window
  • el botón Open RTS Terrain Studio del Inspector de RTS Terrain
  • el botón de la ventana en el panel superpuesto RTS Terrain Studio de la Scene View
La ventana de RTS Terrain Studio con la pestaña Decor y la paleta de decoración
La ventana de RTS Terrain Studio (pestaña Decor). Arriba, las categorías; debajo, las herramientas y, a continuación, la descripción de la herramienta activa, la simetría y los ajustes con la paleta de miniaturas.

La ventana tiene cuatro áreas: la cabecera arriba, la barra de pestañas, la página desplazable y la barra de estado abajo.

Cabecera

La cabecera muestra qué terreno y qué mapa estás editando, y contiene los comandos de archivo y de deshacer.

ControlQué hace
Selector de terrenoElige el terreno que editan la ventana y las herramientas de la Scene View. Enumera todos los RTS Terrain de las escenas abiertas y tiene una entrada Create New Terrain. Seleccionar un terreno en la Hierarchy o hacer clic en otro terreno en la Scene View también cambia el terreno editado. Los cambios pendientes en campos de texto se aplican antes de cambiar de terreno.
Botón SelectSelecciona el GameObject del terreno y lo resalta en la Hierarchy.
Nombre del mapa y puntoEl nombre del mapa (de la pestaña Map). Un punto junto a él indica que el mapa tiene cambios sin guardar. Debajo ves el tamaño del mapa y el nombre del asset .rtsmap.
SaveEscribe el mapa en su asset .rtsmap. Si no hay asset de mapa, se comporta como Save As. El botón se resalta mientras haya cambios sin guardar. Si el archivo es de solo lectura o está bloqueado, un cuadro de diálogo explica el problema y ofrece Save As.
Save As…Guarda el mapa como un nuevo asset .rtsmap en el proyecto y lo asigna al terreno.
Open…Abre otro asset .rtsmap del proyecto en este terreno. Los archivos que están fuera de la carpeta Assets deben copiarse antes al proyecto.
ReloadVuelve a cargar el mapa desde su asset y descarta los cambios sin guardar. Requiere un asset de mapa.
Undo / RedoDeshacen o rehacen la última edición del terreno. Los botones muestran el nombre del paso, por ejemplo, Undo Raise / Lower. Consulta Deshacer y rehacer.
?Abre este manual en el capítulo de la ventana del editor.
Editing On / Editing OffActiva o desactiva la edición del terreno en la Scene View. Si todavía no hay ninguna herramienta seleccionada, al activar la edición se elige la primera herramienta de la pestaña que estás viendo.

Open… y Reload piden confirmación antes de reemplazar un mapa que tiene cambios sin guardar o historial de deshacer, porque cargar un mapa borra el historial de deshacer. En el modo Play, un aviso te recuerda que las ediciones se pierden al salir del modo Play, a menos que guardes el mapa.

Cuando la escena todavía no tiene ningún terreno, la cabecera muestra en su lugar un botón Create Terrain. Este crea un GameObject RTS Terrain con los componentes de vegetación, agua y decoración mediante el elemento de menú GameObject ▸ RTS Terrain Studio ▸ RTS Terrain.

Barra de pestañas

La barra de pestañas tiene diez pestañas: las ocho categorías de herramientas Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing y Markers, seguidas de Map y Settings. Un pequeño punto marca la categoría de la herramienta con la que estás pintando en ese momento. La ventana recuerda la última pestaña que usaste.

Mientras la ventana tiene el foco del teclado, las teclas del 1 al 8 (también en el teclado numérico) cambian entre las pestañas de categoría. Estas teclas se ignoran mientras escribes en un campo de texto.

Páginas de herramientas

Cada pestaña de categoría muestra una pila de tarjetas. Puedes contraer cualquier tarjeta haciendo clic en su cabecera.

TarjetaContenido
Categoría ToolsLa cuadrícula de herramientas. Haz clic en una herramienta para pintar con ella en la Scene View. Vuelve a hacer clic en la herramienta activa para dejar de editar. Pasa el ratón sobre una herramienta para leer su descripción y sus teclas modificadoras. Las herramientas personalizadas que escribas aparecen aquí automáticamente.
Tarjeta de herramientaEl nombre, la descripción y la sugerencia de la herramienta seleccionada, un botón ? que abre la sección correspondiente de este manual y una línea de estado en directo. Mueve el ratón sobre el terreno para verla, por ejemplo, Ground 4.25 m | Fine +0.25 m | Level 2 para las herramientas de altura o Level 2 → 3 para las herramientas de acantilado.
Getting StartedSe muestra cuando no hay ninguna herramienta de esta categoría seleccionada. Si estás pintando con una herramienta de otra categoría, indica Active tool: X y ofrece Go to Tool.
BrushForma, radio, intensidad, dureza, curva de atenuación, rotación, rotación aleatoria, espaciado y variación aleatoria. Todas las herramientas de pincel comparten estos ajustes. Se oculta en las herramientas que no usan el pincel. Consulta Pinceles.
SymmetryModo y centro de simetría (no se muestra en las herramientas de arrastre). Consulta Simetría.
Herramienta SettingsLos ajustes de la herramienta seleccionada, generados automáticamente a partir del código de la herramienta. Se recuerdan entre sesiones del editor. Las tablas de la referencia de herramientas enumeran todos los ajustes.
Acciones de categoríaTarjetas adicionales para algunas categorías: Auto Paint Whole Map (Paint), Selection (Decor, mientras Select Decor está activa), Water at Cursor (Water) y Pathing Overlay (Pathing).

Mientras la edición está en pausa, un aviso indica Editing is paused – the Scene View uses Unity's tools. Su botón Resume vuelve a activar la edición.

Hoja con todos los iconos de RTS Terrain Studio: categorías, herramientas, acciones, modos de simetría y formas de pincel
Cada categoría, herramienta, acción, modo de simetría y forma de pincel tiene su propio icono. Los mismos iconos se usan en la ventana, en el panel superpuesto y en el editor en el juego.

Ajustes de las herramientas y paletas

La tarjeta de ajustes elige un control para cada ajuste según su tipo:

Tipo de ajusteControl
Número con un rangoControl deslizante con un campo de valor
Valor activado/desactivadoInterruptor
Opción (enum)Menú desplegable
Color / textoCampo de color / campo de texto
Capa de sueloPaleta de las capas de suelo del tileset con miniaturas de las texturas. Las ranuras vacías siguen visibles y explican en su descripción emergente cómo rellenarlas.
Tipo de acantiladoPaleta de las capas de acantilado del tileset.
Nivel de acantiladoBotones para los niveles 0 a 15. Cada descripción emergente muestra la altura del suelo de ese nivel, y la paleta muestra el nivel que hay bajo el cursor.
JugadorLos 16 colores de jugador (Red, Blue, Teal, Purple, Yellow, Orange, Green, Pink, …).
Prototipos de detalle, árboles o decoraciónCuadrícula con los prototipos de la biblioteca y sus vistas previas, un campo de búsqueda y, para la decoración, filtros por categoría. Un clic selecciona un prototipo, Ctrl+clic añade o quita uno y Shift+clic selecciona un rango. None borra la selección; en ese caso, las herramientas de colocación usan el primer prototipo de la biblioteca.
Referencia a un assetCampo de objeto (por ejemplo, el perfil de Auto Paint). Se aceptan objetos de escena cuando el tipo lo permite.

Cuando una paleta necesita un asset que el terreno no tiene, la tarjeta explica qué falta y ofrece un botón para crearlo: Create Tileset, Create Vegetation Library o Create Decor Library. También puedes asignar un asset existente en el campo que hay al lado. Cada paleta tiene además un botón Edit que selecciona el tileset o la biblioteca para que puedas añadir entradas en el Inspector.

Tomar un valor con Ctrl+clic en la Scene View también cambia el ajuste en la ventana, por ejemplo, la capa de suelo seleccionada o la altura de aplanado.

Barra de estado

La barra de estado, en la parte inferior de la ventana, muestra de izquierda a derecha:

  • el punto bajo el ratón: celda, posición en el mundo, nivel de acantilado, altura del suelo, profundidad del agua (si hay agua) y el nombre de la capa de suelo dominante;
  • los indicadores de pathing en ese punto (transitable, edificable, etc.);
  • el pincel, por ejemplo, Brush 6.0 m · 50%;
  • el historial de deshacer, por ejemplo, Undo 12 (+2) · 48 MB: los pasos que puedes deshacer, los pasos que puedes rehacer y la memoria que ocupan.

Pestaña Map

La pestaña Map contiene los ajustes y las operaciones que afectan a todo el mapa. Todos los cambios se pueden deshacer, salvo que su cuadro de diálogo indique lo contrario. El botón ? de la parte superior abre esta sección.

Map

AjusteDescripción
NameNombre del mapa que se muestra en los exploradores de mapas y en las salas de espera. También se muestra en la cabecera de la ventana.
AuthorQuién creó el mapa.
DescriptionDescripción breve que se muestra a los jugadores.
Max PlayersNúmero recomendado de jugadores (1–16).
Map AssetEl asset .rtsmap que este terreno carga y guarda. Asignar otro asset abre ese mapa, con la misma confirmación que Open….

Debajo de los campos, la tarjeta muestra el tamaño del mapa, la resolución por celda de las capas de splat, detalles y pathing, y la memoria que usa el mapa. Escribir en estos campos crea un único paso de deshacer (Edit Map Info) al salir del campo.

New Map

Reemplaza el mapa del terreno por un mapa nuevo y plano.

AjustePredeterminadoDescripción
Presets de tamaño–Botones para mapas cuadrados de 64, 96, 128, 192, 256, 384 y 512 celdas.
Width / Heightsegún el Default Map del terreno (128)Tamaño en celdas. Múltiplo de 16, de 16 a 1024.
Cell Size2 mTamaño de una celda en metros. Los mapas de los RTS clásicos usan unos 2 m.
Cliff Level Height2 mAltura en el mundo de un nivel de acantilado.
Base Cliff Level2Nivel de acantilado inicial de todo el mapa. Con 2 queda margen para excavar lagos y valles.
Base Ground Layer0Capa de suelo que cubre inicialmente todo el mapa.
Splat4Texels de pintura del suelo por celda (1, 2, 4 u 8). 4 es un buen valor predeterminado; 8 da una pintura muy fina.
Details2Texels de densidad de hierba por celda (1, 2 o 4).
Pathing2Celdas de pathing por celda del mapa (1, 2 o 4). Los valores más altos permiten colocar las unidades con más precisión.
NameNew MapNombre del mapa nuevo.

Una línea de resumen muestra el tamaño resultante en el mundo antes de que hagas clic en Create New Map.

Crear un mapa nuevo borra el historial de deshacer y no se puede deshacer. El asset de mapa anterior se desvincula del terreno, pero no se modifica. Usa Save As… para guardar el mapa nuevo en su propio archivo.

Resize

Amplía o reduce el mapa conservando su contenido.

  1. Introduce New Width y New Height en celdas (múltiplos de 16).
  2. Haz clic en uno de los nueve botones de ancla. Las flechas muestran la dirección en la que crece el mapa; el contenido existente se queda en el lado anclado.
  3. Elige Fill Level (nivel de acantilado) y Fill Layer (capa de suelo) para las zonas nuevas.
  4. Haz clic en Resize Map. La indicación que hay encima del botón muestra cuánto se desplaza el contenido y el nuevo tamaño en el mundo.

Al reducir el mapa se eliminan el terreno, los árboles, la decoración y los marcadores que quedan fuera de la nueva área. Cambiar el tamaño borra el historial de deshacer.

Generator

Genera la distribución completa de un mapa a partir de una semilla: mesetas, rampas, agua, pintura del suelo, vegetación, decoración y posiciones iniciales. La misma semilla con los mismos ajustes produce siempre el mismo mapa.

ControlDescripción
Seed, Style, SymmetryLa semilla, el estilo (Plains, Hills, Highlands, Islands, Canyon, Arena) y la simetría de la distribución (predeterminado: Rotate 2, la distribución de 180°; consulta Simetría).
Noise, Cliffs, Ramps, Water, Border, Paint, Vegetation, Decor, Start LocationsSecciones plegables con los ajustes detallados del generador para cada fase.
Randomize SeedElige una nueva semilla aleatoria.
Apply Style PresetCarga los valores de ruido, acantilados, rampas y agua del estilo seleccionado.
ResetRestablece todos los ajustes del generador a los valores predeterminados del estilo seleccionado.
GenerateReemplaza las alturas, los acantilados, el agua, la pintura del suelo, la vegetación, la decoración y las posiciones iniciales. Se conserva el tamaño del mapa. Es un único paso de deshacer.

El generador trabaja sobre el terreno vinculado en la cabecera de la ventana. Si hay otro terreno seleccionado, un cuadro de diálogo te pide que primero elijas este terreno en la cabecera. Mientras se ejecuta se muestra una barra de progreso, y debajo de los botones aparece un resumen de lo que se ha generado.

Mapa de alturas

Importa o exporta la altura del terreno como archivo PNG o RAW de 16 bits, por ejemplo, para trabajar con World Machine, Gaea o Photoshop.

AjusteDescripción
Mode (importación)Fine Heights Only: añade colinas sobre los niveles de acantilado existentes. Cliff Levels And Fine Heights: divide la imagen en niveles de acantilado y alturas finas, de modo que el suelo coincide exactamente con la imagen (ideal para mapas de alturas con terrazas; las pendientes suaves que cruzan un nivel se convierten en pequeños acantilados). Absolute Heights Keep Levels: la imagen es la altura absoluta del suelo, pero se conservan los acantilados; úsalo para volver a importar una exportación de Ground Height que hayas editado en otro programa.
Height ScaleMetros que representa el rango completo de 16 bits. 0 = automático.
Height OffsetImportación: metros que se suman a cada altura. Exportación: la altura que se escribe como 0.
Flip VerticallyInvierte la imagen a lo largo de Z. Úsalo cuando el mapa aparezca reflejado.
RAW Width / RAW HeightTamaño de los archivos RAW. 0 = se deduce del tamaño del archivo (solo archivos cuadrados).
RAW Little EndianOrden de bytes de los archivos RAW. Windows y la mayoría de las herramientas usan little endian.
Source (exportación)Ground Height: niveles de acantilado más altura fina. Fine Heights: solo la altura fina sobre los niveles.

Los botones son Import PNG… (PNG en escala de grises de 8 o 16 bits, JPG o EXR), Import RAW…, Export PNG…, Export RAW… y Export Splat PNG… (una imagen de la pintura del suelo con los colores de la capa dominante). Las imágenes de otro tamaño se remuestrean. Un mapa tiene un vértice más que celdas en cada dirección, así que un mapa de 256 usa 257 × 257 muestras. Cada importación es un paso de deshacer.

Minimapa

Precalcula una imagen de minimapa del mapa. Elige con Size el tamaño del lado más largo (128, 256, 512 o 1024 px) y ajusta las Bake Options: colores del suelo (capas dominantes o mezcladas), brillo de los niveles de acantilado, sombreado del relieve, acantilados, agua, árboles, decoración y posiciones iniciales. Bake Preview muestra el resultado en la tarjeta; Save PNG… lo precalcula y lo guarda como archivo. Si editas el mapa después de precalcularlo, la tarjeta te indica que vuelvas a precalcularlo.

Validación

Validate Map revisa el mapa en busca de problemas, como texturas que faltan, posiciones iniciales inalcanzables o límites de rendimiento. Cada problema aparece como error, advertencia o información, con una pista sobre cómo corregirlo. Haz clic en un problema que tenga una ubicación para mostrarlo en la Scene View. Si editas el mapa después de validarlo, la tarjeta indica que la lista describe el mapa tal como era antes de las últimas ediciones.

Entorno

El sol, la luz ambiental y la niebla se guardan en el mapa, y el componente RTS Environment los aplica a la escena.

AjusteDescripción
Sun ColourColor de la luz del sol.
Sun IntensityIntensidad de la luz del sol (0–8).
Sun RotationGrados: x = inclinación (altura sobre el horizonte), y = giro horizontal.
Ambient ColourColor de la luz ambiental (del cielo).
FogActiva la niebla de distancia.
Fog Colour / Fog DensityColor y densidad (0–0.05) de la niebla exponencial.

Capture From Scene copia la iluminación actual de la escena en el mapa (un paso de deshacer). Apply aplica ahora mismo el entorno del mapa a la escena, y Select Component selecciona el componente RTS Environment. Si el terreno no tiene RTS Environment, la tarjeta ofrece Add Environment Component.

Pestaña Settings

TarjetaContenido
ViewShow Grid (la cuadrícula de celdas sobre el terreno editado, también con G), Show Markers (posiciones iniciales, regiones y marcadores, incluso cuando no hay ninguna herramienta de marcadores activa), Pathing Overlay (Off, Walkability, Buildability, Flyability, Water, Combined) y Overlay Opacity.
Terrain ComponentEl Inspector completo del componente RTS Terrain editado (consulta El componente RTS Terrain) y un botón Select Terrain.
Undo HistoryMax Steps (10–1000, predeterminado: 100) y Memory Limit (MB) (32–4096, predeterminado: 512), el uso actual de memoria y Clear History. Consulta Límites y memoria.
ShortcutsLos atajos de la Scene View con sus asignaciones de teclas actuales, leídas del Shortcut Manager de Unity. Edit Shortcuts… abre el Shortcut Manager; Refresh vuelve a leer las asignaciones después de que las hayas cambiado.
Help & MaintenanceOpen Documentation, Validate Project (ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project) y Reset All Tool Settings, que restablece el pincel, la simetría y los ajustes de todas las herramientas a sus valores predeterminados y borra el historial de deshacer del terreno.
AboutVersión del producto y versión de Unity.

Pintar en la Scene View

Toda la edición se hace en la Scene View. Cuando la edición del terreno está activada y hay una herramienta seleccionada, el botón izquierdo del ratón pinta con la herramienta en lugar de seleccionar objetos. Activa la edición con el botón Editing de la ventana, el botón Start Editing del panel superpuesto, el botón Edit Terrain del inspector o haciendo clic en una herramienta. Desactívala con Esc.

Ratón y teclas modificadoras

EntradaAcción
Botón izquierdo del ratónPinta con la herramienta activa (presiona, arrastra, suelta). Un trazo es un paso de deshacer. Muchas herramientas siguen actuando mientras mantienes el botón presionado sin mover el ratón; por ejemplo, Raise / Lower sigue elevando la colina.
Shift (mantenida)Invierte la herramienta: bajar en lugar de subir, borrar en lugar de pintar, quitar en lugar de colocar. El cursor del pincel se vuelve rojizo y el panel de estado muestra (inverted).
Ctrl+clicToma un valor bajo el cursor en lugar de pintar: una altura, una capa de suelo, un nivel de acantilado, un tipo de acantilado o una altura del agua. Las herramientas que no permiten tomar valores muestran This tool has nothing to pick y no pintan.
Alt+arrastrarOrbita la cámara como de costumbre. Nunca pinta.
Botón derecho / central del ratónConservan su función habitual en Unity (volar, desplazar).
Herramienta Hand (Q)Con la herramienta Hand de Unity, arrastrar con el botón izquierdo desplaza la cámara y no pinta. El panel de estado muestra Hand tool (Q) active: press W to paint, or drag to pan.
EscHace lo primero que corresponda: cancela un arrastre de manipulador de decoración en curso, cancela el trazo en curso (sus cambios se revierten), borra la selección de decoración (Select Decor) o detiene la edición. La tecla Esc la lee la Scene View que tiene el foco del teclado (haz clic en ella primero); no basta con pasar el ratón por encima. Con el foco en la ventana de RTS Terrain Studio, Esc cancela el trazo y desactiva la edición.
Hacer clic en otro terrenoConvierte ese terreno en el terreno editado (notificación Editing X). Mientras pasas el ratón por encima, el panel de estado muestra Click to edit 'X'.

En macOS, Cmd funciona en todos los lugares donde este manual indica Ctrl.

La intersección del puntero con el terreno se calcula matemáticamente, así que no se necesitan colliders. El shader del terreno dibuja el cursor del pincel exactamente donde pintas. Con varias Scene Views abiertas, solo pinta la vista que está bajo el ratón, y un trazo permanece en la vista donde empezó.

La edición solo funciona en un terreno que esté activado, tenga un mapa cargado y pertenezca al stage actual. En Prefab Mode, edita el terreno dentro del prefab.

Panel superpuesto de RTS Terrain Studio

En cuanto la escena contiene un RTS Terrain, aparece en la Scene View un panel superpuesto (overlay) compacto llamado RTS Terrain Studio. Te ofrece los controles más usados sin necesidad de abrir la ventana completa:

  • el botón Start Editing / Editing (Esc to stop), un botón que abre la ventana y un menú de opciones;
  • el terreno editado (Terrain: X • unsaved cuando tiene cambios sin guardar);
  • los ocho botones de categoría (teclas 1–8) y las herramientas de la categoría seleccionada;
  • Size (un control deslizante con control fino para pinceles pequeños, más un campo para el valor exacto) y Strength;
  • el menú desplegable Symmetry, con una advertencia cuando el modo necesita un mapa cuadrado;
  • los interruptores Grid y Markers y el menú desplegable de superposición Pathing.

El menú de opciones contiene Show Status Panel, Save Map, Save Map As…, Save Maps When Saving the Scene, Offer Save As for New Maps, Shortcuts… y Documentation. Como cualquier panel superpuesto de Unity, se puede mover, acoplar o contraer, y Unity recuerda su posición.

El panel superpuesto Tools de Unity

Mientras hay un RTS Terrain seleccionado, el propio panel superpuesto Tools de Unity contiene una herramienta RTS Terrain Studio. Al activarla, el terreno seleccionado pasa a ser el terreno editado y se activa la edición. Al cambiar a otra herramienta de Unity, la edición se desactiva de nuevo.

La excepción es Select Decor: mientras está activa, las herramientas Move, Rotate, Scale y Transform de Unity eligen el manipulador que se usa para la selección de decoración, así que la edición sigue activada. Consulta Manipuladores de la selección de decoración.

Panel de estado y notificaciones

Un panel de estado en la parte inferior de la Scene View muestra dos líneas:

  1. la herramienta activa, (inverted) mientras se mantiene presionada Shift, el tamaño y la intensidad del pincel (solo en las herramientas de pincel) y el modo de simetría, con una advertencia cuando el modo necesita un mapa cuadrado;
  2. la celda bajo el cursor, su nivel de acantilado y su altura, el estado o la pista de la herramienta, y lo que hace Esc en ese momento (Esc: cancel stroke, Esc: cancel edit, Esc: clear selection o Esc: stop editing).

Ocúltalo o muéstralo con Show Status Panel en el menú de opciones del panel superpuesto. Los atajos confirman lo que han hecho con una breve notificación en la Scene View, por ejemplo, Brush 8.0 m, Strength 55%, Grid on o Stroke cancelled. Si al iniciar Unity la edición seguía activada desde tu última sesión, un aviso que solo se muestra una vez indica Terrain editing is on (Esc to stop).

Vistas previas y guías

  • Cursor del pincel: se proyecta sobre el terreno con el color de la herramienta, más una copia por cada copia simétrica.
  • Fantasmas: en el modo Single, Place Trees y Place Decor muestran una vista previa translúcida del siguiente objeto en el cursor.
  • Guías de simetría: ejes magenta y el punto central mientras hay un modo de simetría activo.
  • Marcadores: posiciones iniciales (con los colores de los jugadores), regiones y marcadores, que se dibujan mientras Show Markers está activado o hay una herramienta de marcadores activa.
  • Cuadrícula (G) y superposición de pathing (P): las dibuja el shader del terreno.

Manipuladores de la selección de decoración

Con Select Decor activa, haz clic en objetos para seleccionarlos, arrastra un rectángulo para hacer una selección por área y mantén presionada Shift para añadir objetos a la selección o alternar su estado de selección. La selección muestra manipuladores al estilo de Unity en el centro de los objetos seleccionados:

Herramienta de UnityManipulador
Move (W)Manipulador de posición.
Rotate (E)Disco de giro horizontal: la decoración solo gira alrededor del eje vertical. Mantén presionada Ctrl para ajustar por incrementos.
Scale (R)Manipulador de escala uniforme. Con Select Decor activa, el atajo R selecciona esta herramienta en lugar de activar o desactivar la rotación aleatoria.
Transform (Y)Manipulador de posición y disco de giro horizontal.
Rect (T)Manipulador de posición.
Hand (Q)Sin manipulador; arrastrar con el botón izquierdo desplaza la cámara.

Delete elimina la selección. Ctrl+D duplica la selección, desplazada una celda en X y Z para que las copias sean visibles; muévelas con el manipulador. Una notificación como Duplicated 3 objects lo confirma. Ctrl+A selecciona todos los objetos decorativos desbloqueados y F encuadra la selección. Son los comandos propios de Unity (Delete, Duplicate, Select All y Frame Selected), así que siguen tus asignaciones del Shortcut Manager. Cada arrastre de un manipulador es un paso de deshacer. Esc durante un arrastre lo revierte sin crear un paso. Hay más comandos en la tarjeta Selection de la pestaña Decor (consulta Herramientas de decoración).

Guardado y protecciones de guardado

El mapa se mantiene en memoria mientras editas, fuera del archivo de escena. Por eso, los avisos Save scene? de Unity no lo cubren. RTS Terrain Studio añade sus propias protecciones:

CuándoQué ocurre
Al guardar una escenaLos mapas de sus terrenos se guardan automáticamente en sus assets .rtsmap (opción del panel superpuesto Save Maps When Saving the Scene, activada de forma predeterminada). Para un terreno sin asset de mapa, se ofrece Save As… una vez por sesión del editor, con las opciones Not Now y Never Ask Again (opción del panel superpuesto Offer Save As for New Maps).
Al entrar en el modo PlaySave map changes before entering Play Mode? con Save, Cancel (permanece en el modo Edit) y Don't Save. Unity recarga la escena y, con ella, el mapa desde su asset. El aviso se omite cuando las Enter Play Mode Options desactivan tanto la recarga de la escena como la del dominio.
Al salir del modo PlaySave map changes made in Play Mode? con Save o Discard. Se pregunta cuando el mapa se ha editado con las herramientas de la Scene View durante el modo Play, o siempre que la escena contenga un In-Game Editor (RTSRuntimeEditor) y el mapa tenga cambios sin guardar. En otras escenas, los cambios producidos al jugar, como los árboles talados, no lo activan.
Al cerrar una escena o salir de UnityPregunta si quieres guardar los mapas modificados.

Antes de sobrescribir un archivo de mapa, el paquete pide a tu sistema de control de versiones que haga checkout del archivo. Si falla, un cuadro de diálogo explica qué hacer. Las protecciones nunca muestran cuadros de diálogo en modo batch ni mientras se compila una build del juego.

Los scripts de compilación y los tests del editor pueden desactivar la protección temporalmente:

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 ...
        }
    }
}

Atajos de teclado

Los atajos de la Scene View están registrados en el Shortcut Manager de Unity, en la categoría RTS Terrain Studio. Solo están activos mientras la edición del terreno está activada, una Scene View o una de las ventanas del editor de RTS Terrain Studio tiene el foco del teclado (o el ratón está sobre una Scene View) y no estás escribiendo en un campo de texto o numérico. Fuera de la edición, teclas como R o las teclas numéricas conservan su función normal en Unity.

Tecla predeterminadaEntrada del Shortcut ManagerAcción
[RTS Terrain Studio/Brush SmallerRadio del pincel × 0.87.
]RTS Terrain Studio/Brush LargerRadio del pincel × 1.15.
-RTS Terrain Studio/Strength DownIntensidad del pincel − 5 %.
=RTS Terrain Studio/Strength UpIntensidad del pincel + 5 %.
RRTS Terrain Studio/Toggle Random RotationActiva o desactiva la rotación aleatoria del pincel. Con Select Decor activa, selecciona en su lugar la herramienta Scale de Unity.
GRTS Terrain Studio/Toggle GridMuestra u oculta la cuadrícula de celdas.
PRTS Terrain Studio/Cycle Pathing OverlayRecorre en ciclo Off → Walkability → Buildability → Combined → Off.
1RTS Terrain Studio/Height ToolsSelecciona la primera herramienta de la categoría. Vuelve a presionar la tecla para recorrer las herramientas de esa categoría.
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

El propio paquete gestiona estas teclas y acciones del ratón, que no se pueden reasignar:

TeclaDóndeAcción
Shift (mantenida)Scene ViewInvertir la herramienta (bajar, borrar, quitar).
Ctrl+clic (Cmd+clic en macOS)Scene ViewTomar un valor (altura, capa, nivel, tipo de acantilado, altura del agua).
EscScene View con el foco del tecladoCancelar el arrastre del manipulador o el trazo, borrar la selección de decoración o detener la edición.
1–8 (también en el teclado numérico)Ventana de RTS Terrain StudioCambiar de pestaña de categoría.
EscVentana de RTS Terrain StudioCancelar el trazo y detener la edición.
Tab, Enter, SpaceVentana de RTS Terrain StudioMover el foco entre herramientas y pestañas; activar el elemento que tiene el foco.
Ctrl+clic, Shift+clicCuadrículas de prototiposAñadir o quitar un prototipo; seleccionar un rango.

Las siguientes teclas son atajos y comandos propios de Unity. RTS Terrain Studio reacciona a ellas, pero siguen las asignaciones que tengas en Edit ▸ Shortcuts..., dentro de las categorías de Unity (Main Menu/Edit, Tools):

Tecla predeterminadaDóndeAcción
Ctrl+Z / Ctrl+YEn cualquier parte de UnityDeshacer / rehacer ediciones del terreno a través del menú Edit de Unity.
W E YScene View, Select DecorHerramientas Move, Rotate y Transform de Unity: eligen el manipulador para la selección de decoración.
Delete, Ctrl+D, Ctrl+A, FScene View, Select DecorDelete, Duplicate, Select All (decoración desbloqueada) y Frame Selected.

R con Select Decor es la entrada propia del paquete RTS Terrain Studio/Toggle Random Rotation: mientras Select Decor está activa, cambia a la herramienta Scale de Unity en lugar de activar o desactivar la rotación aleatoria.

Reasignar atajos

  1. Abre Edit ▸ Shortcuts... (en macOS, Unity ▸ Shortcuts...) o haz clic en Edit Shortcuts… en la pestaña Settings.
  2. Selecciona la categoría RTS Terrain Studio.
  3. Asigna nuevas teclas a las entradas indicadas más arriba.
  4. Haz clic en Refresh en la pestaña Settings para ver las nuevas asignaciones en la ventana.

Si una tecla entra en conflicto con otro atajo de tu configuración, por ejemplo, el atajo Toggle 2D Mode de la Scene View en la tecla 2, Unity pregunta una vez cuál usar. Puedes reasignar cualquiera de los dos en el Shortcut Manager.

Pinceles

Todas las herramientas de pincel comparten un mismo pincel. Lo configuras en la tarjeta Brush de cualquier página de herramienta, en el panel superpuesto (Size y Strength) o con los atajos. El pincel se recuerda entre sesiones del editor.

AjusteRangoPredeterminadoDescripción
ShapeCircle, Square, Diamond, CustomCircleContorno del pincel. Square y Diamond se pueden rotar. Custom usa un asset Brush Shape.
Custom Shape–ningunoEl asset Brush Shape que usa la forma Custom. Haz clic en el botón de forma Custom: la tarjeta muestra entonces cada asset Brush Shape del proyecto como una miniatura, además de un campo Custom Shape. Haz clic en una miniatura (o asigna un asset en el campo) para usarlo.
Radius0.25–128 m6 mRadio del pincel en metros. El control deslizante es logarítmico para permitir un control fino de los pinceles pequeños. Atajos [ y ].
Strength0.01–10.5Efecto de cada toque. Atajos - y =.
Hardness0–10.350 = borde suave en todo el radio, 1 = borde duro. Desactivado mientras Falloff Curve está activado.
Falloff Curveactivado/desactivadodesactivadoUsa una curva personalizada en lugar de Hardness.
Curve–suavizado de entrada y salida de 1 a 0Peso desde el centro del pincel (x = 0) hasta su borde (x = 1).
Rotation0–360°0Rotación de las formas cuadrada, de diamante y personalizada, en sentido horario visto desde arriba. Desactivado con la forma Circle y mientras Random Rotation está activado.
Random Rotationactivado/desactivadodesactivadoRota cada toque de forma aleatoria. Ideal con sellos personalizados. Atajo R.
Spacing0.05–20.2Distancia entre toques en relación con el radio. Los valores más pequeños dan trazos más suaves.
Jitter0–10Desplazamiento aleatorio de cada toque en relación con el radio.

Cómo se pondera un toque

Cada toque asigna a cada vértice, texel de splat u objeto un peso entre 0 y 1, que la herramienta multiplica por Strength. Con Hardness, el peso es 1 dentro de hardness × radius y luego se atenúa suavemente hasta 0 en el borde. Con Falloff Curve activado, es la curva la que define el peso: x es la distancia desde el centro (0 = centro, 1 = borde) e y es el peso. En las formas cuadrada y de diamante, la «distancia» sigue el contorno, así que un pincel cuadrado tiene la misma intensidad a lo largo de sus lados.

Algunas herramientas usan el pincel como una selección estricta: las herramientas de acantilado, agua y pathing solo cambian lo que tiene un peso de al menos 0.5, o el vértice bajo el centro del pincel. En ese caso, los bordes suaves significan «un poco más pequeño» en lugar de «más débil».

Espaciado, variación aleatoria y repetición

  • Spacing: mientras arrastras, se coloca un toque cada radius × spacing metros a lo largo del recorrido del ratón, pero como mínimo cada décima parte de una celda. Las herramientas que se ajustan a los vértices colocan los toques separados al menos media celda.
  • Jitter: cada toque se desplaza una distancia aleatoria de hasta jitter × radius. Jitter solo se aplica a las herramientas que usan el pincel.
  • Repetición: muchas herramientas siguen aplicando toques mientras mantienes el botón presionado sin mover el ratón, por ejemplo, 30 toques por segundo con Raise / Lower. Las tablas de herramientas indican qué herramientas lo hacen.

Convención de rotación

Todas las rotaciones de RTS Terrain Studio (rotación del pincel, giro horizontal de árboles y decoración, dirección de posiciones iniciales y marcadores) usan la misma convención: grados, en sentido horario visto desde arriba, donde 0° apunta a lo largo de +Z (norte) y 90° a lo largo de +X (este). Por eso, las copias simétricas de un pincel u objeto rotado se rotan y reflejan exactamente igual que sus posiciones. Con Random Rotation activado, se elige un ángulo aleatorio por toque y lo comparten todas sus copias simétricas, de modo que los trazos simétricos siguen siendo simétricos.

Formas de pincel personalizadas

Un asset Brush Shape convierte cualquier textura en escala de grises en un sello de pincel: las zonas blancas aplican el efecto completo y las negras, ninguno.

  1. Importa una textura en escala de grises, por ejemplo, el contorno de una roca o una salpicadura. No hace falta que sea legible (readable).
  2. Crea el asset con Assets ▸ Create ▸ RTS Terrain Studio ▸ Brush Shape y asigna la textura.
  3. Revisa la vista previa en el Inspector. Muestra la máscara precalculada exactamente como la usan los pinceles, además de su cobertura.
  4. En la tarjeta Brush, haz clic en el botón de forma Custom. La tarjeta muestra ahora cada asset Brush Shape como una miniatura; haz clic en la miniatura de la nueva forma.
AjustePredeterminadoDescripción
TextureningunoTextura en escala de grises o con canal alfa. Blanco = efecto completo.
Use Red ChannelactivadoLee el canal rojo. Desactívalo para leer el canal alfa en su lugar.
InvertdesactivadoInvierte la máscara.
Resolution64Resolución de la máscara precalculada (16–256).

La máscara se precalcula automáticamente después de cada cambio. Bake Mask la precalcula de inmediato. Un sello personalizado cubre el cuadrado que rodea el centro del pincel y se sigue multiplicando por la dureza o por la curva de atenuación. Baja la dureza para obtener bordes de sello suaves, o ponla en 1 para usar el sello tal cual.

Simetría

La simetría repite cada toque, clic y objeto colocado en posiciones reflejadas o rotadas. Así es como se crean mapas competitivos justos: cada jugador recibe las mismas colinas, acantilados, rampas, árboles y minas de oro. Elige el modo en la tarjeta Symmetry o en el panel superpuesto. Las guías magenta de la Scene View muestran los ejes y el centro.

La simetría se aplica a las herramientas de pincel y de clic. Las herramientas de arrastre (Regions y Select Decor) la ignoran, y la ventana oculta la tarjeta Symmetry mientras una de ellas está activa. Crea las regiones reflejadas a mano.

Nombre en la ventanaNombre en el panel superpuesto / en el códigoCopiasQué haceÚsalo para
OffNone1Solo pinta el propio pincel.Mapas de forma libre, mapas de campaña, detalles que no deben repetirse.
Mirror XMirror X (MirrorX)2Refleja izquierda ↔ derecha respecto al eje vertical que pasa por el centro.Dos jugadores o equipos enfrentados, oeste contra este.
Mirror ZMirror Z (MirrorZ)2Refleja arriba ↔ abajo respecto al eje horizontal.Dos jugadores o equipos enfrentados, norte contra sur.
Mirror XZMirror XZ (MirrorXZ)4Refleja en ambos ejes.Cuatro bases en las esquinas, o mapas 2v2 en los que cada equipo ocupa un lado.
Rotate 180°Rotate 2 (Rotate2)2Simetría central respecto al centro.La clásica disposición 1v1 con bases en esquinas opuestas. A diferencia de un reflejo, una rotación mantiene la izquierda y la derecha iguales para ambos jugadores: una ruta a la izquierda de un jugador también está a la izquierda del otro.
Rotate 90°Rotate 4 (Rotate4)4Simetría rotacional en cuartos de vuelta. Necesita un mapa cuadrado.El clásico todos contra todos de cuatro jugadores.
DiagonalMirror Diagonal (MirrorDiagonal)2Refleja respecto a la diagonal (x ↔ z). Necesita un mapa cuadrado.Mapas 1v1 con bases en las esquinas superior izquierda e inferior derecha.

Centre es el centro de simetría en coordenadas normalizadas del mapa: (0.5, 0.5) es el centro del mapa y el valor predeterminado. Reset lo devuelve a esa posición. Si mueves el centro, algunas copias pueden quedar fuera del mapa y se omiten; la tarjeta muestra una advertencia.

Rotate 90° y Diagonal intercambian los ejes X y Z. En un mapa que no es cuadrado, las copias caen fuera del mapa o quedan desiguales. La tarjeta Symmetry, el panel superpuesto y el panel de estado te avisan. Redimensiona el mapa para que sea cuadrado o elige otro modo.

Cómo se comportan las copias

  • Las copias que están a menos de media celda (o de una décima parte del radio del pincel, lo que sea mayor) de otra copia se fusionan. Por eso, pintar sobre un eje de simetría nunca aplica el efecto dos veces.
  • Los árboles, la decoración, las posiciones iniciales y los marcadores colocados se rotan con su copia. Las copias reflejadas reciben direcciones reflejadas.
  • Los pinceles y sellos personalizados rotados y reflejados giran con su copia (consulta Convención de rotación).
  • Start Locations: con Symmetry Assigns Next Players activado (predeterminado), un clic coloca la posición inicial del jugador seleccionado y sus copias colocan las de los jugadores siguientes. Con Rotate 90° y el jugador 1 seleccionado, un clic coloca las posiciones iniciales de los jugadores 1 a 4.
  • Raise / Lower Level calcula su nivel objetivo a partir del toque original, así que todas las copias pintan el mismo nivel.
  • Fill / Replace en el modo Replace Dominant Layer o Fill All actúa sobre todo el mapa y se ejecuta una sola vez. Fill Connected rellena desde cada copia.

Crea la forma del mapa (acantilados, rampas, agua, posiciones iniciales) con la simetría activada y luego desactívala para las decoraciones pequeñas, para que el mapa no parezca copiado y pegado. La superposición de pathing (P) es una forma rápida de comprobar que todas las bases tienen las mismas rutas.

Deshacer y rehacer

Cada edición del terreno se registra en el propio historial de deshacer del mapa, que está vinculado al sistema de deshacer de Unity. Ctrl+Z, Ctrl+Y y el menú Edit ▸ Undo funcionan con las ediciones del terreno exactamente igual que con cualquier otro cambio, y el menú Edit muestra el nombre del paso, por ejemplo, Undo Raise / Lower. Los botones Undo y Redo de la cabecera de la ventana hacen lo mismo y mantienen sincronizado el historial de Unity. Deshacer entretanto un cambio en otro objeto no altera el historial del terreno.

AcciónPaso de deshacer
Un trazo de pincel (de presionar a soltar) o un clicUn paso con el nombre de la herramienta, por ejemplo, Paint Layer. Todas las copias simétricas pertenecen al mismo paso.
Arrastre de un manipulador de decoraciónUn paso (Move Decor, Rotate Decor, Scale Decor).
Botones de la tarjeta Selection (Delete, Duplicate, Lock …)Un paso por clic.
Generate, importación de mapa de alturas, Auto Paint Whole Map, Capture From SceneUn paso cada uno (Generate Map, Import Heightmap, Auto Paint Map …).
Campos de información del mapa y del entornoUn paso al salir del campo (Edit Map Info, Edit Environment).
Trazo cancelado con EscNingún paso. Los cambios del trazo se revierten.
New Map, Resize, Open, Reload, cambiar a otro terrenoNo se pueden deshacer. Borran el historial (sus cuadros de diálogo lo indican).

Límites y memoria

Cada paso guarda solo las capas que ha cambiado. Las capas ráster (alturas, niveles de acantilado, agua, splat maps, detalles, ajustes manuales de pathing) se guardan en bloques de 16 × 16 celdas alrededor de la zona editada. Las capas de objetos (árboles, decoración, regiones, posiciones iniciales, marcadores, información del mapa, entorno) se guardan como copias de la lista completa, así que un solo trazo de árboles en un mapa con muchísimos árboles consume memoria en proporción al número total de árboles del mapa. Por eso, un trazo pequeño de altura o de pintura ocupa unos pocos kilobytes, mientras que generar un mapa completo guarda todas las capas del mapa. La barra de estado y la tarjeta Undo History muestran la memoria usada actualmente.

En la pestaña Settings se configuran dos límites. Cuando se supera cualquiera de ellos, se descartan primero los pasos más antiguos; el paso más reciente se conserva siempre.

AjusteRangoPredeterminadoDescripción
Max Steps10–1000100Número de pasos de deshacer que se conservan.
Memory Limit (MB)32–4096512Memoria que puede usar el historial de deshacer.

Los límites se guardan por usuario en las preferencias del editor y se aplican incluso con la ventana cerrada. Clear History elimina todos los pasos de deshacer y rehacer del terreno, por ejemplo, para liberar memoria después de una generación grande.

Ediciones que se pueden deshacer desde tus propios scripts de editor

RTSEditSession.RunOperation registra las capas indicadas de todo el mapa, ejecuta tu código y apila el resultado como un único paso de deshacer. Después, Ctrl+Z funciona con él igual que con cualquier herramienta.

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);
        });
    }
}

El editor en el juego usa el mismo tipo de historial de deshacer (MapUndoStack) con sus propios botones Undo y Redo. La conexión con el Ctrl+Z de Unity solo existe en el Unity Editor.

Referencia de herramientas

Las siguientes secciones describen todas las herramientas de las ocho categorías. Todas funcionan igual en el Unity Editor y en el editor en el juego. Algunas convenciones se aplican a todas ellas:

  • Id es el identificador único de la herramienta. Úsalo en código, por ejemplo, RTSEditorContext.ActivateTool("height.raise").
  • Entrada: las herramientas de pincel pintan mientras arrastras. Las herramientas de clic actúan una vez por clic. Las herramientas de arrastre funcionan presionando, arrastrando y soltando (rectángulos, selección por rectángulo).
  • Repeticiones: toques por segundo mientras mantienes presionado el botón sin mover el ratón.
  • Shift invierte el efecto y Ctrl+clic toma un valor del terreno, donde la tabla lo indica.
  • Cada ajuste aparece con su rango y su valor predeterminado. Los ajustes se conservan entre sesiones del editor. Reset All Tool Settings, en la pestaña Settings, restablece los valores predeterminados.

Niveles de acantilado y altura fina

La altura del suelo de cada vértice tiene dos partes. El nivel de acantilado (0–15) se establece con las herramientas de acantilado y crea paredes verticales de acantilado; un nivel mide Cliff Level Height metros de alto (2 m de forma predeterminada). La altura fina se establece con las herramientas de altura y añade colinas y depresiones suaves sobre el nivel. Las alturas finas están limitadas a ±8 niveles de acantilado (±16 m con niveles de 2 m). Mantener ambas partes por separado es lo que te permite pintar acantilados nítidos, al estilo de los RTS clásicos, sobre un terreno ondulado.

Añadir tu propia herramienta

Cualquier clase que derive de RTSTool (o de una de las clases base de categoría, como HeightTool) y tenga un constructor público sin parámetros aparece automáticamente en la ventana, en el panel superpuesto (overlay) y en el editor en el juego. Los campos públicos marcados con [ToolSetting] obtienen su propio control en la interfaz. Este ejemplo sube o baja el suelo en un incremento fijo por clic:

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);
    }
}

Consulta el capítulo Guía de scripting para ver la API completa de herramientas (herramientas de arrastre, toma de valores, gizmos y texto de estado). En las builds con un Managed Stripping Level alto, marca tu herramienta con [UnityEngine.Scripting.Preserve] (consulta Eliminación de código y herramientas personalizadas).

Herramientas de altura

Las herramientas de altura esculpen la altura fina: colinas, valles, caminos y detalles naturales sobre los niveles de acantilado. Nunca cambian los niveles de acantilado, así que las paredes de acantilado se mantienen nítidas. La línea de estado muestra la altura del suelo, la altura fina y el nivel de acantilado bajo el cursor.

HerramientaIdQué haceEntrada y modificadores
Raise / Lowerheight.raiseSube el suelo bajo el pincel; mantén presionado el botón para seguir levantando colinas.Pincel, 30 repeticiones/s. Shift: bajar.
Smoothheight.smoothSuaviza los baches y las aristas del suelo sin desdibujar las paredes de acantilado.Pincel, 20 repeticiones/s.
Flattenheight.flattenNivela el suelo a una misma altura; ideal para zonas de construcción y caminos.Pincel, 20 repeticiones/s. Ctrl+clic: tomar la altura.
Set Heightheight.setLleva el suelo a una altura exacta; con intensidad máxima, el centro del pincel la alcanza en un solo toque.Pincel, 20 repeticiones/s. Ctrl+clic: tomar la altura.
Noiseheight.noiseAñade baches, colinas o crestas de aspecto natural a partir de ruido procedural con semilla.Pincel, 10 repeticiones/s. Shift: restar ruido.
Terraceheight.terraceConvierte las pendientes en escalones planos con bordes redondeados, como bancales de arroz o roca estratificada.Pincel, 15 repeticiones/s.
Erodeheight.erodeSimula el deslizamiento de material por las pendientes empinadas, lo que da a las colinas un aspecto natural y erosionado.Pincel, 15 repeticiones/s.

Ajustes de Raise / Lower

AjusteRangoPredeterminadoDescripción
Speed0.01–20.15Cambio de altura por toque, en metros, con la intensidad máxima del pincel.

Ajustes de Smooth

AjusteRangoPredeterminadoDescripción
Filter Radius1–41Tamaño, en vértices, de la vecindad que se promedia. Los valores más altos eliminan formas más grandes.
Iterations1–82Pasadas de suavizado por toque.

Ajustes de Flatten

AjusteRangoPredeterminadoDescripción
TargetStroke Start, Fixed HeightStroke StartStroke Start usa la altura que hay bajo el cursor al comenzar el trazo. Fixed Height usa el valor de Target Height.
Target Height−32–32 m0Altura fina fija relativa al nivel de acantilado. Ctrl+clic la toma del terreno. Los valores que superan el límite de altura fina del mapa (±8 niveles de acantilado, ±16 m con niveles de 2 m) se ajustan a ese límite al aplicarlos.
ModeFlatten, Raise Only, Lower OnlyFlattenAplanar en ambas direcciones, solo rellenar el suelo que queda por debajo de la altura objetivo o solo rebajar el que queda por encima.

Ajustes de Set Height

AjusteRangoPredeterminadoDescripción
Height−32–64 m0Altura que se establece, interpretada según Height Space. Ctrl+clic la toma del terreno. Los valores que superan el límite de altura fina del mapa (±8 niveles de acantilado, ±16 m con niveles de 2 m) se ajustan a ese límite al aplicarlos.
Height SpaceFine Height, Ground HeightFine HeightAltura fina relativa al nivel de acantilado o altura absoluta del suelo.

Ajustes de Noise

AjusteRangoPredeterminadoDescripción
Noise TypeFbm, Ridged, BillowFbmFbm: colinas onduladas. Ridged: crestas afiladas. Billow: protuberancias redondeadas.
Scale2–256 m24Tamaño de las formas más grandes.
Amplitude0.01–4 m0.25Altura que se añade por toque con intensidad máxima.
Octaves1–84Número de capas de detalle. Más octavas añaden detalles más finos.
Roughness0.2–0.80.5Relación de amplitud entre octavas. Los valores más altos dan un suelo más irregular.
Seed0–99991Cambia el patrón. La misma semilla siempre produce el mismo patrón.

Ajustes de Terrace

AjusteRangoPredeterminadoDescripción
Step Height0.1–8 m1Altura de un escalón de la terraza.
Sharpness0–10.70 = escalones suaves y redondeados. 1 = escalones nítidos con frentes empinados.
Offset−4–4 m0Desplaza todos los escalones hacia arriba o hacia abajo.

Ajustes de Erode

AjusteRangoPredeterminadoDescripción
Talus Angle5–70°30Se erosionan las pendientes más empinadas que este ángulo.
Erosion Strength0.05–10.5Proporción del material sobrante que se desplaza en cada iteración.
Iterations1–508Pasadas de erosión por toque.

Para una zona de construcción, usa Flatten con Target = Stroke Start: empieza el trazo en el punto cuya altura quieres y luego pinta sobre la zona. Para un camino a una altura exacta, haz Ctrl+clic en un punto para tomar su altura, cambia Target a Fixed Height y pinta.

Herramientas de acantilado

Las herramientas de acantilado cambian el nivel de acantilado de los vértices (0–15) y el aspecto de las paredes de acantilado entre niveles. El pincel se ajusta a los vértices y actúa como una selección de bordes duros, así que pintas vértices completos, nunca medios niveles. La línea de estado muestra el nivel que hay bajo el cursor y el nivel que vas a pintar, por ejemplo, Level 2 → 3.

HerramientaIdQué haceEntrada y modificadores
Raise / Lower Levelcliff.raiseSube el suelo un nivel de acantilado; arrastra para ampliar la meseta en la que empezaste.Pincel. Shift: bajar un nivel.
Set Levelcliff.setPinta un nivel de acantilado exacto; útil para mesetas grandes, cuencas y esbozos rápidos del mapa.Pincel. Ctrl+clic: tomar el nivel.
Rampcliff.rampConvierte los bordes de acantilado en pendientes transitables; pinta a través de un acantilado de un nivel para que las cuatro esquinas de una celda sean rampa.Pincel. Shift: quitar la rampa.
Cliff Typecliff.typeCambia el material de las paredes de acantilado, por ejemplo, acantilados de roca junto a acantilados con hierba.Pincel. Ctrl+clic: tomar el tipo de acantilado.
Clean Cliffscliff.smoothEdgesElimina pilares y hoyos aislados de un solo vértice para que los contornos de los acantilados queden limpios.Pincel.

Ajustes de Raise / Lower Level

El nivel objetivo se toma del vértice que hay bajo el cursor al comenzar el trazo: un nivel más alto (o más bajo con Shift). Al arrastrar se pinta ese mismo nivel, así que puedes ampliar una meseta sin apilar niveles. La herramienta solo sube (o solo baja) vértices.

AjusteRangoPredeterminadoDescripción
Break Rampsactivado/desactivadoactivadoQuita el indicador de rampa de los vértices cuyo nivel cambia.
Paint Cliff Edgesactivado/desactivadoactivadoPinta la capa de borde del tipo de acantilado a lo largo de los nuevos bordes de acantilado al terminar el trazo (consulta Bordes de acantilado automáticos).
Edge Width0.25–4 celdas1Ancho del borde pintado, medido desde la pared de acantilado.
Edge Strength0.1–10.9Con qué intensidad cubre la capa de borde el suelo justo al lado de la pared.
Edge SidesBoth / Top / BottomBothPintar el reborde de la meseta, el pie del acantilado o ambos.
Clean Up Old Edgesactivado/desactivadoactivadoQuita la capa de borde donde ya no existe ningún borde después del trazo (acantilados desplazados, rampas nuevas).

Ajustes de Set Level

AjusteRangoPredeterminadoDescripción
Level0–15 (paleta)2Nivel de acantilado que se pinta.
Break Rampsactivado/desactivadoactivadoQuita el indicador de rampa de los vértices cuyo nivel cambia.
Ajustes de bordeigual que arribaPaint Cliff Edges, Edge Width, Edge Strength, Edge Sides y Clean Up Old Edges funcionan igual que en Raise / Lower Level. Ramp y Clean Cliffs tienen los mismos cinco ajustes.

Ramp

La herramienta Ramp no tiene ajustes. Una celda se convierte en rampa cuando sus cuatro vértices de esquina están marcados como rampa y la celda abarca una diferencia de exactamente un nivel. La línea de estado te ayuda: cuenta las celdas de rampa válidas bajo el pincel, menciona las celdas marcadas que no tienen diferencia de nivel y avisa cuando una rampa abarca más de un nivel (quedaría estirada).

Ajustes de Cliff Type

AjusteRangoPredeterminadoDescripción
Cliff Type0–3 (paleta)0Capa de acantilado del tileset que se pinta.

Ajustes de Clean Cliffs

AjusteRangoPredeterminadoDescripción
Strictness5–88Cuántos de los 8 vecinos deben compartir el otro nivel para que un vértice lo adopte. Con 8 solo se eliminan los vértices completamente aislados; los valores más bajos también redondean las esquinas dentadas. Los vértices del borde del mapa, que tienen menos vecinos, necesitan la misma proporción de ellos.
Break Rampsactivado/desactivadoactivadoQuita el indicador de rampa de los vértices cuyo nivel cambia.

Bordes de acantilado automáticos

En los editores de mapas de los RTS clásicos, subir un acantilado también cambia el suelo que lo rodea. Las herramientas de acantilado hacen lo mismo: cuando termina un trazo, el suelo a lo largo de cada nuevo borde de acantilado se pinta con la capa de suelo indicada en el ajuste Edge Ground Layer del tipo de acantilado, por ejemplo, un reborde pedregoso de «Rocky Dirt». El reborde sigue el contorno redondeado del acantilado, se desvanece a lo largo del ancho definido en Edge Width y tiene un contorno ligeramente irregular, de modo que se ve natural y no cuadriculado.

  1. Selecciona el tileset y abre Cliff Layers. En cada tarjeta de acantilado, elige una capa en el ajuste Edge Layer (los tilesets de demostración usan Rocky Dirt).
  2. Elige Raise / Lower Level, Set Level, Ramp o Clean Cliffs y deja activado Paint Cliff Edges.
  3. Pinta los acantilados como de costumbre. El reborde aparece al soltar el botón del ratón.
  • Un solo paso de deshacer: el cambio de nivel y la pintura del borde se deshacen juntos.
  • Simetría: todas las copias simétricas reciben sus bordes en la misma pasada.
  • Rampas: las celdas de rampa nunca reciben reborde. Pintar una rampa a través de un borde elimina allí el reborde cuando Clean Up Old Edges está activado.
  • Acantilados desplazados: con Clean Up Old Edges activado, el reborde de un borde que ha desaparecido (una meseta bajada, una meseta ampliada) se vuelve a eliminar. Esto también elimina la capa de borde si la pintaste allí a mano.
  • Opcional: desactiva Paint Cliff Edges para una herramienta o establece el ajuste Edge Layer del tipo de acantilado en None para todo el tileset. Si la opción está activada pero no hay ninguna capa de borde asignada, la línea de estado lo indica.

Desde código, llama a CliffEdgePainter.PaintEdges(map, tileset, cellRect, CliffEdgeOptions.Default) después de cambiar tú mismo los niveles de acantilado (por ejemplo, en un generador personalizado) y luego a terrain.NotifyChanged(MapLayers.Splat, returnedRect).

Un flujo de trabajo típico: esboza las mesetas con Set Level, refina sus bordes con Raise / Lower Level, pasa Clean Cliffs por los contornos y luego añade rampas con Ramp donde las unidades deban subir. Comprueba el resultado con la superposición de pathing (P).

Herramientas de pintura

Las herramientas de pintura pintan las capas de suelo del tileset (hasta 16). Cada texel del splat map guarda los pesos de todas las capas, que siempre suman el 100 %. Si la capa seleccionada no existe en el tileset o no tiene textura, la línea de estado lo indica en lugar de pintar en silencio con una capa alternativa.

HerramientaIdQué haceEntrada y modificadores
Paint Layerpaint.layerPinta una capa de textura de suelo con bordes de pincel suaves.Pincel, 10 repeticiones/s. Shift: borrar la capa. Ctrl+clic: tomar la capa que hay bajo el cursor.
Tile Paintpaint.tilePinta celdas completas con una capa de suelo, como en los editores de mapas de los RTS clásicos. Cada celda cuyo centro queda dentro del contorno del pincel se pinta al 100 %.Pincel, se ajusta a los centros de las celdas. Shift: quitar la capa de las celdas. Ctrl+clic: tomar la capa.
Fill / Replacepaint.fillReemplaza una capa de suelo en todo el mapa, rellena una zona por inundación o rellena todo el mapa con un solo clic.Clic. Ctrl+clic: tomar la capa.
Smooth Paintpaint.smoothDifumina las transiciones entre capas de suelo bajo el pincel para lograr mezclas más suaves.Pincel, 15 repeticiones/s.
Auto Paintpaint.autoPinta capas de suelo según la altura, la pendiente, los acantilados y el agua usando un perfil de pintado automático.Pincel, 8 repeticiones/s. Shift: pintar la capa base del perfil.

Ajustes de Paint Layer

AjusteRangoPredeterminadoDescripción
Layerpaleta0Capa de suelo del tileset que se pinta.
Opacity0.05–11Cobertura máxima que puede alcanzar la capa. Los valores más bajos mezclan la capa con el suelo que hay debajo.

Ajustes de Tile Paint

AjusteRangoPredeterminadoDescripción
Layerpaleta0Capa de suelo del tileset que se pinta.
Soft Borderactivado/desactivadodesactivadoMezcla un texel alrededor de las celdas pintadas para obtener bordes algo más suaves.

Ajustes de Fill / Replace

AjusteRangoPredeterminadoDescripción
Layerpaleta0Capa de suelo con la que se rellena la zona.
ModeReplace Dominant Layer, Fill Connected, Fill AllFill ConnectedReplace Dominant Layer: todos los texels del mapa cuya capa dominante es la capa en la que haces clic. Fill Connected: relleno por inundación de los texels conectados cuya capa dominante es la capa en la que haces clic. Fill All: todo el mapa.
Preserve Blendsactivado/desactivadoactivadoSolo sustituye la capa en la que haces clic por la nueva y conserva las mezclas con otras capas (Replace y Flood Fill).

Cada clic es un paso de deshacer que solo registra la zona que realmente ha cambiado.

Ajustes de Smooth Paint

AjusteRangoPredeterminadoDescripción
Blur Radius1–31Tamaño del kernel de desenfoque, en texels del splat map. Los valores más altos suavizan más rápido.

Ajustes de Auto Paint

AjustePredeterminadoDescripción
ProfileningunoEl asset Auto Paint Profile con el que pinta el pincel. Vacío = reglas integradas que se asignan según los nombres de las capas del tileset. Consulta Auto Paint.

La pestaña Paint también muestra la tarjeta Auto Paint Whole Map, que vuelve a pintar todo el mapa a partir del perfil en un solo paso.

Herramientas de vegetación

Las herramientas de vegetación pintan hierba y plantas pequeñas (detalles) y colocan árboles a partir de la Vegetation Library del terreno. Si el terreno no tiene ninguna, la página de la herramienta ofrece el botón Create Vegetation Library. Elige prototipos en la cuadrícula; si no hay ninguno seleccionado, se usa el primer prototipo de la biblioteca.

HerramientaIdQué haceEntrada y modificadores
Paint Detailsveg.detailsPinta hierba, flores y otras plantas pequeñas según la densidad.Pincel, 10 repeticiones/s. Shift: borrar los detalles seleccionados.
Erase Detailsveg.detailsEraseElimina la hierba y otra vegetación de detalle que hay bajo el pincel.Pincel, 10 repeticiones/s.
Place Treesveg.treesEsparce árboles con el pincel o coloca árboles individuales con un clic.Scatter: pincel, Shift quita árboles. Single: clic, Shift+clic quita el árbol más cercano.
Erase Treesveg.treesEraseElimina los árboles que hay bajo el pincel. Con menos intensidad, solo borra cerca del centro del pincel.Pincel.

Ajustes de Paint Details

AjusteRangoPredeterminadoDescripción
Detailscuadrícula de prototiposningunoPrototipos de detalle que se pintan.
Target Density0–10.6Densidad hacia la que pinta el pincel (1 = densidad máxima del prototipo).
ModeRaise, SetRaiseRaise solo aumenta la densidad. Set también aclara las zonas más densas hasta el valor objetivo.

Ajustes de Erase Details

AjusteRangoPredeterminadoDescripción
Only Selectedactivado/desactivadodesactivadoBorrar solo los prototipos de detalle seleccionados en lugar de todos los detalles.
Detailscuadrícula de prototiposningunoPrototipos de detalle que se borran cuando Only Selected está activado.

Ajustes de Place Trees

AjusteRangoPredeterminadoDescripción
Treescuadrícula de prototiposningunoPrototipos de árbol que se colocan al azar.
ModeScatter, SingleScatterScatter pinta árboles con el pincel. Single coloca un árbol por clic y muestra una vista previa translúcida.
Density0.05–50.6Árboles por cada 100 m² que genera cada toque con intensidad máxima. El espaciado mínimo de los prototipos se sigue aplicando.
Max Slope0–90°35Los árboles esparcidos no se colocan en pendientes más empinadas.
Avoid Wateractivado/desactivadoactivadoNo esparcir árboles bajo el agua.

Ajustes de Erase Trees

AjusteRangoPredeterminadoDescripción
Only Selectedactivado/desactivadodesactivadoQuitar solo los árboles de los prototipos seleccionados.
Treescuadrícula de prototiposningunoPrototipos de árbol que se quitan cuando Only Selected está activado.

Herramientas de decoración

Las herramientas de decoración colocan, borran y editan props como rocas, ruinas y vallas de la Decor Library del terreno. Si el terreno no tiene ninguna, la página de la herramienta ofrece el botón Create Decor Library. La cuadrícula de prototipos tiene un campo de búsqueda y filtros por categoría.

HerramientaIdQué haceEntrada y modificadores
Place Decordecor.placeColoca props, rocas y otros objetos decorativos uno a uno o dispersos con el pincel.Single: clic; Shift+clic elimina el objeto más cercano. Scatter: pincel; Shift elimina la decoración bajo el pincel.
Erase Decordecor.eraseElimina los objetos decorativos bajo el pincel. Los objetos bloqueados se conservan. Con una intensidad menor, solo se borra cerca del centro del pincel.Pincel.
Select Decordecor.selectSelecciona objetos decorativos haciendo clic o arrastrando un rectángulo, para moverlos, rotarlos, escalarlos o eliminarlos.Arrastrar. Clic: seleccionar. Arrastrar: selección por rectángulo. Shift: añadir / alternar.

Ajustes de Place Decor

AjusteRangoPredeterminadoDescripción
ModeSingle, ScatterSingleSingle coloca un objeto por clic con una vista previa fantasma. Scatter pinta objetos con el pincel.
Decorcuadrícula de prototiposningunoPrototipos de decoración que se colocan al azar. Si no hay ninguno seleccionado, se usa el primero de la biblioteca.
Density0.05–50.4Scatter: objetos por cada 100 m² que genera cada toque a intensidad máxima.
Use Prototype Defaultsactivado/desactivadoactivadoToma del prototipo el giro horizontal aleatorio, la alineación con el suelo y el ajuste a la cuadrícula.
Random Yawactivado/desactivadoactivadoRotación aleatoria alrededor del eje vertical (cuando no se usan los valores predeterminados del prototipo).
Yaw0–360°0Rotación alrededor del eje vertical cuando Random Yaw está desactivado.
Scale0.1–51Multiplicador que se aplica a la escala aleatoria del prototipo.
Align To Ground0–100 = vertical, 1 = sigue la pendiente del suelo (cuando no se usan los valores predeterminados del prototipo).
Snap To Gridactivado/desactivadodesactivadoColoca los objetos en el centro de las celdas.
Random Variantactivado/desactivadoactivadoUsa una variante de prefab aleatoria del prototipo.
Max Slope0–90°90Scatter: omite las pendientes más empinadas que este valor. 90 = sin límite.
Avoid Wateractivado/desactivadodesactivadoScatter: no coloca objetos bajo el agua.

Ajustes de Erase Decor

AjusteRangoPredeterminadoDescripción
Only Selectedactivado/desactivadodesactivadoElimina solo los objetos de los prototipos seleccionados.
Decorcuadrícula de prototiposningunoPrototipos de decoración que se eliminan cuando Only Selected está activado.

Ajustes de Select Decor

AjusteRangoPredeterminadoDescripción
Pick Radius0.25–10 m1Distancia mínima a la que un clic todavía selecciona un objeto (los objetos más grandes se seleccionan dentro de su huella).
Select Lockedactivado/desactivadodesactivadoPermite seleccionar objetos bloqueados, por ejemplo, para desbloquearlos.

Tarjeta Selection

Mientras Select Decor está activa, la pestaña Decor muestra la tarjeta Selection con el número de objetos seleccionados. Cada botón de edición (Delete, Duplicate, Snap to Ground, Rotate 90°, Lock, Unlock) cuenta como un solo paso de deshacer. Select All y Clear solo cambian la selección.

BotónAcción
Select AllSelecciona todos los objetos decorativos del mapa (se omiten los objetos bloqueados).
ClearAnula la selección.
DeleteElimina los objetos seleccionados.
DuplicateDuplica la selección desplazada una celda en diagonal (+X y +Z). Las copias pasan a ser la selección y quedan desbloqueadas.
Snap to GroundVuelve a colocar los objetos seleccionados sobre el suelo.
Rotate 90°Gira la selección 90° en el sentido de las agujas del reloj alrededor de su centro.
Lock / UnlockLas herramientas ya no pueden seleccionar ni borrar los objetos bloqueados. Activa Select Locked para seleccionarlos y desbloquearlos. Aunque se seleccionen de esa forma, ni Delete ni los manipuladores modifican los objetos bloqueados.

El uso de manipuladores para mover, rotar y escalar se describe en Manipuladores de la selección de decoración.

Herramientas de agua

El agua se guarda por vértice: un vértice está mojado, con una altura de la superficie del agua, o seco. Las herramientas de agua usan una selección estricta, como las herramientas de acantilado. La tarjeta Water at Cursor muestra el nivel y la profundidad del agua bajo el ratón y si cuenta como agua poco profunda (transitable) o profunda, según el límite de agua poco profunda del tileset.

HerramientaIdQué haceEntrada y modificadores
Paint Waterwater.paintPinta lagos y ríos a la altura objetivo; el suelo que queda por encima de la superficie del agua permanece seco.Pincel. Shift: borrar agua. Ctrl+clic: tomar la altura.
Erase Waterwater.eraseElimina el agua de los vértices bajo el pincel.Pincel.
Flood Fillwater.floodLlena de agua la cuenca bajo el cursor hasta la altura objetivo con un solo clic.Clic. Shift+clic: vaciar el agua conectada. Ctrl+clic: tomar la altura.

Ajustes de altura del agua (Paint Water y Flood Fill)

AjusteRangoPredeterminadoDescripción
Water HeightCliff Level Plus Offset, Absolute Height, Picked HeightCliff Level Plus OffsetCómo se determina la altura de la superficie del agua.
Level0–15 (paleta)1Nivel de acantilado en el que se basa la superficie del agua (modo Cliff Level Plus Offset).
Offset−8–16 m0.6 nivelesMetros por encima del nivel de acantilado. Mientras no lo cambies, se mantiene en 0.6 × la altura de nivel de acantilado del mapa (1.2 m con niveles de 2 m).
Absolute Height−16–64 m3.2Superficie del agua en metros del espacio local del mapa (modo Absolute Height).
Picked Height−16–64 m3.2Altura tomada con Ctrl+clic (modo Picked Height). Al tomarla, se usa la superficie del agua si el punto está mojado y, si no, el suelo; además, el modo cambia a Picked Height.

Ajustes de Paint Water

AjusteRangoPredeterminadoDescripción
Forceactivado/desactivadodesactivadoMarca también los vértices cuyo suelo queda por encima de la superficie del agua, por ejemplo, para cascadas o pantanos.

Ajustes de Flood Fill

AjusteRangoPredeterminadoDescripción
Max Vertices100–1,000,00020,000Límite de seguridad: un llenado o vaciado que abarcaría más vértices se cancela sin cambios.

El valor predeterminado (nivel 1 + 0.6 niveles) llena los lagos excavados un nivel por debajo del nivel base 2 de un mapa nuevo. Excava una cuenca con Raise / Lower Level manteniendo presionada Shift y luego haz clic dentro de ella con Flood Fill.

Herramientas de pathing

El mapa de pathing (transitable, edificable, sobrevolable, agua) se deriva automáticamente de las pendientes, los acantilados, el agua, las capas de suelo, los árboles y la decoración. Las herramientas de pathing pintan encima ajustes manuales, con una selección estricta en celdas de pathing. Activa la superposición en la tarjeta Pathing Overlay o con P para ver el resultado.

HerramientaIdQué haceEntrada y modificadores
Paint Pathingpathing.paintPinta reglas de pathing invisibles, como bloqueadores, zonas no edificables o zonas forzadas como transitables.Pincel. Shift: eliminar el ajuste manual seleccionado.
Clear Pathingpathing.clearElimina todos los ajustes manuales de pathing pintados para que vuelva a aplicarse el pathing automático.Pincel.

Ajustes de Paint Pathing

AjusteRangoPredeterminadoDescripción
Overrideconsulta más abajoBlockerQué ajuste manual de pathing se pinta.
Ajuste manualEfecto
BlockerNo transitable y no edificable: un muro invisible, típico de los bordes del mapa y de las zonas decorativas.
UnwalkableLas unidades terrestres no pueden caminar aquí.
UnbuildableAquí no se pueden colocar edificios, por ejemplo, para mantener libres los caminos y las expansiones.
UnflyableLas unidades aéreas no pueden volar aquí.
Force WalkableLas unidades terrestres pueden caminar aquí aunque la pendiente, el agua o las huellas lo impidan.
Force BuildableAquí se pueden colocar edificios aunque las reglas derivadas lo prohíban.

Colores de la superposición de pathing

SuperposiciónColores
WalkabilityVerde = transitable, rojo = obstruido, rojo oscuro = acantilado, naranja = demasiado empinado, azul = agua profunda, cian = agua poco profunda.
BuildabilityVerde = edificable, amarillo = transitable pero no edificable, rojo / rojo oscuro = no transitable.
FlyabilityAzul claro = sobrevolable, magenta = no sobrevolable.
WaterCian = agua poco profunda, azul = agua profunda.
CombinedTodo a la vez, como la vista de pathing de los editores de mapas de los RTS clásicos.

Las reglas de la parte automática (pendientes máximas, margen de acantilado, etc.) se configuran en el componente RTS Terrain; consulta Pathing Rules.

Herramientas de marcadores

Las herramientas de marcadores añaden información de juego al mapa: dónde empiezan los jugadores, regiones con nombre para disparadores (triggers) y puntos con nombre, como posiciones de cámara. Los marcadores se dibujan en la Scene View mientras hay una herramienta de marcadores activa o la opción Show Markers está activada.

HerramientaIdQué haceEntrada y modificadores
Start Locationsmarkers.startColoca el punto de aparición de un jugador. Haz clic de nuevo para moverlo.Clic. Shift+clic: eliminar la posición inicial más cercana.
Regionsmarkers.regionArrastra rectángulos para crear regiones con nombre para la lógica del juego.Arrastrar. Shift+clic: eliminar la región bajo el cursor.
Markersmarkers.markerColoca puntos con nombre y dirección, por ejemplo, cámaras o puntos de ruta.Clic. Shift+clic: eliminar el marcador más cercano.

Ajustes de Start Locations

AjusteRangoPredeterminadoDescripción
PlayerPlayer 1–16 (paleta)Player 1 (Red)Jugador cuya posición inicial se coloca. Cada jugador tiene una única posición inicial.
Yaw0–360°0Orientación de la posición inicial.
Symmetry Assigns Next Playersactivado/desactivadoactivadoCon la simetría activada, las copias simétricas colocan las posiciones iniciales de los jugadores siguientes. Si está desactivado, solo el clic original coloca o mueve la posición inicial.

Ajustes de Regions

AjusteRangoPredeterminadoDescripción
Colorcolorazul translúcidoColor de las regiones nuevas.
Snap To Cellsactivado/desactivadoactivadoAjusta los bordes de las regiones a la cuadrícula de celdas.

Las regiones nuevas reciben automáticamente un nombre único.

Ajustes de Markers

AjusteRangoPredeterminadoDescripción
NametextoMarkerNombre de los marcadores nuevos. Se añade un número si el nombre ya está en uso.
TagtextovacíoTexto libre que el código de tu juego usa para encontrar marcadores, por ejemplo, camera o waypoint.
Yaw0–360°0Orientación de los marcadores nuevos.

Pintado automático

Auto Paint pinta las capas de suelo a partir de la forma del terreno: hierba en el suelo llano, roca en las pendientes empinadas y junto a los acantilados, arena en la orilla y manchas de tierra fragmentadas por ruido. Es la forma más rápida de dar un aspecto acabado a un mapa recién esculpido o generado. Después puedes retocarlo a mano.

Hay tres formas de usarlo:

  • la herramienta de pincel Auto Paint de la pestaña Paint pinta según las reglas dentro del pincel y mezcla el resultado con la pintura existente según el peso y la intensidad del pincel. Con Shift, pinta la capa base en su lugar, lo que elimina lo pintado por las reglas;
  • la tarjeta Auto Paint Whole Map de la pestaña Paint vuelve a pintar todos los texels de suelo del mapa en un solo paso de deshacer. Tiene un campo Profile y un botón Create Profile;
  • el botón Apply To Map del Inspector del perfil y la etapa Paint del generador.

Sin perfil, se usan las reglas predeterminadas integradas. Estas eligen sus capas según los nombres de las capas de suelo del tileset.

Perfiles de pintado automático

  1. En la pestaña Paint, haz clic en Create Profile de la tarjeta Auto Paint Whole Map. Así se crea y se asigna un perfil con las reglas predeterminadas para el tileset del terreno. También puedes usar Assets ▸ Create ▸ RTS Terrain Studio ▸ Auto Paint Profile.
  2. Selecciona el perfil. En el Inspector, asigna un Preview Tileset para que las tarjetas de reglas muestren los nombres y las miniaturas de las capas. Solo se usa para la visualización; el perfil funciona con cualquier tileset.
  3. Edita las reglas y, después, haz clic en Apply To Map para volver a pintar el terreno seleccionado o en edición. Si hay varios terrenos y ninguno está seleccionado, un menú te permite elegir uno.
  4. Haz clic en Use In Auto Paint Tool para que la herramienta de pincel pinte con este perfil.
Ajuste del perfilPredeterminadoDescripción
Base Layer0Capa que cubre todos los texels antes de aplicar las reglas.
Start From ExistingdesactivadoParte de los pesos de splat actuales en lugar de la capa base, lo que conserva la pintura hecha a mano donde no coincide ninguna regla.
Rules–Se aplican de arriba abajo; las reglas posteriores pintan sobre las anteriores. Cada tarjeta de regla tiene un interruptor para activarla, un nombre, su capa, ▲ / ▼ para reordenar, botones para duplicar y eliminar, y una sección plegable Conditions. Los botones Add Rule y Reset To Defaults están debajo de la lista.

Para cada texel de splat, Auto Paint empieza con el 100 % de la capa base (o con los pesos existentes), aplica las reglas activadas de arriba abajo y normaliza el resultado. Cada condición activada de una regla aporta un peso entre 0 y 1, con una transición suave en los límites del rango. Los pesos se multiplican, se fragmentan con ruido y se escalan según la intensidad de la regla.

Ajustes de las reglas

AjustePredeterminadoDescripción
NameRuleNombre visible de la regla.
Layer0Capa de suelo (índice del tileset) que pinta esta regla.
EnabledactivadoLas reglas desactivadas se omiten.
Use Height, Min Height, Max Heightdesactivado, 0, 20Pinta solo donde la altura del suelo en el espacio local del mapa (en metros) está dentro del rango.
Height Falloff0.5Transición suave fuera del rango de altura, en metros (0 = borde duro).
Use Slope, Min Slope, Max Slopedesactivado, 0, 90Pinta solo donde la pendiente (en grados) está dentro del rango.
Slope Falloff5Transición suave fuera del rango de pendiente, en grados.
Use Cliff Level, Min Cliff Level, Max Cliff Leveldesactivado, 0, 15Pinta solo en estos niveles de acantilado (borde duro).
Near Cliff Distance0Pinta solo hasta esta distancia de una pared de acantilado, medida en celdas (0 = desactivado). El efecto se atenúa en la mitad exterior.
Near Water Distance0Pinta solo hasta esta distancia del agua, medida en celdas (0 = desactivado). El efecto se atenúa en la mitad exterior.
Noise Scale16Tamaño de las formas que genera el ruido, en metros.
Noise Amount0Cuánto fragmenta el ruido la regla (0 = sin ruido, 1 = manchas).
Noise Seed0Las reglas con semillas distintas producen manchas distintas.
Strength1Intensidad global de la regla (0–1).
Blend ModeReplaceCómo se combina la regla con las reglas situadas por encima de ella (consulta más abajo).
Modo de mezclaEfecto
AddAñade encima la capa de la regla. Las capas existentes se reducen proporcionalmente al normalizar el resultado.
ReplaceMezcla todas las capas hacia la capa de la regla según el peso de la regla. Es la opción habitual.
MultiplyMultiplica el peso existente de la capa de la regla por la máscara de la regla, lo que elimina la capa donde la regla no coincide.

Reglas predeterminadas

Las reglas integradas usan como capa base la capa cuyo nombre contiene grass, meadow o green, y buscan capas de dirt, rock y sand mediante palabras clave similares (capas 0–3 si no hay tileset).

ReglaPintaCondiciones
Dirt PatchestierraManchas de ruido (escala de 14 m), intensidad 0.55.
Sand ShorearenaA menos de 3 celdas del agua, ligeramente fragmentada por ruido.
Rock SlopesrocaPendientes de 28° o más (atenuación de 8°).
Rock Near CliffsrocaA menos de 1.6 celdas de una pared de acantilado, fragmentada por ruido, intensidad 0.85.

Pintado automático desde código

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();
    }
}

El componente RTS Terrain

El componente RTS Terrain muestra un mapa. Construye el mesh del terreno en chunks, renderiza el suelo y los acantilados con el tileset, mantiene actualizado el mapa de pathing y ofrece consultas como la altura, el agua y el pathing en una posición del mundo. Créalo con GameObject ▸ RTS Terrain Studio ▸ RTS Terrain o añádelo a cualquier GameObject con Add Component ▸ RTS Terrain Studio ▸ RTS Terrain.

El elemento de menú crea un terreno listo para editar: un GameObject RTS Terrain con los componentes RTS Terrain, Vegetation Renderer, Water Renderer y Decor Manager, el tileset y las bibliotecas predeterminados del proyecto, y un archivo .rtsmap nuevo con un mapa llano junto a la escena activa (o en Assets/Maps). Si el proyecto aún no tiene ningún tileset, se te ofrece crear primero el contenido de demostración.

Inspector

ÁreaContenido
HeaderNombre del mapa, tamaño en el mundo, tamaño de celda y asset de mapa, una insignia Unsaved changes y estadísticas en tiempo real: Cells, Chunks, Map Memory, Trees, Decor, Detail Layers, Regions, Start Locations y Markers.
ActionsOpen RTS Terrain Studio, Edit Terrain (activa o desactiva la edición en la Scene View para este terreno), Save, Save As… y Reload.
WarningsProblemas, con corrección en un solo clic cuando es posible: URP inactivo, shaders que faltan, sin tileset (con un botón para usar el tileset para el que se creó el mapa), arrays de texturas sin construir o desactualizados (Build), un mapa creado para otro tileset, vegetación o decoración en el mapa sin biblioteca, un terreno rotado alrededor de X o Z (Fix) o escalado (Reset Scale), y componentes de subsistemas que faltan (Add).
AssetsCampos Map, Tileset, Vegetation y Decor, cada uno con un botón New que crea y asigna un asset nuevo.
Chunks & Geometry, Cliff Shape, Physics, RenderingLos ajustes del terreno (tabla más abajo).
Pathing RulesCómo se deriva el mapa de pathing (tabla más abajo). Se reconstruye automáticamente tras los cambios.
Default MapTamaño y valores predeterminados del mapa que se crea cuando no hay ningún asset de mapa asignado.
Rebuild TerrainDestruye y vuelve a crear todos los chunks, las texturas y el mapa de pathing; la vegetación, el agua y la decoración también se reconstruyen. Úsalo después de cambiar ajustes desde código o cuando algo parezca desactualizado.

El mismo Inspector está integrado en la pestaña Settings de la ventana, en Terrain Component. La asignación de un tileset y los arrays de texturas se explican en Tilesets; los archivos de mapa, en Assets de mapa.

El terreno solo usa la rotación Y de su transform e ignora la escala. Para agrandar un mapa, añade celdas con Resize (RTSTerrain.ResizeMap) o crea un mapa nuevo con un Cell Size mayor (MapCreationSettings.cellSize).

Ajustes del terreno

Estos campos pertenecen a RTSTerrainSettings (propiedad RTSTerrain.Settings).

AjustePredeterminadoDescripción
Chunks & Geometry
Chunk Size32Celdas por lado de chunk (16, 32 o 64). Los chunks más pequeños se reconstruyen más rápido durante la edición; los más grandes implican menos draw calls.
Surface Subdivisions2Quads por lado de celda en el suelo suave (1–4). 1 = aspecto de RTS clásico, 2–4 = colinas más suaves.
Cliff Subdivisions4Resolución de la subcuadrícula que se usa para trazar contornos de acantilado redondeados dentro de una celda de acantilado (2–8).
Cliff Shape
Cliff Noise0.18Desplazamiento horizontal aleatorio de los vértices de las paredes de acantilado, como fracción de una celda (0–0.5). Da un aspecto de roca natural.
Cliff Noise Scale1.6Escala del ruido de acantilado en unidades del mundo (mínimo 0.1).
Cliff Slant0.12Cuánto se retrae el borde superior de un acantilado para que las paredes queden ligeramente inclinadas, como fracción de una celda (0–0.4).
Cliff Wall Segments3Segmentos verticales por nivel de acantilado para la geometría de las paredes (1–8).
Physics
Collider ModeMesh ColliderNone: sin colliders; usa el raycast del terreno para las consultas. Mesh Collider: un MeshCollider por chunk, que se reconstruye tras las ediciones.
Chunk LayerDefault (0)Capa de los GameObjects de los chunks; la usan los colliders y las cámaras.
Rendering
Cast ShadowsOnModo de proyección de sombras de los chunks.
Receive ShadowsactivadoSi los chunks reciben sombras.
Material OverrideningunoMaterial opcional. Si está vacío, se crea un material que usa el shader RTS Terrain Studio/Terrain.
Show GriddesactivadoMuestra la cuadrícula de celdas, también en el modo Play y en las builds. El interruptor G del editor es independiente y solo afecta a la edición.
Grid Opacity0.35Opacidad de la cuadrícula (0–1).

Pathing Rules

Estos campos pertenecen a RTSPathingSettings (propiedad RTSTerrain.PathingSettings). Deciden cómo las pendientes, los acantilados, el agua y las capas de suelo se convierten en zonas transitables y edificables. Los ajustes manuales pintados con las herramientas de pathing se aplican encima.

AjustePredeterminadoDescripción
Max Walkable Slope42°El suelo con una pendiente mayor que este ángulo no es transitable.
Max Buildable Slope18°El suelo con una pendiente mayor que este ángulo no es edificable.
Cliff Margin0.25Cuánto se extiende la zona obstruida más allá de una pared de acantilado, como fracción de una celda de pathing (0–1). Los valores más altos mantienen las unidades más alejadas de los bordes de acantilado.
Shallow Water BuildabledesactivadoPermite construir edificios en agua poco profunda.
Tree Footprint Scale1Multiplicador del radio de la huella de pathing de cada árbol.
Use Ground Layer RulesactivadoAplica el efecto de pathing de la capa de suelo dominante, por ejemplo, Unbuildable en roca abrupta.

Default Map

El mapa que se crea cuando no hay ningún asset de mapa asignado. La tarjeta New Map de la pestaña Map empieza con estos valores.

AjustePredeterminadoDescripción
Width / Height128 / 128Tamaño en celdas, múltiplo de 16 (16–1024).
Cell Size2Tamaño de una celda en unidades del mundo.
Cliff Level Height2Altura en el mundo de un nivel de acantilado.
Base Cliff Level2Nivel de acantilado inicial de cada vértice (0–15).
Base Ground Layer0Capa de suelo que cubre inicialmente el mapa.
Splat Resolution4Texels de splat por celda (1, 2, 4 u 8).
Detail Resolution2Texels de densidad de detalles por celda (1, 2 o 4).
Pathing Resolution2Celdas de pathing por celda del mapa (1, 2 o 4).

Componentes de subsistemas

El terreno solo dibuja el suelo y los acantilados. Las demás partes del mapa las muestran componentes situados en el mismo GameObject o en otro lugar de la escena. El Inspector del terreno avisa cuando falta alguno de los cuatro primeros y ofrece un botón Add.

ComponenteLo añade el menú de creaciónFinalidad
Vegetation RenderersíRenderiza la hierba y las plantas pintadas, así como los árboles, con instanciación por GPU o como instancias de prefab. Solo regenera las zonas que han cambiado.
Water RenderersíConstruye y renderiza la superficie del agua por chunk, con el aspecto del perfil de agua del tileset. Para obtener el mejor aspecto, activa Depth Texture y Opaque Texture en el asset de URP (o en la cámara).
Decor ManagersíMuestra los objetos decorativos del mapa y los mantiene sobre el suelo cuando cambian las alturas.
Environmentlo añade el New Map Wizard (opcional), o puedes añadirlo con GameObject ▸ RTS Terrain Studio ▸ EnvironmentAplica a la escena el sol, la luz ambiental y la niebla del mapa. La iluminación original de la escena se restaura al desactivarlo y nunca se escribe en el archivo de la escena.
Fog of War ManagerGameObject ▸ RTS Terrain Studio ▸ Fog of War ManagerCalcula la niebla de guerra para el equipo local. Uno por escena.
Pathfinding ServiceAdd Component ▸ RTS Terrain Studio ▸ Pathfinding Service, o se crea cuando hace falta mediante RTSPathfindingService.GetOrCreate()Ejecuta las búsquedas de rutas integradas sobre el mapa de pathing del terreno.

Cambiar los ajustes del terreno desde código

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;
    }
}

Si cambias los campos de la instancia actual de PathingSettings en lugar de asignar una nueva, llama después a terrain.RebuildPathing().

Editor en el juego

El editor en el juego lleva el editor de mapas de RTS Terrain Studio a tu juego compilado. Los jugadores y los modders pueden crear, pintar, guardar, cargar y probar mapas sin Unity, igual que con los editores de mapas que incluían los RTS clásicos. Funciona con UI Toolkit, usa las mismas herramientas que la ventana del editor en Unity (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing y Markers) y escribe los mismos archivos .rtsmap. Un mapa creado en tu juego se puede copiar a un proyecto de Unity y abrir allí sin cambios, y viceversa.

El editor de mapas en el juego ejecutándose en la vista Game
El editor en el juego: barra superior, categorías de herramientas y herramientas a la izquierda, ajustes del pincel y de la herramienta a la derecha, barra de estado abajo.

Todo está en un único componente, RTSRuntimeEditor. Añádelo a una escena que tenga un RTS Terrain y una cámara, presiona Play y ya tendrás un editor funcional. Este capítulo explica la configuración, cada parte de la interfaz, todos los controles de teclado y ratón, el botón Play Test, cómo reacciona a él el código de tu juego, dónde se guardan los mapas, cómo cambiar el estilo de la interfaz y qué comprobar antes de publicar.

En el Unity Editor, el botón Open Manual de la ventana Shortcuts & Help (F1 o el botón ? de la barra superior) abre este capítulo. En una build, el botón abre la página que indiques en Manual Url, por ejemplo, tu propia guía de modding. Si ese campo está vacío, el botón se oculta en las builds.

Probarlo en dos minutos

La forma más rápida de ver el editor en el juego es la escena de demostración incluida en el paquete.

  1. Elige Tools ▸ RTS Terrain Studio ▸ Create Demo Content. Esto genera las texturas, bibliotecas, mapas y escenas de demostración. Solo tienes que hacerlo una vez.
  2. En la ventana Project, abre Samples/Scenes/RTS Terrain Studio - In-Game Editor Demo. Contiene el mapa Sandbox, un objeto RTS In-Game Editor, un objeto RTS Demo Game y un gestor de niebla de guerra.
  3. Presiona Play. El editor se abre con un mensaje de bienvenida. Elige una herramienta a la izquierda (la herramienta de altura Raise / Lower está activa de forma predeterminada) y pinta sobre el terreno con el botón izquierdo del ratón.
  4. Mantén presionada Shift mientras pintas para bajar el suelo. Usa [ y ] para cambiar el tamaño del pincel. Arrastra con el botón derecho del ratón para mover la cámara y usa la rueda del ratón para acercar y alejar.
  5. Presiona F5 (o Play Test en la barra superior). El juego de demostración genera unidades en las posiciones iniciales y activa la niebla de guerra. Selecciona unidades y haz clic con el botón derecho para moverlas.
  6. Presiona Esc o F5 para volver al editor. El mapa vuelve exactamente al estado que tenía antes de la prueba de juego.
  7. Presiona Ctrl+S, escribe un nombre y presiona Enter para guardar tu mapa.

Añadir el editor en el juego a tu escena

Requisitos

  • Un RTS Terrain con un mapa en la escena (GameObject ▸ RTS Terrain Studio ▸ RTS Terrain).
  • Una cámara. De forma predeterminada, el editor usa Camera.main, la cámara con la etiqueta MainCamera. También puedes asignar cualquier otra cámara.
  • La carpeta Resources/RTSTerrainStudio/UI del paquete. Contiene el tema y la hoja de estilos de la interfaz.

Pasos

  1. Abre tu escena y elige GameObject ▸ RTS Terrain Studio ▸ In-Game Editor. Esto crea un objeto llamado RTS In-Game Editor con el componente RTSRuntimeEditor. Si haces clic con el botón derecho en un objeto de la ventana Hierarchy y usas el menú contextual, el nuevo objeto se convierte en hijo de ese objeto.
  2. Si la Console avisa de que no hay ningún RTS Terrain, añade uno. Si avisa de que ninguna cámara tiene la etiqueta MainCamera, etiqueta tu cámara o asígnala a Target Camera.
  3. Si quieres, asigna explícitamente el terreno y la cámara y configura el resto de ajustes (consulta Referencia del Inspector).
  4. Presiona Play. Aparece el editor; si desactivaste Start Visible, presiona F2 para abrirlo.

El elemento de menú está desactivado en el modo Play. Si la escena ya contiene un editor en el juego, Unity pregunta si quieres crear otro (Create Another) o seleccionar el existente (Select Existing). Basta con un editor por escena. El componente también se puede añadir mediante Add Component ▸ RTS Terrain Studio ▸ In-Game Editor, y solo puede haber uno en el mismo GameObject.

Qué crea el componente en tiempo de ejecución

No necesitas construir ninguna interfaz ni ningún sistema de cámara por tu cuenta. Cuando está activado, RTSRuntimeEditor crea:

ObjetoFinalidad
RTSEditSessionLa sesión de edición, disponible como Session. Contiene las herramientas, el pincel, la simetría, la selección y el historial de deshacer, y es la misma clase que usa la ventana del editor en Unity.
Objeto hijo RTS In-Game Editor UIContiene un UIDocument con la interfaz completa del editor. Se destruye al desactivar el componente.
RTSRuntimeEditorCameraSe añade a la cámara de destino cuando Use Built In Camera está activado. Si la cámara ya tiene uno, se usa ese componente. Si fue el propio editor quien añadió el componente de cámara, este se vuelve a quitar al destruirse el editor.
RuntimeToolGizmosDibuja en la vista Game los contornos del pincel, los rectángulos de selección, las vistas previas de decoración, las posiciones iniciales y las guías de simetría. Disponible como Gizmos.
PanelSettingsSe crea cuando dejas vacío el campo Panel Settings. Usa el tema RTS y se escala con la pantalla. Si asignas unos Panel Settings que se escalan con el tamaño de la pantalla y no renderizan en una textura, el editor usa en su lugar una copia de ellos creada en tiempo de ejecución, salvo que Enforce Minimum UI Scale esté desactivado (consulta Personalizar la interfaz).

Cómo se encuentran el terreno y la cámara

  • Terreno: un terreno asignado se usa en cuanto está activado. Si el campo está vacío, el editor usa RTSTerrain.Main, el primer RTS Terrain de la escena. Mientras no encuentra ningún terreno, el editor vuelve a comprobarlo dos veces por segundo y muestra el aviso No RTS Terrain found.
  • Varios terrenos: si la escena contiene más de un RTS Terrain, el menú Map los muestra en Edited terrain. Cambiar a otro terreno borra el historial de deshacer.
  • Cámara: si Target Camera está vacío, el editor usa Camera.main y sigue buscándola. Hasta que exista una cámara, muestra el aviso No camera found.

Referencia del Inspector

AjustePredeterminadoDescripción
Scene
TerrainvacíoEl terreno que se edita. Si está vacío, se usa el primer RTS Terrain de la escena (RTSTerrain.Main).
Target CameravacíoLa cámara que se usa para detectar lo que hay bajo el cursor, para los gizmos y para la cámara integrada del editor. Si está vacío, se usa Camera.main.
Use Built In CameraactivadoAñade un RTSRuntimeEditorCamera a la cámara de destino y la controla mientras el editor está visible. Se desactiva durante las pruebas de juego. Desactiva este ajuste si tu propio script de cámara debe mover la cámara durante la edición.
Behaviour
Start VisibleactivadoMuestra el editor al iniciarse la escena. Si está desactivado, el jugador lo abre con la tecla de mostrar/ocultar, o tu código establece EditorVisible.
Toggle KeyF2Muestra u oculta todo el editor. Elige None para desactivar la tecla. En ese caso, el botón de cerrar de la barra superior se oculta, así que tu juego tiene que abrir y cerrar el editor por su cuenta. En los navegadores web y en teléfonos y tabletas, el botón de cerrar sigue visible, porque allí el botón Open Map Editor vuelve a mostrar el editor.
Start Toolheight.raiseEl id de la herramienta que está activa al inicio, por ejemplo, paint.layer, veg.trees, decor.place o markers.start. Si el id no existe, se usa la primera herramienta.
Show First Time HintactivadoMuestra el mensaje de bienvenida la primera vez que se abre el editor, hasta que el jugador hace clic en Got it o lo cierra. Esa elección se guarda en PlayerPrefs.
Show Quit CommandactivadoAñade File ▸ Quit, que cierra la aplicación. En el Unity Editor, detiene el modo Play. Nunca se muestra en WebGL, iOS ni Android.
Confirm Quit With Unsaved ChangesactivadoPide confirmación antes de cerrar la aplicación si el mapa tiene cambios sin guardar, también cuando la petición de cierre viene de fuera del editor (botón de cerrar de la ventana, Alt+F4, Application.Quit). Primero se muestra el editor y, si hay una prueba de juego en curso, se detiene. Las builds para Web no pueden cerrar la aplicación; en ellas, el navegador pregunta Leave site? antes de que se cierre o se recargue la página.
Undo Memory MB512La memoria máxima que puede usar el historial de deshacer, en megabytes (mínimo 16). Cuando se llena, se descartan los pasos más antiguos. Además, el historial guarda como máximo 100 pasos. Las builds para Web usan como máximo 192 MB, porque el navegador asigna a todo el juego un único heap de memoria limitado.
Manual UrlvacíoLa página web que abre el botón Open Manual de la ventana de ayuda en una build. Si está vacío, el botón se oculta en las builds. En el Unity Editor, un campo vacío abre este manual.
Files
Maps FoldervacíoLa carpeta donde se listan, cargan y guardan los mapas. Si está vacío, se usa la carpeta Maps dentro de la carpeta de almacenamiento RTSMapSerializer.StorageRoot. Una ruta relativa como UserMaps es relativa a la carpeta de almacenamiento. Una ruta absoluta se usa tal cual. La carpeta de almacenamiento es Application.persistentDataPath; en las builds para Web es una carpeta del almacenamiento del navegador que no cambia aunque cambie la dirección de la página (consulta Mapas en el navegador).
Write Preview ImagesactivadoEscribe una pequeña imagen de minimapa (192 × 192 píxeles) junto a cada mapa guardado. El explorador de mapas la muestra.
User Interface
Panel SettingsvacíoPanel Settings de UI Toolkit (opcional). Si está vacío, el editor crea unos Panel Settings en tiempo de ejecución con el tema RTS. Se escalan con el tamaño de la pantalla (resolución de referencia de 1920 × 1080) y nunca se reducen por debajo del 85 %, para que el texto siga siendo legible a 1280 × 720.
Enforce Minimum UI ScaleactivadoMantiene el editor legible en ventanas pequeñas y en el lienzo del navegador. Los Panel Settings asignados que se escalan con el tamaño de la pantalla se copian en tiempo de ejecución (el asset en sí nunca se modifica), y la copia nunca reduce la escala de la interfaz por debajo del 85 %. Desactívalo para usar el asset asignado tal cual, por ejemplo, cuando el editor comparte un mismo panel con el HUD de tu juego.
Ui Scale1Un factor de escala adicional de 0.5 a 2. Se aplica a los Panel Settings que el editor crea en tiempo de ejecución y a la copia en tiempo de ejecución de los Panel Settings asignados.
Sorting Order1000El orden de dibujado de la interfaz del editor. Gracias a su valor predeterminado alto, el editor queda por encima del HUD de tu juego.

Recorrido por la interfaz

La pantalla se divide en cinco áreas. Cada botón, campo y elemento de estado muestra una descripción emergente si mantienes el puntero sobre él un momento, y los botones desactivados también la muestran. Si no tienes claro para qué sirve algo, pasa el puntero por encima.

ÁreaPosiciónContenido
Barra superiorborde superiorMenús File y Map, deshacer y rehacer, simetría, interruptores de vista, Play Test, ayuda, ocultar paneles, cerrar.
Barra de herramientasborde izquierdoOcho botones de categoría (teclas del 1 al 8) y las herramientas de la categoría seleccionada.
Panel de herramientaborde derechoNombre y descripción de la herramienta activa, pincel, simetría, los ajustes de la herramienta y comandos adicionales. Se puede contraer.
Barra de estadoborde inferiorInformación sobre el punto bajo el cursor, el estado de la herramienta, el pincel, la memoria de deshacer y el nombre del mapa.
Elementos superpuestossobre el mapaNotificaciones emergentes, descripciones emergentes, cuadros de diálogo, el mensaje de bienvenida, la barra de prueba de juego y avisos.

Barra superior

ControlQué hace
FileAbre el menú File (consulta más abajo).
MapAbre el menú Map (consulta más abajo).
Undo / RedoDeshacer (Ctrl+Z) y rehacer (Ctrl+Y). Sus descripciones emergentes muestran el nombre del paso, por ejemplo, Undo "Raise / Lower". Los botones aparecen en gris cuando no hay nada que deshacer o rehacer.
Symmetry: OffAbre el menú de simetría. Con la simetría activada, cada trazo se refleja o se rota, lo que facilita crear mapas competitivos equilibrados. El botón muestra el modo actual, por ejemplo, Symmetry: 180°.
GridMuestra u oculta la cuadrícula de celdas (G).
PathingAbre el menú de superposición de pathing. Cuando hay una superposición activa, el botón muestra su modo, por ejemplo, Pathing: Walkability. P activa la superposición con el último modo usado o la desactiva.
MarkersMuestra u oculta las posiciones iniciales, las regiones y los marcadores mientras hay otras herramientas activas (M). Está activado de forma predeterminada.
Play TestInicia una prueba de juego (F5).
Help (?)Abre la ventana Shortcuts & Help (F1).
EyeOculta los paneles para tener una vista despejada del mapa (H). La edición sigue funcionando. Un pequeño botón Panels hidden - press H to show them los vuelve a mostrar.
Close (×)Oculta el editor y muestra una notificación emergente con la tecla que lo vuelve a abrir. Este botón no aparece cuando Toggle Key es None. En los navegadores web y en teléfonos y tabletas, donde las teclas de función no son fiables, además muestra un botón Open Map Editor abajo a la izquierda y está disponible aunque no haya tecla de mostrar/ocultar (consulta El editor en el juego en el navegador).

Menú File

ElementoAtajoQué hace
New Map...Ctrl+N (Alt+N en los navegadores web)Abre el cuadro de diálogo New Map. Si el mapa actual tiene cambios sin guardar, el editor pregunta primero. Alt+N funciona en todas las plataformas.
Open...Ctrl+OAbre el explorador de mapas para que elijas un mapa. Si el mapa actual tiene cambios sin guardar, el editor pregunta primero.
SaveCtrl+SGuarda en el archivo del que procede el mapa. La primera vez, abre Save Map As para pedir un nombre.
Save As...Ctrl+Shift+SAbre el explorador de mapas para que guardes con un nombre nuevo.
Download MapSolo en builds para Web. Guarda una copia del mapa, incluidos los cambios sin guardar, como archivo .rtsmap en el equipo del jugador (normalmente en la carpeta de descargas). Consulta Mapas en el navegador.
Import Map...Solo en builds para Web. Abre un archivo .rtsmap del equipo del jugador. Se añade a los mapas guardados en este navegador y se abre.
Show Maps FolderAbre la carpeta de mapas en el explorador de archivos del sistema operativo. Este elemento solo existe en Windows, macOS y Linux, en el Unity Editor y en las builds de escritorio.
QuitCierra la aplicación, pero antes pregunta qué hacer con los cambios sin guardar. Solo se muestra cuando Show Quit Command está activado, y nunca en WebGL, iOS ni Android.

Menú Map

ElementoQué hace
Map Info...Nombre, autor, descripción y número de jugadores recomendado.
Resize...Agranda o reduce el mapa.
Generate...Crea una distribución aleatoria del mapa a partir de una semilla. Puedes deshacerlo en un solo paso.
Frame Whole MapMueve la cámara del editor para que se vea el mapa entero. Solo está disponible con la cámara integrada.
Edited terrainSolo se muestra cuando la escena tiene más de un RTS Terrain. Elige qué terreno editas. Cambiar de terreno borra el historial de deshacer.

Menú de simetría

ModoCopiasUso típico
Off1Pintar sin simetría.
Mirror Left / Right2Refleja respecto a la línea central vertical.
Mirror Top / Bottom2Refleja respecto a la línea central horizontal.
Mirror 4-Way4Refleja en ambos ejes.
Rotate 180°2Simetría central respecto al centro, típica de los mapas 1v1.
Rotate 90° (4 players)4Simetría rotacional para mapas de cuatro jugadores. Requiere un mapa cuadrado; en un mapa no cuadrado, la entrada aparece en gris.
Mirror Diagonal2Refleja respecto a la diagonal. Requiere un mapa cuadrado; en un mapa no cuadrado, la entrada aparece en gris.

Reset Centre devuelve el centro de simetría al medio del mapa. La sección Symmetry del panel de herramienta tiene los controles deslizantes Centre X y Centre Z para mover el centro. Unas líneas guía moradas sobre el terreno muestran los ejes de simetría.

Menú de superposición de pathing

La superposición colorea el terreno según lo que pueden hacer allí las unidades. Elige Off, Walkability (verde = transitable, rojo = obstruido), Buildability, Flyability, Water (agua poco profunda y profunda) o Combined (celdas transitables, edificables, de agua y obstruidas en una sola vista). Las entradas Opacity fijan la opacidad de la superposición en 30 %, 55 % (predeterminado) u 80 %.

Barra de herramientas (izquierda)

La columna superior tiene un botón por cada categoría de herramientas. El pequeño número de cada botón es su tecla. La columna inferior muestra las herramientas de la categoría seleccionada con su icono y su nombre. Pasa el puntero sobre una herramienta para ver su descripción y su sugerencia de uso.

TeclaCategoríaDescripciónHerramientas
1HeightSube, baja, suaviza y modela el suelo.Raise / Lower, Smooth, Flatten, Set Height, Noise, Terrace, Erode
2CliffsNiveles de acantilado al estilo de los RTS clásicos, rampas y materiales de acantilado.Raise / Lower Level, Set Level, Ramp, Cliff Type, Clean Cliffs
3PaintPinta texturas de suelo.Paint Layer, Tile Paint, Fill / Replace, Smooth Paint, Auto Paint
4VegetationHierba, flores y árboles.Paint Details, Erase Details, Place Trees, Erase Trees
5DecorColoca, borra y selecciona objetos (doodads).Place Decor, Erase Decor, Select Decor
6WaterLagos, ríos y mares.Paint Water, Erase Water, Flood Fill
7PathingDecide dónde pueden caminar, construir y volar las unidades.Paint Pathing, Clear Pathing
8MarkersPosiciones iniciales, regiones y marcadores con nombre.Start Locations, Regions, Markers

El editor recuerda la última herramienta que usaste en cada categoría. Al elegir una categoría con su botón o su tecla numérica, se vuelve a activar esa herramienta. Si presionas la tecla numérica de la categoría en la que ya estás, pasas a la siguiente herramienta de esa categoría. Las herramientas personalizadas que registres aparecen aquí automáticamente, con una interfaz de ajustes generada a partir de sus campos [ToolSetting].

Panel de herramienta (derecha)

La cabecera muestra el icono y el nombre de la herramienta activa. Pasa el puntero sobre el nombre para leer la descripción de la herramienta. El botón de flecha contrae el panel. Debajo de la cabecera, el panel tiene secciones plegables. Las secciones que contraigas siguen contraídas cuando cambias de herramienta.

SecciónSe muestra paraContenido
(acerca de)todas las herramientasLa descripción y la sugerencia de uso de la herramienta, por ejemplo, Click: place / move · Shift+click: remove the nearest.
Brushherramientas de pincelShape (Circle, Square, Diamond y Stamp cuando hay un sello personalizado definido), Size (un control deslizante logarítmico más un campo en metros), Strength, Hardness, Rotation, Random Rotation, Spacing, Jitter y un botón Reset en la cabecera de la sección. Todas las herramientas comparten el mismo pincel.
Symmetrytodas las herramientasModo de simetría, Centre X y Centre Z, y una advertencia cuando el modo requiere un mapa cuadrado.
Settingsherramientas con opcionesLas opciones de la herramienta activa, como la altura objetivo, las paletas de texturas, las miniaturas de árboles y de decoración, el nivel de acantilado o el jugador.
Actionsalgunas herramientasComandos adicionales: Apply to Whole Map (Auto Paint), New Random Layout (Place Trees, Place Decor) y otros similares.
SelectionSelect DecorDelete, Duplicate, rotar (−15°, +15°, +90°), desplazar media celda (−X, +X, −Z, +Z), Smaller / Larger (10 %), Snap (ajustar al suelo), Lock / Unlock, Select All, Clear.

Tanto el panel como la barra de estado muestran There is no map to edit. cuando el terreno no tiene ningún mapa.

Barra de estado (abajo)

ElementoEjemploSignificado
CeldaCell 42, 17La celda bajo el cursor (x, z).
AlturaHeight 3.5 mAltura del suelo bajo el cursor, en metros.
NivelLevel 2El nivel de acantilado bajo el cursor (de 0 a 15).
PathingWalkable, BuildableLo que pueden hacer aquí las unidades, por ejemplo, Shallow water, Cliff, Too steep, Blocked o No flying.
AguaWater 1.2 m o DryProfundidad del agua bajo el cursor.
Estado de la herramientaInformación de la herramienta activa, o su sugerencia de uso.
PincelBrush 8.0 m 50 %Radio e intensidad del pincel.
DeshacerUndo 12 4.2 MBEl número de pasos de deshacer y la memoria que ocupan.
Nombre del mapa● SandboxEl nombre del mapa. Un punto indica que hay cambios sin guardar, y Ctrl+S los guarda.

Notificaciones emergentes, descripciones emergentes y avisos

  • Las notificaciones emergentes son mensajes breves que aparecen sobre la barra de estado y se desvanecen solas, por ejemplo, Saved "Sandbox" o Undo: Raise / Lower. Hay cuatro tipos. Info y Success permanecen unos 3 segundos, Warning 5 segundos y Error 6.5 segundos. Haz clic en una notificación para descartarla. Se muestran como máximo cuatro a la vez. Las notificaciones siguen visibles mientras el editor está oculto, para que se pueda leer el mensaje Editor closed. Press F2 to open it again.
  • Open Map Editor: en los navegadores web y en teléfonos y tabletas, un botón abajo a la izquierda que aparece después de usar el botón de cerrar y vuelve a abrir el editor. Si hay teclado, muestra la tecla de mostrar/ocultar, por ejemplo, Open Map Editor (F2).
  • Las descripciones emergentes aparecen al cabo de medio segundo aproximadamente sobre cualquier control, incluso sobre los desactivados y durante una prueba de juego. Un clic oculta la descripción hasta que el puntero sale del control.
  • Mensaje de bienvenida: un pequeño panel con los cuatro controles más importantes y los botones All Shortcuts (F1) y Got it. Puedes volver a mostrarlo con Open Welcome Hint en la ventana de ayuda.
  • Avisos: No RTS Terrain found o No camera found aparecen en el centro de la pantalla cuando el editor no puede funcionar, con una breve explicación de lo que hay que corregir.
  • Barra de prueba de juego: durante una prueba de juego, una pequeña barra en la parte superior central muestra Play test running y tiene un botón Back to Editor (Esc or F5).

Cuadros de diálogo

Los cuadros de diálogo son modales: mientras hay uno abierto, no se puede editar el mapa y los atajos están desactivados. Enter ejecuta el botón resaltado (principal) y Esc cierra el cuadro de diálogo.

Cuadro de diálogoSe abre conContenido
New MapFile ▸ New Map...Name; presets de Size: Tiny (64 × 64), Small (96), Medium (128), Large (192), Huge (256), Epic (384), Giant (512); Width y Depth (de 16 a 1024 celdas, redondeados a múltiplos de 16; en los navegadores web el límite es menor, consulta Límites); Cell Size de 1, 2 o 4 m (2 m corresponde a la escala de los mapas de los RTS clásicos); Base Level (nivel de acantilado de 0 a 15; 2 deja margen para excavar lagos y ríos); Base Layer (la textura de suelo). Una línea debajo muestra el tamaño resultante en metros. Crear un mapa nuevo no se puede deshacer, así que guarda antes.
Generate MapMap ▸ Generate...Style (restablece las opciones de forma), Seed con un botón Random, Symmetry; sección Terrain: Cliffs, Plateaus, Ramps, Ramp Count, Water, Lake Coverage, Island, Border; sección Content: Textures, Vegetation, Forests, Decor, Decor Count, Start Locations, Players. Sustituye el contenido del mapa actual en un solo paso de deshacer. Los ajustes se conservan para la próxima vez que se abra el cuadro de diálogo.
Map InfoMap ▸ Map Info...Name, Author, Description (se muestra en el explorador de mapas), Players (de 1 a 16), además de estadísticas: tamaño, posiciones iniciales, árboles, objetos y regiones. Aplicar los cambios cuenta como un solo paso de deshacer.
Resize MapMap ▸ Resize...Width y Depth (múltiplos de 16, con el mismo límite en el navegador que en New Map), Anchor (una cuadrícula de 3 × 3: qué parte del mapa se queda en su sitio), Fill Level y Fill Layer para las zonas nuevas. Cambiar el tamaño borra el historial de deshacer. Cuando el mapa se reduce, se elimina el contenido que queda fuera de la nueva área.
Open Map / Save Map AsFile ▸ Open... / Save As...El explorador de mapas.
Unsaved ChangesNew, Open o Quit con cambios sin guardarSave (guarda primero y pide un nombre si el mapa no tiene ninguno), Don't Save, Cancel.
Shortcuts & HelpF1 o el botón ?Todos los controles de ratón y teclado (consulta Controles y atajos), Open Welcome Hint, Open Manual (cuando hay un manual disponible) y Close.

El explorador de mapas

File ▸ Open... (Ctrl+O) y File ▸ Save As... (Ctrl+Shift+S) abren el mismo explorador, con el título Open Map o Save Map As. Muestra todos los archivos .rtsmap de la carpeta de mapas.

ParteDescripción
Línea de carpetaLa ruta completa de la carpeta de mapas. Refresh vuelve a leer la carpeta, por ejemplo, después de copiar mapas en ella. En las builds web, la línea muestra el texto Saved in this browser. Use Download to keep a copy on your computer. y, en el explorador de Open, incluye un botón Import... (consulta Mapas en el navegador).
Lista de mapasUna fila por mapa con su nombre, tamaño, número de jugadores y fecha de última modificación; los más recientes aparecen primero. Los archivos que no se pueden leer se marcan como Unreadable or damaged file y no se pueden abrir. Pasa el puntero sobre una fila para ver la ruta de su archivo (en las builds web, solo el nombre del archivo).
DetallesLa vista previa del minimapa, el nombre, el tamaño en celdas y en metros, el autor, los jugadores, la fecha de modificación y la descripción del mapa seleccionado. La vista previa solo existe si Write Preview Images estaba activado cuando se guardó el mapa.
Map NameSolo en Save As. El nombre del archivo se genera a partir de este nombre. Al hacer clic en un mapa de la lista, su nombre se copia en el campo, así que puedes sobrescribir ese mapa.
DeleteElimina el archivo del mapa seleccionado y su imagen de vista previa tras pedirte confirmación. Esta acción no se puede deshacer.
DownloadSolo en builds web, en el explorador de Open. Guarda una copia del mapa seleccionado como archivo .rtsmap en el equipo del jugador.
Cancel / Open / SaveOpen carga el mapa seleccionado. Hacer doble clic en una fila o presionar Enter tiene el mismo efecto. Save guarda el mapa con el nombre introducido.

El explorador solo lee la pequeña cabecera de cada archivo, así que se abre rápidamente incluso cuando la carpeta contiene muchos mapas grandes.

Guardar con otro nombre

  1. Presiona Ctrl+Shift+S. El campo de nombre ya contiene el nombre del mapa actual.
  2. Escribe un nombre nuevo, por ejemplo, Glacier Pass (2v2), y presiona Enter.
  3. Si ya existe otro archivo con ese nombre, el editor pregunta Replace Map?. Cancel es la opción predeterminada al presionar Enter, así que tienes que hacer clic en Replace para sobrescribir el archivo.
  4. El mapa se guarda, se genera una imagen de vista previa y una notificación emergente confirma Saved "Glacier Pass (2v2)". A partir de ese momento, Ctrl+S guarda en este archivo sin preguntar.

Si guardas con otro nombre, también cambia el nombre del propio mapa (el que se muestra en Map Info). Este cambio de nombre es un paso de deshacer normal llamado Rename Map.

Controles y atajos

La misma lista está disponible en el editor con F1. En macOS, puedes usar Cmd siempre que aparezca Ctrl. Por teclas numéricas se entiende la fila de números del teclado principal. Los atajos de letras siguen la letra impresa en la tecla, así que Ctrl+Z es la tecla marcada con la Z también en los teclados alemanes (QWERTZ) y franceses (AZERTY). Las teclas del pincel siguen el carácter que escriben, así que [ y ] funcionan allí donde tu teclado los tenga, por ejemplo, con AltGr. En los navegadores web, algunas teclas se comportan de otra forma; consulta El editor en el juego en el navegador.

Ratón

EntradaAcción
Botón izquierdo del ratónPinta o usa la herramienta activa. Mantenlo presionado y arrastra para hacer un trazo. Un trazo completo es un solo paso de deshacer.
Shift + botón izquierdo del ratónLa acción contraria: bajar en lugar de subir, borrar en lugar de pintar, quitar en lugar de colocar.
Ctrl + clic izquierdoToma un valor del terreno (altura, capa de suelo, nivel de acantilado...) en las herramientas que lo admiten.
Arrastrar con el botón derecho o central del ratónDesplaza la cámara («agarrar el suelo»).
Alt + arrastrar con el botón izquierdo o derecho del ratónGira la cámara. Mientras se mantiene presionada Alt, el botón izquierdo no pinta.
Rueda del ratónAcerca y aleja el zoom en dirección al punto que está bajo el cursor.

Cámara (cámara integrada del editor)

TeclaAcción
W A S D o las teclas de flechaDesplaza la cámara. Mantén presionada Shift para desplazarla más rápido.
Q / EGira a la izquierda / a la derecha.
FCentra la cámara en el punto que está bajo el cursor.

Pincel y herramientas

TeclaAcción
[ / ]Pincel más pequeño / más grande (alrededor de un 15 % por pulsación).
- / = (o +)Menor / mayor intensidad (5 % por pulsación). También funcionan - y + del teclado numérico.
1 a 8Selecciona una categoría de herramientas (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers). Vuelve a presionar la tecla para pasar a la siguiente herramienta de la categoría.
EscCancela el trazo que estás pintando. El terreno vuelve al estado en que estaba cuando presionaste el botón.

Archivo e historial

TeclaAcción
Ctrl+N o Alt+NNuevo mapa. En los navegadores web solo funciona Alt+N, porque el navegador reserva Ctrl+N para abrir una ventana nueva.
Ctrl+OAbrir un mapa (explorador de mapas).
Ctrl+SGuardar. La primera vez pide un nombre.
Ctrl+Shift+SGuardar como.
Ctrl+ZDeshacer.
Ctrl+Y o Ctrl+Shift+ZRehacer.

Vista y editor

TeclaAcción
GMuestra u oculta la cuadrícula de celdas.
PActiva la superposición de pathing (con el último modo usado; al principio, Combined) o la desactiva.
MMuestra u oculta las posiciones iniciales, las regiones y los marcadores.
HOculta o muestra los paneles del editor.
F1Shortcuts & Help.
F5Inicia una prueba de juego o termina la que está en curso.
Esc (durante una prueba de juego)Termina la prueba de juego y vuelve al editor.
F2 (la tecla Toggle Key)Muestra u oculta todo el editor. Esta tecla no hace nada durante una prueba de juego.

Selección de decoración (solo con la herramienta Select Decor)

EntradaAcción
Clic / arrastrar un rectánguloSelecciona objetos.
Shift + clic / rectánguloAñade objetos a la selección o los quita de ella.
Arrastrar un objeto seleccionadoMueve la selección. Todo el arrastre es un solo paso de deshacer. Si presionas Esc durante el arrastre, los objetos vuelven a su sitio. Los objetos bloqueados no se pueden arrastrar.
Clic en un objeto que forma parte de una selección de varios objetosSelecciona solo ese objeto.
Delete / BackspaceElimina los objetos seleccionados.
Ctrl+DDuplica la selección con un desplazamiento de una celda en diagonal (+X y +Z).
Ctrl+ASelecciona todos los objetos desbloqueados.
EscAnula la selección.

Qué hace Esc

Esc ejecuta la primera de estas acciones que corresponda:

  1. Cierra un menú emergente abierto.
  2. Quita el foco del teclado de un campo de texto o de un control deslizante.
  3. Cierra el cuadro de diálogo que está en primer plano.
  4. Cancela el arrastre de objetos decorativos (los objetos vuelven a su sitio).
  5. Cancela el trazo que estás pintando (notificación emergente Stroke cancelled).
  6. Anula la selección de decoración (herramienta Select Decor).
  7. Vuelve a mostrar los paneles si se ocultaron con H.

¿Los atajos no hacen nada? Mientras un campo de texto tiene el foco del teclado, las teclas de letras se escriben en el campo. Mientras hay un cuadro de diálogo abierto, solo funcionan Enter y Esc. Haz clic en el mapa o presiona Esc y vuelve a intentarlo. Además, mientras el puntero está sobre un panel, no se puede pintar ni hacer zoom con la rueda del ratón.

La cámara integrada del editor

Con Use Built In Camera activado, el editor añade un componente RTSRuntimeEditorCamera a la cámara de destino. Esta cámara orbital gira alrededor de un punto de enfoque en el suelo. Se mantiene dentro de los límites del terreno más un margen, nunca queda por debajo del suelo y se inclina más hacia abajo cuanto más alejas el zoom. Usa tiempo no escalado, así que sigue funcionando mientras tu juego está en pausa (Time.timeScale = 0). Mientras el editor está visible, activa la cámara; mientras está oculto y durante las pruebas de juego, la desactiva. Después de una prueba de juego, la cámara vuelve a la posición que tenía antes.

Para cambiar su comportamiento, añade tú mismo RTSRuntimeEditorCamera a tu cámara (Add Component ▸ RTS Terrain Studio ▸ In-Game Editor Camera) y configura los campos. El editor usa el componente que ya existe en lugar de añadir uno nuevo.

AjustePredeterminadoDescripción
Movement
Pan Speed18Velocidad de desplazamiento con el teclado, en metros por segundo, a la altura mínima. Aumenta con la altura.
Fast Multiplier2.5Multiplicador de la velocidad de desplazamiento mientras se mantiene presionada Shift.
Edge PandesactivadoDesplaza la cámara cuando el ratón toca el borde de la pantalla.
Edge Pan Margin12Ancho del borde de la pantalla para el desplazamiento por bordes, en píxeles.
Rotate Speed100Velocidad de giro de Q / E, en grados por segundo.
Mouse Rotate Speed0.25Giro al arrastrar el ratón con Alt, en grados por píxel.
Zoom
Min Height8Altura mínima de la cámara sobre el punto de enfoque, en metros.
Max Height120Altura máxima de la cámara sobre el punto de enfoque, en metros.
Zoom Step0.14Cambio de altura por cada paso de la rueda del ratón, como fracción de la altura actual.
Zoom Towards CursoractivadoHace zoom hacia el punto que está bajo el cursor en lugar de hacia el centro de la pantalla.
Min Pitch40Inclinación en grados por debajo del horizonte a la altura mínima.
Max Pitch70Inclinación a la altura máxima.
Pitch Curveease in-outTransición de Min Pitch (0) a Max Pitch (1) según la altura normalizada.
Limits & Smoothing
Ground Clearance3Distancia mínima permitida entre la cámara y el suelo que tiene debajo, en metros.
Bounds Padding8Espacio adicional alrededor del terreno hacia el que puede desplazarse el punto de enfoque, en metros.
Smooth Time0.1Tiempo de suavizado en segundos (0 = sin suavizado).

Desde el código, RTSRuntimeEditor.EditorCamera devuelve el componente, o null cuando la cámara integrada está desactivada:

MiembroDescripción
void Focus(Vector3 worldPosition, bool instant = false)Mueve el punto de enfoque a una posición del mundo, de forma suave salvo que instant sea true.
void FrameTerrain(bool instant = false)Centra la cámara sobre el terreno y aleja el zoom para mostrar la mayor parte de este. Los mapas nuevos y los redimensionados se encuadran automáticamente.
void SnapToTargets()Salta a la posición, la rotación y la altura de destino sin suavizado.
Vector3 FocusPoint, float Yaw, float HeightLa órbita actual (solo lectura).
bool IsDragging, bool WantsAltDragSon true mientras se arrastra la cámara o mientras se mantiene presionada Alt.
bool PointerBlocked, bool KeyboardBlockedEl editor los establece en cada fotograma mientras el puntero está sobre la interfaz, un campo de texto tiene el foco o hay un cuadro de diálogo abierto.
RTSTerrain TerrainEl terreno que se usa para los límites y la altura del suelo.

Usar tu propio script de cámara durante la edición: desactiva Use Built In Camera. Así, el editor solo lee la cámara de destino para detectar lo que hay bajo el puntero y para dibujar los gizmos. Asegúrate de que tu cámara ignore la entrada mientras RTSRuntimeEditor.IsPointerOverUI sea true, para que, al desplazarte por un panel, la cámara no haga zoom también.

Play Test: probar el mapa en tu juego

Play Test (F5) convierte el editor en tu juego por un momento: la interfaz del editor deja paso, tu código de juego genera unidades y activa su cámara, y el jugador puede probar el mapa. Esc, F5 o Back to Editor termina la prueba, y el mapa vuelve exactamente al estado en que estaba antes. Desaparecen las unidades y los edificios, y se deshacen la tala de árboles y los cambios de pathing.

Una prueba de juego dentro del editor en el juego, con unidades, minimapa, HUD y panel de controles
Una prueba de juego iniciada desde el editor en el juego: el juego de demostración genera unidades, el minimapa muestra la niebla de guerra y con Esc se vuelve al editor.

El editor no sabe nada de tu juego. Comunica el inicio y el final de una prueba de juego a través de la clase estática RTSRuntimeEditorEvents, y tu juego decide qué hacer. El componente de ejemplo RTSDemoGame (GameObject ▸ RTS Terrain Studio ▸ Demo Game) ya escucha sus eventos. Añádelo a la escena de tu editor para tener unidades, niebla de guerra, minimapa y HUD durante las pruebas de juego sin escribir código.

Qué ocurre paso a paso

CuándoEl editor...
Al presionar Play Test
  1. termina el trazo que estás pintando,
  2. toma una copia completa (instantánea) del mapa y recuerda la ruta del archivo y el estado de cambios sin guardar,
  3. cierra los cuadros de diálogo y los menús, y oculta la vista previa del pincel, los gizmos, la cuadrícula y la superposición de pathing,
  4. desactiva su cámara integrada y sustituye los paneles por la pequeña barra de prueba de juego,
  5. lanza RTSRuntimeEditorEvents.PlaytestChanged(true). Tu juego se inicia.
Durante la prueba de juegoIgnora toda la entrada de edición, la acción de deshacer y los comandos de archivo (que muestran Stop the play test first.), así como la tecla que muestra u oculta el editor. El código de juego puede modificar el mapa libremente.
Esc, F5 o Back to Editor
  1. primero lanza RTSRuntimeEditorEvents.PlaytestChanged(false), para que tu juego pueda hacer limpieza mientras el mapa de la prueba sigue cargado,
  2. copia la instantánea de vuelta en el mapa, lo que reconstruye el terreno,
  3. restaura la ruta del archivo y el estado de cambios sin guardar, los paneles, la vista del editor y la cámara integrada,
  4. muestra la notificación emergente Play test ended: the map was restored.

Historial de deshacer: el editor copia la instantánea de vuelta en la misma instancia del mapa, así que tu historial de deshacer se conserva tras las pruebas de juego. Si durante la prueba el código de juego sustituyó, recargó o redimensionó el mapa, la instantánea se carga en su lugar como un mapa nuevo. En ese caso, la notificación emergente dice (undo history was reset).

Referencia de RTSRuntimeEditorEvents

RTSRuntimeEditorEvents se encuentra en el núcleo de tiempo de ejecución (Runtime/Core). Tu código de juego se compila y funciona aunque quites el editor en el juego de un proyecto.

MiembroDescripción
static bool IsPlaytestingEs true mientras se está ejecutando una prueba de juego.
static event Action<bool> PlaytestChangedSe lanza cuando una prueba de juego empieza (true) o termina (false).
static event Action<RTSTerrain> MapOpenedSe lanza después de que el editor en el juego abra un mapa desde un archivo o cree un mapa nuevo.
static void ConsumeEscape()Llama a este método en un fotograma en el que tu código de juego haya usado Esc por su cuenta, por ejemplo, para cancelar la colocación de un edificio. Así, el editor no termina la prueba de juego en ese fotograma.
static bool EscapeConsumedThisFrameEs true cuando se ha llamado a ConsumeEscape en el fotograma actual.
static void SetPlaytesting(bool playtesting)Establece IsPlaytesting y lanza PlaytestChanged. Lo llama el editor. Para iniciar una prueba desde tu propio código, llama en su lugar a RTSRuntimeEditor.StartPlaytest(), porque solo el editor toma la instantánea del mapa.
static void RaiseMapOpened(RTSTerrain terrain)Lanza MapOpened. Lo llama el editor.

Los eventos se vacían cuando se inicia el modo Play. Suscríbete en OnEnable (o Awake / Start) de un objeto de la escena, no en un constructor estático ni en un script de editor, y cancela la suscripción en OnDisable.

Ejemplo: generar unidades para una prueba de juego

Este componente genera una unidad por cada posición inicial cuando empieza una prueba de juego y activa tus sistemas de juego (cámara RTS, niebla de guerra, HUD). Cuando termina la prueba, elimina las unidades y vuelve a desactivar los sistemas. Añádelo a cualquier objeto de la escena del editor y asígnale un prefab de unidad.

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 es un Unity.Mathematics.float3, que se convierte automáticamente a Vector3. Si tu script está en su propia definición de ensamblado (assembly definition), añade referencias a RTSTerrainStudio.Runtime y Unity.Mathematics.

Ejemplo: compartir la tecla Esc con tu juego

Muchos juegos RTS usan Esc para cancelar una orden o la colocación de un edificio. Llama a ConsumeEscape() en el fotograma en el que uses la tecla y el editor no terminará la prueba de juego. El editor lo comprueba en LateUpdate, así que no importa el orden en que se ejecuten tus scripts.

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
        }
    }
}

Los scripts de ejemplo RTSBuildingPlacementDemo, RTSCommandController y RTSSelectionManager ya lo hacen. En la demo, la primera pulsación de Esc anula la selección y la siguiente vuelve al editor.

Prueba de juego: lista de comprobación para tu propio juego

  • Deja el script de tu cámara RTS, el gestor de la niebla de guerra y el HUD desactivados en la escena y actívalos en PlaytestChanged(true). La escena de demostración hace lo mismo con RTSCameraController y RTSFogOfWarManager.
  • La cámara integrada del editor está desactivada durante la prueba, así que tu script de cámara tiene el control total de la cámara.
  • Elimina en PlaytestChanged(false) todo lo que hayas generado. No hace falta deshacer los cambios del mapa, porque el editor lo restaura.
  • En multijugador (integración con Mirror), las ediciones del mapa hechas durante una partida en curso no se transmiten en directo. Llama a RTSNetworkMapSync.SendMapToAll() para volver a enviar el mapa.

Guardar y cargar mapas en las builds

Dónde se guardan los mapas

Los mapas se guardan en RTSRuntimeEditor.MapsFolder. De forma predeterminada, es la carpeta Maps dentro de RTSMapSerializer.StorageRoot, también disponible como RTSMapSerializer.DefaultMapsFolder. StorageRoot es Application.persistentDataPath en todas las plataformas excepto en la web. La carpeta se crea al guardar por primera vez. Unity elige persistentDataPath según la plataforma. En Windows está en %USERPROFILE%\AppData\LocalLow\<Company>\<Product>; los nombres de la empresa y del producto se toman de los Player Settings. En plataformas de escritorio, File ▸ Show Maps Folder abre la carpeta, y el explorador de mapas muestra la ruta completa.

En las builds web, los mapas se guardan en el almacenamiento del navegador: File ▸ Download Map los descarga al equipo del jugador y File ▸ Import Map... los importa desde él. Consulta Mapas en el navegador.

Cada mapa tiene hasta dos archivos:

ArchivoEjemploDescripción
MapaGlacier Pass (2v2).rtsmapEl mapa completo: alturas, acantilados, texturas, agua, vegetación, decoración, pathing, posiciones iniciales, regiones, marcadores, información del mapa y entorno. Usa el mismo formato que los mapas en el Unity Editor.
Vista previaGlacier Pass (2v2).preview.pngUna imagen de minimapa de 192 × 192 para el explorador de mapas. Solo se guarda cuando la opción Write Preview Images está activada, y se elimina junto con el mapa.

El nombre del archivo se forma a partir del nombre del mapa. Los caracteres no permitidos en nombres de archivo se convierten en _, se eliminan los puntos del final y un nombre vacío se sustituye por New Map.

Cambios sin guardar

  • La barra de estado muestra un punto delante del nombre del mapa mientras haya cambios sin guardar.
  • New Map, Open y Quit preguntan antes qué hacer: Save, Don't Save o Cancel.
  • Si el jugador cierra la ventana del juego mientras el mapa tiene cambios sin guardar, el editor cancela el cierre y también pregunta, salvo que la opción Confirm Quit With Unsaved Changes esté desactivada. En un navegador web, en cambio, es el propio navegador el que pregunta Leave site? antes de que se cierre o se recargue la pestaña; este aviso también depende de esa misma opción.
  • Una prueba de juego no cambia el estado de cambios sin guardar. El editor lo restaura junto con el mapa.
  • El mapa con el que empieza la escena (el asset de mapa asignado al terreno) todavía no tiene archivo en la carpeta de mapas, así que la primera vez que presiones Ctrl+S se te pedirá un nombre.

Pruebas en el Unity Editor: si, en el modo Play, editas un mapa con el editor en el juego y luego sales del modo Play, Unity pregunta Save map changes made in Play Mode?. El botón Save guarda los cambios en el asset de mapa del terreno, dentro de tu proyecto. En cambio, los mapas guardados con Ctrl+S van a la carpeta de mapas. Puedes copiar uno de esos archivos .rtsmap a tu carpeta Assets para usarlo como asset de mapa.

Distribuir mapas con tu juego

El explorador de mapas solo muestra los mapas de la carpeta de mapas. Para dar a los jugadores tus propios mapas como punto de partida, cópialos allí la primera vez que se inicie el juego. Este ejemplo copia todos los archivos de StreamingAssets/Maps (mapas e imágenes de vista previa) y nunca sobrescribe la versión editada por un jugador:

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);
        }
    }
}

Jugar mapas creados por jugadores en tu juego

Tu juego puede cargar mapas de la carpeta de mapas sin el editor, por ejemplo, en un lobby de partidas personalizadas. RTSMapSerializer.TryReadHeaderFromFile lee el nombre, el tamaño, el autor y el número de jugadores sin cargar el mapa completo. RTSTerrain.LoadFromFile carga el mapa.

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;
        }
    }
}

Un mapa guarda su contenido por id (capas de suelo y prototipos de vegetación y de decoración). Los mapas creados por jugadores solo se ven correctamente con el mismo tileset, la misma biblioteca de vegetación y la misma biblioteca de decoración que usa el terreno de la escena del editor, así que asigna esos mismos assets al terreno de las escenas de tu juego.

Controlar el editor desde código

Todo lo que se hace en la interfaz también se puede hacer desde código mediante RTSRuntimeEditor (espacio de nombres RTSTerrainStudio). Estos son los miembros más útiles:

MiembroDescripción
Estado
bool EditorVisible { get; set; }Muestra u oculta todo el editor (igual que la tecla para mostrarlo u ocultarlo). Al ocultarlo se termina el trazo en curso, se cierran los cuadros de diálogo y la vista del terreno vuelve al juego.
bool PanelsVisible { get; set; }Oculta solo los paneles (H). La edición sigue funcionando.
bool IsPlaytesting { get; }Es true mientras se ejecuta una prueba de juego iniciada por este editor.
RTSKey ToggleKey { get; set; }La tecla para mostrar u ocultar el editor. RTSKey.None la desactiva y oculta el botón de cerrar (salvo en navegadores web y en teléfonos y tabletas; consulta HasReliableFunctionKeys).
RTSTerrain Terrain { get; set; }El terreno que se edita. Al asignarlo se cambia de terreno y se borra el historial de deshacer.
Camera TargetCamera { get; set; }La cámara que se usa para detectar lo que hay bajo el puntero y para los gizmos.
RTSRuntimeEditorCamera EditorCamera { get; }La cámara integrada, o null.
RTSEditSession Session { get; }La sesión de edición: herramientas, pincel, simetría, selección, deshacer.
ToolCategory ActiveCategory { get; }La categoría de la herramienta activa.
bool HasHover, Vector3 HoverWorldPositionIndican si el puntero está sobre el terreno y cuál es la posición en el mundo que queda debajo de él.
bool IsPointerOverUI { get; }Es true mientras el puntero está sobre un panel del editor.
bool ShowGrid, bool ShowMarkersLas opciones que se activan o desactivan con G y M.
PathingOverlayMode? PathingOverlay, float OverlayOpacityLa superposición de pathing (null = desactivada) y su opacidad (de 0 a 1).
string MapsFolder { get; set; }La carpeta de mapas. Si está vacía, se usa RTSMapSerializer.DefaultMapsFolder; una ruta relativa se interpreta respecto a RTSMapSerializer.StorageRoot.
bool ShowQuitCommand, string ManualUrlEl elemento de menú File ▸ Quit y el enlace al manual (consulta el Inspector).
bool HasManual { get; }Es true cuando Open Manual tiene una página que abrir.
static bool CanShowFolders, static bool CanQuitApplicationComprobaciones de plataforma: si hay disponible un explorador de archivos de escritorio y si se puede cerrar la aplicación (no es posible en WebGL, iOS ni Android).
static bool RunsInBrowser, static bool StoresMapsInBrowserRunsInBrowser es true en las builds web, donde se aplican las limitaciones del navegador y el atajo de New Map es Alt+N. StoresMapsInBrowser es true cuando los mapas se guardan en el almacenamiento del navegador (builds de WebGL). Consulta Builds web (WebGL).
static bool HasReliableFunctionKeysEs false en navegadores web y en plataformas móviles. En ellos, el botón de cerrar hace aparecer el botón Open Map Editor.
VisualElement RootElement, UIDocument Document, RuntimeToolGizmos GizmosLa raíz de la interfaz del editor, su documento de UI y el renderizador de gizmos. Todos son null hasta que se activa el componente.
Comandos
bool SetActiveTool(string toolId)Activa una herramienta por su id, por ejemplo, "paint.layer". Devuelve false si el id no existe.
void SelectCategory(ToolCategory category)Igual que las teclas numéricas: activa la última herramienta usada en la categoría, o la siguiente herramienta si la categoría ya está activa.
RTSTool GetRememberedTool(ToolCategory category)La última herramienta usada en una categoría, o su primera herramienta.
void Undo(), void Redo()Deshacer y rehacer. No hacen nada durante una prueba de juego.
bool OpenMap(string path)Carga un archivo .rtsmap. Los cambios sin guardar se descartan sin preguntar.
bool SaveMap(string path)Guarda en una ruta, añade .rtsmap si hace falta y escribe la imagen de vista previa.
bool SaveCurrent()Igual que Ctrl+S. Devuelve true si el mapa se escribió inmediatamente. Si el mapa aún no tiene archivo, abre el cuadro de diálogo Save As y devuelve false.
void NewMap(MapCreationSettings settings)Crea un mapa plano nuevo sin preguntar por los cambios sin guardar.
bool DeleteMapFile(string path)Elimina un archivo de mapa y su imagen de vista previa.
ShowNewMapDialog(), ShowOpenDialog(), ShowSaveAsDialog(), ShowGenerateDialog(), ShowMapInfoDialog(), ShowResizeDialog(), ShowHelp()Abren los cuadros de diálogo con el mismo comportamiento que los menús. New y Open preguntan antes por los cambios sin guardar.
void StartPlaytest(), void StopPlaytest()Inician o terminan una prueba de juego.
void TogglePathingOverlay()Igual que P.
void DeleteSelectedDecor(), void DuplicateSelectedDecor()Eliminan o duplican la selección de decoración. Cada una de estas acciones es un único paso de deshacer.
void ShowMapsFolder(), void OpenManual(), void Quit()Los comandos de los menús File y Help. Quit pregunta por los cambios sin guardar.
bool DownloadMap(), void ImportMap()Solo en builds web: File ▸ Download Map y File ▸ Import Map.... Los navegadores solo muestran el selector de archivos como respuesta a un clic o a la pulsación de una tecla, así que llama a ImportMap desde un controlador de clic.
void ShowToast(string message, ToastType type = ToastType.Info)Muestra una notificación emergente. ToastType puede ser Info, Success, Warning o Error.
void ShowToast(string message, ToastType type, string key)Si todavía hay visible una notificación emergente con la misma clave, se actualiza esa notificación en lugar de añadir una nueva.
Eventos
event Action<string> MapSavedDespués de guardar un mapa (ruta completa del archivo).
event Action<string> MapLoadedDespués de abrir un mapa desde un archivo (ruta completa) o de crearlo (null).
event Action<bool> VisibilityChangedCuando cambia EditorVisible.

MapLoaded y RTSRuntimeEditorEvents.MapOpened se lanzan con File ▸ New y Open. Para reaccionar a cualquier cambio de mapa, incluidos Resize y los mapas que cargue tu propio código, suscríbete a RTSTerrain.MapLoaded en el terreno.

Ejemplo: abrir el editor desde tu propio menú

Muchos juegos abren el editor de mapas desde el menú principal en lugar de hacerlo con una tecla. Desactiva la tecla para mostrar u ocultar el editor, ábrelo desde un botón y añade un botón «Back to Menu» a la capa superior del editor:

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);
    }
}

Ejemplo: ocultar el HUD de tu juego mientras editas

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);
}

Personalizar la interfaz

Dónde están los estilos

Archivo (en Resources/RTSTerrainStudio/UI)Se carga comoFinalidad
RTSRuntimeTheme.tssRTSRuntimeEditor.ThemeResourcePath = "RTSTerrainStudio/UI/RTSRuntimeTheme"La hoja de estilos del tema. Importa el tema de tiempo de ejecución predeterminado de Unity (controles deslizantes, interruptores, campos de texto, vistas con desplazamiento) y RTSRuntimeEditor.uss.
RTSRuntimeEditor.ussRTSRuntimeEditor.StyleSheetResourcePath = "RTSTerrainStudio/UI/RTSRuntimeEditor"Contiene todos los estilos del editor. Cada nombre de clase empieza por rts-, y los selectores de tipo como Label se limitan a los descendientes de .rts-root, así que el editor nunca cambia el aspecto de la interfaz de tu juego, aunque tu panel use este tema.

Si la configuración de tu panel usa otro tema, el editor añade RTSRuntimeEditor.uss a su propia raíz, así que se ve igual en ambos casos.

Tokens de color

Todos los colores son variables definidas en .rts-root. Modifícalas para cambiar el aspecto de todo el editor:

VariablePredeterminadoSe usa para
--rts-panelrgba(20, 24, 30, 0.92)Fondos de los paneles (barra superior, barra de herramientas, panel de herramientas, barra de estado).
--rts-panel-solidrgb(22, 26, 33)Fondos opacos (cuadros de diálogo, ventanas emergentes).
--rts-raised / --rts-hover / --rts-pressedblanco al 6 % / 11 % / 3 %Estados de los botones.
--rts-border / --rts-border-strongblanco al 8 % / 16 %Bordes y separadores.
--rts-inputrgba(0, 0, 0, 0.32)Fondos de los campos de texto y de los controles deslizantes.
--rts-text / --rts-text-dim / --rts-text-faintrgb(226, 231, 238) / rgb(150, 160, 174) / rgb(104, 114, 128)Texto, texto secundario, indicaciones.
--rts-accent / --rts-accent-hover / --rts-accent-dim / --rts-accent-textgama de azules en torno a rgb(61, 169, 252)Herramientas seleccionadas, botones principales, Play Test.
--rts-danger / --rts-danger-dimrgb(239, 83, 80)Botones de eliminar y notificaciones emergentes de error.
--rts-warning / --rts-successrgb(255, 183, 77) / rgb(102, 187, 106)Notificaciones emergentes de advertencia y de éxito.
--rts-radius / --rts-radius-small6px / 4pxRadio de las esquinas.

Cambiar el aspecto sin tocar los archivos del paquete

No edites directamente los archivos de la carpeta Resources del paquete. Una actualización del paquete los sobrescribe. En su lugar, pon tus cambios en tu propia hoja de estilos.

  1. Crea una hoja de estilos en tu proyecto, por ejemplo, Assets/UI/MyEditorSkin.uss, y redefine en ella los tokens. El selector :root .rts-root es más específico que el .rts-root del paquete, así que tus valores tienen prioridad:
    /* 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. Añade la hoja al documento de UI del editor al iniciar:
    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);
        }
    }

Con el mismo método también puedes cambiar el estilo de partes concretas. Las clases principales son .rts-topbar, .rts-leftbar, .rts-toolpanel, .rts-statusbar, .rts-dialog, .rts-popup, .rts-toast (con las variantes --success, --warning y --error), .rts-hint, .rts-playtest-bar, .rts-button y .rts-selected. El UI Toolkit Debugger (Window ▸ UI Toolkit ▸ Debugger) muestra las clases de cada elemento mientras se ejecuta el juego.

Panel Settings, escala y orden de dibujado

  • Panel Settings vacío (predeterminado): el editor crea su propia configuración de panel con el tema RTS. La interfaz se escala con la altura de la pantalla (referencia de 1920 × 1080), pero nunca por debajo del 85 %. Ui Scale multiplica el resultado, lo que resulta útil en pantallas 4K o para jugadores que quieren un texto más grande.
  • Tus propios Panel Settings: asigna un asset para controlar tú mismo el tema y el escalado. Tools ▸ RTS Terrain Studio ▸ Create Demo Content crea un asset listo para usar en Resources/RTSTerrainStudio/UI/RTSRuntimePanelSettings.asset (tema RTS, escalado según el tamaño de pantalla, 1920 × 1080). Es el que usa la escena In-Game Editor Demo.
  • Enforce Minimum UI Scale (activado de forma predeterminada): si tu asset se escala con el tamaño de pantalla y no renderiza en una textura, el editor trabaja con una copia de ese asset creada en tiempo de ejecución. El asset en sí nunca se modifica. La copia se escala exactamente igual que el asset en pantallas grandes, nunca baja del 85 % en ventanas pequeñas y Ui Scale la multiplica. Su orden de dibujado queda medio paso por encima del que tiene el asset, así que el editor queda por encima de un HUD del juego que use el mismo asset (en ese caso, el HUD tiene su propio panel). Desactiva la opción para usar el asset tal cual, por ejemplo, para compartir un panel con la interfaz de tu juego; en ese caso no se aplican Ui Scale ni el mínimo del 85 %.
  • Builds web: el mínimo del 85 % se mide en píxeles CSS, así que la interfaz mantiene el mismo tamaño en pantallas de alta resolución.
  • Sorting Order: 1000 de forma predeterminada, así que el editor tapa tu HUD. Bájalo si parte de tu interfaz debe aparecer por encima del editor.
  • Añadir tus propios controles: añade elementos a RootElement (consulta el ejemplo del lanzador más arriba). Se ocultan junto con el editor y, cuando el puntero está sobre ellos, no se pinta en el terreno, igual que con cualquier otro panel.

Publicar el editor en el juego

Repasa esta lista antes de compilar un juego que incluya el editor en el juego.

  1. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y corrige lo que indique. La comprobación Shader references es la más importante para las builds (consulta el paso siguiente).
  2. Los shaders se incluyen mediante RTSShaderReferences. Unity deja fuera de la build los shaders que ningún material de la build utiliza. El paquete crea la mayoría de sus materiales en tiempo de ejecución (terreno, agua, niebla de guerra y los shaders de gizmos del editor Hidden/RTS Terrain Studio/GizmoLines y Hidden/RTS Terrain Studio/GizmoGhost), por lo que registra esos shaders en el asset Resources/RTSTerrainStudio/RTSShaderReferences. Este asset se actualiza automáticamente después de compilar los scripts, al importar shaders y antes de cada build del juego. Si falta o está desactualizado, usa la corrección Update del validador. Sin estos shaders, los contornos del pincel y las vistas previas de decoración no aparecen en la build.
  3. Conserva la carpeta Resources/RTSTerrainStudio con UI/RTSRuntimeTheme.tss, UI/RTSRuntimeEditor.uss y RTSShaderReferences.asset. El editor los carga mediante su ruta de Resources. Si falta el tema, la ventana Console muestra Controls will be unstyled.
  4. Panel Settings: si asignaste un asset Panel Settings, la escena lo referencia y se incluye en la build automáticamente. Si dejaste el campo vacío, la configuración de panel creada en tiempo de ejecución carga el tema desde Resources (paso 3).
  5. Entrada: el editor funciona con el paquete Input System y con el antiguo Input Manager. Consulta Gestión de entrada.
  6. Decide las opciones visibles para el jugador: Manual Url (enlace a tu guía de modding, o déjalo vacío para ocultar el botón), Show Quit Command (desactívalo si tu juego tiene su propio menú), Toggle Key y Start Visible.
  7. Carpeta de mapas: la carpeta predeterminada Maps dentro de RTSMapSerializer.StorageRoot (persistentDataPath) tiene permisos de escritura en todas las plataformas de escritorio. Para dar mapas de ejemplo a los jugadores, cópialos ahí en el primer inicio (consulta Distribuir mapas con tu juego).
  8. Builds web: instala la plantilla de ventana completa y lee Builds web (WebGL), donde se explican el almacenamiento del navegador, las descargas, las teclas y el alojamiento web.
  9. Herramientas personalizadas y eliminación de código: con un Managed Stripping Level superior a Minimal, marca tus propias herramientas con [Preserve] (consulta Eliminación de código y herramientas personalizadas).
  10. Contenido: los jugadores solo pueden usar el tileset, la biblioteca de vegetación y la biblioteca de decoración asignados al terreno en la escena del editor. Se incluyen en la build porque la escena los referencia.
  11. Prueba una build de desarrollo: pinta, guarda, abre, haz una prueba de juego y sal una vez con cambios sin guardar.

¿No vas a publicar el editor en el juego? Su código está en Runtime/RuntimeEditor y el código de soporte de los menús, en Editor/RuntimeEditorSupport. Un proyecto que no lo necesite puede eliminar la carpeta Runtime/RuntimeEditor. RTSRuntimeEditorEvents se queda en Runtime/Core, así que el código de jugabilidad que escucha las pruebas de juego sigue compilando.

Solución de problemas

ProblemaCausa y solución
Aparece el aviso No RTS Terrain found.La escena no tiene ningún RTS Terrain activado, o el terreno asignado está desactivado. Añade uno con GameObject ▸ RTS Terrain Studio ▸ RTS Terrain o actívalo.
Aparece el aviso No camera found.Ninguna cámara tiene la etiqueta MainCamera y Target Camera está vacío. Etiqueta tu cámara o asígnala.
Al hacer clic en el mapa no pasa nada.El puntero está sobre un panel, mantienes presionada Alt (rotación de la cámara), hay un cuadro de diálogo abierto o no hay ninguna herramienta activa. Cuando no hay ninguna herramienta activa, la barra de estado muestra Pick a tool on the left.
Los atajos no hacen nada.Un campo de texto tiene el foco del teclado o hay un cuadro de diálogo abierto. Presiona Esc o haz clic en el mapa.
F5 o F2 no hacen nada.F5 solo funciona mientras el editor está visible y no hay un cuadro de diálogo abierto. La tecla para mostrar u ocultar el editor se ignora durante una prueba de juego (termínala antes con Esc o F5) y mientras escribes en un campo. En los Mac y en muchos equipos portátiles, las teclas de función son teclas multimedia: mantén presionada Fn o usa el botón Play Test y, en las builds web, el botón Open Map Editor.
Esc termina la prueba de juego aunque mi juego ya la ha usado.Llama a RTSRuntimeEditorEvents.ConsumeEscape() en ese fotograma (consulta el ejemplo).
No pasa nada durante una prueba de juego.Ningún script escucha RTSRuntimeEditorEvents.PlaytestChanged. Añade RTSDemoGame o tu propio componente (consulta Play Test).
Después de una prueba de juego, la notificación emergente dice undo history was reset.Tu código de jugabilidad reemplazó, recargó o redimensionó el mapa durante la prueba. El mapa en sí se restaura correctamente.
La interfaz es demasiado pequeña o demasiado grande.Cambia Ui Scale. Si asignaste tus propios Panel Settings y no se escalan con el tamaño de pantalla, o si desactivaste Enforce Minimum UI Scale, cambia en su lugar el escalado en ese asset. En una build web con una plantilla de tamaño fijo, instala la plantilla de ventana completa (consulta La plantilla de ventana completa).
Mi HUD tapa el editor, o el editor tapa un menú.Ajusta Sorting Order (predeterminado: 1000).
Los controles aparecen sin estilo (aspecto blanco predeterminado de Unity).No se encontró el tema en una carpeta Resources. Restaura Resources/RTSTerrainStudio/UI.
Los contornos del pincel y las vistas previas no aparecen en la build.Los shaders de gizmos quedaron fuera de la build. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y actualiza las referencias de shaders.
El mensaje de bienvenida no vuelve a aparecer.El jugador lo descartó. Usa Open Welcome Hint en la ventana de F1, o elimina la clave de PlayerPrefs RTSTerrainStudio.InGameEditor.HintDismissed.
Mis mapas no aparecen en el explorador de mapas.El explorador solo muestra los archivos .rtsmap que están directamente en la carpeta de mapas indicada en su parte superior. Copia ahí los archivos y haz clic en Refresh.
Un mapa muestra Unreadable or damaged file.El archivo no es un archivo .rtsmap válido, o está truncado (por ejemplo, por una copia interrumpida).
Los mapas guardados en una build web han desaparecido.El navegador los guarda por sitio web, así que con otra dirección o después de borrar los datos de navegación se empieza sin mapas, y Safari puede eliminar los datos de los sitios web que no se hayan usado en siete días. Usa File ▸ Download Map para conservar copias (consulta Mapas en el navegador).
El editor no tiene herramientas en una build.La eliminación de código las quitó. Conserva el archivo Runtime/link.xml del paquete y marca tus propias herramientas con [Preserve] (consulta Eliminación de código y herramientas personalizadas).

Renderizado y sistemas del mundo

Este capítulo explica cómo un mapa se convierte en píxeles: cómo se construye y se sombrea el mesh del terreno, y cómo se dibujan la vegetación, el agua, la decoración y la iluminación de la escena. Cada sección empieza con lo que ves y los ajustes que puedes cambiar. Más abajo se tratan la API de scripting, el contrato de los shaders y las cifras de rendimiento que necesitan los programadores.

Los componentes de renderizado

Todos los sistemas leen el mismo MapData de un RTSTerrain y solo reconstruyen lo que ha cambiado una edición. Los sistemas son componentes independientes en el GameObject del terreno. GameObject ▸ RTS Terrain Studio ▸ RTS Terrain los añade todos por ti.

ComponenteDibujaObjetos generadosSección
RTSTerrainSuelo, acantilados, rampas (meshes de chunks, un material compartido), la cuadrícula de celdas, las superposiciones y el cursor del pincelRaíz «RTS Chunks» con un hijo por chunkRenderizado del terreno, Shader del terreno
RTSVegetationRendererHierba y plantas pintadas (detalles), árboles«RTS Vegetation Objects» oculto (colliders sustitutos, árboles como GameObjects)Vegetación
RTSWaterRendererSuperficie de agua transparente con color según la profundidad, olas, refracción y espumaRaíz «RTS Water» oculta con un hijo por cada chunk que contiene aguaAgua
RTSDecorManagerProps y doodads, como GameObjects o mediante instanciación por GPUContenedor «RTS Decor» para los objetos generadosDecoración
RTSEnvironment (opcional)Nada por sí mismo. Aplica a la escena el sol, la luz ambiental y la niebla del mapaNingunoEntorno

Los objetos generados están marcados como DontSave. Nunca se escriben en el archivo de la escena: el archivo .rtsmap es la única fuente de verdad, y todo se reconstruye a partir de él al abrir la escena. Si haces clic en un chunk, un árbol o una superficie de agua generados en la Scene View, se selecciona en su lugar el terreno al que pertenecen.

Si falta uno de los componentes de renderizado, el Inspector de RTSTerrain muestra una advertencia (por ejemplo, «No Water Renderer: painted water is not shown.») con un botón Add.

Renderizado del terreno

Chunks

El mapa se divide en chunks (bloques de celdas) cuadrados de 16, 32 (predeterminado) o 64 celdas de lado. Cada chunk es un GameObject con un MeshFilter, un MeshRenderer, un MeshCollider opcional y un componente RTSTerrainChunk. Todos los chunks comparten una única instancia de material (RTSTerrain.TerrainMaterial), lo que mantiene el terreno compatible con el SRP Batcher.

  • Los chunks se almacenan fila por fila: índice = z * ChunkCount.x + x. La última fila y la última columna pueden ser más pequeñas cuando el tamaño del mapa no es múltiplo del tamaño de chunk.
  • Una edición solo reconstruye los chunks que afecta. Cada rectángulo modificado se amplía en una celda, porque un cambio en un vértice también afecta a las normales y a la forma de los acantilados de las celdas vecinas. Por eso, pintar cerca del borde de un chunk también reconstruye el chunk vecino.
  • Pintar texturas de suelo no reconstruye los meshes. Solo se actualizan las texturas de splat (consulta resolución de splat).
  • Pintar agua tampoco reconstruye los chunks del terreno. Solo reacciona el renderizador de agua.

El mesh de cada chunk está en el espacio local del mapa: los objetos de chunk tienen una transformación local de identidad bajo la raíz oculta RTS Chunks del terreno, que compensa la escala del terreno, de modo que el espacio de objeto coincide con el espacio local del mapa (X/Z de 0 hasta el tamaño del mapa en el mundo, Y hacia arriba). El Transform del terreno se puede mover y rotar alrededor de Y. La escala se ignora.

Generación del mesh

RTSChunkMeshBuilder genera los meshes de los chunks con jobs de Burst y los escribe mediante Mesh.MeshDataArray:

  1. Los chunks solicitados se agrupan en lotes de hasta RTSChunkMeshBuilder.MaxChunksPerBatch (256) chunks, lo que limita el pico de memoria en mapas muy grandes.
  2. Si solo hay que reconstruir unos pocos chunks (lo habitual al pintar), cada chunk se divide en franjas de filas. Las franjas se generan en distintos hilos de trabajo y después se combinan, así que incluso la edición de un solo chunk aprovecha todos los núcleos de la CPU.
  3. BuildChunkGeometryJob (Burst, en paralelo) crea los triángulos. Las celdas suaves y de rampa se convierten en una cuadrícula regular de S × S quads (S = Surface Subdivisions). Las celdas de acantilado se trazan en una subcuadrícula más fina (Cliff Subdivisions) con marching squares. Así se obtienen contornos redondeados, un suelo plano para cada nivel y paredes verticales entre ellos.
  4. Los datos de colisión se precalculan en paralelo con Physics.BakeMesh y se asignan a los MeshCollider de los chunks.
  5. Build termina todo el trabajo antes de devolver el control, así que una edición se ve en el mismo fotograma.

Las celdas y los chunks vecinos siempre producen vértices de borde idénticos bit a bit, porque la posición de cada vértice depende solo de su posición en el mapa. Por eso no hay grietas entre chunks. El generador sigue la superficie de referencia MapGeometryView, que también usan las consultas de altura, el pathing (dónde pueden caminar, construir y volar las unidades) y el renderizador de agua. Por tanto, el suelo renderizado es exactamente el suelo que ve la lógica del juego.

El formato de vértice es RTSTerrainVertex (28 bytes: posición, normal y un color empaquetado). El color transporta datos para el shader: r = máscara de pared de acantilado, g = tipo de acantilado, b = oclusión ambiental precalculada. No hay coordenadas UV ni tangentes: el shader proyecta las texturas a partir de la posición del vértice.

Forma de las paredes de acantilado

Los acantilados se generan de forma procedimental, así que no necesitan piezas de acantilado hechas por un artista. Cuatro ajustes controlan el aspecto de las paredes:

AjustePredeterminadoRangoDescripción
Cliff Noise0.180–0.5Desplazamiento horizontal aleatorio de los vértices de las paredes de acantilado, como fracción de una celda. Da a las paredes un aspecto natural de roca. 0 = paredes perfectamente lisas.
Cliff Noise Scale1.6≥ 0.1Tamaño de los detalles del ruido de acantilado en unidades del mundo. Los valores pequeños dan una roca fina y escarpada; los grandes, abultamientos amplios.
Cliff Slant0.120–0.4Cuánto se desplaza hacia atrás el borde superior de un acantilado, como fracción de una celda. Hace que las paredes queden ligeramente inclinadas en lugar de perfectamente verticales.
Cliff Wall Segments31–8Segmentos verticales por nivel de acantilado. Más segmentos dan al ruido más vértices con los que trabajar (más relieve), pero cuestan más vértices.

El ruido más la inclinación nunca pueden desplazar un vértice más del 45 % de una celda (RTSChunkMeshParams.MaxDisplacementFraction). Si la suma de ambos ajustes supera ese valor, los dos se reducen a la vez, de modo que los triángulos del suelo junto a una pared nunca se pliegan unos sobre otros. Cerca de las rampas y del borde del mapa el desplazamiento se atenúa, así que esos bordes se mantienen rectos.

Además, la oclusión ambiental precalculada (color de vértice b) oscurece la geometría de la pared en su base. Donde dos paredes se encuentran en el borde de una celda, ambas usan el mismo brillo, así que los acantilados apilados y fusionados se ven sin costuras.

Colliders y consultas

Con Collider Mode = MeshCollider (predeterminado), cada chunk recibe un MeshCollider en la capa indicada en Chunk Layer. Se reconstruye junto con el mesh después de cada edición. Úsalo cuando tu juego dependa de la física de Unity (raycasts de física, rigidbodies, precálculo del NavMesh).

Con ColliderMode.None no se crea ningún collider. Aun así, el terreno se puede consultar sin física: RTSTerrain.Raycast, GetHeight, GetNormal, GetCliffLevel, IsWater y GetPathing trabajan sobre la superficie de referencia analítica, tanto en el modo Edit como en tiempo de ejecución. Así te ahorras la memoria y el tiempo de cocinado (cooking) de los colliders.

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}'");
        }
    }
}

Ajustes de renderizado del terreno

Estos ajustes están en el Inspector de RTSTerrain, agrupados en Chunks & Geometry, Cliff Shape, Physics y Rendering. Si cambias uno en el Inspector, el terreno se reconstruye automáticamente. Desde código, cambia terrain.Settings y luego llama a terrain.RebuildAll().

AjustePredeterminadoDescripción
Chunk Size32Celdas por lado de chunk (16, 32 o 64). Los chunks más pequeños se reconstruyen más rápido durante la edición; los más grandes implican menos draw calls. Consulta Elegir el tamaño de chunk.
Surface Subdivisions2Quads por lado de celda en el terreno suave y en las rampas (1–4). 1 = aspecto de RTS clásico, 2–4 = colinas más suaves.
Cliff Subdivisions4Resolución de la subcuadrícula que se usa para trazar contornos de acantilado redondeados dentro de una celda de acantilado (2–8). Se redondea hacia arriba a un número par que además sea múltiplo de Surface Subdivisions (consulta Subdivisiones).
Cliff Noise, Cliff Noise Scale, Cliff Slant, Cliff Wall Segmentsconsulta Forma de las paredes de acantiladoAspecto de las paredes de acantilado.
Collider ModeMeshColliderNone o MeshCollider (uno por chunk, se reconstruye tras cada edición).
Chunk Layer0 (Default)Capa de GameObject de los chunks. La usan los colliders y las máscaras de culling de la cámara. Los detalles pintados también se dibujan en esta capa.
Cast ShadowsOnModo de proyección de sombras de los renderers de los chunks.
Receive ShadowsactivadoIndica si los chunks reciben sombras.
Material OverridevacíoMaterial opcional que se usa en lugar del material predeterminado (consulta Materiales).
Show GriddesactivadoDibuja la cuadrícula de celdas de forma permanente (sobre todo para editar).
Grid Opacity0.35Opacidad de la cuadrícula cuando Show Grid está activado.

El botón Rebuild Terrain de la parte inferior del Inspector (RTSTerrain.RebuildAll()) destruye y vuelve a crear todos los chunks, las texturas y el mapa de pathing. La vegetación, el agua y la decoración se reconstruyen al mismo tiempo.

Materiales, superposiciones y la cuadrícula

De forma predeterminada, el terreno crea su propio material a partir del shader RTS Terrain Studio/Terrain (RTSTerrain.TerrainShaderName). El terreno elige el origen del material en este orden:

  1. Material Override en los ajustes del terreno (RTSTerrainSettings.materialOverride).
  2. Material Override del tileset (RTSTileset.materialOverride), para que todos los terrenos que usan ese tileset compartan el mismo aspecto.
  3. Un material nuevo que usa RTS Terrain Studio/Terrain.

El terreno siempre trabaja con una copia del material de origen y la rellena con los splat maps, los arrays de texturas y los parámetros del mapa. Un material de reemplazo debe usar el shader del terreno o un shader que declare las mismas propiedades (consulta Contrato de propiedades del shader).

Como el terreno renderiza una copia, los cambios que hagas después en el asset del material de reemplazo no se aplican automáticamente. Haz clic en Rebuild Terrain (o llama a RebuildAll()) después de editarlo. Para cambiar un valor en tiempo de ejecución sin reconstruir, asígnalo directamente en terrain.TerrainMaterial.

Si no se encuentra el shader del terreno, por ejemplo, en una build del juego que no lo incluye, se registra la advertencia «Shader 'RTS Terrain Studio/Terrain' was not found.» y el terreno se dibuja con un material URP Lit gris liso. Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project. Su comprobación «Shaders included in builds» crea el asset RTSShaderReferences, que mantiene todos los shaders del paquete en las builds (consulta Compilar tu juego).

Superposición. SetOverlayTexture(texture, opacity) coloca cualquier textura sobre todo el mapa, desde el primer vértice hasta MapData.WorldSize. El color del terreno se mezcla con el RGB de la textura en proporción al alfa de la textura × la opacidad. El editor lo usa para la vista de pathing. Tú puedes usarlo para mapas de calor, territorios o vistas previas de colocación de edificios. La superposición forma parte del color de la superficie, así que recibe la iluminación y las sombras igual que el terreno.

Cuadrícula. SetGridVisible(visible, opacity) fuerza desde código que se muestre la cuadrícula de celdas con antialiasing. Una de cada 4 líneas se dibuja más gruesa. En la Scene View, el editor la activa o desactiva con G durante la edición. IsGridVisible es true cuando la cuadrícula se muestra por código o mediante Show Grid.

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();
}

La API de renderizado de RTSTerrain de un vistazo:

MiembroDescripción
Material TerrainMaterialLa instancia de material que comparten todos los chunks (null antes de que se active el terreno).
Transform ChunkRootEl objeto «RTS Chunks», que contiene los GameObjects de los chunks.
IReadOnlyList<RTSTerrainChunk> Chunks, int2 ChunkCount, int ChunkSizeInCellsLa cuadrícula de chunks.
RTSTerrainChunk GetChunk(int2 chunkCoord), GetChunkAtCell(int2 cell)Busca un chunk. Devuelve null fuera del mapa.
bool IsRenderingReadyEs true en cuanto existen los chunks, las texturas y el material.
Texture GetSplatTexture(int index)Textura de splat 0–3 (la capa i está en el mapa i / 4, canal i % 4).
void SetOverlayTexture(Texture tex, float opacity), OverlayTexture, OverlayOpacitySuperposición para todo el mapa. Pasa null o 0 para ocultarla.
void SetGridVisible(bool visible, float opacity), IsGridVisibleCuadrícula de celdas desde código.
void RebuildChunkMeshes(RectInt cellRect)Reconstruye al instante los meshes y los colliders de los chunks afectados. Normalmente no hace falta: NotifyChanged ya se encarga de ello.
void RefreshMaterial()Vuelve a aplicar al instante el tileset, los arrays de texturas y los parámetros del mapa al material.
RTSTerrainChunk: Terrain, ChunkCoord, CellRect, Mesh, MeshRenderer, MeshCollider, LocalBounds, static GetTerrain(Collider)Datos por chunk. GetTerrain obtiene el terreno al que pertenece un impacto de física.

El shader del terreno

RTS Terrain Studio/Terrain (Shaders/RTSTerrain.shader) es un shader con iluminación (lit) para URP 17.3, escrito a mano. Admite renderizado Forward, Forward+ y Deferred, el SRP Batcher, sombras, SSAO, decals, capas de luz, cookies de luz y lightmaps. Nunca tienes que editar su material a mano: el terreno rellena todas las propiedades a partir del mapa y de su tileset.

Características

CaracterísticaQué haceSe controla con
16 capas de sueloHasta 16 capas mezcladas mediante splat, muestreadas con proyección planar XZ. En cada píxel solo se muestrean las capas con un peso visible (ramas dinámicas), así que pintar más capas en el mapa no encarece cada píxel.Capas de suelo del tileset; Tile Size, Tint, Normal Strength, Smoothness, Metallic
Mezcla por alturaEn una transición gana la capa cuya textura tiene más altura, de modo que la hierba crece entre las piedras en lugar de difuminarse sobre ellas. Contraste = Height Blend² × 16; 0 da una mezcla lineal simple.Height Blend por capa (0–1) más una fuente de altura: un mapa de relieve (canal R) o el alfa del albedo (Height From Albedo Alpha)
Anti-tilingEn regiones elegidas mediante ruido de baja frecuencia, la textura se funde con una segunda muestra con UV rotadas y escaladas, lo que rompe la repetición visible. Con la intensidad máxima, aproximadamente la mitad del suelo usa la segunda muestra. Cuesta una muestra de textura adicional en esas regiones.Anti Tiling por capa (0–1)
Variación macroUna variación de brillo a gran escala y una sutil variación de tono cálido/frío sobre todo el mapa. Oculta la repetición a distancia. Los acantilados reciben la mitad de la intensidad.Macro Variation del tileset (0–1, predeterminado: 0.25)
Acantilados triplanaresHasta 4 tipos de acantilado se proyectan a lo largo de los tres ejes, así que las paredes verticales nunca se ven estiradas. Las normales usan una mezcla whiteout. Donde se encuentran dos tipos de acantilado, se funden entre sí en una franja corta.Capas de acantilado del tileset; Triplanar Sharpness (1–16, predeterminado: 6)
Roca en pendientesEl suelo con una pendiente superior a unos 50° se convierte gradualmente en el material de acantilado (roca por completo a partir de unos 63°), así que las colinas empinadas nunca muestran hierba estirada. La roca asoma a través del suelo donde su textura de altura es alta.Automático
OclusiónOclusión ambiental (AO) por vértice precalculada al pie de los acantilados, multiplicada por un término de cavidad obtenido de la textura de altura del acantilado.Automático
Cuadrícula de celdasLíneas con antialiasing (más gruesas cada 4 celdas). Se desvanecen a distancia antes de que puedan provocar patrones de muaré.Show Grid / SetGridVisible / G en la Scene View
SuperposiciónSuperposición RGBA para todo el mapa (vista de pathing, mapas de calor).SetOverlayTexture; P recorre los modos de la superposición de pathing en la Scene View
Cursor del pincelEl contorno y la atenuación del pincel se proyectan en el shader en la posición exacta de la superficie, así que pintas justo donde lo ves (sin depender del escalado de la pantalla). Hasta 4 posiciones para pintar con simetría. Se dibuja al final, por lo que sigue visible dentro de la niebla de guerra.RTSBrushPreview (lo configuran las herramientas del editor)
Niebla de guerra (alternativa)Cuando la renderer feature de niebla de guerra no está instalada, el propio shader oscurece las zonas inexploradas y las que no están visibles.RTSFogOfWarManager (consulta Niebla de guerra)
Alternativa sin tilesetSin tileset, el terreno muestra suelo gris neutro y acantilados de un gris más oscuro en lugar de fallar.Automático
Opciones de iluminaciónDesactiva los brillos especulares y los reflejos del entorno para obtener un sombreado más barato.Interruptores del material Specular Highlights / Environment Reflections (en un material de reemplazo)

La mayoría de estos valores son ajustes del tileset: consulta Tilesets para saber cómo crear las capas.

Pasadas: ForwardLit (UniversalForward), GBuffer (UniversalGBuffer, no disponible en GLES/GLCore), ShadowCaster, DepthOnly, DepthNormals y Meta. Todas las pasadas salvo GBuffer se compilan para Shader Model 3.5, así que el terreno también se renderiza en WebGL 2.0 y OpenGL ES 3.0; la pasada GBuffer necesita 4.5. La cola de renderizado es Geometry-100, de modo que el terreno, que es grande, rellena el búfer de profundidad antes de que se dibujen los demás objetos opacos. No se usa el cross-fade de LOD.

Desactivar los brillos especulares y los reflejos del entorno

Para dispositivos de gama baja, o para conseguir un aspecto pintado más plano, usa un material de reemplazo con los interruptores de iluminación desactivados:

  1. En la ventana Project, elige Create ▸ Material y ponle un nombre, por ejemplo, Terrain Low End.
  2. En el Inspector del material, selecciona el shader RTS Terrain Studio ▸ Terrain.
  3. Desmarca Specular Highlights y/o Environment Reflections en la parte inferior del Inspector del material. No toques las demás propiedades: el terreno las sobrescribe.
  4. Asigna el material al campo Material Override del tileset (afecta a todos los terrenos que lo usan) o al de un solo terreno (sección Rendering del Inspector de RTSTerrain).
  5. Si el terreno no cambia de inmediato, haz clic en Rebuild Terrain.

Para esto, usa un asset de material, no un material creado desde código. Unity solo conserva las variantes de shader de las palabras clave de material que usa algún asset de material incluido en la build, así que una palabra clave activada solo desde código puede eliminarse (stripping) de las builds del juego.

Contrato de propiedades del shader

El código de renderizado de RTSTerrain asigna todas las propiedades mediante los ids de RTSShaderIDs. Un shader personalizado debe declarar las propiedades que usa con los mismos nombres. Los escalares y los vectores están en el constant buffer UnityPerMaterial (SRP Batcher).

PropiedadTipoContenido
_RTS_SplatMap0 … _RTS_SplatMap3Texture2D, RGBA linealPesos de splat. La capa i está en el mapa i / 4, canal i % 4. Todos los pesos suman 1. UV = positionOS.xz * _RTS_MapSize.zw. Se muestrea con filtrado bilineal y en modo clamp.
_RTS_AlbedoArrayTexture2DArray, sRGBRGB = albedo, A = altura (0.5 cuando una capa no tiene altura). Slices 0–15 = capas de suelo, 16–19 = capas de acantilado (RTSTileset.CliffSliceOffset).
_RTS_NormalArrayTexture2DArray, linealRGB = normal en espacio tangente × 0.5 + 0.5 (ya desempaquetada de DXT5nm/BC5 al construir el array).
_RTS_LayerDataTexture2D 20 × 4, float, se lee con LoadUna columna por slice del array; consulta la tabla de más abajo.
_RTS_LayerCountfloatNúmero de capas de suelo del tileset, como mínimo 1 (limitado a 1–16). Sin tileset, RTSTerrain envía 1, una configuración neutra de datos de capa y arrays de texturas sustitutos (RTSTextureArrayBuilder.FallbackResolution = 128), de modo que el terreno muestra suelo gris y acantilados de un gris más oscuro. El shader también interpreta 0 como «sin datos» y entonces dibuja RTS_FALLBACK_GROUND_ALBEDO / RTS_FALLBACK_CLIFF_ALBEDO sin leer _RTS_LayerData; esto solo ocurre con un material personalizado que se usa fuera de RTSTerrain.
_RTS_MapSizefloat4(tamaño en el mundo en X, tamaño en el mundo en Z, 1 / X, 1 / Z).
_RTS_MapParamsfloat4(tamaño de celda, altura de nivel de acantilado, variación macro, nitidez triplanar). El shader del paquete no usa la altura de nivel de acantilado; está ahí para personalizaciones como un tintado basado en la altura.
_RTS_OverlayTex, _RTS_OverlayOpacityTexture2D RGBA, floatSuperposición para todo el mapa. Opacidad 0 = desactivada.
_RTS_GridParamsfloat4(activada 0/1, opacidad, sin uso, sin uso).
_SpecularHighlights, _EnvironmentReflectionsinterruptores floatPalabras clave _SPECULARHIGHLIGHTS_OFF, _ENVIRONMENTREFLECTIONS_OFF.
_RTS_LayerParams[20], _RTS_LayerTints[20], _RTS_LayerParams2[20]arrays de float4Los mismos valores por capa en forma de arrays de float4, para shaders personalizados que trabajan con arrays. El shader del paquete solo lee _RTS_LayerData.

RTSShaderIDs.WorldToMap (_RTS_WorldToMap) está reservado y RTSTerrain no lo asigna. Usa el espacio de objeto (positionOS), que en los meshes de los chunks equivale al espacio local del mapa.

Estructura de _RTS_LayerData (columna = slice del array):

Filaxyzw
01 / tamaño de tileintensidad de normalessuavidadmezcla por altura
1tinte RGB como color lineal (Color.linear)metálico
2anti-tilingtiene altura (0/1)capa usada (0/1)0
3color del minimapa (el shader no lo lee)

Una ranura de acantilado (16–19) con «capa usada» = 0 se renderiza como la ranura de acantilado 16. Los tipos de acantilado pintados por encima del número de capas de acantilado del tileset siguen mostrando roca.

Entrada del mesh: POSITION (local del mapa), NORMAL (suavizada, espacio de objeto), COLOR (r = máscara de pared de acantilado, g = tipo de acantilado × 85 / 255, de modo que los tipos 0–3 corresponden a 0, 1/3, 2/3 y 1, b = oclusión ambiental, a = sin uso). Sin UV ni tangentes.

Variables globales (de Shaders/Include/RTSCommon.hlsl, compartidas por todos los shaders del paquete):

Variable globalLa asignaContenido
_RTS_FogOfWarTex, _RTS_FogOfWarParams, _RTS_FogOfWarUnexplored, _RTS_FogOfWarExplored, _RTS_FogOfWarEnabledRTSFogOfWarManagerTextura de visibilidad (R = visible, G = explorado), mapeo y tintes. HLSL: RTS_SampleFogOfWar(positionWS), RTS_ApplyFogOfWar(color, positionWS).
_RTS_BrushPositions[4], _RTS_BrushRotations[4], _RTS_BrushCount, _RTS_BrushParams, _RTS_BrushColor, _RTS_BrushMaskRTSBrushPreviewCursor del pincel. HLSL: RTS_BrushCursor(positionWS) devuelve el color (rgb) y la cobertura (a).
_RTS_WindRTSVegetationRendererxy = dirección, z = intensidad, w = velocidad.

Escribir un shader de terreno personalizado

Toda la lógica de superficie del terreno está en un único archivo include, Shaders/Include/RTSTerrainInput.hlsl. Los archivos de pasada solo se encargan de iluminar la superficie resultante. Para cambiar el aspecto, parte de una copia del shader del paquete:

  1. Copia Shaders/RTSTerrain.shader y toda la carpeta Shaders/Include en una carpeta de tu propio proyecto (por ejemplo, Assets/MyGame/Shaders), manteniendo la estructura relativa. El shader incluye sus archivos de pasada como Include/…, y los includes se referencian entre sí mediante rutas relativas.
  2. Cambia el nombre del shader en la primera línea de tu copia, por ejemplo, Shader "MyGame/Terrain", para que no entre en conflicto con el original.
  3. Haz tus cambios, normalmente en RTS_EvaluateTerrainSurface de tu copia de RTSTerrainInput.hlsl.
  4. Crea un material con tu shader y asígnalo como Material Override (del tileset o del terreno). El asset de material también garantiza que el shader se incluya en las builds del juego.
  5. Haz clic en Rebuild Terrain.

Trabajar con copias significa que una actualización del paquete nunca sobrescribe tus cambios. Después de una actualización, compara tu copia con los nuevos archivos del paquete si quieres incorporar las últimas correcciones.

Ejemplo: nieve en las mesetas altas. Añade esto al final de RTS_EvaluateTerrainSurface en tu copia, justo antes de 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);

Los principales bloques de RTSTerrainInput.hlsl:

Función / tipoFinalidad
struct RTSTerrainSurfaceResultado de la evaluación de la superficie: albedo, metálico, suavidad, oclusión, normal por píxel y normal geométrica (espacio de objeto), marco tangente, cliffBlend (0 = suelo, 1 = acantilado).
RTS_EvaluateTerrainSurface(positionOS, normalOS, vertexColor, dPdx, dPdy)La superficie completa: cobertura de acantilado, suelo, acantilados, transición basada en la altura, variación macro, oclusión.
RTS_EvaluateGround(positionXZ, dPdx, dPdy, detileNoise), RTS_AccumulateGroundLayer(…)Capas de suelo mezcladas mediante splat, con mezcla por altura y anti-tiling (acumulador float RTSGroundAccumulator).
RTS_EvaluateCliff(cliffTypeValue, positionOS, dPdx, dPdy, normalOS), RTS_SampleCliffLayer(…), RTS_TriplanarWeights(normalOS, sharpness)Material de acantilado triplanar y fundido entre tipos de acantilado.
RTS_LoadLayerData(slice) → RTSLayerDataLee una columna de _RTS_LayerData.
RTS_DecodeArrayNormal(encoded, strength)Decodifica un texel del array de normales.
RTS_EvaluateNoise(positionXZ), RTS_ValueNoise, RTS_Hash21Ruido procedural (sin textura), estable en GPU móviles.
RTS_GridMask(…), RTS_ApplyEditorOverlays(…)Líneas de la cuadrícula y textura de superposición.
RTS_CreateSurfaceData(surface)Convierte la superficie en el SurfaceData de URP.
ConstantesRTS_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

El muestreo de texturas usa gradientes explícitos (SampleGrad). Los gradientes dPdx/dPdy se calculan una sola vez al principio del fragment shader, fuera de cualquier rama. Mantenlo así si añades tus propios muestreos dentro de ramas; de lo contrario, aparecen costuras de mipmaps en los bordes de las ramas.

La pasada forward aplica, en este orden: evaluación de la superficie, superposiciones del editor, iluminación de URP (UniversalFragmentPBR), niebla de Unity, niebla de guerra y cursor del pincel.

Vegetación

RTSVegetationRenderer dibuja dos tipos de vegetación a partir del mapa:

  • Detalles: hierba, flores y plantas pequeñas que se pintan como densidad. Los generan jobs de Burst por parche y se dibujan con instanciación procedural por GPU. Nunca existen como GameObjects.
  • Árboles: instancias colocadas una a una, ya sea instanciadas por GPU (lo más rápido) o generadas como GameObjects a partir de un prefab (para mecánicas de juego como la recolección).

Ambos se organizan en parches de RTSVegetationRenderer.PatchCells (16) × 16 celdas. Los parches son la unidad de culling y de las reconstrucciones incrementales: una edición solo regenera los parches afectados. El renderizador funciona en el modo Edit y en el modo Play, para las cámaras de la Game View y de la Scene View.

Configuración

  1. Selecciona el GameObject del terreno. Si no tiene un Vegetation Renderer, haz clic en Add junto a la advertencia del Inspector de RTSTerrain (o usa Add Component ▸ RTS Terrain Studio ▸ Vegetation Renderer).
  2. Asigna una Vegetation Library en la sección Assets del Inspector de RTSTerrain. Sin biblioteca, aparece la advertencia «The terrain has no vegetation library, so nothing can be drawn.» en el Inspector de Vegetation Renderer.
  3. Pinta detalles y coloca árboles con las herramientas de Vegetation (tecla 4 en la Scene View durante la edición).
  4. Para los árboles que se dibujan con instanciación, marca Enable GPU Instancing en sus materiales. Tools ▸ RTS Terrain Studio ▸ Validate Project enumera los materiales en los que está desactivado y puede corregirlos con un clic.

La biblioteca define los prototipos (meshes, texturas, colores, tamaños, distancias). Consulta Biblioteca de vegetación para ver todos los campos de los prototipos.

El Inspector de Vegetation Renderer muestra estadísticas en tiempo real (Visible Details, Total Details, Visible Trees, Tree Objects, Colliders) y tiene un botón Rebuild (RebuildAll()) y un botón Select Library.

Ajustes del renderizador

AjustePredeterminadoDescripción
Quality
Detail Density Multiplier1Multiplica el número de instancias de detalle por texel (0–2, 0 = sin detalles). Al cambiarlo se regeneran todos los detalles.
Detail Distance Multiplier1Multiplica la distancia de renderizado de cada prototipo de detalle (0.1–3).
Tree Distance Multiplier1Multiplica la distancia de renderizado de cada prototipo de árbol instanciado (0.1–3).
Wind
Wind Direction(1, 0.35)Dirección horizontal del viento en el espacio del mundo (x = X del mundo, y = Z del mundo).
Wind Strength0.6Intensidad global del viento (0–3). Cada prototipo y cada material la escalan a su vez.
Wind Speed1.6Velocidad de la animación del viento (0–10).
Rendering
Draw DetailsactivadoDibuja los detalles pintados por densidad.
Draw TreesactivadoDibuja los árboles instanciados por GPU.
Draw In Scene ViewactivadoDibuja la vegetación también en las cámaras de la Scene View.
Crossed Quad Count3Número de quads del mesh de hierba generado para los detalles Crossed Quads (2–3). Al cambiarlo se reconstruye la vegetación.
Collider Layer0Capa de los proxies de collider que se crean para los árboles instanciados.

Detalles

Para cada capa de detalle pintada y cada parche, DetailScatterJob (Burst) distribuye instancias a partir del mapa de densidad. Una densidad de 255 genera, por texel de detalle, tantas instancias como indique el ajuste Max Density del prototipo, multiplicadas por el Detail Density Multiplier. No se colocan instancias en pendientes más empinadas que Max Slope ni, con Avoid Water activado, en suelo inundado. Pueden inclinarse siguiendo el suelo (Align To Ground) y reciben, a partir de ruido, un color entre Healthy Color y Dry Color.

Las instancias de un parche se suben a un GraphicsBuffer estructurado y se dibujan con Graphics.RenderMeshPrimitives mediante el shader interno Hidden/RTS Terrain Studio/Detail. WebGL 2.0 no admite búferes estructurados y OpenGL ES 3 no garantiza que estén disponibles en los vertex shaders, así que en esos dispositivos las instancias se guardan en una textura en formato float (dos texels por instancia) en lugar de en el búfer. El renderizador elige una u otra vía en tiempo de ejecución, y la hierba se ve igual en ambos casos. El shader lee cada instancia del búfer o de la textura y se encarga del viento, de una reducción suave de tamaño durante el último 25 % de la distancia de renderizado, del oscurecimiento de la base, de la translucidez y de la niebla de guerra alternativa. En cada cámara, a cada parche se le aplica culling por frustum y por distancia.

Modo de renderizadoMeshMaterial
CrossedQuadsMesh de hierba generado con 2 o 3 quads cruzados (Crossed Quad Count)Se crea a partir de la Texture del prototipo (recorte alfa en 0.5)
MeshEl Mesh del prototipo (todos los submeshes)Si el material ya usa Hidden/RTS Terrain Studio/Detail, se usa tal cual. Si no, se crea un material de detalle a partir de su textura principal y su color. Su umbral de recorte alfa (alpha cutoff) solo se conserva si el material de origen usa recorte alfa.

Los demás shaders no pueden leer los datos de las instancias, así que los detalles en modo Mesh siempre se renderizan con el shader de detalle. Las características personalizadas del shader del material de origen (animación de vértices, iluminación especial) no se usan en los detalles. Si las necesitas, usa un prototipo de árbol.

Los detalles solo proyectan sombras cuando el ajuste Cast Shadows del prototipo está activado (desactivado de forma predeterminada). Se dibujan en la capa Chunk Layer del terreno, sin light probes ni reflection probes.

Árboles: instanciados o GameObject

Cada prototipo de árbol elige un Spawn Mode:

Instanced (predeterminado)GameObject
RenderizadoGraphics.RenderMeshInstanced, hasta 1023 instancias por draw call, con culling por parche según el frustum y Render Distance × Tree Distance MultiplierUna instancia de prefab por árbol, que Unity renderiza como cualquier GameObject
LODLOD0 y LOD1 de un LODGroup, elegidos por grupo de parches con la regla de LODGroup de Unity (respeta QualitySettings.lodBias). Los prefabs sin LODGroup usan todos sus MeshRenderer activos.El LODGroup propio del prefab
Tinte por instanciaSí, para materiales que usan RTS Terrain Studio/Vegetation (aleatorio entre Tint A y Tint B)No
CollidersProxies de cápsula reutilizados desde un pool (consulta más abajo)Los colliders del prefab
Scripts del prefabNo se ejecutan (no se instancia nada)Se ejecutan con normalidad
Ideal paraBosques, escenografía, grandes cantidades de árbolesÁrboles con mecánicas de juego: recolección, animación, scripts personalizados

En ambos modos, los árboles se colocan a la altura del suelo + position.y, se rotan según su giro horizontal y se escalan de forma uniforme. Se vuelven a asentar en el suelo automáticamente cuando se edita el terreno. Los árboles generados y los proxies de collider se encuentran bajo un hijo oculto del terreno llamado «RTS Vegetation Objects» y siguen su transform.

Los árboles instanciados necesitan que sus materiales tengan activado Enable GPU Instancing. Si está desactivado, el renderizador dibuja mediante una copia oculta del material con instanciación (el asset del proyecto nunca se modifica). En el modo Edit, la copia se vuelve a sincronizar con el original unas 4 veces por segundo. En las builds del juego, las variantes con instanciación de esos materiales pueden eliminarse, así que marca la casilla (el validador de proyecto puede hacerlo por ti).

El renderizador conserva el modo de proyección de sombras y la máscara de capas de renderizado de cada parte. Las partes configuradas como Shadows Only (proxies de sombra) funcionan como se espera. Un prototipo con Cast Shadows desactivado desactiva las sombras de todas sus partes.

Colliders de los árboles

Cuando un prototipo de árbol tiene activado Colliders:

  • Modo Instanced: cada árbol recibe un GameObject invisible llamado «Tree Collider» con un CapsuleCollider (Collider Radius, Collider Height, escalados con el árbol) y un componente RTSTreeInstance (IsColliderProxy = true). Los proxies se reutilizan desde un pool y usan la capa Collider Layer del renderizador.
  • Modo GameObject: se usan los colliders del prefab. El renderizador añade un RTSTreeInstance a cada árbol generado.

En ambos casos, hit.collider.GetComponentInParent<RTSTreeInstance>() te indica qué árbol ha alcanzado una consulta de física. El pathing y la niebla de guerra no necesitan colliders: usan los ajustes Footprint Radius y Blocks Vision del prototipo.

Viento

El renderizador envía en cada fotograma el vector global _RTS_Wind (dirección, intensidad, velocidad) a todos los shaders. El balanceo total de una planta es:

  • Detalles: Wind Strength global × Wind Strength del prototipo (0–1).
  • Árboles y plantas que usan RTS Terrain Studio/Vegetation: Wind Strength global × Wind Strength del material, ponderado según Wind Mode. Con Object Height, la flexión aumenta hacia la parte superior, en relación con Wind Reference Height. Vertex Color R usa el canal rojo del color de vértice como peso por vértice (pinta 0 en el tronco y 1 en las puntas de las ramas). Leaf Flutter superpone un movimiento rápido y pequeño.

Pon Wind Strength a 0 en el renderizador para detener todo el viento, por ejemplo, para una escena nocturna en calma.

El shader Vegetation

RTS Terrain Studio/Vegetation es un shader de follaje con iluminación para los materiales de los prefabs de árboles y plantas. Es compatible con el SRP Batcher, la instanciación por GPU con tinte por instancia (_RTS_InstanceTint), Forward+, los decals DBuffer, el Rendering Debugger, APV y los lightmaps, además de la alternativa (fallback) de la niebla de guerra. Otros materiales compatibles con instanciación (como URP Lit) también funcionan, pero sin tinte por instancia, translucidez ni viento.

PropiedadPredeterminadoDescripción
Albedo, ColorblancoTextura base (el alfa se usa para el recorte) y color.
Alpha Clipping, Alpha Cutoffactivado, 0.5Prueba alfa (alpha test) para las hojas. El recorte está activado sin ninguna keyword; la keyword invertida _ALPHATEST_OFF lo desactiva.
Use Normal Map, Normal Map, Normal Strengthdesactivado, –, 1Mapa de normales opcional (_NORMALMAP).
Smoothness, Specular Color0.2, gris oscuroRespuesta especular.
Wrap Lighting0.35Suaviza la atenuación de la luz en las hojas (0–1).
Translucency, Translucency Color0.6, verde claroLuz que atraviesa las hojas al mirar hacia el sol.
Wind ModeObject HeightOff, Vertex Color R u Object Height.
Wind Strength, Wind Reference Height, Leaf Flutter1, 6, 0.5Escala del viento propia del material (se multiplica por el viento global del renderizador).
CullOffCulling de caras. Con Off se renderizan ambas caras de los planos de hojas.
Receive ShadowsactivadoCuando está desactivado, se usa la keyword _RECEIVE_SHADOWS_OFF.

Para el código que crea materiales de vegetación: el recorte alfa está activado de forma predeterminada. Activa la keyword _ALPHATEST_OFF (RTSVegetationShaderIDs.VegetationAlphaTestOffKeyword) solo cuando quieras desactivar el recorte.

Multiplicadores de calidad

Los tres campos de calidad escalan los valores de la biblioteca sin modificarla. Son ideales para las opciones gráficas del menú de tu juego:

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.
        }
    }
}

API de árboles y tala

MiembroDescripción
bool RemoveTree(uint id)Elimina el árbol del mapa, desactiva de inmediato su GameObject o su proxy de collider, notifica al terreno (MapLayers.Trees, de modo que el pathing vuelve a liberar la huella y la niebla de guerra retira el bloqueador de visión) y lanza TreeRemoved. No registra ningún paso de deshacer. Devuelve false si ningún árbol tiene ese id.
event Action<RTSVegetationRenderer, TreeInstanceData> TreeRemovedLo lanza RemoveTree con los datos del árbol eliminado.
bool TryFindNearestTree(Vector3 worldPosition, float maxDistance, out TreeInstanceData tree)Árbol más cercano, medido en el plano del suelo (XZ). Mathf.Infinity busca en todo el mapa.
GameObject GetTreeObject(uint id)La instancia del prefab generada (modo GameObject) o el proxy de collider (modo instanciado con colliders). En cualquier otro caso, null.
Vector3 GetTreeWorldPosition(in TreeInstanceData tree)Posición en el mundo de la base del árbol (altura del suelo + desplazamiento).
void RebuildAll(), void ProcessChanges()Reconstrucción completa; aplica ahora los cambios pendientes del mapa (normalmente ocurre de forma automática en cada fotograma).
VisibleDetailInstances, VisibleTrees, TotalDetailInstances, SpawnedTreeObjects, TreeColliderCountEstadísticas. Los valores «visibles» corresponden a la última cámara de Game o de Scene que se renderizó.
RTSTreeInstance: TreeId, Owner, IsColliderProxy, TryGetData(out TreeInstanceData), Remove()Componente de los árboles generados y de los proxies de collider. Remove() equivale a 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}");
    }
}

Los scripts de ejemplo incluyen una demostración completa de tala (RTSTreeHarvestDemo: los trabajadores caminan hasta un árbol, lo talan y reproducen una animación de caída con RTSFallingTreeEffect). Léela para ver un ejemplo de jugabilidad completo, incluido cómo se restauran los árboles talados cuando termina una prueba de juego.

Eliminar un árbol modifica el mapa. En el editor en el juego o en el Unity Editor, el mapa queda entonces marcado con cambios sin guardar. El código del juego normalmente trabaja sobre un mapa cargado para la partida y nunca lo vuelve a guardar.

Agua

RTSWaterRenderer renderiza el agua pintada como meshes transparentes divididos en chunks. Cada vértice marcado como agua tiene su propia altura de superficie, por lo que es posible tener lagos en distintos niveles de acantilado. Los meshes siguen MapGeometryView.SampleWater, que es exactamente lo que usan las consultas de jugabilidad y el pathing. Los chunks de agua usan el tamaño de chunk del terreno, se generan con jobs de Burst (WaterChunkMeshJob) y solo existen donde hay agua. Solo se reconstruyen los chunks afectados por una edición.

Configuración

  1. Asegúrate de que el GameObject del terreno tenga un componente Water Renderer (Add Component ▸ RTS Terrain Studio ▸ Water Renderer, o el botón Add del Inspector de RTSTerrain).
  2. Abre el asset de URP activo, activa Depth Texture y Opaque Texture y pon Opaque Downsampling en None (consulta más abajo). Tools ▸ RTS Terrain Studio ▸ Validate Project lo comprueba y lo corrige. Una cámara también puede activar ambas texturas solo para sí misma.
  3. Pinta agua con las herramientas Water (tecla 6 en la Scene View mientras editas).
  4. Cambia el aspecto en la sección Water del Inspector del tileset (el perfil de agua del tileset).

Requisitos de URP

Ajuste (asset de URP o cámara)Necesario paraSin él
Depth Texture: activadoColor según la profundidad, espuma de orilla y transición suave en la orilla alrededor de los objetos que están en el agua, y una refracción que excluye los objetos situados delante del agua (_CameraDepthTexture)El agua usa la profundidad guardada en su mesh. El color según la profundidad, la espuma y la transición suave en la orilla se mantienen, pero ignoran los objetos que están en el agua.
Opaque Texture: activadoRefracción (_CameraOpaqueTexture)Mezcla alfa simple sin refracción.
Opaque Downsampling: None (asset de URP)Un fondo de lago nítido y objetos sumergidos nítidosLa escena bajo el agua se ve borrosa (el valor predeterminado de URP es 2x Bilinear).

URP decide por cámara: los ajustes Depth Texture y Opaque Texture propios de una cámara (en la sección Rendering de su Inspector) tienen prioridad sobre el asset de URP, y la Scene View siempre usa el asset. El agua adapta su material a cada cámara justo antes de que esa cámara renderice, de modo que cada vista muestra el agua con la mejor calidad posible. Se omiten las cámaras que no renderizan la capa del agua, y las cámaras superpuestas (overlay) nunca reciben la textura opaca.

El Inspector de Water Renderer muestra un mensaje por cada ajuste que falta, con un botón Select URP Asset. En el modo Play también tiene en cuenta los ajustes propios de la cámara principal. Aunque falte una textura, el agua nunca deja de funcionar: las vistas que no la tienen usan la alternativa descrita más arriba. El agua simplemente se ve bastante mejor con ambas texturas activadas.

Desde código: RTSWaterMaterialUtility.CheckPipelineSupport(Camera camera, out bool depthTexture, out bool opaqueTexture) te indica qué genera URP para una cámara concreta, resolviendo los ajustes igual que lo hace URP (pasa null para comprobar solo el asset de URP). CheckPipelineSupport(out bool depthTexture, out bool opaqueTexture) comprueba las vistas que muestran el agua: en las builds, la cámara principal (o el asset de URP si no hay ninguna); en el editor, además, el asset de URP, porque la Scene View lo usa. Ambos devuelven false cuando URP no está activo. RTSWaterMaterialUtility.IsOpaqueTextureDownsampled() comprueba el submuestreo (downsampling).

Ajustes del renderizador

AjustePredeterminadoDescripción
Material TemplatevacíoMaterial opcional. Se copian su shader y sus propiedades y, después, se aplica encima el perfil de agua del tileset. Úsalo para ajustar valores que el perfil no cubre: Reflection Strength, Specular Strength, Foam Tile Size, Foam Lines, Shore Fade.
Cast ShadowsdesactivadoNormalmente, las superficies de agua solo reciben sombras.
Receive ShadowsactivadoSombras de la luz principal sobre el agua. También funciona con el material de tiempo de ejecución en las builds del juego.
Render Layer4 (Water)Capa de GameObject de los chunks de agua (RTSWaterRenderer.DefaultLayer es la capa Water integrada de Unity).
Wave Amplitude Multiplier1Escala el Wave Height del perfil (0–4, 0 = agua en calma).
Cell Subdivisions2Quads por lado de celda (1–4). Los valores más altos dan orillas y olas más suaves, pero generan más vértices.
Light Probe UsageOffCon Off, todos los chunks se iluminan con la sonda ambiental (ambient probe). Mezclar sondas daría a cada chunk un color ambiental ligeramente distinto y se verían escalones a lo largo de los bordes de los chunks.
Reflection Probe UsageBlend ProbesEn Forward+, las sondas se evalúan por píxel. En Forward, cada chunk elige las sondas de sus propios límites, por lo que, con varias sondas, los chunks vecinos pueden reflejar sondas distintas.

Botones del Inspector: Rebuild (RebuildAll()) y Refresh Material (RefreshMaterial(), vuelve a aplicar la plantilla y el perfil). El Inspector también muestra la cuadrícula de chunks, los chunks con agua, el número total de vértices y de dónde procede el perfil.

Perfil de agua (tileset)

El aspecto y las reglas de pathing proceden de RTSTileset.water (un WaterProfile). Sin tileset, se usa un perfil predeterminado compartido. Trátalo como de solo lectura y usa WaterMath.CreateDefaultProfile() o WaterProfile.Clone() cuando necesites una copia editable.

CampoPredeterminadoDescripción
Shallow Colorverde azulado, alfa 0.55Color del agua poco profunda (alfa = opacidad).
Deep Colorazul oscuro, alfa 0.95Color que se alcanza a la profundidad Color Depth (alfa = opacidad).
Color Depth3 mProfundidad a la que se alcanza por completo el color profundo.
Shallow Depth Limit0.9 mEl agua más profunda que este valor es agua profunda (no transitable). El agua menos profunda es transitable, como en los RTS clásicos.
Normal Map, Normal Strength, Normal Tile Sizeninguno, 0.6, 8 mOndulaciones de la superficie. Sin mapa de normales, la superficie solo usa las olas de vértices.
Flow Speed(0.03, 0.02)Desplazamiento del mapa de normales.
Wave Height0.05 mAmplitud de las olas de vértices (multiplicada por el multiplicador del renderizador).
Smoothness0.92Suavidad de la superficie.
Refraction0.03Intensidad del desplazamiento por refracción (0–0.2).
Foam Color, Foam Distance, Foam Textureblanco 0.8, 0.6 m, ningunoEspuma de orilla. Foam Distance también calma las olas en agua poco profunda.
Minimap ColorazulColor del agua en el minimapa.

El shader Water

RTS Terrain Studio/Water (RTSWaterMaterialUtility.ShaderName) es un shader transparente de URP. Los meshes de los chunks proporcionan posiciones en el espacio local del mapa y datos de agua por vértice (profundidad, cobertura y distancia a la orilla, hasta ShoreSearchRadius = 4 celdas). Características:

  • Color según la profundidad, del agua poco profunda a la profunda, con orillas suaves que se desvanecen donde el agua se encuentra con el suelo.
  • Cuatro olas de vértices direccionales, atenuadas en agua poco profunda para que la superficie nunca se eleve por encima de la playa.
  • Ondulaciones con un mapa de normales en movimiento, refracción de la escena bajo el agua, brillo especular del sol y sondas de reflexión.
  • Espuma de orilla (líneas procedurales o una textura de espuma).
  • Luz principal y luces adicionales (incluidas las luces adicionales por vértice), light cookies, light layers, recepción de sombras y la alternativa de la niebla de guerra. Compatible con el SRP Batcher.

El material se crea en tiempo de ejecución (WaterMaterial, compartido por todos los chunks) y se rellena con RTSWaterMaterialUtility.ApplyProfile. El shader se referencia mediante un campo serializado del componente, por lo que se incluye en las builds.

En el modo por vértice, las luces adicionales solo aportan luz difusa (sin brillo especular ni sombras), igual que la iluminación por vértice de URP.

API del renderizador de agua

MiembroDescripción
Material WaterMaterialMaterial de tiempo de ejecución compartido por todos los chunks (null mientras el componente está desactivado).
Material MaterialTemplate { get; set; }Plantilla opcional. Al asignarla, se actualiza el material.
WaterProfile ProfilePerfil del tileset o el perfil predeterminado compartido.
Transform ChunkRoot, ChunkCountX, ChunkCountZ, ActiveChunkCount, VertexCount, bool HasWaterCuadrícula de chunks y estadísticas.
MeshRenderer GetChunkRenderer(Vector2Int chunkCoord)Renderer de un chunk, o null si el chunk no tiene agua.
void RebuildAll(), void RebuildCells(RectInt cellRect), void RefreshMaterial()Reconstrucción completa, reconstrucción parcial, actualización del material.
event Action<RTSWaterRenderer> MeshesRebuiltSe lanza tras una reconstrucción completa o parcial.
RTSWaterChunk: Owner, Terrain, ChunkCoord, CellRectComponente marcador de los objetos de chunk ocultos.

WaterMath: agua para la jugabilidad

WaterMath responde a consultas sobre el agua sin usar física. Todas las posiciones están en el espacio del mundo.

MétodoDevuelve
GetWaterHeight(RTSTerrain, Vector3, out float height), GetWaterHeight(Vector3, out float height)Coordenada Y en el mundo de la superficie del agua en calma. False si el punto está seco o fuera del terreno. La segunda sobrecarga busca el terreno con RTSTerrain.FindAt.
GetWaterDepth(RTSTerrain, Vector3, out float depth)Superficie del agua menos suelo, en metros. False si el punto está seco.
Classify(RTSTerrain, Vector3, out float depth)WaterDepthClass.Dry, Shallow o Deep (según Shallow Depth Limit).
IsShallowWater(…), IsDeepWater(…), GetShallowDepthLimit(RTSTerrain)Atajos para la clasificación.
GetWaterSurface(RTSTerrain, Vector3, float time, out float height, out Vector3 normal)La superficie animada exactamente como se renderiza: altura en calma + olas, atenuadas cerca de la orilla, incluido el Wave Amplitude Multiplier del renderizador.
float ShaderTimeEl reloj con el que anima el shader (Time.time en el modo Play y en las builds, Time.realtimeSinceStartup en el modo Edit). Pásalo como time para que las olas de CPU y GPU coincidan.
SampleWaveOffset(Vector3, float waveHeight, float time)Desplazamiento de las olas sin la atenuación junto a la orilla.
EvaluateWaves(float2, float time), EvaluateWaveSurface(float2, float time, float amplitude, out float offset, out float3 normal), WaveAttenuation(float depth, float foamDistance)Bloques básicos compatibles con Burst para sistemas de jobs (por ejemplo, miles de unidades flotantes).
GetProfile(RTSTerrain), CreateDefaultProfile()El perfil en uso; un perfil predeterminado nuevo que se puede modificar sin riesgo.
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;
    }
}

Decoración

RTSDecorManager mantiene la escena sincronizada con la decoración del mapa (doodads: rocas, cajas, ruinas, vallas, cualquier prefab). Escucha los eventos de cambio del terreno y trabaja de forma incremental. Una edición solo afecta a las instancias que se añadieron, eliminaron o modificaron. Los objetos se reutilizan cuando solo cambia su transform y se guardan en un pool cuando se eliminan. Las ediciones de altura del terreno vuelven a asentar sobre el suelo las instancias de la zona modificada. Cargar un mapa o cambiar la biblioteca de decoración lo reconstruye todo.

Modos de generación

Cada prototipo de la biblioteca de decoración elige un Spawn Mode:

GameObject (predeterminado)Instanced
RepresentaciónUna copia del prefab (variante) bajo el objeto hijo «RTS Decor», con un componente RTSDecorInstanceSe dibuja con Graphics.RenderMeshInstanced (primer nivel de LOD del prefab), hasta 1023 instancias por draw call
CullingEl propio de UnityPor parche (frustum + Max Draw Distance)
Scripts, colliders, animaciónSíNo (no existen objetos)
TinteMediante un MaterialPropertyBlock (_BaseColor y _Color)Solo con shaders que lo admiten (consulta Tinte por instancia)
Ideal paraProps interactivos o destructibles, desde unos cientos hasta unos miles de objetosEscenografía estática en grandes cantidades

Regla de colocación: posición = LocalToWorld(x, ground + y + prototype yOffset, z), o la altura absoluta guardada con el indicador KeepHeight. Rotación = rotación del terreno × rotación de la instancia; escala = escala de la instancia. Además, los prefabs generados conservan la rotación y la escala definidas en su raíz, que se aplican sobre estos valores.

La decoración instanciada necesita Enable GPU Instancing en sus materiales. Si está desactivado, se usa una copia en tiempo de ejecución con instanciación y una advertencia te pide que marques la casilla. Usa el validador de proyecto para corregir todos los materiales a la vez.

Los campos del prototipo (variantes, rotación y escala aleatorias, tinte, huella, bloqueo de visión, sombras) se describen en Decor Library.

Ajustes del gestor

AjustePredeterminadoDescripción
Show In Scene ViewactivadoDibuja la decoración instanciada en las cámaras de la Scene View. Los GameObjects generados siempre son visibles.
Max Draw Distance0La decoración instanciada que está más lejos de la cámara que este valor no se dibuja (0 = sin límite).
Shadow Culling Margin12 mMargen adicional alrededor de los parches para el frustum culling, de modo que los props que quedan justo fuera de la vista sigan proyectando sombras dentro de ella.
Instanced TintactivadoPasa tintes por instancia a la decoración instanciada cuyo shader lo admita.
Patch Size16Longitud del lado de un parche de culling y de consulta, en celdas (4–64). Al cambiarlo, se reconstruye la decoración.
Pool Capacity Per Prefab64La decoración de tipo GameObject eliminada se conserva inactiva para reutilizarla, hasta este número de objetos por prefab.
Log Missing PrototypesactivadoRegistra una advertencia por cada id de prototipo que falte, biblioteca que falte o prefab destruido.

El Inspector de Decor Manager muestra Instances, GameObjects, Instanced y Sync (si hay cambios pendientes), además de los botones Rebuild (RebuildAll(): destruye y vuelve a crear todo, vacía los pools y vuelve a leer los prefabs), Sync Now (SyncNow()) y Select Library.

Objetos sustitutos

Si un mapa hace referencia a un id de prototipo que no está en la biblioteca, si el terreno no tiene ninguna biblioteca de decoración o si se eliminó un prefab, la instancia se muestra como un pequeño cubo magenta. No se pierde nada: la instancia permanece en el mapa y vuelve a aparecer con normalidad en cuanto el prototipo vuelve a estar disponible. IsPlaceholder(id) indica si una instancia es un objeto sustituto. Los objetos sustitutos cuentan como instanciados.

Si aparecen cubos magenta al abrir un mapa de otra persona, normalmente significa que el terreno usa una biblioteca de decoración distinta de aquella con la que se creó el mapa. Asigna la biblioteca correcta en el Inspector de RTSTerrain.

Tinte por instancia con tus propios shaders

La decoración instanciada solo se tiñe por instancia cuando el shader puede leer el tinte del búfer de instanciación. URP Lit, Simple Lit y Unlit conservan el color de su material. Los shaders lo activan mediante una etiqueta de SubShader:

EtiquetaConstanteEl shader debe
"RTSInstanceTint" = "True"RTSDecorManager.InstanceTintShaderTagMultiplicar su albedo por el valor instanciado _RTS_InstanceTint (rgb = tinte, a = 1). El shader RTS Terrain Studio/Vegetation del paquete se detecta automáticamente.
"RTSInstancedColor" = "True"RTSDecorManager.InstancedColorShaderTagLeer _BaseColor y _Color con UNITY_ACCESS_INSTANCED_PROP desde un UNITY_INSTANCING_BUFFER. Los valores escritos son color del material × tinte.

API de decoración: selección, consultas y eventos

La selección y las consultas no necesitan colliders. Cada instancia se comprueba como una caja orientada construida a partir de los límites del mesh del prefab, así que funcionan en ambos modos de generación, incluso con la decoración instanciada, que no tiene GameObject.

MiembroDescripción
bool TryPick(Ray worldRay, float maxDistance, out uint instanceId) (+ sobrecarga con out float hitDistance)Instancia más cercana que alcanza un rayo en espacio del mundo.
int QueryRadius(Vector3 world, float radius, List<uint> results)Añade los ids que están dentro de un radio en el plano del suelo. La lista no se vacía. No asigna memoria. Una sobrecarga con IEnumerable<uint> asigna una lista nueva.
int QueryLocalRect(Rect localRect, List<uint> results)Añade los ids cuyo pivote se encuentra dentro de un rectángulo XZ en el espacio local del mapa (selección por rectángulo).
GameObject GetInstanceObject(uint id)El objeto generado o null (decoración instanciada o id desconocido).
ContainsInstance, TryGetInstance(uint, out DecorInstanceData), IsInstanced, IsPlaceholderBúsquedas de instancias (datos según la última sincronización).
TryGetWorldBounds(uint, out Bounds), TryGetObjectMatrix(uint, out Matrix4x4)Límites en espacio del mundo; matriz de la raíz del objeto, incluidas la rotación y la escala de la raíz definidas en el prefab.
Matrix4x4 GetWorldMatrix(in DecorInstanceData), GetWorldMatrix(in DecorInstanceData, string prototypeId)Matriz del pivote en espacio del mundo, también para datos que aún no están en el mapa (vistas previas de colocación).
SpawnedCount, InstancedCount, InstanceCount, Container, PatchSize, HasPendingChangesEstado y estadísticas.
InstancesRebuilt, InstancesChangedTras una reconstrucción completa; tras una actualización incremental.
InstanceSpawned, InstanceDespawningSe generó un objeto o se tomó del pool; un objeto está a punto de volver al pool o de destruirse. Los objetos que se conservan durante una reconstrucción suave no vuelven a lanzar InstanceSpawned.
RTSDecorInstance: InstanceId (0 mientras está en el pool), PrototypeId, Variant, Owner, Prototype, IsLive, TryGetData, static FromCollider(Collider)Componente en la raíz de cada objeto generado. Sus campos se rellenan antes de que se ejecuten los propios Awake/OnEnable del prefab, así que los scripts de tu prefab pueden leerlos ahí.
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;
}

Solo los prototipos en modo GameObject lanzan InstanceSpawned, porque la decoración instanciada no crea objetos.

Entorno

Cada mapa guarda su propia iluminación en MapData.Environment: color, intensidad y rotación del sol, color ambiental y niebla. Así, un mapa nevado puede incluir una luz fría y un mapa desértico, una luz cálida. El componente RTSEnvironment aplica estos valores a la escena cuando se carga un mapa y cada vez que cambia el entorno.

Configuración

  1. Elige GameObject ▸ RTS Terrain Studio ▸ Environment. Esto crea un GameObject «Environment» con RTSEnvironment y, si la escena no tiene ninguna, una luz direccional «Sun». Si las escenas abiertas ya contienen un entorno, puedes seleccionarlo en su lugar. Varios entornos competirían por los mismos ajustes de iluminación.
  2. Como alternativa, añade el componente al GameObject del terreno con Add Component ▸ RTS Terrain Studio ▸ Environment (el Inspector de RTSTerrain ofrece un botón Add).
  3. Edita los valores en la pestaña Map de la ventana del editor (Tools ▸ RTS Terrain Studio ▸ Editor Window), en la tarjeta Environment: Sun Colour, Sun Intensity, Sun Rotation (x = inclinación sobre el horizonte, y = giro horizontal), Ambient Colour, Fog, Fog Colour, Fog Density. Los cambios se pueden deshacer y se guardan con el mapa.
  4. O bien ilumina la escena a mano y haz clic en Capture From Scene (ventana del editor) o en Use Current Lighting (Inspector del componente) para guardar en el mapa el sol, la luz ambiental y la niebla actuales, en un solo paso de deshacer.

La iluminación aplicada es una vista previa. No se registra en Undo y no marca la escena como modificada. Justo antes de guardar la escena se restaura su iluminación original y, después, se vuelve a aplicar la iluminación del mapa. Por eso, la iluminación del mapa nunca se escribe en tu archivo de escena. El botón Restore Scene Lighting del Inspector recupera la iluminación que había antes de aplicar por primera vez la del mapa.

Ajustes del componente

AjustePredeterminadoDescripción
TerrainvacíoTerreno cuya iluminación de mapa se aplica. Vacío = el RTSTerrain de este GameObject o, si no lo hay, el terreno principal (RTSTerrain.Main). Un terreno que se active más tarde se detecta automáticamente.
SunvacíoLuz direccional que se usa como sol. Vacío = RenderSettings.sun o la luz direccional activa más brillante.
Apply In Edit ModeactivadoAplica la iluminación del mapa mientras editas. Si está desactivado, solo se aplica en el modo Play.
Apply SunactivadoAplica el color, la intensidad y la rotación del sol (espacio del mundo).
Apply AmbientactivadoAplica la luz ambiental.
Ambient ModeTrilightFlat = un solo color ambiental. Trilight = degradado cielo / ecuador / suelo derivado del color ambiental del mapa.
Sky Factor1.2Trilight: color del cielo = color ambiental × este factor (0–3).
Ground Factor0.55Trilight: color del suelo = color ambiental × este factor (0–3).
Apply FogactivadoAplica el estado activado/desactivado de la niebla, su color y su densidad.
Fog ModeExponential SquaredModo de atenuación que se usa al aplicar la niebla (el mapa solo guarda el color y la densidad).
Restore On DisableactivadoAl desactivar el componente, restaura la iluminación de la escena que había antes de aplicar por primera vez la del mapa.

Valores predeterminados de un mapa nuevo: color del sol blanco cálido (1, 0.956, 0.84), intensidad 1.2, rotación (50, −30), luz ambiental (0.45, 0.5, 0.58), niebla desactivada, color de la niebla (0.6, 0.68, 0.75), densidad 0.004.

Para un ciclo de día y noche en tu juego, controla la luz del sol directamente desde tu propio script. RTSEnvironment solo vuelve a aplicar los valores del mapa cuando se carga un mapa o cambia su entorno. Como alternativa, desmarca Apply Sun y conserva del mapa solo la luz ambiental y la niebla.

API de entorno

MiembroDescripción
void Apply()Aplica ahora el entorno del mapa a la escena (respeta el ajuste Apply In Edit Mode).
void CaptureFromScene(RTSEditSession session)Escribe la iluminación de la escena en el mapa en un solo paso de deshacer de la sesión.
void CaptureFromScene(RTSToolContext ctx = null)Escribe la iluminación de la escena en el mapa. La acción de deshacer solo se registra dentro de RTSEditSession.RunOperation.
void RestoreSceneState()Restaura la iluminación que se guardó antes de aplicar por primera vez la del mapa.
RTSTerrain Terrain { get; set; }, Light Sun { get; set; }, bool CanApplyTerreno vinculado, luz del sol y si actualmente se permite aplicar.
static Light FindSun()RenderSettings.sun si es una luz direccional; si no, la luz direccional activa más brillante.
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);
    }
}

Guía de rendimiento

Los valores predeterminados funcionan bien en escritorio con mapas RTS típicos (128–256 celdas por lado). Esta sección explica cuánto cuesta cada ajuste, para que puedas optimizar con criterio los mapas grandes y las builds para móviles.

Lista de comprobación rápida

  • Mantén instalado el paquete Burst y activada la compilación con Burst (Jobs ▸ Burst ▸ Enable Compilation).
  • Construye los arrays de texturas del tileset en el editor con Compress Arrays activado, para la plataforma en la que vayas a publicar (el Inspector del tileset muestra el formato y un tamaño estimado).
  • Marca Enable GPU Instancing en todos los materiales de los árboles, detalles y objetos decorativos instanciados (el validador de proyecto lo hace con un clic).
  • Usa el modo de generación Instanced para los árboles y props que solo sirven de paisaje. Reserva el modo GameObject para los objetos que intervienen en la jugabilidad.
  • Elige la resolución de splat y la resolución de los arrays según la plataforma (consulta más abajo). Ambas cuestan sobre todo memoria.
  • Ejecuta Validate Map (pestaña Map ▸ Validation). Avisa si hay demasiados árboles (20 000), árboles GameObject (1 500), objetos decorativos (6 000), objetos decorativos GameObject (2 000) o capas de detalle (8), y también si el mapa es grande (más de 512 celdas).

Elegir el tamaño de chunk

Chunk SizeDraw calls (mapa de 256 × 256)Coste de reconstrucción por ediciónÚsalo para
16256 chunksEl menor: respuesta del pincel más rápidaMapas pequeños, edición intensiva (editor en el juego en equipos más lentos)
32 (predeterminado)64 chunksEquilibradoLa mayoría de los mapas
6416 chunksEl mayor: cada edición reconstruye hasta 64 × 64 celdas, más los chunks vecinos en los bordesMapas grandes que rara vez se editan (mapas publicados en los que los jugadores solo miran)

Cada chunk supone una draw call por vista renderizada (la cámara principal y cada cascada de sombras), y el SRP Batcher los agrupa con poco coste porque comparten un mismo material. Los chunks son también la unidad de culling, así que los chunks más pequeños permiten un culling más preciso cuando la cámara está cerca del suelo.

Subdivisiones

El número de vértices del suelo suave crece con el cuadrado de Surface Subdivisions:

Surface SubdivisionsVértices de un chunk plano de 32 × 32Cliff Subdivisions efectivo con el valor predeterminado 4 (con 8)
11 0894 (8)
2 (predeterminado)4 2254 (8)
39 4096 (12)
416 6414 (8)

Cliff Subdivisions se redondea hacia arriba a un número par que además sea múltiplo de Surface Subdivisions, de modo que las celdas de acantilado y las celdas suaves comparten todos los vértices del borde. Con 3 subdivisiones de superficie, la subcuadrícula de los acantilados pasa a ser múltiplo de 6, lo que resulta costoso. Es preferible usar 1, 2 o 4. Cada celda de acantilado cuesta aproximadamente (subdivisiones de acantilado)² × 2 triángulos más sus paredes (tantas filas por nivel como indique Cliff Wall Segments), así que los mapas con muchos acantilados son los que más se benefician de unos ajustes de acantilado moderados.

Un vértice de chunk ocupa 28 bytes. Los índices son de 16 bits mientras un chunk se mantenga por debajo de 65 536 vértices. Un mapa plano de 256 × 256 con 2 subdivisiones tiene unos 260 000 vértices, aproximadamente 10 MB de datos de vértices e índices. Los acantilados añaden más, y los colliders de los chunks necesitan sus propios datos de colisión precalculados.

Resolución de splat

La resolución de splat (texels por celda: 1, 2, 4 u 8; predeterminado: 4) se elige al crear un mapa (asistente New Map). Determina con qué precisión se pueden pintar las texturas de suelo. Los datos de splat siempre constan de 4 mapas RGBA32 (16 bytes por texel) y se almacenan por triplicado: en los datos del mapa, en la copia de CPU de las texturas y en la GPU (estas dos últimas con mip maps, +33 %).

Tamaño del mapa (celdas)Resolución de splat 2Resolución de splat 4 (predeterminada)Resolución de splat 8
128 × 1281 MiB de datos + 1.3 MiB en 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

(Suma otra vez el valor de GPU para la copia de CPU de las texturas.) El coste de pintar es bajo: solo se copian las filas modificadas. En texturas de más de 1 millón de texels, si cambia una zona de hasta 512 × 512 texels, solo se sube esa zona. Su cadena de mips se reconstruye en la CPU mediante un job de Burst (SplatMipDownsampleJob) y se copia a su sitio en la GPU.

La resolución 2 basta para el aspecto de mosaico de bordes duros, al estilo de los RTS clásicos (Tile Paint). La resolución 4 ofrece transiciones suaves y detalladas. La resolución 8 es solo para tamaños de celda muy pequeños o cámaras muy cercanas.

Resolución de los arrays de texturas

Cada tileset empaqueta sus texturas de suelo y de acantilado en dos Texture2DArray (albedo/altura y normales) de 20 capas (slices) cada uno, a la resolución que indica el ajuste Array Resolution del tileset (256, 512, 1024, que es el predeterminado, o 2048). Memoria de ambos arrays juntos, incluidos los mip maps:

Array ResolutionRGBA32 sin comprimirBC7 / DXT5 (escritorio, consolas)ASTC 6×6 (Android, iOS)
25613 MiB3.3 MiB1.5 MiB
51253 MiB13 MiB5.9 MiB
1024 (predeterminado)213 MiB53 MiB24 MiB
2048853 MiB213 MiB95 MiB

Con Compress Arrays activado, el Inspector del tileset precalcula arrays comprimidos para la plataforma de build activa: BC7 para Windows, macOS, Linux y consolas; ASTC 6×6 para Android, iOS, tvOS y visionOS; para Web, la compresión de texturas de la build (DXT5, ASTC 6×6 o ETC2 RGBA8); y DXT5 en los demás casos. Si faltan los arrays construidos, o están desactualizados en el editor, el renderizador construye en su lugar arrays RGBA32 sin comprimir en la GPU. Eso sirve para iterar rápido, pero ocupa cuatro veces más memoria que BC7. Antes de compilar una build del juego, se reconstruyen los arrays desactualizados (consulta Arrays de texturas en las builds del juego). Todos los terrenos que usan el mismo tileset comparten los arrays.

Si una GPU o un navegador no puede muestrear el formato precalculado, Unity descomprime los arrays al cargarlos, y entonces ocupan unas cuatro veces más memoria. Las builds del juego liberan la copia en la CPU de los arrays construidos la primera vez que los usan, así que en ellas los arrays construidos solo ocupan la memoria de GPU que indica la tabla.

Para comprobar qué arrays usa un juego en ejecución, haz una captura en el Memory Profiler y busca render textures llamadas «RTS Albedo Array (…)» y «RTS Normal Array (…)». Si las encuentras, se están usando los arrays sin comprimir construidos en tiempo de ejecución.

Distancia de detalles y vegetación

  • Los detalles son el cuello de botella más probable en la GPU, porque la hierba está formada por muchos triángulos pequeños con alpha clipping. La cantidad visible es proporcional a Render Distance² × densidad. Reducir a la mitad la distancia de detalles reduce la hierba dibujada a aproximadamente una cuarta parte.
  • Memoria de detalles: cada parche pintado mantiene sus instancias en la GPU, independientemente de la cámara: 32 bytes por instancia. Ejemplo: una capa de detalle con densidad máxima (Max Density 6, resolución de detalles 2 = 4 texels por celda) sobre todo un mapa de 256 × 256 genera unos 1.6 millones de instancias, es decir, unos 50 MB. Pinta hierba solo donde importe o reduce Max Density.
  • Las sombras de los detalles están desactivadas de forma predeterminada. Al activarlas, toda la hierba se vuelve a renderizar en cada cascada de sombras.
  • Árboles: los árboles instanciados cuestan una draw call por cada 1 023 instancias, por parte y por grupo de parches. Usa un LODGroup con un LOD1 barato. Los árboles GameObject cuestan un GameObject cada uno (no superes unos 1 500).
  • Decoración: en mapas grandes, configura Max Draw Distance para la decoración instanciada. Unity aplica culling a la decoración GameObject, pero cada objeto sigue costando un GameObject (no superes unos 2 000).
  • Agua: Cell Subdivisions 1 reduce a una cuarta parte los vértices del agua en comparación con el valor predeterminado 2.

Burst y jobs

Todos los bucles pesados se ejecutan en jobs compilados con Burst: meshes de chunks (BuildChunkGeometryJob, CopyChunkPartsJob), mips de splat (SplatMipDownsampleJob), meshes de agua (WaterChunkMeshJob), dispersión de detalles (DetailScatterJob), matrices de árboles (TreeMatrixJob) y colocación de decoración (DecorPlacementJob, DecorPartMatricesJob). Los pinceles, el pathing, la niebla de guerra, el buscador de rutas y el precálculo del minimapa también usan Burst. Los colliders de los chunks se precalculan en paralelo en hilos de trabajo (worker threads).

  • Los jobs de meshes se compilan de forma síncrona, así que la primera construcción tras una recarga de dominio no recurre a una alternativa lenta en código gestionado.
  • Mide en una build del juego o con las comprobaciones de seguridad de Burst desactivadas (Jobs ▸ Burst ▸ Safety Checks). Las comprobaciones de seguridad del editor y el Jobs Debugger hacen que los jobs sean notablemente más lentos.
  • Con la compilación de Burst desactivada todo sigue funcionando, pero las reconstrucciones de meshes y los trazos de pincel se vuelven muchas veces más lentos.
  • Las builds para Web no tienen hilos de trabajo, así que todos los jobs se ejecutan uno tras otro en el hilo principal. En ellas, las operaciones sobre todo el mapa tardan notablemente más (consulta Renderizado y rendimiento de las builds para Web).

Perfilado

Abre Window ▸ Analysis ▸ Profiler y busca estos marcadores en el hilo principal:

MarcadorQué mide
RTSVegetation.UpdateLa aplicación de los cambios del mapa a la vegetación (incluye los dos marcadores de construcción siguientes).
RTSVegetation.BuildDetailsLa regeneración de los parches de detalles modificados.
RTSVegetation.BuildTreesLa reconstrucción de los grupos de árboles instanciados.
RTSVegetation.DrawEl culling y la emisión de las draw calls de vegetación para una cámara.

Cambia el módulo CPU a Timeline para ver en los hilos de trabajo los nombres de los jobs mencionados arriba. En la GPU, usa el Frame Debugger (Window ▸ Analysis ▸ Frame Debugger) para contar las draw calls de los chunks del terreno, de los detalles y de los objetos instanciados. En el Inspector, los componentes Vegetation Renderer, Water Renderer y Decor Manager muestran en tiempo real el número de instancias y de vértices. El Inspector de RTSTerrain muestra el número de chunks y la memoria del mapa.

Cifras de memoria

Los datos del mapa en sí (el valor Map Memory del Inspector de RTSTerrain, MapData.GetMemoryUsage()) ocupan:

  • 11 bytes por vértice (altura, nivel de acantilado, indicadores, tipo de acantilado, altura del agua),
  • 16 bytes por texel de splat,
  • 1 byte por texel de pathing,
  • 1 byte por texel de detalle y por capa de detalle.
Tamaño del mapa (valores predeterminados: splat 4, detalle 2, pathing 2, 1 capa de detalle)Datos del mapaTexturas de splat (GPU)Arrays de texturas (1024, BC7)
128 × 128 (256 m con celdas de 2 m)≈ 4.3 MiB≈ 5.3 MiB53 MiB por tileset
256 × 256≈ 17 MiB≈ 21 MiB53 MiB por tileset
512 × 512≈ 69 MiB≈ 85 MiB53 MiB por tileset
1024 × 1024≈ 275 MiB≈ 341 MiB53 MiB por tileset

A esto se suman los meshes y colliders de los chunks (consulta Subdivisiones), las instancias de detalle (32 bytes cada una), el mapa de pathing y las texturas de la niebla de guerra. El historial de deshacer del editor tiene su propio presupuesto de memoria (consulta la configuración de la ventana del editor).

Tamaños de mapa recomendados

Los mapas pueden tener de 16 a 1024 celdas por lado, en pasos de 16. El tamaño predeterminado es de 128 × 128 celdas de 2 m.

Tamaño (celdas)Uso típicoNotas
64–128Mapas 1v1 y mapas pequeños de escaramuza, móvilesTodo se reconstruye al instante. Un buen tamaño para el editor en el juego en cualquier dispositivo.
128–2562–4 jugadoresEl punto óptimo. Todos los valores predeterminados están ajustados para este rango.
256–512Mapas grandes, 4–8 jugadoresEscritorio. Plantéate una resolución de splat de 2–4 y un Chunk Size de 32–64.
más de 512Mapas muy grandesLa validación del mapa muestra un aviso de tipo Info: la edición y las reconstrucciones del pathing tardan más. Vigila la memoria de splat (tabla de arriba) y plantéate dividir el mundo en varios RTSTerrain.

Se pueden colocar varios terrenos uno junto a otro, cada uno con su propio transform. RTSTerrain.FindAt y RTSTerrain.RaycastAll encuentran el adecuado para una posición del mundo.

Notas para móviles

  • Arrays de texturas: constrúyelos con la plataforma Android/iOS activa, para que se precalculen como ASTC 6×6 (una vez construidos, las builds del juego los mantienen actualizados para la plataforma activa). En teléfonos suele bastar con Array Resolution 512.
  • Resolución de splat: 2 texels por celda reducen la memoria de splat a una cuarta parte en comparación con el valor predeterminado 4.
  • Capas: cada capa de suelo visible en un píxel cuesta muestreos de textura. Usa menos capas por zona y pon Anti Tiling a 0 en las capas donde la repetición no se note (ahorra el segundo muestreo).
  • Iluminación: usa un material de sustitución (override) con Specular Highlights y Environment Reflections desactivados (consulta más arriba).
  • OpenGL ES y WebGL 2.0: los shaders se compilan para Shader Model 3.5, así que funcionan en dispositivos OpenGL ES 3.0 y en navegadores. Estas API solo garantizan 16 texturas de fragmento por draw call, y la pasada forward del terreno ya usa 10 propias. El asset de URP Mobile de las plantillas de proyecto URP de Unity (sombras de la luz principal, mezcla de reflection probes) ya añade otras 4, y con las light cookies se alcanza el límite. Si una variante necesita más texturas, no se carga, y el terreno desaparece o se ve rosa. En el asset de URP que se usa para los dispositivos GLES y las builds para Web, desactiva la mezcla de reflection probes si tus escenas no mezclan probes, y mantén desactivados los decals, las light cookies, el SSAO, las sombras de las luces adicionales y los adaptive probe volumes salvo que los necesites. El comentario Texture slot budget de Shaders/Include/RTSTerrainInput.hlsl indica el coste de cada función de URP.
  • Agua: si, para ahorrar ancho de banda, desactivas Depth Texture u Opaque Texture en el asset de URP para móviles, el agua pasa a su modo alternativo: sin la textura opaca no hay refracción, y sin la textura de profundidad las orillas ignoran los objetos que hay en el agua. Una cámara que las active por su cuenta sigue mostrando el agua completa. Usa Cell Subdivisions 1.
  • Vegetación: reduce los multiplicadores de Detail Density y Detail Distance, usa Crossed Quad Count 2 y desactiva las sombras de los detalles.
  • Física: si tu juego no usa la física de Unity en el terreno, pon Collider Mode en None y usa RTSTerrain.Raycast / RaycastAll para la selección.
  • Geometría: Surface Subdivisions 1 (el aspecto clásico de los RTS) y Cliff Wall Segments 2 reducen considerablemente los vértices.

Un preset para equipos de gama baja desde código

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 elimina los colliders de los chunks. Todo lo que en tu juego use Physics.Raycast contra el terreno deja de funcionar, igual que los rigidbodies apoyados sobre él o el precálculo del NavMesh a partir de los colliders de los chunks. Haz este cambio solo si usas las consultas que no dependen de la física.

Sistemas de juego

RTS Terrain Studio es más que un editor de mapas. Los mapas que creas incluyen los sistemas que necesita un RTS clásico: una niebla de guerra que respeta los niveles de acantilado y los árboles, un mapa de pathing (dónde pueden caminar, construir y volar las unidades) con búsqueda de rutas integrada, scripts de juego de ejemplo listos para usar (cámara, unidades, selección, órdenes, minimapa, HUD) y un generador de contenido de demostración que crea texturas, modelos, mapas y escenas de demostración jugables.

Este capítulo presenta los sistemas uno por uno. Cada sección empieza con lo que puedes hacer sin código, después enumera los componentes y sus ajustes, y termina con la API de scripting para programadores.

¿Solo quieres jugar? Ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content, haz doble clic en la escena RTS Terrain Studio - Gameplay Demo, que queda seleccionada en la ventana Project, y presiona Play. Consulta Escenas de demostración.

Niebla de guerra en la escena Gameplay Demo: unidades y un edificio inicial; la zona detrás de un acantilado más alto está negra
Niebla de guerra en la escena Gameplay Demo: las unidades revelan su entorno; la meseta detrás del acantilado más alto sigue negra porque el terreno más alto bloquea la línea de visión (reglas clásicas de los RTS).

Niebla de guerra

La niebla de guerra oculta lo que un jugador no puede ver. Sigue las reglas clásicas de los RTS:

  • Por equipo. La visibilidad se registra para hasta 32 equipos (índices de equipo de 0 a 31). Cada unidad o edificio da visión a su propio equipo.
  • Tres estados. El terreno que un equipo nunca ha visto aparece en negro (inexplorado). El terreno que ha visto antes, pero que no puede ver en este momento, aparece atenuado (explorado). El terreno que ve en este momento se muestra con normalidad (visible).
  • Memoria de exploración. Cada equipo recuerda lo que ha explorado hasta que lo restableces.
  • Línea de visión con niveles de acantilado. Una unidad ve su propio nivel de acantilado y todo lo que está por debajo. El terreno más alto nunca es visible desde abajo y bloquea la vista de las celdas que hay detrás.
  • Bloqueadores. Los árboles (y la decoración que marques como bloqueadora de visión) ocultan las celdas que hay detrás de ellos.
  • Alianzas y visión compartida. Los equipos pueden compartir su visión en un solo sentido o en ambos.
  • Enemigos ocultos. Las unidades enemigas desaparecen mientras no puedes verlas. Los edificios siguen visibles como «fantasmas» una vez que los has descubierto.

La visibilidad se calcula con jobs de Burst unas cuantas veces por segundo y se guarda en una textura. La textura se suaviza con el tiempo y se desenfoca en la GPU, de modo que los bordes de la niebla de guerra se mueven con suavidad en lugar de saltar de celda en celda.

Inicio rápido: añadir niebla de guerra a tu escena

  1. Abre una escena que contenga un RTS Terrain.
  2. Elige GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager. Esto crea un GameObject Fog of War con un componente RTSFogOfWarManager y lo selecciona.
  3. Si tu renderer de URP todavía no tiene la renderer feature de niebla de guerra, un cuadro de diálogo te ofrece añadirla. Haz clic en Add Feature. (También puedes hacerlo más tarde con Tools ▸ RTS Terrain Studio ▸ Validate Project, o a mano; consulta Renderizado).
  4. Añade un componente RTSFogOfWarRevealer (Add Component ▸ RTS Terrain Studio ▸ Fog of War Revealer) a cada unidad y edificio que deba ver. Configura su Team y su Radius.
  5. Añade un componente RTSFogOfWarHideable (Add Component ▸ RTS Terrain Studio ▸ Fog of War Hideable) a cada objeto que los enemigos no deban ver. Configura su Team. Usa Hide When Not Visible para las unidades y Ghost When Explored para los edificios.
  6. Presiona Play. El mapa aparece en negro salvo alrededor de los reveladores del Local Team (el equipo 0 de forma predeterminada).

La niebla de guerra solo funciona en el modo Play. Para verla mientras editas, activa Preview In Edit Mode en el gestor (o haz clic en Enable Preview en su Inspector). Los componentes ocultables solo ocultan objetos en el modo Play; en el modo Edit, todo sigue visible.

La unidad de ejemplo RTSUnit ya requiere y configura un componente revelador y uno ocultable. Si usas los ejemplos (o RTSDemoGame), no necesitas añadir estos componentes a mano. RTSDemoGame incluso crea el gestor por ti cuando la escena no tiene ninguno.

Cómo se calcula la visibilidad

El mapa se divide en celdas de niebla. Con el valor predeterminado de Resolution Per Cell (2) y el tamaño de celda predeterminado del mapa (2 m), una celda de niebla mide 1 m de ancho. Para cada revelador, el gestor:

  1. Determina el nivel de visión: el nivel de acantilado en la posición exacta del revelador, más su Height Bonus.
  2. Recorre todas las celdas de niebla que hay dentro del Radius de visión.
  3. Según el ajuste Line Of Sight, traza una línea desde el revelador hasta la celda. La celda es visible si su propio nivel de acantilado no es más alto que el nivel de visión y ninguna celda del camino es más alta que el nivel de visión ni (con bloqueadores) contiene un árbol o decoración que bloquee la visión. La propia celda que bloquea es visible, así que ves el borde de un bosque, pero no lo que hay detrás.
  4. Marca la celda como visible y explorada para el equipo del revelador y para cada equipo que recibe la visión de ese equipo.
Modo de Line Of SightComportamientoÚsalo para
NoneLos reveladores ven un círculo completo. Los acantilados y los árboles no bloquean la visión. Es el modo más económico.Juegos sin línea de visión, cantidades muy grandes de unidades.
CliffLevelsRegla clásica de los RTS: un revelador ve su propio nivel de acantilado y todo lo que está por debajo. El terreno más alto bloquea la vista.Mapas abiertos, o cuando los árboles no deben bloquear la visión.
CliffLevelsAndBlockers (predeterminado)Como CliffLevels, y además los árboles y la decoración que bloquea la visión ocultan las celdas que hay detrás de ellos.La experiencia RTS clásica completa.

Los objetos que bloquean la visión se definen en las bibliotecas:

  • Árboles: la opción Blocks Vision de un prototipo de árbol en la biblioteca de vegetación (activada de forma predeterminada). La zona bloqueada es el Footprint Radius del árbol multiplicado por la escala de la instancia.
  • Decoración: la opción Blocks Vision de un prototipo de decoración en la biblioteca de decoración (desactivada de forma predeterminada). La zona bloqueada es la huella del prototipo (círculo o rectángulo girado), escalada con la instancia.

Torres de vigilancia, colinas y exploradores voladores: dale a su revelador un Height Bonus de uno o más niveles para que puedan ver por encima de acantilados más altos, o activa Ignore Line Of Sight en las unidades aéreas para que nada les bloquee la visión.

Los cambios en el mapa se detectan automáticamente (en el Unity Editor, en el editor en el juego y desde el código de juego que llama a RTSTerrain.NotifyChanged). Los cambios en los niveles de acantilado (MapLayers.CliffLevels / VertexFlags) vuelven a muestrear solo las celdas modificadas más un margen de una celda. Los cambios en árboles y decoración reconstruyen el ráster de bloqueadores de visión de todo el mapa en la siguiente actualización. Una llamada a NotifyChangedAll reconstruye por completo el ráster de acantilados.

RTSFogOfWarManager

Un único gestor por escena calcula y renderiza la niebla de guerra para un terreno. Créalo con GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager o con Add Component ▸ RTS Terrain Studio ▸ Fog of War Manager. Si hay varios gestores activados, solo se usa el primero (RTSFogOfWarManager.Active) y el Inspector te avisa.

AjustePredeterminadoDescripción
Map
TerrainvacíoTerreno cubierto por la niebla de guerra. Si está vacío, se usa RTSTerrain.Main (el primer terreno activado).
Resolution Per Cell2Celdas de niebla por celda del mapa y por eje: 1, 2 o 4. Los valores más altos dan bordes más suaves, pero son más costosos. Se reduce automáticamente para que la cuadrícula de niebla no supere las 2048 celdas por eje. Cambiarlo reconstruye la cuadrícula y borra la exploración.
Simulation
Update Interval0.1Segundos entre actualizaciones de visibilidad. Con 0 se actualiza en cada fotograma.
Local Team0Equipo cuya vista se renderiza y que usan los objetos ocultables (0 a 31). Cámbialo en el Inspector para comprobar lo que ven otros jugadores.
Shared Visiontodos a 0Para cada equipo receptor, una máscara de bits de los equipos que comparten su visión con él (bit t = equipo t). Normalmente se configura desde código; consulta Alianzas.
Line Of SightCliffLevelsAndBlockersCómo bloquean la visión los acantilados y los objetos. Consulta Cómo se calcula la visibilidad.
Compute All TeamsdesactivadoCalcula la visibilidad de todos los equipos, no solo la del equipo local. Lo necesitan los servidores (gestión de interés) y el código de IA que consulta lo que puede ver otro equipo.
Start ExploreddesactivadoTodo el mapa empieza explorado: sin zonas negras, solo con la niebla atenuada. Se aplica cuando se crea la cuadrícula de niebla.
Reveal AlldesactivadoDepuración: todo es visible y está explorado para todos los equipos.
Look
Unexplored Colornegro, alfa 1Color de las zonas que nunca se han explorado. El alfa es la intensidad.
Explored Color(0.3, 0.32, 0.38), alfa 0.6Tinte que se multiplica sobre las zonas exploradas que no son visibles en este momento. El alfa es la intensidad.
Fade Speed4Velocidad a la que la niebla aparece y desaparece (fundidos completos por segundo). 0 es instantáneo.
Blur Iterations1Pasadas de desenfoque en la GPU que se aplican a la textura de niebla (0 a 4, 0 = desactivado).
Blur Upscale2Multiplicador de resolución de la textura desenfocada (1 a 4). Los valores más altos dan bordes más redondeados. Se reduce automáticamente para que la textura desenfocada no supere los 2048 píxeles.
Editor
Preview In Edit ModedesactivadoEjecuta la niebla de guerra en el modo Edit como vista previa.
Shaders
Blur Shader, Minimap Shaderasignados automáticamenteSe referencian aquí para que los shaders ocultos se incluyan en las builds del juego. Déjalos como están.

El Inspector también muestra estadísticas en vivo (estado, tamaño de la cuadrícula de niebla, memoria, reveladores activos, número de actualizaciones), una vista previa en vivo de la textura de niebla (rojo = visible ahora, verde = explorado, negro = nunca visto) y estos botones:

BotónQué hace
Explore AllMarca todo el mapa como explorado para el equipo local.
Reset ExplorationOlvida todo lo que ha explorado cada equipo.
Update NowRecalcula la visibilidad de inmediato.
Rebuild Terrain DataVuelve a leer del terreno los niveles de acantilado y los bloqueadores.

RTSFogOfWarRevealer

Da a su equipo visión alrededor del GameObject. Ponlo en unidades, edificios, centinelas y cualquier otra cosa que deba ver. El revelador se registra en el gestor cuando se activa; el gestor lee su posición en cada actualización, así que el componente no tiene ningún coste por fotograma. Selecciónalo para ver su radio de visión como un círculo amarillo en la Scene View.

AjustePredeterminadoDescripción
Team0Equipo que recibe la visión (0 a 31).
Radius12Radio de visión en metros.
Height Bonus0Niveles de acantilado adicionales por encima de los que puede ver el revelador (torres de vigilancia, colinas, exploradores voladores).
Ignore Line Of SightdesactivadoVe por encima de acantilados y bloqueadores (unidades aéreas).
Reveal When DisableddesactivadoSigue revelando mientras este componente o su GameObject están desactivados, hasta que se destruye.

Los reveladores con Reveal When Disabled en GameObjects que ya están inactivos cuando se carga la escena se recopilan una sola vez, cuando se activa el gestor. Los objetos que instancias inactivos más tarde (pools de objetos, escenas aditivas) nunca ejecutan Awake, así que regístralos tú mismo con RTSFogOfWarManager.RegisterRevealer(revealer), o asigna RevealWhenDisabled = true desde código.

RTSFogOfWarHideable

Oculta los renderers (y, opcionalmente, los canvas y los colliders) de un objeto enemigo mientras el equipo local no puede verlo. Los objetos del equipo local, y los de los equipos que comparten su visión con él, nunca se ocultan. Solo se vuelven a activar los componentes que estaban activados cuando el objeto se ocultó, así que tu propia lógica de activar y desactivar sigue funcionando.

AjustePredeterminadoDescripción
Team0Equipo propietario del objeto (0 a 31).
ModeHideWhenNotVisibleHideWhenNotVisible: se muestra solo mientras la posición es visible en este momento (unidades). GhostWhenExplored: sigue mostrándose después de haber sido descubierto, aunque la zona vuelva a quedar cubierta por la niebla (edificios y props neutrales, como en los RTS clásicos).
Require SightingactivadoSolo en el modo fantasma: el objeto tiene que haberse visto una vez. Si está desactivado, basta con que la zona esté explorada para mostrarlo.
Hide CanvasesactivadoDesactiva también los componentes Canvas (barras de vida, placas de nombre).
Hide CollidersdesactivadoDesactiva también los colliders mientras está oculto, para que no se pueda hacer clic en los enemigos ocultos. Déjalo desactivado en servidores autoritativos y hosts: sus consultas de física deben seguir alcanzando las unidades que el jugador local no puede ver.
PivotvacíoTransform opcional cuya posición se comprueba. Si está vacío, se usa este transform.

El componente ocultable lanza VisibilityChanged(bool) cuando el objeto se muestra o se oculta. Llama a RefreshComponents() después de añadir o quitar renderers hijos en tiempo de ejecución.

Renderizado: renderer feature y respaldo en los shaders

La niebla de guerra se puede dibujar de dos formas. La elección se hace por cámara, así que la niebla nunca se aplica dos veces.

RutaCuándo se usaQué se oscurece
RTS Fog Of War renderer feature (recomendada)La cámara renderiza con un Universal Renderer que contiene la feature.Todo lo que hay en pantalla: terreno, vegetación, agua, decoración, unidades y cualquier material personalizado. El cielo no se modifica.
Respaldo en los shadersEl renderer de la cámara no contiene la feature.Solo los shaders de terreno, agua y vegetación del propio paquete. Las unidades y los props con otros materiales no se oscurecen (las unidades enemigas siguen ocultas mediante sus componentes ocultables).

Añadir la renderer feature a mano:

  1. Selecciona tu asset Universal Renderer Data (el renderer que usan las cámaras de tu juego).
  2. En su Inspector, haz clic en Add Renderer Feature y elige RTS Fog Of War.
  3. Repite esto en cada renderer que usen las cámaras del juego para obtener resultados coherentes.

O deja que Tools ▸ RTS Terrain Studio ▸ Validate Project lo corrija: añade la feature al renderer predeterminado de cada asset de URP en uso.

Ajuste de la featurePredeterminadoDescripción
Show In Scene ViewactivadoDibuja la niebla también en las cámaras de la Scene View. Si está desactivado, las cámaras de la Scene View de este renderer muestran el mapa sin niebla.
Render Pass EventAfterRenderingTransparentsCuándo se dibuja la niebla. El valor predeterminado se ejecuta antes del posprocesado, así que el bloom y el tonemapping se aplican encima de la niebla.

La feature solicita por sí misma la textura de profundidad de la cámara y la usa para reconstruir la posición en el mundo de cada píxel. RTSFogOfWarRendererFeature.IsActive indica si existe una feature activada en algún renderer del asset de URP activo; es solo informativo.

Shaders personalizados (avanzado). El gestor publica la niebla de guerra como variables globales de shader: _RTS_FogOfWarTex (R = visible, G = explorado), _RTS_FogOfWarParams (X mínima, Z mínima, 1/tamaño X, 1/tamaño Z en espacio del mundo), _RTS_FogOfWarUnexplored, _RTS_FogOfWarExplored y _RTS_FogOfWarEnabled. Los shaders del paquete incluyen Shaders/Include/RTSCommon.hlsl (después del Core.hlsl de URP) y llaman a su función auxiliar sobre el color iluminado. Un shader de URP personalizado puede hacer lo mismo para la ruta de respaldo:

#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);
}

Programar la niebla de guerra

Todos los tipos de la niebla de guerra están en el espacio de nombres RTSTerrainStudio. Obtén el gestor de la escena con RTSFogOfWarManager.Active (suscríbete al evento estático ActiveChanged si los gestores aparecen y desaparecen).

MiembroDescripción
Consultas
bool IsVisible(Vector3 world, int team)Devuelve true si el equipo ve la posición en este momento (visión propia y compartida), y false para los equipos que no se calculan (consulta IsTeamComputed).
bool IsExplored(Vector3 world, int team)Devuelve true si el equipo ha visto la posición al menos una vez.
bool IsVisibleToLocalTeam(Vector3), bool IsExploredByLocalTeam(Vector3)Atajos para el equipo local.
uint GetVisibilityMask(Vector3 world)Máscara de bits de los equipos calculados que ven la posición (bit t = equipo t). Ideal para la gestión de interés en el servidor.
uint GetExploredMask(Vector3 world)Máscara de bits de los equipos que han explorado la posición.
bool IsTeamComputed(int team)Siempre true con Compute All Teams; si no, solo para el equipo local.
bool TryGetFogCell(Vector3, out int2), Vector3 FogCellToWorld(int2)Convierten entre posiciones del mundo y celdas de niebla.
bool IsCellVisible(int2, int team), bool IsCellExplored(int2, int team)Versiones por celda de las consultas.
int GetCellLevel(int2), bool IsCellBlocked(int2)Nivel de acantilado y estado de bloqueo que se usan para la línea de visión.
Vector2 SampleDisplayedFog(Vector2 normalizedMapPosition)Valores suavizados que se muestran en pantalla para el equipo local (x = visible, y = explorado). Devuelve (1, 1) cuando no hay ninguna niebla de guerra activa.
Revelado y exploración
void RevealArea(Vector3 world, float radius, int team, float duration, bool ignoreLineOfSight = false)Revela un círculo para un equipo (bengalas, habilidades del tipo «revelar una zona del mapa»). duration se mide en tiempo de juego en el modo Play y en tiempo real en la vista previa del modo Edit. Un valor de 0 o menos revela solo durante una actualización.
void ClearTemporaryReveals()Elimina todas las zonas reveladas con RevealArea.
void ExploreAll(int team), void ExploreAllTeams()Marcan todo el mapa como explorado.
void ResetExploration(), void ResetExploration(int team)Olvidan la exploración (las zonas visibles en ese momento vuelven a quedar exploradas en la siguiente actualización).
Alianzas y visión compartida
void SetAlliance(int teamA, int teamB, bool allied)Comparte la visión en ambos sentidos o deja de compartirla.
void SetSharedVision(int sourceTeam, int receiverTeam, bool shared)Permite que receiverTeam vea lo que ve sourceTeam (en un solo sentido).
void SetSharedVisionMask(int receiverTeam, uint sourceTeams), uint GetSharedVision(int receiverTeam)Reemplazan o leen la máscara completa de un equipo receptor.
bool HasSharedVision(int sourceTeam, int receiverTeam), uint GetViewMask(int team)Consultan la visión compartida; la máscara de vista incluye al propio equipo.
bool IsFriendlyToLocalTeam(int team)Devuelve true para el equipo local y para los equipos que comparten su visión con él. Sus objetos nunca se ocultan.
Control de las actualizaciones
void RequestUpdate(), void ForceUpdate()Recalculan en la siguiente actualización o de inmediato.
void MarkTerrainDirty()Reconstruye por completo el ráster de niveles de acantilado. Los cambios en el mapa se detectan automáticamente, así que rara vez lo necesitarás.
void MarkBlockersDirty()Reconstruye el ráster de bloqueadores después de mover árboles o decoración desde código sin llamar a RTSTerrain.NotifyChanged.
event Action UpdatedSe lanza después de cada actualización de visibilidad, una vez actualizados los objetos ocultables.
Estado
IsSimulating, IsRunning, IsRenderingFog, HasGrid, HasVisibilityDataIndican si la niebla de guerra se está ejecutando, si tiene una cuadrícula y si ya se ha calculado al menos una vez.
FogTexture, RawFogTexture, FogWidth, FogHeight, FogCellSize, Version, ActiveRevealerCount, MemoryUsageInformación de la cuadrícula y de la textura.
int EffectiveResolutionPerCellCeldas de niebla por celda del mapa que se usan realmente. Es menor que ResolutionPerCell en mapas enormes, donde la cuadrícula se limita a MaxFogGridSize (2048).
RTSTerrain Terrain, RTSTerrain BoundTerrainEl terreno que se debe cubrir (null = RTSTerrain.Main) y el terreno para el que está construida la cuadrícula actualmente.
Ajustes (los mismos que en el Inspector)
ResolutionPerCell, LocalTeam, LineOfSight, ComputeAllTeams, RevealAll, FadeSpeed, BlurIterations, BlurUpscalePropiedades modificables. Cambiar ResolutionPerCell reconstruye la cuadrícula y borra la exploración.
const int MaxTeams (32), const int MaxFogGridSize (2048)Límite de equipos y lado máximo de la cuadrícula de niebla.
Registros
static IReadOnlyList<RTSFogOfWarRevealer> Revealers, static IReadOnlyList<RTSFogOfWarHideable> HideablesTodos los reveladores y objetos ocultables activados de todos los gestores.
static RegisterRevealer / UnregisterRevealer, RegisterHideable / UnregisterHideableLos llaman los propios componentes; solo los necesitas para componentes personalizados.
RTSFogOfWarHideable y RTSFogOfWarRevealer
bool IsVisible, bool HasBeenSighted (ocultable)Estado actual, y si el objeto se ha visto alguna vez (modo fantasma).
Vector3 TestPosition (ocultable)La posición que se comprueba contra la niebla (el Pivot o el transform).
void Reevaluate(), void ResetSighting() (ocultable)Vuelve a comprobar ahora; olvida que el objeto se ha avistado.
event Action<bool> VisibilityChanged (ocultable)Se lanza cuando el objeto aparece o desaparece para el equipo local.
bool IsRevealing (revelador)Devuelve true mientras el revelador se tiene en cuenta (porque está activado o porque Reveal When Disabled está activado).

Ejemplo: una habilidad de bengala y una comprobación de visibilidad.

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);
    }
}

Ejemplo: alianzas y una unidad generada desde código.

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"));
    }
}

Minimapa con niebla de guerra

RTSFogOfWarMinimapUtility oscurece una imagen de minimapa con la niebla de guerra del equipo local, como en el minimapa de un RTS clásico. El minimapa debe cubrir exactamente el área del mapa, con las UV (0, 0) en la esquina de origen del mapa y las UV (1, 1) en la esquina opuesta. Las imágenes precalculadas con RTSMinimapBaker tienen esta disposición. El ejemplo RTSMinimapUI usa esta utilidad.

MétodoDescripción
static bool Composite(Texture minimap, RenderTexture destination, RTSFogOfWarManager manager = null)Variante en GPU, ideal para un minimapa en tiempo real. Llámalo después de Updated o cada pocos fotogramas. Sin una niebla de guerra activa, el minimapa se copia sin cambios. Devuelve false cuando el shader no está disponible.
static bool CompositeCpu(Texture2D source, Texture2D destination, RTSFogOfWarManager manager = null)Variante en CPU (job de Burst), sin necesidad de render textures; por ejemplo, para capturas de pantalla. source debe ser legible; destination debe ser una textura RGBA32 legible del mismo tamaño (puede ser la misma textura).
static bool CompositeCpu(NativeArray<Color32> source, NativeArray<Color32> destination, int width, int height, RTSFogOfWarManager manager = null)Variante en CPU sobre búferes de píxeles sin procesar.
static void ReleaseResources()Destruye el material y el búfer de comandos almacenados en caché. Se llama automáticamente antes de cada recarga del dominio; llámalo tú mismo cuando cierres el minimapa definitivamente.
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);
    }
}

Consejos para multijugador

  • Cada cliente ejecuta su propia niebla de guerra para su Local Team. La niebla de guerra nunca se envía por la red.
  • En un servidor autoritativo, activa Compute All Teams y usa GetVisibilityMask(position) para decidir qué clientes reciben qué unidades. Una sola consulta por unidad es muy barata.
  • Mantén Hide Colliders desactivado en los objetos ocultables del servidor y del host de un listen server, para que los raycasts del lado del servidor sigan alcanzando las unidades que el jugador del host no puede ver. En su lugar, filtra la selección del jugador con IsVisibleToLocalTeam.
  • Las alianzas son ajustes locales. Llama a SetAlliance en el servidor y en los clientes si los clientes deben mostrar la visión aliada.

La integración de Mirror hace todo esto por ti: RTSNetworkFogOfWar configura el gestor (el servidor calcula todos los equipos; un cliente, solo el suyo), y RTSFogOfWarInterestManagement nunca envía unidades enemigas ocultas por la niebla de guerra (protección contra maphack). Consulta el capítulo de la integración de Mirror.

Rendimiento y límites

  • La visibilidad se calcula con jobs de Burst cada Update Interval segundos (0.1 de forma predeterminada), no en cada fotograma. Los reveladores y los objetos ocultables se guardan en listas internas, así que nada recorre la escena en cada fotograma.
  • El consumo de memoria es de unos 18 bytes por celda de niebla. Un mapa de 128 × 128 con Resolution Per Cell igual a 2 tiene una cuadrícula de 256 × 256, que ocupa unos 1.2 MB.
  • Mapas grandes: la cuadrícula está limitada a 2048 celdas por eje, y la resolución por celda se reduce automáticamente para no superar ese límite.
  • None es el modo de línea de visión menos costoso; CliffLevelsAndBlockers es el más costoso, pero solo se trazan líneas para los reveladores que tienen cerca una celda más alta o un bloqueador.
  • La textura de la niebla de guerra está alineada con los ejes del mapa. Un terreno rotado registra una advertencia.
  • Tras editar árboles o decoración, el ráster de bloqueadores de visión se reconstruye para todo el mapa; las ediciones de acantilados solo actualizan la zona modificada.

¿No aparece la niebla de guerra? Comprueba que el gestor está activado y que estás en el modo Play (o que Preview In Edit Mode está activado), que Reveal All está desactivado y que el terreno tiene un mapa. Si la niebla de guerra solo cubre el terreno, pero no las unidades ni los props, añade la renderer feature RTS Fog Of War. Si todo se ve negro, no hay ningún revelador activo del equipo local (ni de un equipo que comparta visión con él). El mensaje «No revealers exist» del Inspector solo aparece cuando no hay ningún revelador en toda la escena, así que comprueba también el Team de tus reveladores y compáralo con Local Team.

Pathing y búsqueda de rutas

Cada RTS Terrain mantiene un mapa de pathing: una cuadrícula que indica, para cada pequeño cuadrado del mapa, si las unidades terrestres pueden caminar por él, si se pueden colocar edificios, si las unidades aéreas pueden volar y si hay agua. Se deriva automáticamente del terreno y se actualiza mientras editas. Además, el paquete incluye un buscador de rutas integrado y rápido (A* con Burst) que puedes usar sin ningún paquete de terceros.

Si prefieres A* Pathfinding Project o el NavMesh de Unity, las integraciones construyen sus grafos a partir del mismo mapa de pathing; consulta sus capítulos.

El mapa de pathing

El mapa de pathing tiene PathingResolution celdas de pathing por celda del mapa y por eje (1, 2 o 4, se elige al crear el mapa; predeterminado: 2). Con las celdas de mapa predeterminadas de 2 m, una celda de pathing mide 1 m de ancho. Cada celda guarda un conjunto de PathingFlags:

IndicadorSignificado
WalkableLas unidades terrestres pueden caminar aquí.
BuildableSe pueden colocar edificios aquí.
FlyableLas unidades aéreas pueden volar aquí. Está activo en todas partes, salvo que un efecto Unflyable lo elimine: un ajuste manual de pathing pintado, la regla de capa de suelo del tileset o el Footprint Effect de un prototipo de decoración.
ShallowWaterAgua poco profunda: transitable, pero no edificable de forma predeterminada.
DeepWaterAgua profunda: solo para unidades navales.
CliffLa celda toca una pared de acantilado.
BlockedLa celda está obstruida por la huella de un árbol o de un objeto decorativo.
SteepLa celda es demasiado empinada para las unidades terrestres.

Cómo se derivan los indicadores. Las reglas se aplican en este orden, como jobs de Burst:

  1. Terreno. Las paredes de acantilado (ensanchadas por Cliff Margin) pasan a ser Cliff. Las pendientes por encima de Max Walkable Slope pasan a ser Steep. El agua más profunda que el límite de profundidad de agua poco profunda del tileset pasa a ser DeepWater; el resto, ShallowWater. Flyable se activa en todas partes. Buildable = transitable, más plano que Max Buildable Slope y seco (o agua poco profunda con Shallow Water Buildable). Después se aplica el efecto de pathing de la capa de suelo dominante (por ejemplo, «Unbuildable» en roca irregular). Una rampa válida entre dos niveles de acantilado solo se evalúa según su altura fina, por lo que sigue siendo transitable; las rampas nunca son edificables.
  2. Huellas. Los árboles obstruyen un círculo (Footprint Radius del prototipo de árbol × escala de la instancia × Tree Footprint Scale): activan Blocked y eliminan Walkable y Buildable. La decoración aplica el Footprint Effect de su prototipo (de forma predeterminada, Unwalkable y Unbuildable) en un círculo o en un rectángulo rotado. Se omiten las instancias de decoración con el indicador IgnorePathing.
  3. Ajustes manuales de pathing pintados con las herramientas de Pathing (más abajo). Un efecto Unwalkable también elimina Buildable, salvo que esté activado Force Buildable.

El mapa se reconstruye de forma incremental: cuando editas una región, solo esa región (más un pequeño margen) se vuelve a derivar.

Reglas de pathing (RTSPathingSettings)

Selecciona el RTS Terrain y abre la sección Pathing Rules de su Inspector. Al cambiar un valor, se reconstruye el mapa de pathing.

AjustePredeterminadoDescripción
Max Walkable Slope42El suelo con una pendiente mayor que este ángulo (en grados) no es transitable.
Max Buildable Slope18El suelo con una pendiente mayor que este ángulo (en grados) no es edificable.
Cliff Margin0.25Hasta dónde se extiende la zona obstruida más allá de una pared de acantilado (en fracciones de una celda de pathing). Los valores más altos mantienen a las unidades más alejadas de los bordes de acantilado.
Shallow Water BuildabledesactivadoPermite construir edificios en agua poco profunda.
Tree Footprint Scale1Multiplicador del radio de la huella de pathing de todos los árboles.
Use Ground Layer RulesactivadoAplica el efecto de pathing de la capa de suelo dominante (se define por capa en el tileset; por ejemplo, «Unbuildable» en roca irregular).

Desde código, asigna una nueva configuración con terrain.PathingSettings = settings (esto provoca una reconstrucción), o cambia campos de la instancia actual y llama a terrain.RebuildPathing().

Pintar el pathing a mano

A veces las reglas automáticas no dan lo que necesitas: un muro invisible en el borde del mapa, una zona no edificable para dejar libre una expansión o un puente transitable. Usa las herramientas de Pathing (en la Scene View, 7 selecciona la categoría Pathing):

Herramienta / ajuste manualEfecto
Paint Pathing: BlockerNi transitable ni edificable: un muro invisible (bordes del mapa, zonas decorativas).
Paint Pathing: UnwalkableLas unidades terrestres no pueden caminar aquí.
Paint Pathing: UnbuildableNo se pueden colocar edificios aquí (para mantener libres los caminos y las expansiones).
Paint Pathing: UnflyableLas unidades aéreas no pueden volar aquí.
Paint Pathing: Force WalkableLas unidades terrestres pueden caminar aquí aunque la pendiente, el agua o las huellas lo impidan.
Paint Pathing: Force BuildableSe pueden colocar edificios aquí aunque las reglas derivadas lo prohíban.
Clear PathingElimina todos los ajustes manuales pintados bajo el pincel, de modo que vuelve a aplicarse el pathing automático.

Mantén presionada Shift mientras usas Paint Pathing para eliminar el ajuste manual seleccionado. Al pintar un modo, se elimina el ajuste manual que lo contradice (pintar Force Walkable elimina Unwalkable), de modo que una celda nunca contiene reglas en conflicto.

Superposiciones de pathing

Una superposición colorea el terreno según su pathing, como la vista de pathing de los editores de mapas de los RTS clásicos. Actívala con:

  • el menú desplegable Pathing del panel superpuesto (overlay) de la Scene View (Pathing: Off, Walkability, Buildability, Flyability, Water, Combined);
  • el menú desplegable Pathing Overlay y el control deslizante de opacidad de la ventana RTS Terrain Studio;
  • P en la Scene View, que alterna Off → Walkability → Buildability → Combined → Off (atajo RTS Terrain Studio/Cycle Pathing Overlay, reasignable en Edit ▸ Shortcuts...; en macOS, en Unity ▸ Shortcuts...);
  • la vista de pathing del editor en el juego.
ModoColores
WalkabilityVerde = transitable, rojo = obstruido, rojo oscuro = acantilado, naranja = demasiado empinado, azul = agua profunda, cian = agua poco profunda.
BuildabilityVerde = edificable, amarillo = transitable pero no edificable, rojo / rojo oscuro = no transitable.
FlyabilityAzul claro = sobrevolable, magenta = no sobrevolable.
WaterCian = agua poco profunda, azul = agua profunda.
CombinedTodo a la vez (vista de pathing al estilo de los RTS clásicos).

En tu propio juego, muestra una superposición con un PathingOverlayController. Este mantiene la textura actualizada cuando cambia el pathing:

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;
    }
}

Cambia la información mostrada con overlay.Mode = PathingOverlayMode.Walkability (vuelve a dibujar la textura) o fuerza un redibujado completo con overlay.Refresh(); no hace falta crear un controlador nuevo.

Para texturas de un solo uso, usa PathingOverlay.CreateOrUpdate(map, mode, ref texture), PathingOverlay.UpdateRegion(...) y PathingOverlay.DestroyTexture(ref texture); PathingOverlay.GetColor(flags, mode) devuelve el color de la leyenda para una combinación de indicadores.

Leer el mapa de pathing desde código

Miembro (en RTSTerrain)Descripción
PathingFlags GetPathing(Vector3 world)Indicadores en una posición del mundo (None fuera del mapa o antes de que se construya el pathing).
bool IsWalkable(Vector3 world), bool IsBuildable(Vector3 world)Atajos para los dos indicadores más usados.
PathingMap PathingLa cuadrícula de pathing: Width, Height, CellSize, Flags (un byte por celda, índice = z × Width + x), Get(x, z), Version.
event Action<RTSTerrain, RectInt> PathingChangedSe lanza tras una reconstrucción completa o parcial, con el rectángulo modificado en celdas de pathing. Persiste tras recargar el mapa. Las unidades de ejemplo lo usan para volver a planificar su ruta.
RTSPathingSettings PathingSettings, void RebuildPathing()Reglas y reconstrucción completa.
ClearanceMap GetPathingClearance()El mapa de holgura, ya actualizado (consulta Tamaño de unidad).
void RegisterPathingReader(JobHandle), void CompletePathingReaders()Para tus propios jobs que leen Flags o la holgura: el terreno espera a que terminen antes de escribir datos de pathing.

Buscar una ruta: RTSPathfinder

RTSPathfinder busca una ruta en el mapa de pathing con un A* compilado con Burst: 8 direcciones sin cortar esquinas, holgura para unidades grandes y string pulling para obtener rutas suaves. Si el inicio o el destino están obstruidos, se sustituyen por la celda libre más cercana. Se ejecuta de forma síncrona en el hilo principal, lo que basta para unas pocas solicitudes por fotograma y para el modo Edit.

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]);
    }
}
MétodoDescripción
static PathStatus FindPath(RTSTerrain, Vector3 startWorld, Vector3 endWorld, List<Vector3> resultWorld)Usa PathRequestOptions.Default.
static PathStatus FindPath(RTSTerrain, Vector3, Vector3, PathRequestOptions, List<Vector3>)resultWorld se vacía y recibe los puntos de ruta, incluidos el inicio y el final.
static PathResult FindPathDetailed(RTSTerrain, Vector3, Vector3, PathRequestOptions, List<Vector3>)También devuelve la longitud, estadísticas y si se desplazó el inicio o el destino.
static void DisposeSharedBuffers()Libera los búferes de búsqueda (también se hace automáticamente al recargar el dominio y al salir).

PathRequestOptions (toma como base PathRequestOptions.Default o PathRequestOptions.ForUnit(radius)):

CampoPredeterminadoDescripción
unitRadius0Radio de la unidad en unidades del mundo. Las rutas mantienen esta distancia respecto a los obstáculos. La holgura solo se aplica cuando required contiene Walkable: en las solicitudes navales (DeepWater) o aéreas (Flyable), unitRadius no tiene ningún efecto. El mapa de holgura mide distancias a celdas no transitables, y el borde del mapa cuenta como obstáculo. Lo mismo se aplica a PathingQueries.FindNearest, LineOfSight y Raycast.
requiredWalkableIndicadores que debe tener cada celda de la ruta (None se trata como Walkable).
allowShallowWatertruePermite caminar por agua poco profunda (vados).
maxSearchNodes500000Número máximo de nodos expandidos antes de abandonar la búsqueda (0 = sin límite).
smoothPathtrueElimina los puntos de ruta innecesarios mediante línea de visión en la cuadrícula (string pulling).
snapToGroundtrueAjusta la Y de cada punto de ruta a la altura del suelo.
allowPartialtrueSi el destino es inalcanzable, devuelve una ruta hasta la celda alcanzable más cercana.
goalSearchRadius16Si la celda de destino está obstruida, busca la celda libre más cercana hasta esta distancia (en unidades del mundo).
PathStatusSignificado
SuccessSe encontró una ruta hasta el destino (o hasta la celda libre más cercana a un destino obstruido).
PartialEl destino es inalcanzable; la ruta lleva a la celda alcanzable más cercana a él.
NoPathNo existe ninguna ruta (o se alcanzó el límite de búsqueda) y no se permitían rutas parciales.
InvalidStartEl inicio está fuera del mapa, no hay datos de pathing o no hay ninguna celda accesible cerca del inicio.

Campos de PathResult: requestId, terrain, status, points (puntos de ruta en el mundo), length (horizontal, en unidades del mundo), searchedNodes, startAdjusted, goalAdjusted, userData y HasPath (Success o Partial con al menos un punto de ruta).

Muchas unidades: RTSPathfindingService

Para muchas unidades, usa RTSPathfindingService. Reúne las solicitudes de un fotograma, las ejecuta como jobs de Burst en paralelo en LateUpdate (después de que el terreno haya aplicado sus ediciones) y entrega los resultados mediante callbacks en el Update del fotograma siguiente. Añádelo con Add Component ▸ RTS Terrain Studio ▸ Pathfinding Service o llama a RTSPathfindingService.GetOrCreate() en el modo Play (crea un objeto de servicio que sobrevive a las cargas de escena). El servicio solo funciona en el modo Play; en el modo Edit, usa RTSPathfinder.

AjustePredeterminadoDescripción
Max Requests Per Frame64Búsquedas de ruta que se inician por fotograma. Las solicitudes adicionales esperan en una cola.
Max Parallel Jobs4Jobs que se ejecutan en paralelo (de 1 a 16). Cada job necesita sus propios búferes de búsqueda (unos 24 bytes por celda de pathing).
Memory Budget MB256Límite superior de memoria para los búferes de búsqueda. En cuadrículas muy grandes se usan menos jobs en paralelo; se registra una advertencia cuando incluso un solo conjunto de búferes lo supera.
Complete In Same FramedesactivadoCompleta los jobs al final del mismo fotograma en el que se programaron (menor latencia, menos paralelismo).
MiembroDescripción
int RequestPath(RTSTerrain, Vector3 startWorld, Vector3 endWorld, PathRequestOptions, Action<PathResult> callback, List<Vector3> resultBuffer = null, object userData = null)Pone una solicitud en cola y devuelve su id. Con resultBuffer, los puntos de ruta se escriben en tu lista (que se vacía primero), lo que evita una asignación de memoria por solicitud.
int RequestPath(RTSTerrain, Vector3, Vector3, Action<PathResult>)Igual, pero con las opciones predeterminadas.
bool Cancel(int requestId)Cancela una solicitud en cola, en ejecución o terminada pero aún no entregada. Su callback no se ejecutará.
void CancelAll(), void CompleteAll()Cancela todo o termina todo ahora (los callbacks se ejecutan antes de que el método devuelva el control).
PendingCount, InFlightCountEstadísticas de la cola y de los jobs.
static Instance, static GetOrCreate()El primer servicio activado, o uno nuevo.
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}");
    }
}

Tamaño de unidad: holgura

Las unidades grandes no deben colarse por huecos pensados para las pequeñas. El terreno mantiene un mapa de holgura: para cada celda transitable, la distancia a la celda no transitable más cercana (con un máximo de 64 celdas). Cuando una solicitud tiene un unitRadius, el buscador de rutas solo usa celdas en las que cabe la unidad. El mapa de holgura se actualiza de forma incremental tras cada edición.

  • PathingQueries.GetClearance(terrain, world) devuelve el radio libre en una posición, en unidades del mundo (negativo en celdas no transitables).
  • ClearanceMap (obtenido con terrain.GetPathingClearance()) ofrece GetClearanceCells(x, z), GetClearanceMetres(x, z) y ClearanceMap.RequiredValue(unitRadius, cellSize). Cerca de esquinas diagonales de obstáculos, el valor puede ser hasta unas 0.13 celdas demasiado generoso; así, las unidades con un radio de 0.3 a 0.5 celdas pueden usar pasillos de una celda de ancho por los que caben físicamente.

Consultas de jugabilidad: PathingQueries

PathingQueries responde a las preguntas que la jugabilidad de un RTS plantea constantemente. Todas las posiciones están en espacio del mundo; los tamaños y los radios, en unidades del mundo.

MétodoDescripción
bool FindNearestWalkable(RTSTerrain, Vector3 world, float maxRadius, float unitRadius, out Vector3 result)Posición transitable más cercana a world en la que cabe una unidad de radio unitRadius (puntos de aparición, puntos de reunión).
bool FindNearest(RTSTerrain, Vector3 world, float maxRadius, float unitRadius, PathingFlags required, out Vector3 result)Lo mismo para cualquier indicador; por ejemplo, DeepWater para barcos.
Vector3 SnapToBuildGrid(RTSTerrain, Vector3 world, Vector2 sizeMetres)Ajusta la posición de un edificio para que su huella cubra celdas de pathing completas (tamaños pares en una esquina de celda, impares en el centro de una celda). El resultado queda sobre el suelo.
bool IsAreaBuildable(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres)Devuelve true cuando todas las celdas de pathing de la huella son Buildable.
bool IsAreaClear(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres, PathingFlags required)Lo mismo para cualquier indicador. Devuelve false fuera del mapa.
bool TryGetAreaCells(RTSTerrain, Vector3 worldCenter, Vector2 sizeMetres, out RectInt cells)Celdas de pathing cubiertas por una huella. Devuelve false cuando la huella no está completamente dentro del mapa.
float GetClearance(RTSTerrain, Vector3 world)Radio libre alrededor de una posición.
bool LineOfSight(RTSTerrain, Vector3 fromWorld, Vector3 toWorld, PathingFlags required = Walkable, float unitRadius = 0)Devuelve true cuando es posible desplazarse en línea recta (línea de visión en la cuadrícula, sin cortar esquinas). Se refiere al movimiento, no a la visión de la niebla de guerra.
bool Raycast(RTSTerrain, Vector3 fromWorld, Vector3 toWorld, PathingFlags required, float unitRadius, out Vector3 lastFreeWorld)Recorre el segmento e indica la última posición libre antes de un obstáculo (cargas, retrocesos).

Colocación de edificios y cambios de pathing en tiempo de ejecución

La colocación de edificios de los RTS clásicos consta de tres pasos: ajustar la huella a la cuadrícula, comprobarla y obstruirla en el mapa de pathing. Para obstruirla se escriben ajustes manuales en MapData.PathingOverrides y se notifica al terreno. El terreno reconstruye la zona de forma incremental y lanza PathingChanged, y las unidades en movimiento recalculan su ruta para rodear el nuevo edificio.

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));
    }
}

Para eliminar un edificio, pasa las celdas y los bytes guardados a BuildingFootprint.Remove: el método restaura los bytes de ajuste manual anteriores y vuelve a llamar a NotifyChanged. El ejemplo RTSDemoBuilding hace exactamente eso; consulta Demostración de colocación de edificios.

La eliminación de árboles funciona igual a través del renderizador de vegetación: RTSVegetationRenderer.RemoveTree(id) notifica al terreno, de modo que el mapa de pathing libera la huella del árbol y la niebla de guerra retira automáticamente el bloqueador de visión. Consulta Demostración de tala de árboles.

Avanzado: datos de pathing en tus propios jobs

Para jobs de Burst personalizados (campos de flujo, mapas de influencia), crea una vista de solo lectura con new PathingGrid(terrain.Pathing, terrain.GetPathingClearance()). Ofrece IsPassable, LineOfSight, Trace y FindNearestPassable, que trabajan en unidades de celda de pathing y filtran según un PathingFilter (PathingFilter.ForUnit(required, allowShallowWater, unitRadius, cellSize)). Registra el handle de tu job con terrain.RegisterPathingReader(handle) para que el terreno lo complete antes de modificar los datos de pathing.

PathingMapBuilder crea mapas de pathing para objetos MapData que no muestra ningún terreno (por ejemplo, un servidor que carga mapas sin renderizarlos): PathingMapBuilder.Build(map, pathing, settings, tileset, vegetation, decor).

Scripts de ejemplo de jugabilidad

La carpeta Samples/Scripts contiene una capa de juego RTS pequeña pero completa: una cámara RTS, unidades que siguen el terreno, selección con grupos de control, órdenes de movimiento con formaciones, un minimapa, un HUD y dos demostraciones que modifican el mapa en tiempo de ejecución. Se encuentran en el ensamblado RTSTerrainStudio.Samples y en el espacio de nombres RTSTerrainStudio.Samples.

Úsalos para probar tus mapas de inmediato, como punto de partida para tu propio juego o como referencia de cómo usar la API de tiempo de ejecución. Todos los scripts están comentados y sus mensajes de Unity son protected virtual donde tiene sentido, para que puedas derivar de ellos.

Todos los componentes de ejemplo están en Add Component ▸ RTS Terrain Studio ▸ Samples. La entrada se lee a través de RTSInput, por lo que los ejemplos funcionan tanto con el paquete Input System como con el antiguo Input Manager. La interfaz está creada con UI Toolkit.

Inicio rápido: hacer jugable cualquier mapa

  1. Abre una escena con un RTS Terrain cuyo mapa tenga posiciones iniciales (colócalas con las herramientas Marker).
  2. Elige GameObject ▸ RTS Terrain Studio ▸ Demo Game. Esto crea un objeto RTS Demo Game con el componente RTSDemoGame.
  3. Presiona Play. El juego de demostración crea la niebla de guerra, el controlador de cámara, la selección, las órdenes, el minimapa y el HUD, genera unidades en cada posición inicial (tú eres el jugador 1; los demás deambulan por el mapa) y mueve la cámara a tu base.
  4. Presiona F1 para mostrar u ocultar los controles.

Controles predeterminados

EntradaAcción
W A S D / teclas de flechaDesplaza la cámara. Mientras hay unidades seleccionadas, S las detiene y no desplaza la cámara (las teclas de flecha siempre la desplazan).
Borde de la pantalla, arrastrar con el botón central del ratónDesplaza la cámara / toma y arrastra el suelo.
Rueda del ratónZoom (al acercar, la vista se vuelve más horizontal).
Q / E, Alt + arrastrar con el botón derechoGira la vista.
Clic izquierdo / arrastrarSelecciona una unidad / dibuja un rectángulo de selección.
Shift + clicAñade o quita unidades.
Doble clic, Ctrl + clicSelecciona todas las unidades de ese tipo que hay en pantalla.
EscCancela el modo actual y, después, borra la selección. Durante una prueba de juego iniciada desde el editor en el juego, la siguiente pulsación de Esc vuelve al editor.
Ctrl+1…9, 0Guarda un grupo de control (Shift+número añade unidades al grupo).
1…9, 0Recupera un grupo (presiona dos veces para centrar la cámara en él).
Clic derechoMueve en formación (Shift pone las órdenes en cola y encadena la formación).
S / H / PDetiene / mantiene la posición / patrulla (después, haz clic con el botón izquierdo en el segundo punto de patrulla).
MinimapaClic izquierdo o arrastrar: mueve la cámara. Clic derecho: mueve las unidades seleccionadas.
BColoca un edificio (cambios de pathing en tiempo de ejecución).
DeleteElimina el edificio que hay bajo el cursor.
TTala el árbol que hay bajo el cursor.
Clic derecho en un árbolLos trabajadores seleccionados caminan hasta él y lo talan.
F1Muestra u oculta el panel de ayuda.

Todas las teclas son campos RTSKey de los componentes (cámara, selección, órdenes, demostraciones, HUD), así que puedes reasignarlas en el Inspector.

RTSDemoGame: el arranque del juego

Convierte cualquier escena con un RTS Terrain en un pequeño RTS jugable. Cuando empieza el juego:

  1. se asegura de que existan un RTSFogOfWarManager (equipo local = Local Team, exploración reiniciada) y el servicio de búsqueda de rutas;
  2. busca el controlador de cámara, la selección, las órdenes, el minimapa, el HUD y las demostraciones de edificios y de árboles, y crea los que falten;
  3. genera unidades en cada posición inicial (equipo = índice del jugador; el jugador 1 es el equipo 0) y asigna a las unidades enemigas un RTSWanderAI, para que puedas verlas aparecer y desaparecer en la niebla de guerra;
  4. mueve la cámara a la posición inicial del jugador local.

Editor en el juego: el componente escucha el evento RTSRuntimeEditorEvents.PlaytestChanged. Al iniciar una prueba de juego en el editor en el juego, empieza la partida; al terminarla, se eliminan las unidades, se quitan los edificios de demostración, se restauran los árboles talados (opcional), se desactivan los sistemas de juego y se devuelve el control de la cámara. Con Auto Start activado y sin ningún editor en el juego en la escena, la partida empieza de inmediato.

AjustePredeterminadoDescripción
Start
Auto StartactivadoInicia la partida al reproducir la escena si no hay ningún editor en el juego (si lo hay, la partida empieza con la prueba de juego del editor).
Local Team0Equipo del jugador local (0 = jugador 1). Si lo asignas desde código, también se actualizan la niebla de guerra y la selección.
TerrainvacíoTerreno en el que se juega. Vacío = RTSTerrain.Main.
Units
Unit PrefabsvacíoPrefabs de unidades, que se usan de forma rotativa. Vacío = unidades sustitutas procedurales.
Units Per Player8Unidades generadas para cada jugador (de 1 a 60).
Max Players8Número máximo de jugadores que reciben unidades.
Enemy Wander AIactivadoLas unidades enemigas deambulan por el mapa.
Wander Radius30Radio de deambulación de la IA enemiga, en metros.
Systems
Create Missing SystemsactivadoCrea el controlador de cámara, la selección, las órdenes, el minimapa, el HUD y las demostraciones si faltan. El generador de unidades se crea siempre.
Fog Of WaractivadoActiva la niebla de guerra durante la partida.
MinimapactivadoMuestra el minimapa.
HudactivadoMuestra el HUD (panel de información, barra de acciones, ayuda con F1).
Show Help On StartactivadoMuestra el panel de ayuda cuando empieza la partida.
Building DemoactivadoActiva la demostración de colocación de edificios (B).
Tree Harvest DemoactivadoActiva la demostración de tala de árboles (T, trabajadores).
Restore Map On StopactivadoRestaura los árboles talados cuando se detiene la partida, para que el mapa editado no cambie tras una prueba de juego.
References
Camera Controller, Selection Manager, Command Controller, Minimap UI, Demo HUD, Spawner, Building Placement, Tree Harvest, Fog Of War ManagervacíoSistemas que se usan. Vacío = se buscan en la escena o se crean.

API: StartGame(), StopGame(), IsRunning, AutoStart, LocalTeam, los eventos GameStarted y GameStopped, y las propiedades Spawner, CameraController, SelectionManager, CommandController. Sobrescribe protected virtual bool DetectInGameEditor() si usas tu propio editor en el juego.

¿Vas a crear tu propio juego? Usa RTSDemoGame como receta y no como dependencia: copia el orden de sus pasos de configuración en tu propio gestor de juego y descarta lo que no necesites. Para multijugador con Mirror, no añadas RTSDemoGame: el gestor de red genera las unidades.

RTSCameraController

Una cámara RTS clásica: desplazamiento con el teclado, por los bordes y arrastrando, zoom con curva de inclinación (al acercar, la vista se vuelve más horizontal), giro, seguimiento suave de la altura del suelo y límites del mapa. Se ejecuta en LateUpdate. RTSCameraController.Main es el primer controlador activado.

AjustePredeterminadoDescripción
Target CameravacíoCámara que se mueve. Vacío = el componente Camera de este GameObject o, si no lo hay, Camera.main.
TerrainvacíoTerreno que se usa para la altura del suelo y los límites. Vacío = RTSTerrain.Main.
Keyboard Pan, Pan Forward/Back/Left/Right Key, Arrow Keysactivado, W/S/A/D, activadoDesplazamiento con el teclado.
Yield Keys To OrdersactivadoLas teclas de desplazamiento que también son teclas de orden del controlador de órdenes (S = Stop) no desplazan la cámara mientras hay unidades seleccionadas.
Pan Speed / Fast Pan Multiplier35 / 2Metros por segundo con el zoom predeterminado (se escala con la distancia del zoom); multiplicador mientras se mantiene presionada Shift.
Edge Scrolling, Edge Size, Edge Speed Multiplieractivado, 12 px, 1Desplaza la cámara cuando el ratón toca el borde de la pantalla. El desplazamiento por los bordes se pausa mientras la ventana del juego no tiene el foco, mientras el puntero está fuera de ella (en las builds web, también cuando el ratón ha salido de la página o de su iframe) y en pantallas táctiles.
Middle Mouse DragactivadoMantén presionado el botón central del ratón para tomar el suelo y moverlo.
Min / Max Distance12 / 75Distancia al pivote con el zoom totalmente acercado / alejado (metros).
Zoom Step, Start Zoom0.08, 0.6Cambio de zoom por cada paso de la rueda; zoom inicial (0 = lo más cerca, 1 = lo más lejos).
Distance Curve, Pitch Curve-Convierten el valor de zoom (de 0 a 1) en la distancia y en la inclinación en grados (unos 55° con el zoom predeterminado).
Keyboard Rotate, Rotate Left/Right Key, Rotate Speedactivado, Q/E, 100Giro con el teclado, en grados por segundo.
Alt Right Drag Rotate, Drag Rotate Sensitivityactivado, 0.3Gira con Alt + arrastrar con el botón derecho.
Snap Back Rotation, Snap Back Delay, Default Yawdesactivado, 0.25, 0Vuelve al ángulo predeterminado cuando no hay entrada de giro (comportamiento clásico de los RTS). Con un giro horizontal de 0, la cámara mira hacia +Z (norte).
Follow Ground Height, Height Smooth Timeactivado, 0.3Mantiene el pivote a la altura del suelo, con suavizado para que los acantilados no hagan saltar la vista.
Clamp To Terrain, Bounds Marginactivado, 4Mantiene el pivote dentro del mapa (los márgenes negativos permiten ver más allá del borde).
Min Camera Clearance2Altura mínima de la cámara sobre el suelo que tiene debajo.
Pan / Zoom / Rotation Smooth Time0.08 / 0.12 / 0.08Suavizado en segundos (0 = instantáneo).
Ignore Mouse Over UIactivadoIgnora la rueda, el desplazamiento por bordes y el arrastre mientras el puntero está sobre la interfaz.
Use Unscaled TimeactivadoLa cámara también se mueve mientras el juego está en pausa.

API: JumpTo(Vector3), FocusOn(Vector3), FocusOn(IReadOnlyList<RTSUnit>, bool instant = false), SetZoom(float, bool instant = false), SetYaw(float, bool instant = false), GetViewFootprint(Vector3[4]) (esquinas de la pantalla proyectadas sobre el suelo, que usa el minimapa), InitializeFromCamera(), InputEnabled y, de solo lectura, Pivot, Zoom, Yaw, Pitch, Distance, IsDragging.

RTSUnit y utilidades de unidades

Una unidad RTS sencilla: equipo, movimiento sobre el terreno, seguimiento de rutas con esquinas suavizadas, evasión local y visión para la niebla de guerra. Pide una ruta a su proveedor de rutas, la sigue con un punto de anticipación, frena al llegar, mantiene la distancia con otras unidades, nunca pisa celdas no transitables y recalcula la ruta cuando se atasca o cuando el mapa de pathing cambia a lo largo de su ruta (edificios colocados, árboles talados, terreno editado en el editor en el juego). Requiere y configura un RTSFogOfWarRevealer (equipo, radio de visión) y un RTSFogOfWarHideable (equipo).

AjustePredeterminadoDescripción
Team0Equipo / índice del jugador (0 = jugador 1). El jugador puede seleccionar las unidades del equipo local y darles órdenes.
Use Team Color, Player Coloractivado, rojoToma el color de la tabla clásica de colores de jugador cada vez que cambia el equipo; se usa para los círculos de selección, los puntos del minimapa y los renderers de color de equipo.
Unit Type«Unit»Nombre de tipo para «el doble clic selecciona todas las unidades del mismo tipo» (por ejemplo, Footman, Worker). La demostración de árboles usa «Worker».
Move Speed, Turn Speed, Acceleration5, 540, 25Metros por segundo, grados por segundo, metros por segundo².
Radius, Height0.5, 1.8Radio de colisión (holgura para la búsqueda de rutas, evasión, círculo de selección) y altura (selección con clic, rectángulo de selección).
Align To Ground, Ground Align Speeddesactivado, 8Se inclina con el suelo (vehículos, animales). Si está desactivado, la infantería se mantiene erguida.
Look Ahead, Arrival Distance, Slow Down Distance1.5, 0.15, 1.5Seguimiento de la ruta (metros).
Stuck Check Interval0.6Segundos entre comprobaciones de progreso. Una unidad que no avanza recalcula su ruta.
Path Request Timeout3Segundos que se espera una ruta antes de reenviar la solicitud una vez; el movimiento falla si la segunda solicitud también agota el tiempo de espera.
Avoidance, Separation Strength, Personal Spaceactivado, 8, 0.1Separación simple. Las unidades inactivas se apartan para dejar pasar a las que se mueven.
Sight Radius14Radio de visión para la niebla de guerra, en metros.
SelectableactivadoEl jugador puede seleccionar esta unidad.
TerrainvacíoTerreno por el que camina la unidad. Vacío = el terreno que hay bajo la unidad (o RTSTerrain.Main).
Path ProvidervacíoComponente opcional que implementa IRTSUnitPathProvider. Vacío = el proveedor global predeterminado (consulta Proveedores de rutas).
Team Color Renderers, Team Color Modeninguno, PropertyBlockRenderers que muestran el color de jugador, mediante un MaterialPropertyBlock (cualquier material) o un material compartido por color (mejor batching).
Draw Path GizmosactivadoDibuja la ruta actual en la Scene View.

Órdenes: MoveTo(Vector3, bool queue = false), Stop(), HoldPosition(), Patrol(Vector3 a, Vector3 b), Warp(Vector3, float? yaw = null), FaceTowards(Vector3), Repath(). Eventos: OnArrived, OnPathFailed (no hay ruta, la unidad desistió al quedar obstruida o se detuvo en el punto alcanzable más cercano), OnStateChanged. Estado: 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);
    }
}

Utilidades:

ClaseFinalidad
RTSUnitFactoryUnidades sustitutas procedurales: estilos Footman, Worker, Scout, Knight y DefaultMix; CreatePlaceholder(team, position, yaw, style, parent), Spawn(prefab, team, position, yaw, parent) (añade RTSUnit a un prefab si no lo tiene), FindSpawnPosition(terrain, position, radius, searchRadius = 16).
RTSUnitSpawnerGenera unidades por jugador en las posiciones iniciales del mapa (o en Custom Spawn Points, o en esquinas opuestas). Ajustes: 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, espacio para un ayuntamiento), Container, Spawn On Start. API: SpawnAll(), SpawnForPlayer(...), SpawnUnit(team, position, yaw, variant), DespawnAll(), TryGetStartLocation(player, out position, out yaw).
RTSWanderAIHace que una unidad camine hacia puntos aleatorios alrededor de su punto de origen. Ajustes: Wander Radius (25 m), Min/Max Wait (2 / 7 s), Home Is Start Position, Home Position.
RTSUnitRegistryTodas las unidades y un hash espacial: All, GetTeam(team), QueryRadius(...), QueryScreenRect(...), PickAtScreen(...), eventos UnitRegistered, UnitUnregistered, UnitTeamChanged.

Proveedores de rutas: IRTSUnitPathProvider

Las unidades no dependen de una única solución de búsqueda de rutas. Consultan un IRTSUnitPathProvider, elegido en este orden: el Path Provider propio de la unidad (o PathProviderOverride); si no lo hay, RTSUnitPathProviderRegistry.Default; y, en su defecto, el proveedor integrado RTSBuiltinPathProvider (el A* con Burst del paquete, a través de RTSPathfindingService en el modo Play). Las integraciones de A* Pathfinding Project y de NavMesh se conectan aquí.

Hay dos tipos de proveedores:

  • Los proveedores de rutas (HandlesMovement = false) devuelven puntos de ruta mediante RequestPath; la unidad los sigue por sí misma (dirección, evasión, alineación con el suelo).
  • Los proveedores de movimiento (HandlesMovement = true) mueven la unidad ellos mismos (por ejemplo, un NavMeshAgent). La unidad les reenvía sus órdenes mediante OnUnitMoveCommand y OnUnitStop; el proveedor informa del resultado con unit.NotifyArrived() o unit.NotifyPathFailed(). Mientras un proveedor de este tipo se encarga de una unidad, la unidad nunca modifica su propio transform.

El contrato: llama al callback onComplete exactamente una vez, en el hilo principal, de inmediato o más tarde, con los puntos de ruta (incluidos el inicio y el final) y true si tiene éxito; si falla, pasa una lista vacía o null y false. Una ruta hasta el punto alcanzable más cercano con true es válida; la unidad detecta que se queda corta. El proveedor que recibió un movimiento lo conserva hasta que este termina; si el proveedor cambia o se destruye durante un movimiento, el anterior recibe OnUnitStop y la orden pasa al nuevo. Una solicitud que no recibe respuesta dentro del plazo de Path Request Timeout se reintenta una vez y después falla.

Deriva de RTSUnitPathProviderComponent para crear un componente de escena que pueda registrarse como proveedor predeterminado (Make Default, activado de forma predeterminada) o arrastrarse al campo Path Provider de una unidad:

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);
    }
}

Desde código: RTSUnitPathProviderRegistry.Default = myProvider; cambia el proveedor de todas las unidades que no tienen uno propio; Default = null vuelve al buscador de rutas integrado. RTSBuiltinPathProvider expone Options (su unitRadius se sustituye por el radio de cada unidad) y UseAsyncService.

RTSSelectionManager

Selección con clic, con rectángulo, con Shift y con doble clic, grupos de control y anillos de selección y de resaltado al pasar el cursor por encima. Solo se pueden seleccionar unidades del equipo local. RTSSelectionManager.Main es el primer gestor activado.

AjustePredeterminadoDescripción
Target CameravacíoCámara que se usa para la selección con clic. Vacío = Camera.main.
Use Fog Of War Local TeamactivadoUsa el equipo local del gestor de niebla de guerra activo.
Local Team0Equipo cuyas unidades se pueden seleccionar (cuando no se toma de la niebla de guerra).
Drag Threshold, Double Click Time6 px, 0.3 sCuándo un clic se convierte en un rectángulo; intervalo del doble clic.
Max Selection0Número máximo de unidades seleccionadas (0 = sin límite; 12 es un límite habitual en los RTS clásicos).
Pick Tolerance6 pxRadio de selección adicional para que sea fácil hacer clic en las unidades pequeñas.
Keep Selection On Empty ClickactivadoHacer clic en suelo vacío o trazar un rectángulo sin unidades propias conserva la selección, como en los RTS clásicos.
Clear Selection KeyEscapeBorra la selección.
Control Groups, Group Double Tap Timeactivado, 0.35 sCtrl+número guarda, Shift+número añade, el número recupera y dos pulsaciones centran la cámara. Hay diez grupos (RTSSelectionManager.GroupCount = 10) en las teclas 1–9 y 0; el índice de grupo i de StoreGroup, RecallGroup y GetGroup corresponde a la tecla numérica i (el índice 0 es la tecla 0).
Show Selection Circles, Show Hover, Use Team Color For Circles, Selection Color, Circle Scale, Circle Width, Box Color-Aspecto visual de los anillos y del rectángulo de selección.

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, eventos SelectionChanged y GroupRecalled.

RTSCommandController y formaciones

Convierte los clics con el botón derecho y las teclas de orden en órdenes para las unidades seleccionadas. RTSCommandController.Main es el primer controlador activado.

AjustePredeterminadoDescripción
Selection, Target CameravacíoVacío = RTSSelectionManager.Main / Camera.main.
Stop Key, Hold Key, Patrol Key, Cancel KeyS, H, P, EscapeTeclas de orden.
Use FormationactivadoMueve los grupos en una formación de cuadrícula (desactivado = cada unidad camina hasta el punto en el que se hizo clic).
Spacing Multiplier, Extra Spacing, Width To Depth1.15, 0.3, 1.5Distancia entre posiciones como múltiplo del mayor diámetro de unidad, separación adicional en metros, proporción preferida entre columnas y filas.
Show Marker, Move Marker Color, Patrol Marker ColoractivadoAnillo animado en el destino de la orden.

API: IssueMove(Vector3 target, bool queue = false), IssueMove(IReadOnlyList<RTSUnit> units, Vector3 target, bool queue = false), IssuePatrol(Vector3), IssueStop(), IssueHold(), BeginPatrolTargeting(), CancelTargeting(), IsOrderKeyActive(RTSKey), eventos MoveIssued, CommandIssued, TargetingChanged.

RTSFormation.ComputeGrid(units, target, slots, spacingMultiplier, extraSpacing, widthToDepth, fromPlannedEnd) calcula una posición por unidad y asigna a cada unidad la posición más cercana a su lugar en el grupo, de modo que las rutas rara vez se cruzan. Devuelve la dirección hacia la que mira la formación. Con fromPlannedEnd (se usa en las órdenes puestas en cola con Shift), la formación continúa desde donde estarán las unidades tras completar sus órdenes actuales.

Coordinación de la entrada. RTSGameInput garantiza que cada clic lo procese exactamente un sistema: un modo como la colocación de edificios se apropia del puntero (Acquire, Release, CanUsePointer), y un sistema que ha procesado un clic o una tecla llama a Consume / ConsumeKey. Los ejemplos se ejecutan en un orden fijo (colocación de edificios, tala de árboles, órdenes, selección, unidades, cámara, interfaz), definido por las constantes RTSGameInput.Order…. Usa el mismo mecanismo en tus propios modos para que cooperen con los ejemplos.

RTSMinimapUI

Un minimapa de UI Toolkit para el juego en ejecución. La imagen del mapa se precalcula con RTSMinimapBaker y se vuelve a precalcular (con limitación de frecuencia) cuando el mapa cambia; encima se superpone la niebla de guerra del equipo local; las unidades se dibujan como puntos en el color de su jugador (las enemigas, solo mientras son visibles); la vista de la cámara se dibuja como un contorno. Hacer clic con el botón izquierdo o arrastrar mueve la cámara; hacer clic con el botón derecho ordena a las unidades seleccionadas que se muevan a ese punto.

AjustePredeterminadoDescripción
Terrain, Camera Controller, Command ControllervacíoVacío = RTSTerrain.Main, RTSCameraController.Main, RTSCommandController.Main.
CornerBottomLeftEsquina de la pantalla (los RTS clásicos usan la inferior izquierda).
Size, Margin248, 16Tamaño del marco y distancia a los bordes de la pantalla en píxeles de UI (resolución de referencia 1920 × 1080).
Resolution256Resolución de la imagen precalculada del mapa (lado más largo).
Rebake Delay, Min Rebake Interval0.35 s, 1 sVarias ediciones se combinan en un solo precálculo.
Bake Options-Aspecto de la imagen precalculada (modo de suelo, sombreado de relieve, acantilados, agua, árboles, decoración, posiciones iniciales, colores de jugador).
Show Fog, Fog Refresh Intervalactivado, 0.1 sOscurece las zonas inexploradas y las cubiertas por la niebla de guerra, como en la vista 3D.
Show Units, Unit Dot Size, Show Camera View, Camera View Color, Overlay Rateactivado, 4, activado, blanco, 20Puntos, contorno de la cámara y su frecuencia de redibujado.
Left Click Moves Camera, Right Click Moves Unitsactivado, activadoInteracción.

API: Visible, Terrain, MapTexture, BakeOptions, Rebake().

RTSDemoHUD y RTSRuntimeUI

RTSDemoHUD muestra un panel de información de la selección, una barra de acciones (Stop, Hold, Patrol, Build), la tasa de fotogramas, mensajes breves de aviso («Can't reach that place.», «Can't build there.», «Can't build in unexplored land.») y el panel de ayuda de F1. Ajustes: Help Key (F1), Show Help On Start, Show Fps, Show Feedback Messages, Message Duration (2.5 s) y referencias a la selección, los comandos y las demos. API: Visible, HelpVisible, SetMode(string), ShowMessage(string). Mientras está activado, mantiene activa la protección RTSInput.SetBrowserShortcutGuard, de modo que en las builds web F1 abre la ayuda del HUD en lugar de la página de ayuda del navegador. Esta protección también impide que el navegador reaccione a las demás teclas de función hasta F10 (incluida F5, que recarga la página) y a Ctrl+S/O/D/A (consulta El editor en el juego en el navegador). En las demás plataformas no tiene ningún efecto.

RTSRuntimeUI es el pequeño conjunto de utilidades de UI Toolkit que comparten los ejemplos: CreateDocument, CreatePanel, CreateLabel, CreateButton(text, tooltip, action), AttachTooltip, IsScreenPointOverUI y la paleta de colores de los ejemplos. Úsalo para añadir tus propios paneles con el mismo aspecto.

Todas las interfaces de usuario de los ejemplos comparten un mismo panel (RTSRuntimeUI.PanelSettings) que se escala con el tamaño de la pantalla (resolución de referencia de 1920 × 1080). Igual que el editor en el juego, nunca se reduce por debajo de RTSRuntimeUI.MinimumScale (predeterminado: 0.85; en las builds web se mide en píxeles CSS), para que el HUD, el minimapa y la selección sigan siendo legibles en ventanas pequeñas y en el lienzo del navegador. Las pantallas grandes no se ven afectadas. Asígnale el valor 0 para desactivar el mínimo.

RTSBuildingPlacementDemo

Muestra cómo el código del juego modifica el mapa de pathing en tiempo de ejecución. Presiona B: una huella fantasma sigue al cursor, ajustada a la cuadrícula de pathing, con cada celda dibujada en verde (edificable) o en rojo (obstruida). Un clic con el botón izquierdo coloca un edificio sencillo en el color del equipo (mantén presionada Shift para colocar varios). La huella se escribe en los ajustes manuales de pathing, el terreno reconstruye la zona y las unidades en movimiento recalculan su ruta para rodear el edificio. Un clic en un lugar obstruido muestra un marcador rojo y un mensaje. Un clic con el botón derecho o Esc cancela; Delete elimina el edificio que está bajo el cursor y restaura el pathing.

AjustePredeterminadoDescripción
Size In Cells2 × 2Tamaño de la huella en celdas del mapa (X × Z).
Building Height3Altura del edificio procedural en metros.
Building PrefabvacíoPrefab opcional en lugar del edificio procedural. Su pivote se coloca sobre el suelo, en el centro de la huella.
Team-1Equipo propietario. -1 = el equipo local del gestor de selección (o de la niebla de guerra).
Sight Radius10Radio de visión de los edificios (0 = sin visión). Los edificios también reciben un objeto ocultable en modo fantasma.
Footprint FlagsUnwalkable, UnbuildableAjustes manuales de pathing que se escriben para la huella.
Push Units OutactivadoLas unidades que están sobre la huella se apartan.
Require ExploredactivadoSolo permite construir en terreno que el equipo local ha explorado (mientras haya una niebla de guerra activa).
Place Key, Cancel Key, Demolish KeyB, Escape, DeleteTeclas.
Valid Color, Invalid Colorverde, rojoColores de las celdas de la huella.
Terrain, Target CameravacíoVacío = el terreno bajo el cursor / Camera.main.

API: BeginPlacement(), CancelPlacement(), IsPlacing, GetFootprintSize(terrain), CanPlaceAt(terrain, worldCenter), GetPlacementError(terrain, worldCenter) (un mensaje para el jugador, o null), PlaceBuilding(terrain, worldCenter, owner), RemoveBuilding(building), RemoveAllBuildings(), Buildings, eventos BuildingPlaced, BuildingRemoved, PlacementModeChanged, PlacementFailed(Vector3, string). Cada edificio colocado tiene un componente RTSDemoBuilding (Team, PathingCells, ContainsPoint, Remove()) que guarda los ajustes manuales anteriores y los restaura cuando se elimina o se destruye.

Para convertir esto en edificios reales, asigna tu prefab a Building Prefab, suscríbete a BuildingPlaced para pagar el coste e iniciar la construcción, y llama a GetPlacementError para mostrar el motivo cuando el jugador no pueda construir.

RTSTreeHarvestDemo

Muestra cómo se eliminan árboles durante la partida. Pasa el cursor sobre un árbol (aparece un anillo amarillo) y presiona T para talarlo al instante. O bien selecciona trabajadores (tipo de unidad «Worker») y haz clic con el botón derecho en un árbol: van hasta él, se orientan hacia él, dan hachazos durante unos instantes (un anillo se va cerrando sobre el tronco) y lo derriban. El árbol se elimina con RTSVegetationRenderer.RemoveTree, de modo que el pathing se libera y la niebla de guerra retira el bloqueador de visión. Donde estaba el árbol se reproduce una animación de caída (RTSFallingTreeEffect).

AjustePredeterminadoDescripción
Harvest KeyTTala el árbol que está bajo el cursor.
Workers Harvest On Right ClickactivadoAl hacer clic con el botón derecho en un árbol con trabajadores seleccionados, estos van a talarlo.
Worker Type«Worker»Tipo de unidad que puede talar árboles.
Pick Radius1.5Distancia máxima entre el rayo del cursor y un árbol para seleccionarlo.
Chop Time, Work Distance1.5 s, 1.2 mCuánto tiempo tala un trabajador y a qué distancia del tronco se coloca.
Fall Duration, Fade Duration1.1 s, 0.5 sAnimación de caída y encogimiento final.
Highlight Color, Show Chop Progress, Chop Progress Color-Anillos bajo el árbol señalado con el cursor y bajo el árbol que se está talando.
Target CameravacíoCámara que se usa para seleccionar árboles con el cursor.

API: TryGetHoveredTree(out RTSVegetationRenderer, out TreeInstanceData), HarvestTree(vegetation, treeId, fallAwayFrom), OrderHarvest(workers, vegetation, tree), RestoreHarvestedTrees(), CancelAllJobs(), HarvestedCount, evento 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);
    }
}

Pequeñas utilidades

ClaseFinalidad
RTSMoveMarkerAnillo animado en el destino de una orden: RTSMoveMarker.Spawn(position, color, radius, seconds).
RTSGroundRingUn anillo que se adapta al terreno (círculos de selección, alcances): RTSGroundRing.Create(name, color, radius, width, parent) y luego Place(position).
RTSSampleMaterials, RTSSampleMeshesMateriales URP en caché por color y meshes primitivos para objetos sustitutos.
RTSSampleUtilityBúsqueda del terreno que se debe usar, selección de puntos del suelo, distancias en XZ y PathingRectToCellRect(map, pathingRect).

Generador de contenido de demostración

El paquete se distribuye sin arte de terceros. En su lugar, un generador crea todo el contenido de demostración de forma procedural dentro de tu proyecto: texturas de suelo, acantilado y agua repetibles en mosaico, sprites de hierba y flores, árboles y props low-poly, materiales, prefabs, dos tilesets, bibliotecas de vegetación y de decoración, un perfil de pintado automático, formas de pincel, tres mapas de demostración y tres escenas de demostración. El resultado es determinista (semillas fijas), así que cada ejecución produce el mismo contenido.

El mapa de demostración Four Kingdoms (4p) con lagos, acantilados, caminos, bosques y el centro de tierra corrupta
El mapa de demostración Four Kingdoms (4p) en la escena Editor Playground.

Crear el contenido de demostración

  1. Asegúrate de estar en el modo Edit y de haber guardado tu escena (Unity solo permite una escena sin título; el generador te pide que la guardes si hace falta).
  2. Elige Tools ▸ RTS Terrain Studio ▸ Create Demo Content. Una barra de progreso muestra los pasos. Tarda un poco.
  3. Cuando se te pregunte si quieres añadir las escenas de demostración a la configuración de build, elige Not Now, a menos que quieras compilar una build con las demos (puedes añadirlas más tarde en File ▸ Build Profiles).
  4. El generador selecciona la escena RTS Terrain Studio - Gameplay Demo en la ventana Project. Haz doble clic en ella para abrirla.

El generador también se ejecuta cuando haces clic en Create en la sección de contenido de demostración de la ventana Welcome (Tools ▸ RTS Terrain Studio ▸ Welcome), o cuando aceptas la propuesta que aparece al crear un RTS Terrain en un proyecto que aún no tiene ningún tileset. Nunca cierra ni reemplaza las escenas que tienes abiertas: las escenas de demostración se crean junto a ellas.

El generador ejecuta estos pasos (puedes llamar a cada uno desde código):

PasoMétodoQué crea
TexturasCreateTextures()Texturas de suelo, acantilado, agua, árboles y props (512 px), sprites de hierba y flores y sellos de pincel (256 px).
MaterialesCreateMaterials()Materiales de árboles y plantas (shader RTS Terrain Studio/Vegetation, viento), materiales de props (URP Lit), los materiales de plantilla que usan los ejemplos, el material del cielo y el perfil de posprocesado.
Meshes y prefabsCreateModels()Árboles, arbustos, rocas y props low-poly, y sus prefabs.
Tilesets, bibliotecas y pincelesCreateDataAssets()Los tilesets Summer y Winter, las bibliotecas Demo Vegetation y Demo Decor (con iconos para la paleta), el perfil Summer Auto Paint y las formas de pincel.
Referencias de shaders y ajustes de UIUpdateResources()Garantiza que todos los shaders del paquete y la configuración del panel de UI Toolkit en tiempo de ejecución se incluyan en las builds del juego.
Mapas de demostraciónCreateMaps()Tres archivos .rtsmap (consulta más abajo).
Escenas de demostraciónCreateScenes(bool interactive)Tres escenas (consulta Escenas de demostración). También añade la renderer feature de la niebla de guerra a tu renderer de URP.

Qué obtienes

  • Tileset Summer (id rts-demo-summer) con diez capas de suelo: Grass, Lush Grass, Dirt, Rocky Dirt, Sand, Cobblestone, Dark Forest Floor, Rock, Snow y Blight. Rock y Blight están configuradas como Unbuildable, un buen ejemplo de reglas de pathing por capa de suelo.
  • Tileset Winter (id rts-demo-winter): las mismas texturas con tonos fríos y el mismo orden de capas, de modo que cada mapa de demostración puede cambiar entre Summer y Winter.
  • Summer Auto Paint: manchas de hierba frondosa y de tierra sobre la hierba, tierra y tierra rocosa a lo largo de los bordes de acantilado, arena en las orillas, roca en las pendientes empinadas y nieve en las zonas altas.
  • Bibliotecas Demo Vegetation y Demo Decor: hierba, hierba alta, flores amarillas y moradas y helechos; pinos, árboles de hoja ancha, árboles muertos y arbustos; rocas, una roca grande, un pilar en ruinas, cajas, barriles, vallas, tocones y hongos.
  • Tres mapas:
    • Two Rivers (1v1): 128 × 128 celdas, con simetría central. Dos ríos sinuosos se unen en un lago central; los vados poco profundos permiten cruzar a las unidades.
    • Four Kingdoms (4p): 192 × 192 celdas, simetría rotacional de orden cuatro, mesetas escalonadas y un centro de tierra corrupta con ruinas.
    • Sandbox: 96 × 96 celdas, casi llano, con una meseta y un estanque, para probar las herramientas.
    Todos los mapas tienen plazas empedradas en las posiciones iniciales, bosques con suelo de bosque y pequeños grupos de props, todo colocado de forma simétrica para que los mapas sigan siendo justos.

Dónde se guardan los archivos

El generador solo escribe en sus propias carpetas dentro de la carpeta del paquete (Assets/RTSTerrainStudio, a menos que la hayas movido; el paquete funciona en cualquier carpeta):

CarpetaContenido
Content/TexturesTexturas generadas, en las subcarpetas Ground, Cliffs, Water, Details, Props y Brushes.
Content/Materials, Content/Meshes, Content/PrefabsMateriales, meshes y prefabs de árboles y props.
Content/TilesetsSummer.asset, Winter.asset y Summer Auto Paint.asset.
Content/LibrariesDemo Vegetation.asset, Demo Decor.asset y sus iconos.
Content/BrushesFormas de pincel.
Content/MapsTwo Rivers (1v1).rtsmap, Four Kingdoms (4p).rtsmap, Sandbox.rtsmap.
Content/SettingsConfiguración de renderizado (perfil de posprocesado).
Samples/ScenesLas tres escenas de demostración.
Resources/RTSTerrainStudioReferencias de shaders y UI/RTSRuntimePanelSettings.asset.

Regenerar

Vuelve a ejecutar Tools ▸ RTS Terrain Studio ▸ Create Demo Content y confirma con Regenerate. Los assets de demostración existentes se sobrescriben en su sitio, de modo que los mapas, escenas y prefabs que los referencian siguen funcionando. Tus propios assets nunca se modifican. Los archivos de mapa cuyo contenido no ha cambiado no se reescriben, lo que evita cambios innecesarios en el control de versiones.

Al regenerar se sobrescriben todos los cambios que hayas hecho en los propios assets generados (por ejemplo, un tileset de demostración retocado o un mapa de demostración editado). Para personalizar el contenido de demostración, primero duplica el asset (Ctrl+D) o guarda el mapa con otro nombre, y edita la copia.

Desde código (ensamblado de editor, espacio de nombres RTSTerrainStudio.Editor):

MiembroDescripción
RTSDemoContent.CreateAll(bool openScene)Ejecuta todos los pasos con una barra de progreso. Devuelve false si se cancela o si falla algún paso.
RTSDemoContent.CreateAllNonInteractive()Sin cuadros de diálogo y sin abrir ninguna escena; para automatización y modo batch. Devuelve un breve resumen.
RTSDemoContent.IsCreatedEs true cuando existen los assets principales de demostración.
RTSDemoContent.ContentFolder, ScenesFolder, SummerTilesetPath, TwoRiversMapPath, GameplayScenePath, …Rutas de las carpetas y los assets generados.
RTSDemoContent.RenderPreview(scenePath, pngPath, width, height)Renderiza una imagen de vista previa de una escena.
// 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
    }
}

Escenas de demostración

El generador crea tres escenas en Samples/Scenes. Todas contienen un objeto Sun, un volumen Post Processing, un RTS Terrain (con componentes de vegetación, agua, decoración y entorno) y una Main Camera que activa la textura de profundidad y la textura opaca que necesita el agua.

EscenaMapaContenido
RTS Terrain Studio - Gameplay DemoTwo Rivers (1v1)Cámara RTS, niebla de guerra y RTSDemoGame con Auto Start. Presiona Play y la partida comienza.
RTS Terrain Studio - In-Game Editor DemoSandboxEl editor en el juego. La niebla de guerra y la cámara RTS permanecen desactivadas mientras editas; las unidades aparecen cuando inicias una prueba de juego.
RTS Terrain Studio - Editor PlaygroundFour Kingdoms (4p)Sin elementos de juego. Un mapa grande para probar las herramientas del Unity Editor.

Gameplay Demo: qué probar

  1. Presiona Play. Tus unidades (jugador 1) están en la plaza de tu posición inicial; el resto del mapa está en negro.
  2. Selecciona algunas unidades con un rectángulo de selección y haz clic con el botón derecho al otro lado del río. Observa cómo, en formación, eligen un vado para cruzar por el agua poco profunda.
  3. Acércate a un acantilado. Fíjate en que las unidades de abajo no pueden ver la meseta de arriba y en que un bosque oculta lo que hay detrás. Después sube por una rampa y mira hacia abajo.
  4. Aléjate de una zona: queda atenuada (explorada) en lugar de en negro. Las unidades enemigas que deambulan por ella desaparecen cuando las pierdes de vista.
  5. Presiona B y coloca un edificio en el camino de unas unidades en movimiento: lo rodean. Presiona Delete con el cursor sobre él para eliminarlo.
  6. Pasa el cursor sobre un árbol y presiona T, o selecciona trabajadores y haz clic con el botón derecho en un árbol. El hueco en el bosque se vuelve transitable y puedes ver a través de él.
  7. Guarda un grupo con Ctrl+1, desplaza la cámara a otra zona y presiona 1 dos veces para volver. Usa el minimapa para mover la cámara y enviar unidades.
  8. Sal del modo Play, selecciona el objeto Fog of War y prueba otros modos de Line Of Sight, o cambia Local Team en RTS Demo Game para jugar como otro jugador.

In-Game Editor Demo: qué probar

  1. Presiona Play. El editor en el juego se abre con el mapa Sandbox; todo el mapa es visible.
  2. Edita el mapa: eleva una meseta, pinta el suelo, planta un bosque y coloca posiciones iniciales con las herramientas de marcadores.
  3. Haz clic en Play Test (F5). RTSDemoGame genera unidades en las posiciones iniciales y activa la niebla de guerra y la cámara RTS. Tus ediciones se aplican al momento: los acantilados impiden la visión y los bosques cortan el paso.
  4. Presiona Esc (después de cancelar cualquier modo y vaciar la selección) o F5, o haz clic en Back to Editor (Esc or F5). Las unidades desaparecen y el mapa vuelve al estado que tenía antes de la prueba de juego.

Editor Playground: qué probar

  1. Abre la escena y la ventana de RTS Terrain Studio (Tools ▸ RTS Terrain Studio ▸ Editor Window).
  2. Selecciona el RTS Terrain y prueba las herramientas en el mapa Four Kingdoms: acantilados, rampas, pintura, vegetación y decoración.
  3. Presiona P en la Scene View para alternar entre las superposiciones de pathing y ver cómo los acantilados, las pendientes, el agua, los árboles y el suelo Blight afectan a la transitabilidad y la edificabilidad.
  4. ¿Quieres jugarlo? Añade un Demo Game con GameObject ▸ RTS Terrain Studio ▸ Demo Game y presiona Play: aparecen unidades en las posiciones iniciales de los cuatro jugadores.

Las integraciones tienen sus propias escenas de demostración, que se crean a petición: Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene, Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene y Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Unity NavMesh ▸ Create Demo Scene. Necesitan el contenido de demostración y el paquete de terceros correspondiente.

¿La demo se ve mal o sin texturas? Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y aplica las correcciones sugeridas (asset de URP, renderer feature, texturas de la cámara). Si falta una escena de demostración, vuelve a ejecutar el generador: reconstruye las escenas que faltan y no toca tus propias escenas.

Generación de mapas y herramientas de mapa

La pestaña Map de la ventana de RTS Terrain Studio (Tools ▸ RTS Terrain Studio ▸ Editor Window, la última pestaña antes de Settings) contiene las tarjetas que trabajan sobre todo el mapa: Map, New Map, Resize, Generator, Heightmap, Minimap, Validation y Environment. Este capítulo explica el generador procedural, la importación y exportación de mapas de alturas, el precálculo del minimapa y el validador de mapas. Todos ellos se pueden usar también desde tu propio código; los ejemplos compilan con el paquete tal como se distribuye.

Todas las tarjetas trabajan sobre el terreno seleccionado en la cabecera de la ventana. Si un botón te pide «Select this terrain in the window header first», elige el terreno en el campo de terreno de la cabecera (si seleccionas un terreno en la escena, también queda elegido).

Generador de mapas

El generador (RTSMapGenerator) crea un diseño completo de RTS clásico a partir de una semilla: mesetas separadas por acantilados limpios, rampas entre ellas, lagos o islas, un borde opcional, pintura del suelo, hierba y bosques, decoración y posiciones iniciales de los jugadores. Trabaja sobre el mapa existente y conserva su tamaño. La misma semilla con los mismos ajustes y el mismo tamaño de mapa produce siempre el mismo mapa, así que puedes compartir una semilla en lugar de un archivo.

Al generar se reemplazan siempre las alturas, los niveles de acantilado, las rampas, los tipos de acantilado, el agua y todos los ajustes manuales de pathing (solo se vuelve a pintar el borde, así que los ajustes manuales pintados se borran incluso con Border en None). La pintura del suelo, las capas de detalle, los árboles, la decoración y las posiciones iniciales solo se reemplazan cuando su opción está activada: Paint Mode no es KeepExisting; Vegetation o Decor está en Enabled, el terreno tiene una biblioteca de vegetación o de decoración y Clear Existing está activado; Start Locations está en Enabled (Max Players se actualiza cuando Update Max Players está activado). En caso contrario, se conserva el contenido existente y, con Clear Existing desactivado, los árboles y la decoración nuevos se añaden a los existentes. Las regiones, los marcadores y la configuración del entorno nunca se modifican. Toda la ejecución es un solo paso de deshacer, así que puedes experimentar sin riesgo.

Tu primer mapa generado (Unity Editor)

  1. Si tu proyecto aún no tiene un tileset ni bibliotecas, ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content una vez. El generador pinta con el tileset y coloca plantas y decoración de las bibliotecas del terreno; sin bibliotecas, omite la vegetación y la decoración.
  2. Selecciona tu RTS Terrain (o crea uno con GameObject ▸ RTS Terrain Studio ▸ RTS Terrain) y abre Tools ▸ RTS Terrain Studio ▸ Editor Window.
  3. Haz clic en la pestaña Map y despliega la tarjeta Generator.
  4. Elige un estilo en Style (por ejemplo, Highlands) y haz clic en Apply Style Preset. Esto carga los valores de ruido, acantilados, rampas y agua que dan al estilo su aspecto correcto.
  5. Elige una simetría en Symmetry: Rotate2 para un mapa 1v1 justo, Rotate4 para 4 jugadores en un mapa cuadrado.
  6. Abre Start Locations y ajusta Count al número de jugadores.
  7. Haz clic en Randomize Seed, luego en Generate y confirma el cuadro de diálogo.
  8. Lee el informe que aparece bajo los botones: número de niveles de meseta, rampas, posiciones iniciales, árboles, decoración y el tiempo que ha tardado. Las advertencias (por ejemplo, only 3 of 4 start locations found) aparecen ahí y en la ventana Console.
  9. ¿No te convence? Presiona Ctrl+Z, haz clic en Randomize Seed y vuelve a generar. Cuando te guste el resultado, guarda el mapa (Save en la cabecera de la ventana o en el Inspector del RTS Terrain).
Los mapas generados son un punto de partida. Después, usa las herramientas normales: mueve una rampa, excava un lago adicional, pinta un camino, coloca algunos doodads (objetos decorativos) elegidos a mano. La pintura con simetría del panel del pincel también mantiene justos tus cambios.

Los demás botones de la tarjeta:

BotónQué hace
Randomize SeedElige una nueva semilla aleatoria. Todo lo demás se queda como está.
Apply Style PresetCarga los valores de ruido, acantilados, rampas y agua del estilo seleccionado. Se conservan los ajustes de semilla, simetría, borde, pintura, vegetación, decoración y posiciones iniciales.
ResetRestablece todos los ajustes del generador a los valores predeterminados del estilo seleccionado.
GeneratePide confirmación y genera el mapa como un solo paso de deshacer llamado Generate Map.

Los ajustes del generador de la ventana se guardan en las preferencias del editor, así que se conservan tras reiniciar Unity.

Generar un mapa completamente nuevo (New Map Wizard)

Tools ▸ RTS Terrain Studio ▸ New Map Wizard... crea un nuevo archivo de mapa. En su paso Terrain, elige Generate en lugar de Flat, selecciona un Preset (los seis estilos más todos los assets RTSMapGeneratorPreset de tu proyecto, que aparecen como Preset: nombre), una semilla y una simetría, y haz clic en Preview para ver el minimapa del resultado antes de guardar nada. El asistente toma el número de jugadores de su propio ajuste de jugadores y lo escribe en las posiciones iniciales.

Generar en el editor en el juego

Los jugadores también pueden generar mapas en un juego publicado. En el editor en el juego, abre Map ▸ Generate... en la barra de herramientas. El cuadro de diálogo ofrece un conjunto simplificado de opciones:

OpciónCorresponde a
StyleAplica el preset de estilo (restablece las opciones de forma que hay debajo).
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

Presiona Enter (o Generate) para generar y Esc para cancelar. El resultado es un solo paso de deshacer (Ctrl+Z), y el cuadro de diálogo recuerda sus ajustes hasta que se cierra el juego.

Presets de estilo

Un estilo (TerrainStylePreset) cambia la forma del campo de alturas base y carga los valores predeterminados correspondientes mediante MapGeneratorSettings.ApplyPreset.

EstiloAspectoNiveles / mesetasRampasCobertura de lagos
PlainsTerreno mayormente llano con algunas mesetas bajas. Un clásico mapa abierto para partidas melee.2–3 / 230.05
Hills (predeterminado)Colinas onduladas (altura fina intensa) con algunas mesetas y rampas. Un buen punto de partida para todo tipo de mapas.2–3 / 240.06
HighlandsMuchas mesetas escalonadas y acantilados con rampas entre ellos.2–6 / 580.04
IslandsMasas de tierra rodeadas de agua (modo isla, agua más profunda, orillas más anchas).2–4 / 350.38
CanyonRuido Ridged convertido en cañones sinuosos entre mesetas altas.2–5 / 360.03
ArenaUna arena central baja rodeada por un anillo de terreno elevado.2–4 / 360.03

Guardar tus propios presets

¿Has encontrado unos ajustes que te gustan? Guárdalos como asset: Assets ▸ Create ▸ RTS Terrain Studio ▸ Map Generator Preset crea un RTSMapGeneratorPreset con un campo Description y un conjunto completo de ajustes en Settings (empieza con los valores de Hills). El New Map Wizard muestra todos los assets de preset del proyecto, y tu código puede pasar preset.settings al generador (consulta Generar desde código). Los presets son assets normales, así que pueden incluirse en tu juego, por ejemplo, como opciones «Cañón para 2 jugadores» o «Islas para 4 jugadores» en un menú de escaramuza.

Simetría: mapas justos para el juego competitivo

Con simetría, el generador crea un sector del mapa y lo copia, de modo que cada jugador recibe el mismo terreno, las mismas rampas, la misma cantidad de árboles y decoración y una posición inicial equivalente. En las copias en espejo, la decoración inclinada se inclina en el sentido reflejado.

SimetríaCopiasUso típicoRequiere un mapa cuadrado
None1Mapas de campaña o de forma libre.No
MirrorX2Reflejo izquierda ↔ derecha (2 jugadores enfrentados en horizontal).No
MirrorZ2Reflejo arriba ↔ abajo.No
MirrorXZ4Reflejo en ambos ejes (4 esquinas).No
Rotate2 (predeterminado)2Simetría central de 180°, el diseño clásico de 1v1.No
Rotate44Simetría rotacional de 90°, el diseño clásico para 4 jugadores.Sí (si no, se usa Rotate2)
MirrorDiagonal2Reflejo a lo largo de la diagonal (x ↔ z).Sí (si no, se usa MirrorX)
Usa un número de posiciones iniciales que sea múltiplo del número de copias (2, 4, 6 … para Rotate2). De lo contrario, el generador avisa de que el diseño no puede ser justo. MapGenerationReport.symmetryUsed te indica qué simetría se ha usado realmente.

Referencia de los ajustes del generador (MapGeneratorSettings)

La tarjeta del generador muestra todos los campos de MapGeneratorSettings, agrupados en secciones plegables. La columna Predeterminado muestra los valores de new MapGeneratorSettings(), que coinciden con el estilo Hills; los grupos Noise, Cliffs, Ramps y Water cambian según el estilo seleccionado. Antes de generar, Validate() ajusta al rango válido los valores que quedan fuera de él.

General

AjustePredeterminadoDescripción
Seed (seed)12345Semilla aleatoria. La misma semilla con los mismos ajustes genera siempre el mismo mapa.
Style (style)HillsForma del terreno. Haz clic en Apply Style Preset (o llama a ApplyPreset) para cargar los valores predeterminados correspondientes.
Symmetry (symmetry)Rotate2Genera un sector y lo copia para que el mapa sea equilibrado (consulta Simetría).

Noise (noise, GeneratorNoiseSettings)

AjustePredeterminadoDescripción
Scale85 mTamaño en metros de las formas grandes del terreno. Los valores más altos producen mesetas más grandes.
Octaves4Número de capas de ruido (1–8). Cuantas más octavas, más detalles pequeños se añaden a los contornos.
Persistence0.5Multiplicador de amplitud por octava (0.5 = cada octava tiene la mitad de intensidad).
Lacunarity2Multiplicador de frecuencia por octava.
Ridged Amount0Grado de mezcla con un ruido de crestas (crestas afiladas y cañones). El estilo Canyon usa 1.
Warp0.4Intensidad de la deformación del dominio (domain warp). Deforma los contornos para darles formas más naturales y fluidas.
Contrast1Los valores más altos desplazan más superficie hacia la meseta más baja y la más alta.
Smoothing2Pasadas de desenfoque antes de convertir el campo en niveles de acantilado (elimina formas diminutas causadas por el ruido).
Fine Height Amplitude1.6 mAmplitud del ruido de altura fina (colinas sobre las mesetas).
Fine Height Scale22 mTamaño de las formas del ruido de altura fina.
Fine Height Octaves3Número de octavas del ruido de altura fina.
Cliff Edge Fade4 celdasDistancia a lo largo de la cual la altura fina se desvanece hacia los bordes de acantilado, para que la parte superior de los acantilados quede limpia.

Cliffs (cliffs, GeneratorCliffSettings)

AjustePredeterminadoDescripción
EnabledactivadoGenera niveles de acantilado. Si está desactivado, todo el mapa usa Min Level y solo varían las alturas finas.
Min Level2Nivel de acantilado de la meseta más baja (0–15). El nivel 2 deja espacio para excavar lagos por debajo.
Max Level3Nivel de acantilado de la meseta más alta.
Plateau Count2Número de niveles de meseta distintos entre Min Level y Max Level.
Hysteresis0.04Banda alrededor de cada umbral de nivel (fracción del rango de alturas). Los vértices que quedan dentro de ella adoptan el nivel de sus vecinos, lo que mantiene suaves las líneas de acantilado.
Min Feature Width8 celdasAnchura mínima de cada meseta, terraza, valle y lago. Las partes más estrechas (terrazas finas entre dos acantilados, crestas, zanjas, mesetas diminutas) se unen al nivel vecino, y cada meseta mantiene un margen de unas tres cuartas partes de esta anchura hasta el acantilado que tiene debajo. Los lagos pueden ser una cuarta parte más estrechos. Los presets usan 6–10.
Cleanup Passes2Pasadas que eliminan vértices aislados y picos.
Cliff Type0Tipo de acantilado (capa de acantilado del tileset, 0–3) que se escribe en todos los vértices.

Ramps (ramps, GeneratorRampSettings)

AjustePredeterminadoDescripción
EnabledactivadoColoca rampas entre mesetas adyacentes.
Count4Número de rampas en el sector generado (las copias simétricas añaden más).
Width3 celdasAnchura de una rampa a lo largo del acantilado.
Length2 celdasLongitud de una rampa por nivel de acantilado, en sentido transversal al acantilado. Con 2 se obtiene una pendiente suave.
Ensure ConnectivityactivadoAñade rampas adicionales donde hace falta para que se pueda llegar a cada meseta desde cualquier otra.
Min Spacing12 celdasDistancia mínima entre dos rampas. Las rampas que se solapan se omiten.
Min Plateau Size40La pasada de conectividad ignora las mesetas con menos vértices.

Water (water, GeneratorWaterSettings)

AjustePredeterminadoDescripción
EnabledactivadoGenera lagos o mar y sus superficies de agua.
Sea Level Offset−0.25Superficie del agua, en niveles de acantilado, respecto a la meseta más baja (de −0.9 a 2). Los valores negativos mantienen seca la meseta más baja; los positivos la inundan.
Lake Coverage0.06Fracción del mapa (las zonas más bajas) que se convierte en lagos o mar (0–0.7).
Max Depth2.5 mPunto más profundo de los lagos por debajo de la superficie del agua.
Shore Width4 celdasAnchura de la orilla en pendiente, desde la línea de costa hasta la profundidad máxima.
Island ModedesactivadoHunde el terreno hacia los bordes del mapa para que este se convierta en una o varias islas. Los mapas de islas tienen siempre al menos un 15 % de mar (el informe avisa cuando se ha aumentado la cobertura).
Border Falloff0.25Modo isla: anchura de la atenuación hacia el borde, como fracción del tamaño del mapa.

Border (border, GeneratorBorderSettings)

AjustePredeterminadoDescripción
ModeNoneNone: sin borde especial. Unwalkable: pinta a lo largo del borde ajustes manuales de pathing que lo hacen no transitable y no edificable. HighCliffs: levanta un anillo de acantilados altos a lo largo del borde (que tampoco es transitable).
Width2 celdasAnchura del borde.
Cliff Level0Nivel de acantilado del borde HighCliffs. 0 = automático (dos niveles por encima de la meseta más alta).

Paint (paint, GeneratorPaintSettings)

AjustePredeterminadoDescripción
ModeAutoPaintKeepExisting: deja intacta la pintura del suelo. FillBaseLayer: rellena todo el mapa con la capa definida en Base Layer. AutoPaint: pinta según la altura, la pendiente, el nivel de acantilado y el agua con un perfil de pintado automático.
ProfileNoneUn RTSAutoPaintProfile (Assets ▸ Create ▸ RTS Terrain Studio ▸ Auto Paint Profile). Vacío = reglas predeterminadas integradas, asignadas según los nombres de las capas del tileset.
Base Layer0Capa de suelo que usa el modo FillBaseLayer.

Vegetation (vegetation, GeneratorVegetationSettings)

AjustePredeterminadoDescripción
EnabledactivadoGenera capas de detalle y bosques (requiere una biblioteca de vegetación en el terreno).
Use Whole Library When EmptyactivadoSi las listas de abajo están vacías, se usan todos los prototipos de la biblioteca de vegetación.
Clear ExistingactivadoElimina las capas de detalle y los árboles existentes antes de generar.
DetailsvacíoCapas de detalle (hierba) que se generan; consulta la tabla siguiente.
Tree Prototype IdsvacíoIds de los prototipos de árbol que se usan para los bosques.
Forest Coverage0.22Fracción del suelo apto que cubren los bosques.
Forest Noise Scale55 mTamaño de las formas del ruido de bosques.
Tree Spacing2.6 mDistancia entre árboles.
Tree Density0.85Probabilidad de que un hueco libre dentro de un bosque reciba un árbol (los valores más bajos dan bosques más dispersos).
Max Tree Slope26°No se colocan árboles en pendientes más empinadas.
Start Location Clearance14 mMantiene libre de árboles y detalles esta distancia alrededor de las posiciones iniciales.

Cada entrada de Details (GeneratorDetailEntry):

AjustePredeterminadoDescripción
Prototype Id""Id del prototipo de detalle en la biblioteca de vegetación.
Density0.6Densidad dentro de las zonas cubiertas (1 = densidad máxima del prototipo).
Coverage0.55Fracción del suelo apto que queda cubierta.
Noise Scale28 mTamaño de las formas del ruido de cobertura.
Max Slope32°No se colocan detalles en pendientes más empinadas.

Decor (decor, GeneratorDecorSettings)

AjustePredeterminadoDescripción
EnabledactivadoDispersa decoración (requiere una biblioteca de decoración en el terreno).
Use Whole Library When EmptyactivadoSi la lista de abajo está vacía, se usan todos los prototipos de la biblioteca de decoración.
Clear ExistingactivadoElimina la decoración existente antes de generar.
Prototype IdsvacíoIds de los prototipos de decoración que se dispersan.
Count60Número de objetos decorativos en el sector generado (las copias simétricas añaden más).
Min Spacing3 mDistancia mínima entre objetos decorativos (si el espaciado propio del prototipo es mayor, se usa ese).
Max Slope30°No se coloca decoración en pendientes más empinadas.
Start Location Clearance12 mMantiene libre de decoración esta distancia alrededor de las posiciones iniciales.

La decoración generada respeta los ajustes de cada prototipo de decoración: giro horizontal aleatorio y paso de giro, rango de escala, tintes, variantes, alineación con la normal e inclinación aleatoria máxima.

Start Locations (startLocations, GeneratorStartLocationSettings)

AjustePredeterminadoDescripción
EnabledactivadoColoca posiciones iniciales. Si está desactivado, se conservan las posiciones iniciales existentes.
Count2Número de posiciones iniciales (1–16).
Flat Radius5 celdasRadio que debe ser llano alrededor de una posición inicial (un solo nivel, sin acantilados, rampas ni agua).
Edge Margin8 celdasDistancia mínima al borde del mapa.
Update Max PlayersactivadoEscribe el valor de Count en el ajuste Max Players del mapa (MapInfo.maxPlayers).
Si el mapa tiene muy poco espacio llano, el generador coloca tantas posiciones iniciales como quepan e informa con «Only N of M». Reduce Flat Radius o Edge Margin, usa menos mesetas o agranda el mapa.

Generar desde código

Tres puntos de entrada cubren todas las situaciones:

MétodoÚsalo para
RTSMapGenerator.Generate(RTSTerrain, MapGeneratorSettings, RTSToolContext ctx = null)Código del juego. Genera en el mapa del terreno, actualiza el terreno y registra las advertencias en el log. Solo se puede deshacer si pasas el ctx de una llamada a RTSEditSession.RunOperation.
RTSMapGenerator.Generate(RTSEditSession, MapGeneratorSettings)Herramientas del editor y extensiones del editor en el juego: un paso de deshacer llamado Generate Map.
RTSMapGenerator.GenerateInto(MapData, settings, tileset, vegetation, decor)Generar en un MapData que no se muestra (vistas previas, generación por lotes, pruebas). No envía notificaciones ni admite deshacer. Devuelve un MapGenerationReport.
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 contiene symmetryUsed, plateauLevels, ramps, startLocations, detailLayers, trees, decor, milliseconds y la lista warnings; ToString() devuelve un resumen de una línea. RTSMapGenerator.GeneratedLayers enumera todos los indicadores de MapLayers que escribe el generador, que son los que debes pasar a RunOperation cuando envuelvas tú mismo GenerateInto.

Importación y exportación de mapas de alturas

La tarjeta Heightmap de la pestaña Map importa y exporta la altura del terreno como PNG de 16 bits o RAW de 16 bits. Úsala para importar terreno desde World Machine, Gaea, Photoshop o de una fuente de elevación del mundo real, o para enviar el mapa a una de esas herramientas y volver a traerlo.

Cómo se corresponden los mapas de alturas con el terreno

  • Un mapa de alturas cubre la cuadrícula de vértices: un mapa de 256 × 256 celdas tiene 257 × 257 vértices, así que un mapa de alturas perfecto mide 257 × 257 píxeles. Las imágenes de cualquier otro tamaño se remuestrean de forma bilineal.
  • Los valores de los píxeles se convierten con height = offset + value / 65535 × scale. También se aceptan imágenes de 8 bits; simplemente tienen menos escalones.
  • El valor predeterminado de Height Scale abarca los 16 niveles de acantilado: 16 × Cliff Level Height (32 m con los niveles predeterminados de 2 m). HeightmapIO.DefaultHeightScale(map) devuelve ese valor.
  • La fila 0 de un archivo RAW y la fila inferior de un PNG corresponden a Z = 0 (la primera fila de vértices). Si tu mapa aparece reflejado, activa Flip Vertically.
  • Cada importación es un paso de deshacer llamado Import Heightmap.

Importar

  1. Abre la pestaña Map y despliega la tarjeta Heightmap.
  2. Elige el Mode de importación (tabla de abajo). Para un mapa de alturas externo típico, usa Cliff Levels And Fine Heights; para añadir colinas sobre mesetas existentes, usa Fine Heights Only.
  3. Deja Height Scale en 0 (automático), a menos que sepas cuántos metros representa tu imagen.
  4. Haz clic en Import PNG… (PNG, JPG o EXR; escala de grises de 8 o 16 bits; se usa el canal rojo) o en Import RAW… (.raw / .r16, 16 bits).
  5. Revisa el resultado. Presiona Ctrl+Z para deshacer y prueba otro modo u otra escala.
Modo de importaciónEscribeDescripción
Fine Heights OnlyAlturasSolo la altura fina (altura fina = valor × escala + desplazamiento). Los niveles de acantilado se conservan y su altura se suma por encima, así que la imagen añade colinas a las mesetas existentes.
Cliff Levels And Fine HeightsAlturas, niveles de acantiladoLa imagen es la altura absoluta del suelo. Se divide en niveles de acantilado (se redondea al nivel más cercano y se limpian los vértices aislados) y alturas finas, de modo que la altura del suelo coincide exactamente con la imagen. Ideal para mapas de alturas escalonados en terrazas; las pendientes suaves que cruzan un nivel se convierten en pequeños acantilados.
Absolute Heights Keep LevelsAlturasLa imagen es la altura absoluta del suelo, pero se conservan los niveles de acantilado existentes (altura fina = altura − nivel × altura de nivel). Úsalo para volver a importar un archivo exportado con Ground Height que hayas editado en una herramienta externa, sin cambiar los acantilados.
Ajuste de importaciónPredeterminadoDescripción
Modeconsulta más arribaCómo se convierte la imagen en niveles y alturas.
Height Scale0 (automático)Metros que representa el rango completo de 16 bits. 0 = 16 × altura de nivel.
Height Offset0Metros que se suman a cada altura importada.
Flip VerticallydesactivadoVoltea la imagen a lo largo de Z (úsalo cuando el mapa aparezca reflejado).
RAW Width / RAW Height0Muestras por fila y número de filas de los archivos RAW. 0 = deducirlo del tamaño del archivo (solo archivos cuadrados).
RAW Little EndianactivadoOrden de bytes de los archivos RAW. Windows y la mayoría de las herramientas escriben en little endian.
Para obtener el resultado más limpio, exporta el mapa de alturas desde la herramienta externa con exactamente celdas + 1 píxeles por lado y escalónalo en terrazas según la altura de nivel de acantilado. Suaviza las irregularidades restantes con la herramienta Smooth y añade rampas donde las unidades deban poder subir.

Exportar

Ajuste / botón de exportaciónPredeterminadoDescripción
SourceGround HeightGround Height: niveles de acantilado más altura fina (absoluta). Fine Heights: solo la altura fina por encima de los niveles.
Height Scale0 (automático)Metros que representa el valor 65535.
Height Offset0Altura que se escribe como 0.
RAW Little EndianactivadoOrden de bytes de los archivos RAW exportados.
Export PNG…PNG en escala de grises de 16 bits.
Export RAW…Archivo RAW de 16 bits.
Export Splat PNG…Una imagen de la pintura del suelo (colores de la capa dominante) para documentación o herramientas externas.

Ida y vuelta: exporta con Ground Height, edita la imagen externamente y vuelve a importarla con Absolute Heights Keep Levels y la misma escala y el mismo desplazamiento.

Mapas de alturas desde código

HeightmapIO ofrece ImportPng, ImportRaw16 e ImportHeights (valores normalizados float[] o NativeArray<float>, por ejemplo, de tu propio ruido) para terrenos; ApplyHeights para un MapData sin terreno asociado; ExportPng16, ExportRaw16 y ExportSplatPng (modos DominantLayerColor y SplatWeights), y además TryGuessRawSize y GetImportLayers(mode) para registrar los pasos de deshacer. Cada exportador tiene una sobrecarga con byte[] y otra con ruta de archivo.

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);
    }
}
A diferencia de la ventana, los métodos de importación usan tal cual el heightScale que les pasas. Pasa siempre un valor mayor que 0, por ejemplo, HeightmapIO.DefaultHeightScale(map). En las exportaciones, 0 significa automático.

Precálculo del minimapa

RTSMinimapBaker renderiza una imagen cenital del mapa: colores del suelo, sombreado del relieve, acantilados, agua según su profundidad, árboles, decoración, posiciones iniciales y regiones. Esta misma clase se usa en todo el paquete:

  • la tarjeta Minimap de la pestaña Map (vista previa y exportación a PNG),
  • la vista previa de los assets .rtsmap en la ventana Project y en el Inspector, y la vista previa del New Map Wizard,
  • el explorador de mapas del editor en el juego (junto a cada mapa se guarda una pequeña imagen de vista previa, controlada por Write Preview Images en RTSRuntimeEditor),
  • el ejemplo RTSMinimapUI (minimapa en el juego con niebla de guerra, puntos de las unidades, contorno de la cámara, clic para mover la cámara y clic con el botón derecho para mover unidades). Se vuelve a precalcular automáticamente, con una frecuencia limitada, cuando cambia el mapa.

Precalcular en el editor

  1. Abre la pestaña Map y despliega la tarjeta Minimap.
  2. Elige un valor de Size (128, 256, 512 o 1024 px para el lado más largo; los mapas no cuadrados conservan su relación de aspecto).
  3. Si quieres, ajusta las Bake Options (tabla de abajo).
  4. Haz clic en Bake Preview para ver el resultado en la tarjeta, o en Save PNG… para guardarlo en un archivo, por ejemplo, para una pantalla de carga o un menú de selección de mapas.

Opciones de precálculo (MinimapBakeOptions)

AjustePredeterminadoDescripción
Ground ModeBlendedLayersBlendedLayers mezcla los colores de todas las capas pintadas; DominantLayer usa la capa más fuerte de cada píxel (más nítido, al estilo de los RTS clásicos). Los colores de las capas proceden del tileset (su color de minimapa o el color medio de la textura de la capa).
Level Brightness0.05Cambio de brillo por nivel de acantilado respecto al nivel 2 (las mesetas más altas se ven más claras).
Hill Shading / Strengthactivado / 1Oscurece las pendientes orientadas en sentido contrario a la luz.
Light Direction(−0.55, 0.75, 0.45)Dirección hacia la luz en el espacio local del mapa (x = este, y = arriba, z = norte).
Use Map Sun DirectiondesactivadoUsa la dirección del sol del entorno del mapa en lugar de Light Direction.
Draw Cliffs / Cliff Color / Cliff Opacityactivado / negro / 0.8Colorea las celdas de acantilado. Negro = usar el color de acantilado del tileset.
Draw WateractivadoAgua coloreada según su profundidad (perfil de agua del tileset).
Draw Trees / Tree Color / Tree Dot Sizeactivado / verde oscuro / 0.75 pxÁrboles como puntos (los valores inferiores a 1 dibujan píxeles sueltos).
Draw Decor / Decor Color / Decor Dot Sizedesactivado / marrón / 0.75 pxObjetos decorativos como puntos.
Draw Start Locations / Start Location Sizeactivado / 4 pxMarcadores con el color de cada jugador.
Scale MarkersactivadoEscala el tamaño de los marcadores según el tamaño de la textura (tamaño de referencia: 256).
Player Colors16 colores de jugador clásicos de RTSColor por índice de jugador. Las entradas que faltan usan MinimapBakeOptions.DefaultPlayerColors.
Draw RegionsdesactivadoDibuja los contornos de las regiones con el color de cada región.

Minimapas desde código

RTSMinimapBaker.Bake(terrain, size, options) devuelve una nueva Texture2D sRGB legible (tú te encargas de destruirla). Update(existing, terrain, size, options) vuelve a precalcular sobre una textura existente y solo crea una nueva cuando la anterior no se puede reutilizar. BakePng(terrain, size) devuelve directamente los bytes PNG, y las sobrecargas con MapData precalculan mapas que no se están mostrando.

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);
    }
}

Validación de mapas

El validador de mapas (MapValidator) busca en un mapa problemas que se pasan por alto con facilidad: texturas que faltan, posiciones iniciales inalcanzables, referencias a objetos rotas y límites de rendimiento. Ejecútalo antes de incluir un mapa en tu juego o de publicarlo para los jugadores.

  1. Abre la pestaña Map y despliega la tarjeta Validation.
  2. Haz clic en Validate Map. El resumen muestra el número de errores, advertencias y mensajes informativos, o No problems found.
  3. Haz clic en un problema para mostrar su posición en la Scene View (en los problemas asociados a un lugar concreto, como una posición inicial).
  4. Corrige los problemas y vuelve a validar.
Las comprobaciones basadas en el pathing (posiciones iniciales transitables, accesibilidad, borde del mapa transitable) necesitan el mapa de pathing del terreno. Sin él, el validador añade un mensaje de tipo Info que indica que se han omitido estas comprobaciones.
Este es el validador de mapas. El validador de proyecto (Tools ▸ RTS Terrain Studio ▸ Validate Project) comprueba URP, los shaders, la gestión de la entrada y los paquetes; consulta Configuración del proyecto.

Qué se comprueba

ÁreaComprobaciónGravedad
TilesetNo hay ningún tileset asignado (el terreno se renderiza con colores neutros de respaldo)Warning
El tileset no tiene capas de sueloWarning
El mapa no guarda ningún id de tileset (se establece automáticamente al guardarlo con un tileset)Info
El mapa se creó para un tileset distinto del asignadoWarning
GeometríaVértices con alturas no válidas (NaN o infinito)Error
Hay celdas de acantilado que usan un tipo de acantilado que el tileset no defineWarning
El mapa tiene acantilados, pero el tileset no tiene capas de acantiladoWarning
Pintura del sueloTexels de splat cuyos pesos no suman 255 (pintura dañada)Warning
Hay una capa de suelo pintada que el tileset no proporcionaWarning
Una capa de suelo pintada no tiene textura albedoInfo
Posiciones inicialesEl mapa no tiene posiciones inicialesError
Hay menos posiciones iniciales que el valor de Max PlayersWarning
Un jugador tiene más de una posición inicialWarning
Una posición inicial está fuera del mapaError
Una posición inicial está sobre un borde de acantiladoWarning
Una posición inicial está cubierta por agua profunda / está en agua poco profundaError / Info
Dos posiciones iniciales están muy cerca (a menos distancia que Min Start Distance Cells)Warning
Una posición inicial no es transitableError
AccesibilidadUn jugador no puede llegar caminando hasta otro jugador (relleno por inundación sobre el mapa de pathing, empezando en el propio nivel de acantilado de la posición inicial)Error
Una gran parte del borde del mapa es transitable (plantéate un borde intransitable)Info
Árboles, decoración, detallesHay árboles, decoración o capas de detalle, pero no hay asignada ninguna biblioteca de vegetación / decoraciónWarning
Hay objetos que hacen referencia a un índice de prototipo que no está en la paleta del mapaError
Hay objetos o capas de detalle que usan un prototipo que falta en la bibliotecaWarning
Un prototipo no tiene prefab (sus objetos son invisibles)Warning
Objetos fuera del mapa, ids de instancia duplicados o un contador de ids de instancia inferior a los ids existentesWarning
RegionesUna región no tiene área; varias regiones comparten nombre (el código del juego que busca regiones por nombre puede elegir la equivocada)Info
RendimientoDemasiados árboles, objetos decorativos o capas de detalleWarning
Demasiados árboles u objetos decorativos en el modo de generación GameObjectWarning
Mapa grande (uso de memoria y tiempos de construcción)Info

Umbrales (MapValidationSettings)

AjustePredeterminadoDescripción
maxTrees20000Número de árboles por encima del cual se muestra una advertencia de rendimiento.
maxGameObjectTrees1500Número de árboles en el modo de generación GameObject por encima del cual se muestra una advertencia.
maxDecor6000Número de objetos decorativos por encima del cual se muestra una advertencia.
maxGameObjectDecor2000Número de objetos decorativos en el modo de generación GameObject por encima del cual se muestra una advertencia.
maxDetailLayers8Número de capas de detalle por encima del cual se muestra una advertencia.
largeMapSize512Los mapas más grandes que este valor (celdas por lado) reciben un mensaje de tipo Info sobre la memoria y los tiempos de construcción.
minStartDistanceCells12Las posiciones iniciales que estén más cerca que este valor generan una advertencia.
walkableEdgeFraction0.1Fracción de celdas transitables en el anillo exterior por encima de la cual se muestra el mensaje Info sobre el borde transitable.
checkReachabilitytrueComprueba que todas las posiciones iniciales puedan alcanzarse entre sí.

Validar desde código

MapValidator.Validate(terrain) devuelve una List<MapIssue>. Cada MapIssue tiene una Severity (Info, Warning, Error), un Message, una WorldPosition opcional y las Layers afectadas. Hay sobrecargas que aceptan MapValidationSettings o un simple MapData junto con el tileset, las bibliotecas y el mapa de pathing.

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;
    }
}

Integraciones

RTS Terrain Studio funciona por sí solo: tiene un buscador de rutas integrado basado en Burst, su propia niebla de guerra y sistemas de juego de ejemplo. Tres integraciones opcionales lo conectan con paquetes populares. Forman parte del paquete (carpeta Integrations) y se activan solas cuando está instalado el paquete que necesitan.

IntegraciónRequisitosQué obtienesMenú de la escena de demostración
MirrorMirror 96 o posteriorJuego en línea en cualquier mapa: transmisión del mapa a los clientes, unidades con servidor autoritativo, niebla de guerra por equipo y gestión de interés con protección contra maphack.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene
A* Pathfinding ProjectA* Pathfinding Project Pro 5.4.4 o posterior, inferior a 6.0Grafos de cuadrícula generados a partir del mapa de pathing con actualizaciones incrementales, y unidades que se mueven con AIPath y evasión local RVO.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene
Unity NavMeshAI Navigation (com.unity.ai.navigation) 2.0 o posteriorUn NavMesh construido a partir de los chunks del terreno y del mapa de pathing, que se mantiene actualizado mientras se edita el mapa, y unidades que siguen rutas del NavMesh o usan componentes NavMeshAgent.Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Unity NavMesh ▸ Create Demo Scene

La ventana Welcome (Tools ▸ RTS Terrain Studio ▸ Welcome) muestra el estado de cada integración: Active (compilada y lista), Installed (se encontró la dependencia, pero la integración no está compilada, normalmente por una versión incompatible) o Not installed.

Todas las escenas de demostración necesitan el contenido de demostración. Ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content una vez antes de crear la demostración de una integración.

Cómo funcionan los ensamblados opcionales

Cada integración se encuentra en sus propias definiciones de ensamblado. Unity solo compila un ensamblado cuando se cumplen todas sus define constraints. Los símbolos provienen de dos fuentes:

  • Version defines (versionDefines en el .asmdef): el ensamblado define un símbolo para sí mismo cuando hay instalado un paquete con una versión compatible. A* y NavMesh usan este mecanismo, así que reaccionan automáticamente cuando se instala o se elimina el paquete.
  • Scripting define symbols de los Player Settings: Mirror suele importarse como una carpeta de la Asset Store, no como un paquete, así que su ensamblado usa el símbolo MIRROR que el propio Mirror añade a los Player Settings.
EnsambladoVersion defineDefine constraints
RTSTerrainStudio.Integrations.Mirror y .Mirror.EditorningunoMIRROR
RTSTerrainStudio.Integrations.Mirror.DefineCleanup.Editorningunoninguna (se compila siempre; elimina los símbolos obsoletos de Mirror, consulta Eliminar integraciones)
RTSTerrainStudio.Integrations.AStar y .AStar.Editorcom.arongranberg.astar en [5.4.4,6.0.0) → RTS_ASTARRTS_ASTAR, !RTS_ASTAR_DISABLED
RTSTerrainStudio.Integrations.NavMesh y .NavMesh.Editorcom.unity.ai.navigation 2.0.0 o posterior → RTS_AI_NAVIGATIONRTS_AI_NAVIGATION

El resultado: sin la dependencia, la integración simplemente se omite, nada se rompe y las unidades usan el buscador de rutas integrado. Con A* y NavMesh no tienes que gestionar ningún símbolo de compilación a mano.

Proveedores de rutas. La unidad de ejemplo (RTSUnit) pide sus rutas a un IRTSUnitPathProvider. De forma predeterminada se usa el proveedor integrado; los proveedores de A* y NavMesh se registran como RTSUnitPathProviderRegistry.Default cuando Make Default está activado, o puedes asignar un proveedor al campo Path Provider de unidades concretas. Tu propia solución de búsqueda de rutas puede conectarse del mismo modo implementando IRTSUnitPathProvider.
Los ensamblados de las integraciones hacen referencia a RTSTerrainStudio.Samples (para usar RTSUnit y los controladores de selección y de órdenes). Conserva la carpeta Samples/Scripts mientras uses una integración.

Multijugador con Mirror

Juega en línea en mapas de RTS Terrain Studio con Mirror (versión 96 o posterior):

  • Transmisión del mapa: el servidor envía su mapa a cada cliente que se une, así que los jugadores pueden unirse a partidas en mapas que no tienen, incluidos los mapas creados con el editor en el juego. Los mapas descargados se guardan en caché, de modo que la siguiente partida en el mismo mapa empieza de inmediato.
  • Unidades con servidor autoritativo: la búsqueda de rutas y el movimiento se ejecutan solo en el servidor. Los clientes envían órdenes y el servidor comprueba que las unidades pertenecen al jugador.
  • Niebla de guerra por equipo en cada cliente.
  • Gestión de interés con protección contra maphack: las unidades ocultas por la niebla de guerra nunca se envían a los clientes enemigos.

Inicio rápido: la escena de demostración

  1. Importa Mirror (desde la Asset Store o GitHub). Mirror añade el símbolo de compilación MIRROR y la integración se compila.
  2. Ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content (una vez por proyecto).
  3. Ejecuta Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Create Demo Scene. Crea, en Integrations/Mirror/Demo: la escena RTS Mirror Demo.unity, los prefabs Prefabs/RTS Network Unit.prefab (una cápsula sustituta con RTSUnit, RTSNetworkUnit, NetworkIdentity y NetworkTransformReliable) y Prefabs/RTS Network Player.prefab, los materiales de las unidades y, si el proyecto no tiene ningún mapa con al menos dos posiciones iniciales, un mapa generado para 4 jugadores, RTS Mirror Demo Map.rtsmap.
  4. Cuando se te pregunte, deja que la herramienta añada la renderer feature de niebla de guerra y coloque la escena en primer lugar de la lista de escenas de la build.
  5. Presiona Play y haz clic en Host (Server + Client) en el HUD de Mirror. Juegas con el equipo 1 (rojo) y tus unidades aparecen en tu posición inicial.
  6. Inicia una segunda instancia (consulta más abajo), haz clic en Client y deja la dirección localhost. El cliente descarga el mapa (se muestra una barra de progreso), se une como equipo 2 (azul) y recibe sus propias unidades. Las unidades enemigas solo aparecen cuando tus unidades pueden verlas.

Probar host y cliente

MétodoCómo
Build del juego (la opción más fiable)File ▸ Build Profiles. Asegúrate de que RTS Mirror Demo sea la primera escena y luego compila. Inicia la build dos veces, o la build y el editor. En una instancia, haz clic en Host; en la otra, en Client.
ParrelSync (gratuito, GitHub)ParrelSync ▸ Clones Manager: crea un clon y ábrelo en un nuevo editor. Presiona Play en ambos y haz clic en Host en el original y en Client en el clon. El clon comparte la carpeta Assets, así que ejecuta el creador de la demo solo en el original.
Multiplayer Play Mode (com.unity.multiplayer.playmode)Window ▸ Multiplayer ▸ Multiplayer Play Mode. Activa un jugador virtual y presiona Play. Haz clic en Host en el editor principal y en Client en el jugador virtual.
A través de la redIntroduce la dirección IP del host en el campo de dirección del cliente. KCP usa el puerto UDP 7777; ábrelo en el cortafuegos del host.
Servidor dedicadoCompila con un perfil de build Dedicated Server. El gestor de red usa Headless Start Mode = Auto Start Server, así que el servidor se inicia solo. Los clientes se conectan con Client.

Cada máquina guarda los mapas descargados en Application.persistentDataPath/MapCache. Usa Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Clear Map Cache o … ▸ Open Map Cache Folder para probar una descarga desde cero. El editor y una build de Windows del mismo proyecto comparten esta carpeta.

¿Quieres ver cómo se transmite el mapa en la demo? Tanto el host como el cliente empiezan con el mapa de demostración, y el cliente detecta que ya muestra exactamente el mapa del servidor, así que no se descarga nada. Desactiva Reuse Loaded Map y Use Cache en el RTSNetworkMapSync del cliente, o cambia el mapa en el host antes de iniciar la partida como host (por ejemplo, con el editor en el juego).

Añadir multijugador a tu propia escena

ComponenteDónde ponerloFinalidad
RTSNetworkManagerObjeto del gestor de redAsigna los equipos, espera a que el cliente tenga el mapa y luego genera el jugador y sus unidades iniciales en su posición inicial. Elimina las unidades cuando un jugador se va.
RTSNetworkMapSyncEl mismo objeto (se añade automáticamente)Transmite el mapa a los clientes en fragmentos, con suma de comprobación, caché y eventos de progreso.
RTSFogOfWarInterestManagementEl mismo objetoGestión de interés de Mirror: cada cliente solo recibe las unidades que su equipo puede ver.
RTSNetworkFogOfWarEl mismo objetoConfigura el RTSFogOfWarManager: el servidor calcula todos los equipos; un cliente, solo su propio equipo.
RTSNetworkStatusUIEl mismo objeto (opcional)Barra de progreso de la descarga y «You are Player N» (IMGUI).
RTSNetworkPlayerPrefab del jugadorSyncVar de equipo. Configura el jugador local (equipo de la niebla de guerra, selección, cámara) y envía las órdenes como comandos.
RTSNetworkUnitPrefabs de unidadesSyncVar de equipo. El movimiento se ejecuta solo en el servidor; los clientes solo muestran la unidad.
RTSNetworkCommandRelaySe añade automáticamente en los clientesEnvía al servidor las órdenes del RTSCommandController de ejemplo.
  1. Monta una escena normal de RTS Terrain Studio: un RTSTerrain con un mapa que tenga posiciones iniciales, un RTSFogOfWarManager, un RTSCameraController, un RTSSelectionManager y un RTSCommandController. No añadas RTSDemoGame: en su lugar, es el gestor de red quien genera las unidades.
  2. Crea un objeto vacío y añade KcpTransport, RTSNetworkManager (este añade RTSNetworkMapSync), RTSFogOfWarInterestManagement, RTSNetworkFogOfWar y, si quieres, NetworkManagerHUD. Asigna el transporte al campo Transport del gestor.
  3. Crea un prefab de jugador con NetworkIdentity y RTSNetworkPlayer y asígnalo a Player Prefab.
  4. Crea un prefab de unidad con NetworkIdentity, RTSUnit, RTSNetworkUnit y NetworkTransformReliable (Sync Direction Server To Client, Sync Scale desactivado). Asígnalo a Unit Prefab; se añade automáticamente a los prefabs que se pueden generar. Cualquier otro prefab en red que generes debe estar en Registered Spawnable Prefabs.
  5. Configura Units Per Player (predeterminado: 5) y, si quieres, Max Players Override (0 = tantos jugadores como posiciones iniciales tenga el mapa).
  6. Coloca los componentes de IA u otra lógica de los prefabs de unidades (por ejemplo, RTSWanderAI o tus propios scripts de IA) en Server Only Behaviours de RTSNetworkUnit, para que los clientes no los ejecuten.
AjustePredeterminadoDescripción
RTSNetworkManager
TerrainvacíoTerreno en el que se juega la partida. Vacío = RTSTerrain.Main.
Unit PrefabvacíoUnidad inicial de cada jugador (necesita NetworkIdentity, RTSUnit y RTSNetworkUnit).
Units Per Player5Número de unidades iniciales (0–64).
Max Players Override0Número de jugadores inferior al de plazas del mapa (0 = todas las posiciones iniciales).
Unit Spacing2 mDistancia entre las unidades iniciales.
Spawn Offset4 mDistancia de la primera fila de unidades iniciales respecto a la posición inicial, en dirección al centro del mapa.
Despawn Units On DisconnectactivadoDestruye las unidades de un jugador que se va.
Reserve Team Of Kept UnitsactivadoCuando las unidades se quedan: mantiene reservada la plaza del equipo mientras alguna de ellas siga viva.
Reject Disconnect Delay0.5 sSegundos entre el mensaje de rechazo y la desconexión, para que el mensaje llegue.
RTSNetworkUnit
Server Only BehavioursvacíoComponentes Behaviour que los clientes desactivan, porque la lógica de la unidad la ejecuta el servidor.
RTSNetworkPlayer
Jump Camera To StartactivadoMueve la cámara RTS a la posición inicial cuando empieza la partida.
Create Command RelayactivadoClientes puros: añade un RTSNetworkCommandRelay cuando la escena no tiene ninguno, para que el controlador de órdenes de ejemplo envíe sus órdenes al servidor.
Max Units Per Command, Max Commands Per Second256, 20Validación de órdenes en el servidor (consulta Unidades con servidor autoritativo).
Formation (Server)-Ajustes de formación que usa el servidor; usa los mismos valores que en RTSCommandController.

Unirse a una partida y transmisión del mapa

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)

El servidor ignora Ready y AddPlayer de un cliente hasta que ese cliente haya comunicado la suma de comprobación correcta del mapa, así que un cliente nunca recibe objetos del mundo de un mapa que no tiene. En modo host, el cliente local usa el terreno del servidor y se une de inmediato.

  • La carga útil es el archivo .rtsmap (ya comprimido con deflate). Un mapa de 128 × 128 con vegetación suele ocupar unos pocos cientos de KB.
  • Cuando el mapa del servidor no ha cambiado desde que se cargó de su asset o archivo, se envían los bytes originales del archivo. Son idénticos en todas las sesiones, así que la caché del cliente funciona de una sesión a otra. Un mapa editado o generado se serializa cuando hace falta.
  • El servidor solo acepta la confirmación del fragmento sin confirmar más antiguo, así que un cliente modificado no puede extraer el mapa entero de una vez.
  • Los cambios de escena (ServerChangeScene o la escena online de Mirror) vuelven a enviar el mapa de la nueva escena a todos los jugadores; los clientes solo se unen a la nueva escena cuando ya tienen ese mapa.
Ajuste de RTSNetworkMapSyncPredeterminadoDescripción
TerrainvacíoTerreno cuyo mapa se envía (servidor) y se reemplaza (cliente). Vacío = RTSTerrain.Main.
Chunk Size16 KBBytes por mensaje de fragmento; se reduce automáticamente para ajustarse al tamaño máximo de mensaje del transporte.
Chunks In Flight8Fragmentos sin confirmar en camino hacia un cliente. Los valores más altos descargan más rápido con un ping alto; los más bajos dejan más ancho de banda para la partida.
Transfer Timeout30 sSegundos sin progreso tras los cuales una transferencia falla.
Load Timeout120 sServidor: tiempo que puede necesitar un cliente después de la descarga para cargar su escena y aplicar el mapa. Mientras esperan, los clientes envían mensajes keep-alive.
Disconnect On FailureactivadoServidor: desconecta a los clientes cuya transferencia ha fallado (no pueden jugar sin el mapa).
Max Map Size MB64Cliente: tamaño máximo de mapa que acepta el cliente (protege contra servidores defectuosos u hostiles).
Reuse Loaded MapactivadoOmite la descarga cuando el cliente ya muestra exactamente el mapa del servidor.
Use Cache / Max Cached Mapsactivado / 20Guarda los mapas descargados en Application.persistentDataPath/MapCache (se eliminan primero los que hace más tiempo que no se juegan; 0 = sin límite).
Log TransfersactivadoRegistra en la consola el inicio y el final de cada transferencia.

Eventos para pantallas de carga: ClientHeaderReceived, ClientProgressChanged(float), ClientMapReady, ClientTransferFailed(string); en el servidor, ServerClientReady y ServerClientFailed. SendMapToAll() vuelve a enviar un mapa modificado (por ejemplo, tras una votación de mapa en un lobby) a todos los clientes conectados.

Unidades con servidor autoritativo

  • En el servidor, RTSUnit planifica rutas y se mueve igual que en el modo de un jugador; NetworkTransformReliable replica la posición y la rotación.
  • En los clientes, RTSNetworkUnit pasa a ser el proveedor de rutas de la unidad con HandlesMovement = true, así que la unidad nunca se mueve por sí misma; solo refleja lo que escribe el transform de red.
  • Las órdenes locales van al servidor: el clic con el botón derecho (mover en formación; con Shift se ponen en cola), Stop, Hold y Patrol a través de RTSNetworkCommandRelay, y cualquier otra llamada a RTSUnit.MoveTo sobre una unidad del cliente a través de RTSNetworkUnit. Desde tu propio código, llama a RTSNetworkPlayer.RequestMove, RequestMoveTo, RequestPatrol, RequestStop o RequestHold, o añade métodos [Command] a una clase derivada de RTSNetworkPlayer.
  • Las formaciones se calculan en el servidor con los ajustes Formation (Server) de RTSNetworkPlayer. Dales los mismos valores que al RTSCommandController de la escena si has cambiado los ajustes de formación del controlador.
  • El servidor valida cada orden: comprueba que las unidades existan y pertenezcan al equipo del jugador, que el destino sea una posición finita sobre un terreno, que no haya más de Max Units Per Command (predeterminado: 256) unidades por orden y que no se supere Max Commands Per Second (predeterminado: 20).
  • Jugadores que se van: con Despawn Units On Disconnect (activado de forma predeterminada), sus unidades se destruyen y la plaza queda libre. Sin esta opción, las unidades se quedan; Reserve Team Of Kept Units (activado de forma predeterminada) mantiene reservada la plaza mientras alguna de ellas siga viva. Un cliente rechazado (por ejemplo, porque la partida está llena) recibe el motivo antes de ser desconectado (RTSNetworkManager.ClientRejected, LastRejectReason).

Niebla de guerra y protección contra maphack

El servidor calcula la niebla de guerra de cada equipo. Cada Rebuild Interval segundos, RTSFogOfWarInterestManagement comprueba cada unidad que tenga RTSNetworkUnit:

  • Las unidades propias y aliadas (equipos que comparten visión mediante RTSFogOfWarManager.SetAlliance) se envían siempre.
  • Las unidades enemigas solo se envían mientras la niebla de guerra se las muestre al equipo del cliente. Linger Time mantiene una unidad un momento más después de que salga del campo de visión, para que las unidades en el límite de la visión no parpadeen.
  • Los objetos sin RTSNetworkUnit (jugadores, gestores) se envían siempre.

Un enemigo oculto se elimina del cliente y no se envía ninguno de sus movimientos, así que un cliente modificado no puede revelarlo. En los clientes, RTSFogOfWarHideable también oculta a los enemigos que ya están dentro del alcance de red pero que todavía no son visibles.

AjustePredeterminadoDescripción
Fog Of WarvacíoNiebla de guerra del lado del servidor. Vacío = RTSFogOfWarManager.Active.
Rebuild Interval0.2 sSegundos entre comprobaciones de visibilidad. Mantenlo en 0.1–0.3 s: cada vez que una unidad se vuelve visible, el cliente recibe un mensaje de generación completo.
Linger Time0.5 sSegundos que una unidad sigue siendo observada después de salir de la visión de un equipo.
Share With AlliesactivadoLos equipos que comparten visión siempre ven las unidades de sus aliados.
Show All Without FogactivadoMientras no haya ninguna niebla de guerra activa en el servidor, cada conexión ve todas las unidades. Desactivado: solo las unidades propias.
Spectators See AlldesactivadoLas conexiones sin equipo (espectadores) ven todas las unidades.

RTSNetworkFogOfWar tiene Compute All Teams On Server (activado; necesario para la gestión de interés), un intervalo de actualización del servidor (0 = conservar el valor del gestor; los valores más bajos hacen que las unidades aparezcan antes) y Disable Reveal All Online (activado). Una comprobación de visibilidad es una sola consulta a la cuadrícula de niebla por unidad, así que miles de unidades cuestan bastante menos de un milisegundo; el coste de la niebla de guerra en el servidor crece con el número de equipos, y con 2–8 equipos sigue siendo bajo.

Scripting con 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();
    }
}
MiembroDescripción
RTSNetworkManager (RTSNetworkManager.Instance)
ServerPlayerJoined(conn, team), ServerConnectionRejected(conn)Servidor: a una conexión se le asignó un equipo (antes de la transferencia del mapa) o se rechazó porque todas las plazas están ocupadas.
ServerPlayerSpawned(conn, player), ServerUnitSpawned(conn, unit)Servidor: se generó el objeto del jugador (después de que su mapa estuviera listo); se generó una unidad, también mediante SpawnUnit. Úsalos para dar a los jugadores sus objetos iniciales.
ServerPlayerLeft(conn, team)Servidor: una conexión abandonó la partida; se lanza antes de que se destruyan sus objetos.
ClientJoinedGame, ClientDisconnected, ClientRejected(reason)Cliente: se unió a la partida tras cargar el mapa; la conexión terminó; el servidor rechazó a este cliente.
RTSNetworkUnit SpawnUnit(owner, team, position, yaw, prefab = null)Servidor: genera una unidad (colocada en el punto transitable más cercano). owner null = sin propietario, por ejemplo, para criaturas neutrales o edificios. El prefab debe estar registrado como prefab generable y tener un RTSNetworkUnit.
GetUnits(conn), TryGetTeam(conn, out team), IsTeamReserved(team)Servidor: las unidades de una conexión, su equipo y si las unidades de un jugador que abandonó la partida siguen ocupando una plaza de equipo.
RejectConnection(conn, reason)Servidor: envía el motivo al cliente y lo desconecta cuando transcurre el tiempo de Reject Disconnect Delay.
RTSNetworkUnit y RTSNetworkPlayer
static RTSNetworkUnit.Spawned, Despawned, AllSe lanzan en cada máquina cuando se genera una unidad de red (después de aplicar su equipo) o cuando se elimina; todas las unidades conocidas en esta máquina.
static RTSNetworkPlayer.LocalPlayerStarted, LocalPlayerStopped, LocalEl objeto del jugador local apareció o desapareció; el jugador local actual.
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);
    }
}

Limitaciones y solución de problemas

  • Las ediciones del mapa durante una partida (hechas en el host con el editor en el juego) no se transmiten en directo. Llama a RTSNetworkMapSync.SendMapToAll() para volver a enviar el mapa.
  • RTSBuildingPlacementDemo y RTSTreeHarvestDemo se ejecutan solo en local y no se sincronizan por la red. RTSDemoGame es un inicializador para un solo jugador y no debe estar en una escena de red.
  • En los clientes, RTSUnit.State se queda en Idle. Usa RTSUnit.Velocity (por ejemplo, para animaciones de caminar) o añade tu propia SyncVar.
  • Las alianzas se establecen en la niebla de guerra del servidor. Los clientes no las reciben; llama también a SetAlliance en los clientes si la visión aliada debe verse allí.
  • Las cargas aditivas de escenas conservan el mapa actual. El servidor y los clientes deben usar el mismo tileset y las mismas bibliotecas (el mismo contenido del proyecto).
ProblemaSolución
«The server's fog of war computes only its local team»Añade RTSNetworkFogOfWar al gestor de red o activa Compute All Teams en el gestor de la niebla de guerra del servidor.
El cliente nunca sale de «Connecting...»No se puede acceder al servidor (dirección, cortafuegos, puerto UDP 7777) o la escena del servidor no tiene ningún RTSNetworkManager.
«Could not join the game: The game is full»El mapa tiene menos plazas libres que jugadores. Añade posiciones iniciales, aumenta el valor Max Players del mapa o revisa Max Players Override. Las plazas de los jugadores que abandonaron la partida siguen reservadas mientras sus unidades sigan con vida.
Errores de compilación por tipos de Mirror que faltan después de quitar MirrorEl símbolo de compilación (define) MIRROR sigue definido. Consulta Eliminar integraciones.
Las unidades no se mueven en el clienteEl prefab de la unidad necesita NetworkTransformReliable y la escena necesita un RTSCommandController.
Las unidades enemigas siempre son visiblesRTSFogOfWarInterestManagement debe estar en un objeto activo, y el gestor de la niebla de guerra debe ejecutarse en el servidor.
El mapa se descarga cada vezEl servidor envía un mapa que se editó o generó después de cargarlo. Guárdalo en su asset o archivo antes de hacer de host.

A* Pathfinding Project

Esta integración convierte el mapa de pathing del terreno en grafos de cuadrícula de A*, los mantiene actualizados mientras editas el mapa (en el editor y en el juego) y permite que las unidades de ejemplo se muevan con A*, ya sea siguiendo rutas de A* con su propio steering o con el AIPath de A* y la evasión local RVO.

Requisitos: A* Pathfinding Project Pro 5.4.4 o posterior, pero anterior a 6.0 (probado con 5.4.7). La integración usa TraversalConstraint (novedad de la 5.4) y la evasión local RVO (RVOController, RVOSimulator), que solo incluye la versión Pro. La versión gratuita usa el mismo nombre de paquete, por lo que Unity no puede distinguirlas: con la versión gratuita, añade RTS_ASTAR_DISABLED a Project Settings ▸ Player ▸ Scripting Define Symbols (o elimina la carpeta de la integración).

Inicio rápido: la escena de demostración

  1. Instala A* Pathfinding Project Pro 5.4 o posterior.
  2. Ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content una vez.
  3. Ejecuta Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Create Demo Scene. La escena se guarda como Integrations/AStarPathfinding/Demo/RTS AStar Demo.unity. Si el contenido de demostración no tiene ningún mapa, se genera RTS AStar Demo Map.rtsmap (mesetas, rampas, lagos) junto a ella.
  4. Presiona Play. Selecciona unidades con el botón izquierdo del ratón y haz clic con el botón derecho para moverlas. Los caballeros usan el grafo RTS Large; todas las demás unidades, RTS Ground.
  5. Selecciona el objeto A* Pathfinding y cambia el ajuste Mode del A* Path Provider entre Agent Movement y Path Only para comparar ambas formas de movimiento.

Añadir A* a tu propia escena

Selecciona tu RTS Terrain y ejecuta Tools ▸ RTS Terrain Studio ▸ Integrations ▸ A* Pathfinding ▸ Add A* To Selected Terrain. Esto añade un GameObject A* Pathfinding con:

ComponenteFinalidad
AstarPathEl componente principal de A*. Scan on Startup está desactivado (el escaneo lo hace el constructor de grafos) y el registro de rutas se limita a los errores.
RTSAstarGraphBuilder (A* Graph Builder)Crea los grafos de cuadrícula a partir del mapa de pathing y los actualiza con cada edición del mapa.
RVOSimulatorEvasión local para los agentes con un RVOController.
RTSAstarPathProvider (A* Path Provider)Hace que todas las unidades usen A* (se registra como RTSUnitPathProviderRegistry.Default).

La configuración manual funciona igual: añade estos componentes a cualquier GameObject (Add Component ▸ RTS Terrain Studio ▸ Integrations) y asigna el terreno al constructor de grafos, o coloca el constructor en el GameObject del terreno, donde encuentra el terreno por sí solo. Desactiva Scan On Startup de AstarPath.

Varios terrenos: cada terreno necesita su propio constructor de grafos con sus propios nombres de grafo. Si ejecutas Add A* To Selected Terrain para un segundo terreno, sus grafos reciben el nombre del terreno, por ejemplo, RTS Ground (Island). Un grafo que ya rellenó otro constructor nunca se comparte.

Cómo se construyen los grafos

  • Disposición exacta. Un nodo por celda de pathing, centrado en el mapa y girado con la rotación Y del terreno. Mover o girar el terreno reconstruye los grafos (en el modo Edit, en cuanto el terreno lleva 0.2 segundos quieto).
  • No se necesita física. Las comprobaciones de colisión y de altura de A* están desactivadas. Las alturas de los nodos proceden de la superficie del terreno (igual que RTSTerrain.GetHeight); la transitabilidad, las etiquetas y las penalizaciones proceden de los indicadores de pathing. Los datos se calculan con jobs de Burst.
  • Etiquetas (modificables en el constructor): 0 suelo, 1 agua poco profunda, 2 agua profunda, 3 borde de acantilado. Úsalas en los ajustes Tags del Seeker para prohibir o penalizar tipos de suelo por unidad.
  • Perfiles de grafo. Un grafo de cuadrícula de A* por perfil (por ejemplo, uno por tamaño de unidad). Cada perfil tiene un nombre de grafo, un Traversal (Land, Shallow Water, Deep Water), un Unit Radius (los nodos más cercanos a un obstáculo pasan a ser no transitables, para que las unidades grandes nunca se solapen con él), penalizaciones por tipo de suelo (1000 ≈ un metro adicional por nodo), Use Water Surface Height para grafos navales y una altura máxima de escalada. Las unidades eligen el grafo más pequeño que se ajusta a su radio.
  • Unidades navales y anfibias. Establece el Traversal del perfil en Deep Water (barcos) o Land | Shallow Water | Deep Water y activa Use Water Surface Height. Asigna a los barcos un RTSAstarUnitSetup con Required Traversal = Deep Water. Desde código, los métodos de fábrica RTSAstarGraphProfile.Ground() («RTS Ground», radio 0.5 m, Land | Shallow Water), Naval() («RTS Naval», radio 1 m, Deep Water, altura de la superficie del agua) y Amphibious() («RTS Amphibious», radio 0.75 m, todos los tipos de suelo, altura de la superficie del agua) crean perfiles listos para usar. De forma predeterminada, un perfil nuevo usa Land | Shallow Water, una penalización de 600 para el agua poco profunda y una penalización de 200 para los bordes de acantilado.
  • Actualizaciones. Un mapa nuevo, un terreno movido o un cambio de ajustes provocan una reconstrucción completa. Cada edición del mapa (RTSTerrain.PathingChanged) reescribe solo los nodos afectados, su margen de holgura y sus conexiones en una llamada a AstarPath.AddWorkItem.
  • Mapas enormes. A* limita los grafos de cuadrícula a 1024 nodos por eje (8192 con el define de optimización ASTAR_LARGER_GRIDS). Los mapas de pathing más grandes usan automáticamente varias celdas de pathing por nodo; Cells Per Node hace lo mismo de forma intencionada para obtener grafos más rápidos.
  • Build In Edit Mode muestra los grafos como gizmos de A* mientras editas el mapa.
Miembro de RTSAstarGraphBuilderDescripción
Cells Per Node (ajuste, predeterminado: 1)Celdas de pathing por nodo del grafo en cada eje. 1 = coincidencia exacta con el mapa de pathing; se aumenta automáticamente en mapas enormes (EffectiveCellsPerNode).
Log Rebuilds (ajuste, predeterminado: desactivado)Escribe una línea en la consola por cada reconstrucción completa.
Profiles, TagsLos perfiles de grafo y los números de etiqueta. Llama a RequestRebuild() después de cambiarlos desde código.
RequestRebuild()Programa una reconstrucción completa para la próxima actualización (llamarlo repetidamente apenas cuesta).
bool RebuildNow()Reconstruye todos los grafos de inmediato. Devuelve false si no hay ningún AstarPath activo o ningún mapa.
UpdateRegion(RectInt pathingRect)Reescribe los nodos de un rectángulo de celdas de pathing. Se llama automáticamente desde RTSTerrain.PathingChanged; llámalo tú mismo después de cambiar PathingMap.Flags sin ese evento.
ProcessPendingChanges()Aplica de inmediato las actualizaciones pendientes (para rutas solicitadas en el mismo fotograma que una edición del mapa).
GetGraph(index), GetGraph(name), GetProfileIndex(name)El GridGraph de A* de un perfil.
FindProfileFor(radius, traversal), FindGraphFor(radius, traversal)El perfil (o grafo) más pequeño que se ajusta a una unidad; -1 / null si ningún perfil permite el traversal indicado.
GetStats(index)RTSAstarGraphStats: tamaño, tamaño de nodo, nodos transitables, tiempos de reconstrucción y de actualización, isBuilt.
IsBuilt, IsProfileBuilt(index), bool TryGetProblem(out string problem)Si los grafos contienen el mapa actual; por qué no se pueden construir (sin AstarPath, sin terreno, sin mapa).
GraphsRebuilt, GraphsUpdatedEventos tras una reconstrucción completa y tras una actualización incremental (con el rectángulo de pathing).
static FindFor(terrain), static AllEl constructor de un terreno; todos los constructores activos.

Modos del proveedor y RVO

Modo (RTSAstarMovementMode)Comportamiento
Agent Movement (predeterminado)HandlesMovement = true. Las órdenes de movimiento establecen el destino del AIPath de la unidad e inician una búsqueda; Stop y Hold borran la ruta, y las unidades que mantienen la posición bloquean su RVOController. La llegada y los fallos se notifican a la unidad, de modo que las órdenes en cola (Shift), las patrullas y los eventos de unidad se comportan igual que con el buscador de rutas integrado. La evasión local RVO mantiene separadas las unidades en movimiento.
Path OnlyHandlesMovement = false. A* solo calcula los puntos de ruta (a través del Seeker de la unidad, por lo que se aplican modificadores como el Funnel Modifier); la unidad los sigue con su propio steering y su propia separación. A las unidades sin Funnel Modifier se les enderezan las rutas (Smooth Paths).
Ajuste del proveedorPredeterminadoDescripción
ModeAgent MovementConsulta más arriba.
Graph BuildervacíoConstructor cuyos grafos se usan. Vacío = el constructor del terreno de la unidad.
Auto Configure UnitsactivadoAñade y configura RTSAstarUnitSetup en cada unidad: un Seeker, un FunnelModifier, un RTSAstarAIPath (un AIPath que se apoya en la superficie del terreno sin raycasts de física) y un RVOController, con la velocidad, la velocidad de giro, la aceleración, el radio y la altura copiados del RTSUnit.
Fallback To BuiltinactivadoUsa el buscador de rutas integrado mientras no haya ningún grafo de A* disponible (en el modo Edit y antes del primer escaneo).
Smooth PathsactivadoPath Only: endereza las rutas de las unidades sin Funnel Modifier; los atajos se mantienen en etiquetas permitidas y nunca cruzan nodos más costosos.
Stuck Time1.5 sAgent Movement: segundos que un agente puede permanecer quieto durante un movimiento antes de recalcular la ruta una vez y, después, desistir.

Añade tú mismo RTSAstarUnitSetup a un prefab de unidad para elegir un grafo por nombre o para desactivar la evasión local o el suavizado de rutas por tipo de unidad. El Inspector de RTSAstarAIPath muestra los ajustes de movimiento habituales de A* y, además, una sección 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();
    }
}

Limitaciones

  • Las reglas de los grafos de cuadrícula de A* (GridGraph.rules) no tienen efecto: el constructor escribe por sí mismo la transitabilidad, las etiquetas, las penalizaciones y las posiciones tras cada escaneo. Usa en su lugar las etiquetas y las penalizaciones del constructor.
  • No se admiten terrenos girados alrededor de X o Z (los grafos de cuadrícula son planos horizontales).
  • Un ajuste manual de pathing Unwalkable pintado solo cierra el paso a las unidades terrestres. A los barcos les cierran el paso los obstáculos y los acantilados; da forma a las rutas navales con doodads o con bordes de agua profunda. Las unidades navales necesitan el modo Agent Movement.
  • Si quitas un perfil de grafo del constructor, su grafo permanece en el componente AstarPath; elimínalo allí si ya no lo necesitas.
  • A* solo busca rutas en el modo Play. En el modo Edit, las solicitudes de ruta recurren al buscador de rutas integrado.

Unity NavMesh (AI Navigation)

Esta integración construye un NavMesh de Unity para un RTSTerrain y permite que las unidades de ejemplo encuentren su camino sobre él. Úsala si tu juego ya trabaja con el NavMesh de Unity (NavMeshAgents, consultas de NavMesh, NavMeshLinks) o si quieres aprovechar la evasión de multitudes de Unity. Necesita el paquete AI Navigation (com.unity.ai.navigation) 2.0 o posterior y funciona en el editor y en el juego, incluidos los mapas cargados o editados en tiempo de ejecución.

Eliminar integraciones

Nunca es obligatorio eliminar una integración: sin su dependencia, no se compila. Elimínala cuando desinstales la dependencia o quieras un proyecto más pequeño. En cualquier caso, si creaste una escena de demostración, elimina primero la carpeta Demo de la integración: sus escenas y prefabs usan los componentes de la integración y mostrarían scripts que faltan.

Eliminar A* Pathfinding Project o AI Navigation

  1. Elimina Integrations/AStarPathfinding/Demo (o Integrations/UnityNavMesh/Demo) si existe.
  2. Quita los componentes de la integración de tus propias escenas y prefabs (constructor de grafos, proveedor de rutas, configuración de unidades / constructor de NavMesh, proveedor de rutas de NavMesh).
  3. Desinstala el paquete (A* Pathfinding Project, o AI Navigation en el Package Manager). El símbolo de versión (version define) desaparece junto con el paquete y los ensamblados de la integración se omiten automáticamente.
  4. Si añadiste RTS_ASTAR_DISABLED para la versión gratuita de A*, puedes volver a quitarlo de los Scripting Define Symbols.

Eliminar Mirror

Mirror añade los símbolos de compilación MIRROR, MIRROR_89_OR_NEWER, MIRROR_90_OR_NEWER, MIRROR_93_OR_NEWER y MIRROR_96_OR_NEWER a los Player Settings, y eliminar Mirror no los quita. Mientras MIRROR siga definido, Unity seguirá compilando la integración contra ensamblados de Mirror que ya no existen, y los errores de compilación bloquearán todos los scripts del proyecto.

  1. Elimina primero Integrations/Mirror/Demo.
  2. Elimina la carpeta Mirror dentro del Unity Editor (en la ventana Project). El pequeño ensamblado de editor RTSTerrainStudio.Integrations.Mirror.DefineCleanup.Editor detecta la eliminación, quita los símbolos de Mirror de todas las plataformas de destino (build targets) y muestra en la consola lo que ha quitado. También se ejecuta cada vez que el editor se inicia sin Mirror. Tools ▸ RTS Terrain Studio ▸ Integrations ▸ Mirror ▸ Remove Stale Mirror Defines hace lo mismo manualmente.
  3. Revisa Project Settings ▸ Player ▸ Other Settings ▸ Scripting Define Symbols en todas las plataformas para las que compilas: no debe quedar ninguna entrada MIRROR ni MIRROR_..._OR_NEWER. Quita los restos a mano, por ejemplo, cuando Mirror se eliminó con el editor cerrado y el editor se inició en Safe Mode. El plugin Edgegap de Mirror también añade EDGEGAP_PLUGIN_SERVERS; es inofensivo, pero también puedes quitarlo.

Los scripts de la integración pueden quedarse en el proyecto: no se compilan hasta que vuelvas a instalar Mirror.

Eliminar las carpetas de integración

Cada integración es independiente. Al eliminar Integrations/Mirror, Integrations/AStarPathfinding o Integrations/UnityNavMesh (en la ventana Project), esa integración desaparece por completo; ninguna otra parte del paquete hace referencia a estas carpetas. También puedes eliminar toda la carpeta Integrations.

Quita los componentes de la integración de tus escenas y prefabs antes de eliminar su carpeta; de lo contrario, quedarán como scripts ausentes (missing scripts). Si eliminas Integrations/Mirror mientras Mirror sigue instalado y más tarde también quitas Mirror, elimina a mano los símbolos de compilación de Mirror (la limpieza automática está en esa carpeta).

Guía de scripting

Todo lo que puedes hacer en las ventanas del editor también puedes hacerlo desde C#. Este capítulo muestra cómo consultar el terreno, modificar el mapa desde código (con y sin deshacer), escribir tus propias herramientas, reaccionar a eventos y guardar y cargar mapas en tiempo de ejecución. Todos los ejemplos siguientes compilan con el paquete tal como se distribuye; cópialos en tu proyecto y adáptalos.

Ensamblados, espacios de nombres y coordenadas

EnsambladoEspacio de nombresContenido
RTSTerrainStudio.RuntimeRTSTerrainStudioTerreno, datos del mapa, herramientas, pinceles, vegetación, agua, decoración, niebla de guerra, pathing, generador, serializador, editor en el juego.
RTSTerrainStudio.SamplesRTSTerrainStudio.SamplesCámara RTS, unidades, selección, órdenes, interfaz del minimapa, juego de demostración.
RTSTerrainStudio.EditorRTSTerrainStudio.EditorVentana del editor, herramientas de la Scene View, inspectores, importadores, ventanas de configuración, RTSEditorContext.
RTSTerrainStudio.Integrations.*RTSTerrainStudio.Integrations.Mirror / .AStar / .NavMeshIntegraciones opcionales.

Los scripts sin definición de ensamblado (Assembly-CSharp, y Assembly-CSharp-Editor para los scripts de carpetas Editor) hacen referencia a estos ensamblados automáticamente. Si tu código tiene su propio .asmdef, añade RTSTerrainStudio.Runtime (y RTSTerrainStudio.Samples o RTSTerrainStudio.Editor si los usas) a sus referencias, además de Unity.Mathematics y Unity.Collections si usas tipos como float3 o NativeArray.

El mapa en tres cuadrículas

  • Celdas: el mapa tiene Width × Height celdas de CellSize metros (predeterminado: 2 m). Los rectángulos que se pasan a NotifyChanged se expresan en celdas.
  • Vértices: las esquinas de las celdas, (Width + 1) × (Height + 1); índice z * VertexCountX + x (MapData.VertexIndex(x, z)). Aquí se guardan las alturas, los niveles de acantilado, los indicadores, los tipos de acantilado y el agua. El vértice (x, z) está en (x * CellSize, z * CellSize).
  • Texels: capas ráster con N texels por celda: splat (SplatResolution), detalles (DetailResolution) y pathing (PathingResolution). El centro del texel (x, z) está en (x + 0.5, z + 0.5) * CellSize / N.

El espacio local del mapa empieza en el primer vértice: X/Z en metros de 0 a MapData.WorldSize, con Y hacia arriba. Solo se usan la posición y la rotación en Y del terreno (la escala se ignora). Convierte coordenadas con WorldToLocal, LocalToWorld, WorldToLocalDirection, LocalToWorldDirection, WorldToCell, CellCenterToWorld y VertexToWorld. La altura del suelo de un vértice es CliffLevels[i] × CliffLevelHeight + Heights[i]. Las rotaciones se expresan en grados, en sentido horario visto desde arriba (0 = +Z, 90 = +X), igual que la rotación en Y de Unity.

Consultar el terreno

La API de consulta de RTSTerrain se ajusta exactamente a la superficie que ves, incluidos los contornos redondeados de los acantilados, y no necesita colliders. Funciona en el modo Edit y en tiempo de ejecución.

MiembroDevuelve
RTSTerrain.Main, RTSTerrain.AllEl primer terreno activado / todos los terrenos activados.
RTSTerrain.FindAt(Vector3 world)El terreno cuya área contiene la posición, o null.
RTSTerrain.RaycastAll(Ray, out RTSTerrainHit, maxDistance)El impacto más cercano en cualquier terreno.
Raycast(Ray, out RTSTerrainHit, maxDistance)Raycast analítico contra este terreno. RTSTerrainHit contiene point, normal, distance, localPoint, cell y terrain.
GetHeight(world), SnapToGround(world)La Y del suelo en el espacio del mundo / la posición colocada sobre el suelo.
GetNormal(world)La normal de la superficie en el espacio del mundo.
GetCliffLevel(world)El nivel de acantilado (0–15) en la posición.
IsWater(world, out depth), TryGetWaterHeight(world, out waterY)Si la posición está bajo el agua, su profundidad y la Y de la superficie en el espacio del mundo.
GetPathing(world), IsWalkable(world), IsBuildable(world)PathingFlags (Walkable, Buildable, Flyable, ShallowWater, DeepWater, Cliff, Blocked, Steep). None fuera del mapa o antes de que se construya el pathing.
Contains(world), WorldBoundsSi la posición está dentro del área del mapa / los límites en el espacio del mundo.
GetGeometry()Un MapGeometryView compatible con Burst (SampleHeight, SampleNormal, SampleCliffLevel, SampleWater, Raycast … en el espacio local del mapa) para tus propios jobs.
RTSPathfinder.FindPath(terrain, from, to, options, list)Una ruta calculada por el buscador de rutas integrado, basado en Burst (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;
    }
}
Para seleccionar con el ratón, usa preferiblemente RTSTerrain.RaycastAll en lugar de Physics.Raycast: es exacto y funciona sin colliders y en el modo Edit. Los colliders (Collider Mode en los ajustes del terreno) siguen siendo útiles para objetos con física y para raycasts que también deben impactar en otros objetos.

Editar el mapa desde código

Todo el contenido del mapa está en terrain.Map, un MapData. Para modificarlo, se siguen siempre los mismos tres pasos:

  1. Modifica los datos en terrain.Map (en el hilo principal).
  2. Llama a terrain.NotifyChanged(layers, cellRect) con los MapLayers modificados y el rectángulo modificado en celdas, o a NotifyChangedAll(layers) tras un cambio en todo el mapa.
  3. Eso es todo. En su siguiente LateUpdate (en el modo Edit: la siguiente actualización del editor), el terreno reconstruye solo los chunks marcados como modificados, las texturas splat, el agua, la vegetación, la decoración y el mapa de pathing. Llama a terrain.ProcessPendingChanges() cuando necesites los resultados en el mismo fotograma (llamarlo a menudo cuesta poco).
Datos (MapData)Cuadrícula / tipoIndicador de MapLayers
Heightsvértices, float: altura fina en metros por encima del nivel de acantiladoHeights
CliffLevelsvértices, byte 0–15CliffLevels
VertexFlagsvértices, VertexFlags (Ramp, Water, User0, User1)VertexFlags
CliffTypesvértices, byte 0–3CliffTypes
WaterHeightsvértices, superficie del agua en metros del espacio local del mapa (se usa donde está activado el indicador Water)Water
SplatMaps[0..3]texels de splat, 4 × RGBA32; la capa i está en el mapa i/4, canal i%4; los pesos suman 255Splat
DetailLayerstexels de detalles, un byte de densidad por capa (DetailLayer.prototypeId, density)Details
Trees, Decorlistas de TreeInstanceData / DecorInstanceData con índices de paleta en TreePrototypeIds / DecorPrototypeIdsTrees, Decor
PathingOverridestexels de pathing, ajustes manuales pintadosPathingOverrides
Regions, StartLocations, MarkerslistasRegions, StartLocations, Markers
Info, Environmentinformación del mapa e iluminaciónInfo, Environment
Reglas para que el mapa siga siendo válido: limita los niveles de acantilado a 0–15 y los tipos de acantilado a 0–3; mantén las alturas finas dentro del rango (TerrainToolUtility.ClampFineHeight); haz que cada texel de splat sume exactamente 255 (usa SplatRaster y SplatMath); asigna los ids de instancia de los nuevos árboles y objetos decorativos con map.AllocateInstanceId() y los índices de prototipo con GetOrAddTreePrototype / GetOrAddDecorPrototype. Nunca conserves referencias a MapData ni a sus arrays después de cargar o redimensionar un mapa: SetMap, LoadFromFile y ResizeMap reemplazan el objeto completo y liberan (dispose) el anterior.

Deformación y pintura en tiempo de ejecución: cráteres y tierra corrupta

Un uso típico en el juego: una explosión excava un cráter y un edificio de los no muertos extiende a su alrededor un suelo de «tierra corrupta» (blight). El pathing y la altura de la vegetación y de la decoración se adaptan automáticamente.

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));
    }
}
Se usa así:TerrainEffects.Crater(RTSTerrain.FindAt(hitPoint), hitPoint, 4f, 1.5f); o TerrainEffects.PaintLayer(terrain, building.position, 12f, blightLayer);. Para efectos que modifican miles de vértices en cada fotograma, traslada el bucle a un job de Burst, como hacen las herramientas integradas.

Ediciones que se pueden deshacer: RTSEditSession.RunOperation

Las ediciones directas no se pueden deshacer, y eso es lo adecuado durante el juego. Para herramientas del editor y funciones de edición de mapas, usa un RTSEditSession: RunOperation(name, layers, action) registra las capas indicadas de todo el mapa, ejecuta tu acción y lo convierte todo en un único paso de deshacer. En el Unity Editor, usa la sesión compartida RTSEditorContext.Session; sus pasos forman parte del sistema de deshacer de Unity, así que Ctrl+Z / Ctrl+Y funcionan como siempre.

// 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));
        });
    }
}

En un juego en ejecución, usa la sesión del editor en el juego (RTSRuntimeEditor.Session) para que tu operación comparta su historial de deshacer, o crea tu propia sesión:

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();
    }
}
Dentro de una herramienta basada en trazos no llamas a RunOperation, sino a ctx.Record y ctx.MarkChanged en cada toque (consulta la siguiente sección). RunOperation registra capas completas, así que en mapas grandes es preferible indicar capas concretas en lugar de MapLayers.All.

Escribir una herramienta personalizada

Las herramientas son clases de C# normales que derivan de RTSTool. El paquete encuentra por reflexión (RTSToolRegistry) todas las herramientas no abstractas que tengan un constructor público sin parámetros, así que tu herramienta aparece automáticamente en la pestaña de su categoría, tanto en la ventana de RTS Terrain Studio como en el editor en el juego, con una interfaz de ajustes generada a partir de sus campos [ToolSetting], el panel de pincel, la simetría, deshacer/rehacer, el cursor de pincel proyectado y la barra de estado. En el editor, sus ajustes se recuerdan entre sesiones.

  1. Crea un archivo de C# en cualquier lugar de tu proyecto (no en una carpeta Editor, para que la herramienta también exista en las builds y pueda usarse en el editor en el juego).
  2. Deriva de RTSTool y sobrescribe Id (único, por ejemplo, "height.crater"), DisplayName y Category.
  3. Añade campos públicos con [ToolSetting("Label", ...)] para las opciones que el usuario puede cambiar.
  4. Sobrescribe ApplyDab: calcula los pesos del pincel con BrushUtility.ComputeWeights, llama a ctx.Record(layers, cells) antes de cambiar los datos, cambia los datos y llama a ctx.MarkChanged(layers, cells) después.
  5. Vuelve a Unity. Cuando el script se haya compilado, la herramienta aparecerá en su pestaña.
  6. Si incluyes la herramienta en una build con un Managed Stripping Level superior a Minimal, marca la clase con [UnityEngine.Scripting.Preserve] para que la eliminación de código la conserve (consulta Eliminación de código y herramientas personalizadas).
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);
    }
}

Qué puedes sobrescribir

MiembroPredeterminadoDescripción
Id, DisplayName, CategoryobligatorioId único, nombre en la interfaz y pestaña (Height, Cliffs, Paint, Vegetation, Decor, Water, Pathing, Markers).
Description, Hint""Descripción emergente y texto de ayuda; indicación de uso en la barra de estado.
IconNameIdNombre del icono. Si el nombre es desconocido, se usa como alternativa el icono del prefijo de grupo del id (height., paint. …) y, en último caso, una llave inglesa genérica. Puedes añadir dibujos propios con RTSIcons.RegisterIcon.
Order0Posición dentro de la categoría.
InputModeBrushBrush: toques a lo largo de la trayectoria del puntero (según el espaciado del pincel). Click: un toque por pulsación. Drag: OnDragBegin / OnDragUpdate / OnDragEnd (rectángulos, líneas).
UsesBrushtrue para BrushMuestra el panel de pincel (tamaño, forma, intensidad …).
RepeatRate0Toques por segundo mientras se mantiene presionado el botón sin mover el puntero (para subir o bajar de forma continua).
ModifiedLayersNoneInformativo: lo usan las superposiciones y la barra de estado.
PreviewColorazul claroColor del cursor de pincel proyectado.
SnapNoneAjusta el centro del pincel a los vértices (herramientas de acantilado) o a los centros de las celdas.
SupportsPick + OnPickfalseCon Ctrl+clic se toma un valor (una altura, una capa) en lugar de pintar.
OnActivated, OnDeactivated, OnSettingChangedCallbacks del ciclo de vida.
OnStrokeBegin, OnStrokeEnd, OnStrokeCancelledSe llaman al principio y al final de un trazo; un trazo cancelado (Esc) se revierte a continuación.
DrawGizmos(ctx, gizmos, hasHover, hoverLocal)Vistas previas adicionales a través de IToolGizmos; se implementan con Handles en el editor y con líneas GL en el juego.
GetStatus(ctx, hoverLocal)nullTexto de la barra de estado, por ejemplo, «Level 3 → 4».

[ToolSetting] acepta Min/Max (rango del control deslizante), Order, Tooltip y Kind. Auto elige el control según el tipo del campo (control deslizante para float/int, interruptor para bool, menú desplegable para enum, Color, string). Tipos especiales: GroundLayer y CliffType (paletas del tileset con miniaturas), CliffLevel, DetailPrototype / DetailPrototypeList, TreePrototype / TreePrototypeList, DecorPrototype / DecorPrototypeList (paletas de la biblioteca), Player (0–15) y Hidden.

Las interfaces de ajustes genéricas no muestran los ajustes Hidden. Hay una excepción: en la ventana del Unity Editor, un campo Hidden cuyo tipo es un UnityEngine.Object (una referencia a un asset, como AutoPaintTool.profile) se sigue mostrando como campo de objeto, porque las interfaces genéricas no tienen otra forma de mostrar referencias a assets. El editor en el juego no lo muestra. Un ajuste oculto se guarda igualmente junto con los demás ajustes de la herramienta.

Lo que conviene saber

  • La simetría es automática. ApplyDab se llama una vez por cada copia simétrica; dab.symmetry te indica de qué copia se trata (con su desfase de giro horizontal y si está reflejada). dab.rotation ya viene transformado.
  • El toque (BrushDab) contiene localPosition, worldPosition, radius, strength, rotation, invert (Shift presionada), isFirstDab y dabIndex.
  • Pesos del pincel: BrushGrid.Vertices con resolución 1 para las capas por vértice, y BrushGrid.Texels con map.SplatResolution, DetailResolution o PathingResolution para las capas ráster. Convierte rectángulos con BrushUtility.VertexRectToCellRect, TexelRectToCellRect o GetCellRect(map, dab). Libera siempre los pesos.
  • Los valores aleatorios deben ser iguales en todas las copias simétricas de un toque. ctx.Random recibe una nueva semilla en cada trazo y avanza con cada llamada, así que una copia que lo llame obtiene un valor distinto del original. Genera valores aleatorios solo cuando dab.symmetry.index == 0, guárdalos en caché para ese dab.dabIndex y reutilízalos en las demás copias, reflejados o rotados con SymmetricScatter.TransformOffset y SymmetryPoint.TransformYaw. La clase pública SymmetricScatter (GetCandidates, GetSingleSeed) se encarga de esto por ti.
  • Selección estricta: para ediciones de todo o nada al estilo de los RTS clásicos, deriva de CliffTool, WaterTargetTool o PathingTool (aportan la categoría, el color del cursor y el texto de estado; WaterTargetTool añade además los ajustes de Water Height y la toma de valores con Ctrl+clic), o llama a TerrainToolUtility.BeginHardVertexSelection(ctx, dab, layers, out weights, out forcedIndex, out cells). Se ven afectados los vértices con un peso de al menos TerrainToolUtility.HardSelectionThreshold (0.5), además del vértice situado bajo el centro del pincel (forcedIndex), y los datos para deshacer solo se registran para esa zona. Libera weights también cuando el método devuelve false.
  • Rendimiento: las herramientas integradas ejecutan sus bucles en jobs de Burst (consulta RaiseLowerTool y PaintLayerTool en Runtime/Tools). Para pinceles pequeños, los bucles normales son suficientes.
  • Una clase marcada con [HideTool] no se registra en absoluto: no aparece en ninguno de los dos editores ni en session.Tools, así que SetActiveTool, RTSEditorContext.ActivateTool y FindTool no la encuentran. Para usar una herramienta así desde código (por ejemplo, una herramienta de depuración), asigna una instancia directamente: session.ActiveTool = new MyDebugTool();. En el Unity Editor, establece además RTSEditorContext.EditingEnabled = true.
  • El registro solo busca en el ensamblado de tiempo de ejecución del paquete y en los ensamblados que lo referencian, ya sea directamente o a través de otro ensamblado de este tipo. Una herramienta siempre deriva de RTSTool, así que su ensamblado referencia de todos modos el ensamblado de tiempo de ejecución (los scripts sin definición de ensamblado están en Assembly-CSharp, que lo referencia automáticamente). Además, omite los ensamblados cuyo nombre empieza por System, Unity., UnityEngine, UnityEditor, Mono., mscorlib, netstandard o nunit. No le pongas un nombre así a tu propio ensamblado.
  • Eliminación de código: las herramientas se crean mediante reflexión, así que en una build con un Managed Stripping Level superior a Minimal no se puede detectar que se usan. El archivo Runtime/link.xml del paquete conserva las herramientas integradas; marca las tuyas con [UnityEngine.Scripting.Preserve] o inclúyelas en un link.xml de tu proyecto (consulta Eliminación de código y herramientas personalizadas).
  • Activa una herramienta desde código con session.SetActiveTool("height.crater") o, en el editor, con RTSEditorContext.ActivateTool("height.crater").

Controlar la sesión desde código

El pincel, la simetría y los trazos de ambos editores pasan por una RTSEditSession. Puedes usar la misma API para crear tu propio front-end, como una interfaz de pintura personalizada, un tutorial que pinta en lugar del jugador o un script de nivel.

MiembroDescripción
BrushSettings BrushEl pincel compartido: shape, customShape, radius, strength, hardness, useFalloffCurve, falloffCurve, rotation, randomRotation, spacing, jitter. Llama a Brush.MarkDirty() después de cambiar desde código las claves de falloffCurve: la máscara de pincel precalculada detecta los cambios de forma, dureza, useFalloffCurve y customShape, pero no los de las claves de la curva.
SymmetrySettings Symmetrymode (SymmetryMode) y center (normalizado, 0.5/0.5 = centro del mapa). GetPoints(localPos, mapWorldSize, mergeDistance, results) devuelve el punto original y sus copias.
ScaleRadius(factor), AddStrength(delta)Hacen lo mismo que los atajos [ ] y - =, con los valores limitados a los rangos válidos.
bool InvertModifierEquivale a mantener presionada Shift: bajar en lugar de subir, borrar en lugar de pintar.
BeginStroke(worldPoint), UpdateStroke(worldPoint, deltaTime), EndStroke(), CancelStroke()Un trazo, desde que se presiona el botón hasta que se suelta. Llama a UpdateStroke en cada fotograma mientras el botón esté presionado (también cuando el puntero no se mueve, para que las herramientas con repetición sigan funcionando). EndStroke añade un paso de deshacer; CancelStroke revierte el trazo.
Pick(worldPoint)La toma de valor con Ctrl+clic de la herramienta activa.
FindTool(id), FindTool<T>(), GetTools(category), SetActiveTool(id), ActiveToolBuscar y activar herramientas.
ActiveToolChanged, StrokeEnded, UndoRedoPerformed, SelectionChangedEventos para tu interfaz: cambió la herramienta activa, terminó un trazo (no se lanza para los trazos cancelados), se ejecutó deshacer o rehacer, cambió la selección de decoración.
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();
    }
}

Eventos

EventoFirmaCuándo se lanza
MapLoadedAction<RTSTerrain>Después de asignar un mapa nuevo (carga, mapa nuevo, cambio de tamaño, reconstrucción). Reconstruye todo lo que derives del mapa.
MapUnloadingAction<RTSTerrain>Antes de que el mapa actual se sustituya o se libere. Descarta aquí las referencias que tengas a él.
MapChangedAction<RTSTerrain, MapChangeEventArgs>Inmediatamente al llamar a NotifyChanged / NotifyChangedAll, antes de que se reconstruyan los meshes. MapChangeEventArgs tiene Layers, CellRect e IsFullRefresh. Se lanza a menudo durante los trazos: mantén ligeros los manejadores de eventos.
ChangesProcessedAction<RTSTerrain, IReadOnlyList<DirtyRegion>>Después de procesar los cambios pendientes: los meshes, las texturas y el pathing están actualizados. Cada DirtyRegion tiene layers y cellRect.
PathingChangedAction<RTSTerrain, RectInt>Después de reconstruir el mapa de pathing (o parte de él); el rectángulo se expresa en celdas de pathing. Sobrevive a las recargas del mapa (a diferencia de PathingMap.Changed).
TilesetChangedAction<RTSTerrain>Cambió la referencia al tileset o su contenido.
LibrariesChangedAction<RTSTerrain>Cambió la referencia a una biblioteca de vegetación o de decoración, o su contenido.
RTSTerrain.TerrainEnabled / TerrainDisabledstatic Action<RTSTerrain>Se activó o desactivó un terreno (cambios en el registro).
RTSRuntimeEditorEvents.PlaytestChangedstatic Action<bool>El editor en el juego inició o detuvo una prueba de juego. Genera y elimina aquí tus objetos de jugabilidad; después, el mapa se restaura.
RTSRuntimeEditorEvents.MapOpenedstatic Action<RTSTerrain>El editor en el juego abrió un mapa.
RTSEditSession: ActiveToolChanged, TerrainChanged, StrokeEnded, UndoRedoPerformed, SelectionChangedEstado de edición de una sesión (editor o editor en el juego).
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"));
    }
}

Guardar y cargar en tiempo de ejecución, secciones personalizadas del archivo

Los mapas son archivos .rtsmap. Este mismo formato lo usan el Unity Editor (que lo importa como RTSMapAsset), el editor en el juego y la transmisión de mapas de Mirror, así que un mapa creado por un jugador se abre sin cambios en Unity, y viceversa.

MiembroDescripción
terrain.SaveToFile(path) / terrain.LoadFromFile(path)Guardar y cargar en tiempo de ejecución. Ambos establecen CurrentFilePath; al guardar, se restablece HasUnsavedChanges.
terrain.MapAsset, terrain.LoadFromAsset()Usar un mapa importado en el proyecto (mapas de campaña, mapas de escaramuza integrados).
terrain.SetMap(MapData)Mostrar cualquier MapData. El terreno pasa a ser su propietario y libera el mapa anterior.
RTSMapSerializer.Serialize(map) / Deserialize(bytes)Mapas como bytes (red, guardados en la nube, descargas de mods). El mapa deserializado pertenece a quien hace la llamada.
RTSMapSerializer.SaveToFile, LoadFromFile, Write(map, stream), Read(stream)Acceso a archivos y streams sin necesidad de un terreno.
RTSMapSerializer.TryReadHeaderFromFile(path, out header)Lee solo la pequeña cabecera (nombre, autor, descripción, tamaño, tamaño de celda, número máximo de jugadores, id del tileset, fecha de modificación), pensada para exploradores de mapas.
RTSMapSerializer.DefaultMapsFolder, FileExtensionLa carpeta Maps dentro de StorageRoot (la usa el editor en el juego, salvo que se haya configurado su ajuste Maps Folder) y ".rtsmap".
RTSMapSerializer.StorageRootLa carpeta para los archivos que se conservan entre sesiones: Application.persistentDataPath. En las builds web es una carpeta del almacenamiento del navegador con el nombre de la empresa y del producto, porque allí persistentDataPath cambia según la dirección de la página.
RTSWebFileSystem.Flush(), DownloadFile(...), IsSupportedBuilds web: escribir los archivos modificados en el almacenamiento del navegador, para que se conserven al recargar la página, y hacer que el navegador descargue un archivo. RTSMapSerializer.SaveToFile y terrain.SaveToFile ya escriben en el almacenamiento por ti; llama a Flush() después de escribir o eliminar archivos tú mismo con System.IO. En otras plataformas, estos métodos no hacen nada. Consulta API de scripting para builds web.
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;
    }
}

Estructura del archivo y secciones personalizadas

"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

Los lectores omiten las secciones desconocidas, así que puedes guardar tus propios datos en un archivo de mapa (disparadores, recursos iniciales, datos de misiones) y las versiones anteriores u otras herramientas seguirán pudiendo leer el mapa. Registra RTSMapSerializer.CustomSectionWriter para añadir secciones al guardar y RTSMapSerializer.CustomSectionReader para recibir cada sección desconocida al cargar. Usa etiquetas que empiecen por 'X' para que nunca entren en conflicto con las secciones integradas. La etiqueta 0 está reservada: marca el final de las secciones.

Cada sistema debe usar su propia etiqueta 'X…' única, porque CustomSectionReader se llama para cada sección desconocida. Si dos escritores emiten la misma etiqueta con cargas útiles distintas, un lector recibe una carga útil que no entiende y no se puede cargar el mapa. Por ejemplo, StartingGoldSection, de Secciones personalizadas, usa XGLD, así que el ejemplo de más abajo usa 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();
    }
}
Los datos de este ejemplo pertenecen a un único objeto MapData. Las operaciones que crean un objeto de mapa nuevo (cargar, cambiar el tamaño, Clone) no los transfieren por sí solas; vuelve a leerlos del archivo o cópialos en tu propio código cuando clones o cambies el tamaño de un mapa.

Preguntas frecuentes

Todo se ve rosa (magenta). ¿Qué ocurre?

El rosa significa que no se pudo usar un shader. RTS Terrain Studio usa shaders escritos a mano exclusivamente para el Universal Render Pipeline.

  1. Abre Tools ▸ RTS Terrain Studio ▸ Validate Project. La comprobación Universal Render Pipeline is active debe estar en verde; Fix All (o el botón de corrección de esa comprobación) asigna un asset de URP.
  2. Revisa la comprobación Package shaders compile en la misma ventana. Si un shader tiene errores, la Console indica cuál es; vuelve a importar la carpeta Shaders del paquete (clic con el botón derecho ▸ Reimport).
  3. Si solo se ven rosas tus propios prefabs (árboles, rocas, unidades), sus materiales usan shaders del pipeline Built-in. Conviértelos con el conversor de pipelines de renderizado de Unity o asígnales materiales de URP.

Añadí un RTS Terrain, pero no se renderiza nada.

  • Selecciona el terreno y mira su Inspector. Si indica que no hay ningún mapa cargado, asigna un asset de mapa, crea un mapa nuevo (ventana ▸ pestaña Map ▸ New Map) o usa el New Map Wizard.
  • Asegúrate de que el componente RTSTerrain esté activado. Un componente desactivado descarga su mapa.
  • Revisa la cámara: los chunks están en la capa definida en los ajustes del terreno (Chunk Layer), que debe estar incluida en la culling mask de la cámara. Encuadra el terreno con F en la Scene View: un mapa de 128 celdas con celdas de 2 m mide 256 m de ancho.
  • Un terreno sin tileset no es invisible: se renderiza con colores neutros de respaldo. Asigna un tileset, o ejecuta Tools ▸ RTS Terrain Studio ▸ Create Demo Content y usa el tileset de demostración.
  • Haz clic en Rebuild Terrain en el Inspector del terreno para volver a crear todos los chunks, las texturas y el mapa de pathing.

El pincel pinta en un lugar distinto al de mi cursor.

El editor usa el rayo de la Scene View de Unity, que tiene en cuenta los DPI, y el cursor del pincel lo dibuja el shader del terreno. Por eso, con una configuración normal, el cursor y la pintura siempre coinciden, incluso con un escalado de pantalla del 150 % o más. Si no coinciden:

  • El terreno o uno de sus objetos padre está escalado o rotado alrededor de X o Z. Solo se usan la posición y la rotación en Y. El Inspector del terreno avisa de ambos casos; restablece la escala a 1 y la rotación en X/Z a 0, y cambia el tamaño de celda del mapa en lugar de escalar el terreno.
  • El terreno usa un Material Override cuyo shader no dibuja el cursor del pincel. En ese caso pintas en el lugar correcto, pero no ves el cursor. Usa el shader de terreno del paquete o incluye su código del cursor del pincel.

La niebla de guerra no se ve.

  • La escena necesita exactamente un RTSFogOfWarManager (GameObject ▸ RTS Terrain Studio ▸ Fog of War Manager).
  • Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y corrige Fog of War renderer feature. La renderer feature oscurece todo lo que aparece en pantalla. Sin ella, la niebla de guerra solo cubre lo que usa los shaders propios del paquete (terreno, agua, vegetación), pero no tus propias unidades ni tus props.
  • En el modo Edit, activa Preview In Edit Mode en el gestor.
  • Reveal All debe estar desactivado. Start Explored hace que todo el mapa parezca explorado (atenuado en lugar de negro).
  • Tus unidades necesitan un RTSFogOfWarRevealer cuyo equipo sea el Local Team del gestor. Los reveladores de otros equipos no revelan nada para el equipo local, así que sin uno el mapa permanece cubierto (en negro de forma predeterminada).

Falta la vegetación (o el agua, o el terreno) en las builds, pero en el editor se ve bien.

  • Ejecuta Tools ▸ RTS Terrain Studio ▸ Validate Project y corrige Shaders included in builds. El paquete guarda sus shaders en un asset de referencias de shaders dentro de Resources, que se actualiza automáticamente antes de cada build del juego. Si lo borraste o lo moviste, la comprobación lo vuelve a crear.
  • Corrige GPU instancing on instanced prototypes: los árboles, detalles y objetos decorativos instanciados necesitan materiales con Enable GPU Instancing.
  • Asegúrate de que el mapa esté en la build: los mapas asignados como RTSMapAsset a un terreno de una escena incluida en la build se incluyen automáticamente; los archivos de mapa que se cargan desde el disco deben existir en el equipo del jugador (por ejemplo, en RTSMapSerializer.DefaultMapsFolder).
  • La eliminación de variantes de shaders (shader stripping) o un asset de URP distinto para la plataforma de destino (ajustes de Quality) pueden quitar funciones. Comprueba que el asset de URP que usa la build tenga la misma configuración que el del editor.
  • En una build para Web con WebGL 2.0, un asset de URP con muchas funciones puede superar el límite de 16 texturas por draw call, y el terreno desaparece. Consulta la nota sobre OpenGL ES y WebGL 2.0 en Notas para móviles.
  • Si falta la niebla atmosférica en las builds, consulta Niebla en las builds del juego.

El agua se ve negra o gris, o no tiene refracción.

  • El shader del agua lee la profundidad y el color de la escena. Activa Depth Texture y Opaque Texture en el asset de URP: Tools ▸ RTS Terrain Studio ▸ Validate Project ▸ Depth and Opaque Texture enabled ▸ corregir.
  • Establece el ajuste Opaque Downsampling del asset de URP en None; el submuestreo (downsampling) desenfoca la refracción.
  • Revisa la cámara: sus valores de anulación de Depth Texture y Opaque Texture no deben estar en Off. El agua sigue el ajuste propio de cada cámara, y la Scene View siempre usa el asset de URP.
  • En una build para Web, revisa el asset de URP del nivel de calidad que usan las builds para Web (la comprobación Web quality levels have Depth and Opaque Texture; consulta Comprobaciones para Web en el Project Validator).
  • Los colores del agua proceden del Water Profile del tileset. Revisa allí los colores del agua poco profunda y de la profunda.

¿Cómo consigo el mejor rendimiento?

  • Burst debe estar activado (Jobs ▸ Burst ▸ Enable Compilation). Los pinceles, el pathing, la niebla de guerra, el generador y el minimapa son jobs de Burst.
  • Chunk size (ajustes del terreno): 16, 32 (predeterminado) o 64 celdas. Los chunks más pequeños se reconstruyen más rápido al editar; los más grandes suponen menos draw calls en el juego.
  • Surface Subdivisions: 1 da el aspecto clásico de RTS con el menor número de triángulos; 2–4, colinas más suaves.
  • Con Collider Mode en None se omiten los mesh colliders cuando solo usas la API de consultas (Raycast, GetHeight).
  • Usa preferentemente el modo de generación Instanced para árboles y decoración; usa el modo GameObject solo para los objetos que tu jugabilidad necesite como GameObjects (árboles talables, decoración con scripts).
  • Reduce Detail Density Multiplier y Detail Distance Multiplier en el renderizador de vegetación para hardware menos potente.
  • Niebla de guerra: Resolution Per Cell en 1 y un Update Interval más grande consumen menos recursos.
  • Ejecuta el validador de mapas: sus advertencias de rendimiento indican si hay demasiados árboles, objetos decorativos, objetos GameObject o capas de detalle.

¿Puedo usar varios terrenos en una escena?

Sí. Cada RTSTerrain tiene su propio mapa, tileset, bibliotecas y transform. RTSTerrain.All los enumera, RTSTerrain.FindAt(position) encuentra el que está bajo una posición y RTSTerrain.RaycastAll lanza un raycast contra todos ellos. En la ventana del editor, elige el terreno editado en la cabecera (seleccionar un terreno en la escena también lo elige); en el editor en el juego, usa Map ▸ Edited terrain. Notas:

  • El historial de deshacer pertenece al terreno editado: al cambiar de terreno editado, se borra.
  • El gestor de la niebla de guerra cubre un solo terreno (el de su campo Terrain, o RTSTerrain.Main si está vacío).
  • A* necesita, por cada terreno, un constructor de grafos con sus propios nombres de grafo; la integración de NavMesh, un constructor por terreno.

¿Puedo mover o rotar un terreno?

Sí: la posición y la rotación en Y son totalmente compatibles, incluso en tiempo de ejecución. Todas las consultas, los pinceles, la vegetación, la decoración, el agua, el pathing, la niebla de guerra y las integraciones siguen al transform. La rotación alrededor de X o Z y la escala no son compatibles (el Inspector muestra un aviso); para agrandar un mapa, usa Resize en la pestaña Map o un tamaño de celda mayor.

¿Funciona con el Input System, con el antiguo Input Manager o con ambos?

Funcionan los tres valores de Project Settings ▸ Player ▸ Active Input Handling. El código de tiempo de ejecución lee la entrada a través de RTSInput, que usa el paquete Input System cuando está instalado y activo, y el Input Manager heredado en caso contrario. La comprobación Input handling de Tools ▸ RTS Terrain Studio ▸ Validate Project muestra qué usa tu proyecto. En tu propio código de juego también puedes usar RTSInput (PointerPosition, GetMouseButtonDown, GetKey(RTSKey) …) para que funcione con cualquiera de los ajustes. Para los atajos de letras que deben seguir la distribución del teclado del jugador, usa GetKeyDownByLabel(RTSKey); para teclas de símbolos como [ o +, usa GetCharacterDown(char).

¿Puedo publicar mi juego o el editor en el juego en un navegador web?

Sí. Las builds para Web (WebGL 2.0) ejecutan el terreno, la vegetación, el agua, la niebla de guerra, el editor en el juego y los ejemplos en navegadores de escritorio con ratón y teclado. Instala la plantilla de ventana completa con Tools ▸ RTS Terrain Studio ▸ Install WebGL Template y lee Builds web (WebGL) para conocer los ajustes, el almacenamiento de los mapas en el navegador y las notas sobre el alojamiento.

¿Puedo mover el paquete a otra carpeta?

Sí. Nada depende de una ruta fija; el paquete encuentra sus archivos en relación con su propia ubicación.

Solución de problemas

Sigue esta lista de comprobación de arriba abajo; la mayoría de los problemas se resuelven en los tres primeros pasos.

  1. Lee la Console. Los mensajes del paquete empiezan por [RTS Terrain Studio] y están vinculados al objeto al que pertenecen (haz clic en el mensaje para seleccionarlo). Corrige primero los errores de compilación: mientras algún script no compile, no se actualiza nada más.
  2. Valida el proyecto. Tools ▸ RTS Terrain Studio ▸ Validate Project comprueba URP, las texturas de profundidad y opacas, la renderer feature de la niebla de guerra, los modos de niebla atmosférica que se conservan en las builds, el espacio de color, Burst/Collections/Mathematics, la gestión de la entrada, la compilación de shaders, los shaders en las builds, los arrays de texturas de los tilesets, la instanciación por GPU, que los terrenos tengan tileset y que los terrenos se guarden en archivos de mapa; con Web como plataforma activa, también comprueba los niveles de calidad para Web y la plantilla Web. Haz clic en Fix All (antes, un cuadro de diálogo enumera todos los cambios) y luego en Run Again.
  3. Revisa el Inspector del terreno. ¿Hay un mapa cargado, un tileset asignado y el componente activado? ¿Hay avisos sobre la escala o la rotación? Haz clic en Rebuild Terrain.
  4. Valida el mapa. Pestaña Map ▸ Validation ▸ Validate Map detecta prototipos que faltan, pintura dañada, posiciones iniciales inalcanzables y problemas de rendimiento.
  5. Revisa el contenido. Si faltan texturas, plantas o decoración, normalmente es porque no hay asignado un tileset o una biblioteca, o porque un id de prototipo del mapa no existe en la biblioteca. Volver a ejecutar Tools ▸ RTS Terrain Studio ▸ Create Demo Content restaura los assets de demostración.
  6. Revisa las integraciones. Si aparecen errores de compilación en Integrations después de quitar Mirror, el símbolo MIRROR sigue definido (Eliminar Mirror). Una integración marcada como Installed pero no como Active en la ventana Welcome tiene una versión que no cumple su requisito.
  7. El modo Play sin recarga de dominio es compatible. Si algo se comporta de forma diferente después del segundo Play, activa la recarga de dominio una vez para ver si la causa es tu propio estado estático.
  8. ¿Sigues sin solucionarlo? Reproduce el problema en una escena nueva con un terreno creado con GameObject ▸ RTS Terrain Studio ▸ RTS Terrain y el contenido de demostración, e incluye la versión de Unity, la versión de URP, la salida de la Console y los pasos cuando pidas ayuda.
SíntomaCausa probableConsulta
Materiales rosasURP no activo, errores de shader, materiales Built-in en tus prefabsPreguntas frecuentes
Terreno invisibleNingún mapa cargado, componente desactivado, capa de chunks descartada por el cullingPreguntas frecuentes
El pincel pinta al lado del cursorTerreno escalado o rotación en X/ZPreguntas frecuentes
No hay niebla de guerraFalta la renderer feature, no hay ningún revelador del equipo local, Reveal All activadoPreguntas frecuentes
Agua negraDepth / Opaque Texture desactivadosPreguntas frecuentes
Falta vegetación en las buildsReferencias de shaders, instanciación por GPUPreguntas frecuentes
Las unidades no se muevenMapa de pathing aún no construido, origen o destino no transitable, proveedor sin grafo ni NavMeshValidación de mapas, Integraciones
El generador coloca menos posiciones inicialesNo hay suficiente espacio llano para Flat Radius / Edge MarginAjustes del generador
La importación del mapa de alturas sale plana o extremaEscala de altura 0 en el código, tamaño RAW u orden de bytes incorrectosMapas de alturas
Errores de compilación sobre Mirror después de quitarloSímbolo de compilación MIRROR obsoletoEliminar integraciones
Cuadro de diálogo RTS Terrain Studio requires URPURP 17.3 no está instalado, o el editor es anterior a Unity 6.3Unity y pipeline de renderizado
Build para Web: interfaz diminuta, mapas perdidos, teclas que abren funciones del navegadorPlantilla de tamaño fijo, almacenamiento del navegador, atajos del navegadorBuilds web (WebGL)

Registro de cambios

Versión 1.0.0 — Versión inicial

Requiere Unity 6.3 LTS (6000.3) y el Universal Render Pipeline 17.3, con Burst, Collections y Mathematics.

Modelo de mapa

  • Cuadrícula de celdas y vértices de esquina al estilo de los RTS clásicos, de 16 a 1024 celdas por lado (múltiplos de 16), tamaño de celda libre.
  • 16 niveles de acantilado con paredes de acantilado redondeadas generadas de forma procedural; altura fina sobre cada nivel; rampas; 4 tipos de acantilado por tileset.
  • 16 capas de suelo por tileset en splat maps de alta resolución, mezcla por altura, proyección triplanar y anti-tiling opcionales; cualquier tamaño y formato de textura.
  • Agua por vértice, con agua poco profunda y profunda.
  • Mapa de pathing (transitable, edificable, sobrevolable, agua poco profunda y profunda) derivado automáticamente, más ajustes manuales de pathing pintados.
  • Regiones, posiciones iniciales, marcadores, información del mapa e iluminación del entorno guardados en el mapa.

Edición

  • Un único conjunto de herramientas para el Unity Editor y el editor en el juego: altura, acantilados, pintura (suave, por celdas, relleno, pintado automático), vegetación, decoración, agua, pathing y marcadores.
  • Pinceles con formas de círculo, cuadrado, rombo y sello de textura, curva de atenuación, rotación, espaciado y variación aleatoria; pintura simétrica (espejo en X/Z, en 4 direcciones, rotación de orden 2 y 4).
  • Deshacer y rehacer con un presupuesto de memoria, integrados con el sistema de deshacer de Unity.
  • Ventana acoplable de RTS Terrain Studio, integración con la Scene View con cursor de pincel proyectado y superposiciones, inspectores e importadores personalizados para todos los assets, ventana Welcome, New Map Wizard y validador de proyecto.
  • Editor en el juego creado con UI Toolkit: barra de herramientas, paneles de herramientas y de pincel, paletas, explorador de mapas, atajos de teclado y prueba de juego.

Herramientas de mapa

  • Generador procedural de mapas basado en semillas con seis estilos, disposiciones simétricas, rampas, lagos e islas, bordes, pintado automático, vegetación, decoración y posiciones iniciales; presets que se pueden guardar.
  • Importación (tres modos) y exportación de mapas de alturas en PNG de 16 bits y RAW, exportación de imágenes de splat.
  • Precálculo del minimapa, validador de mapas, redimensionado con ancla.
  • Formato binario .rtsmap compartido por el editor y el juego, con secciones personalizadas.

Sistemas en tiempo de ejecución

  • Meshes de terreno divididos en chunks y construidos en jobs de Burst, mesh colliders opcionales y una API de consultas sin física.
  • Vegetación con instanciación por GPU, viento y culling; árboles como instancias o como GameObjects.
  • Renderizado de agua con color según la profundidad y refracción; gestor de decoración para cualquier prefab.
  • Niebla de guerra de RTS clásico: visibilidad por equipo, memoria de las zonas exploradas, línea de visión obstruida por acantilados y bloqueadores, visión compartida, compatibilidad con el minimapa.
  • Buscador de rutas A* integrado y basado en Burst, con suavizado de rutas y tamaños de unidad que tienen en cuenta la holgura.
  • Ejemplos: cámara RTS, selección, órdenes de movimiento con formaciones, unidades de prueba, interfaz de minimapa, escenas de demostración y contenido de demostración generado.

Plataformas

  • Shaders para Shader Model 3.5, de modo que el paquete también funciona en OpenGL ES 3.0 y en las builds para Web (WebGL 2.0).
  • Builds para Web: mapas guardados en el almacenamiento del navegador, Download Map, Import Map y arrastrar y soltar, un aviso antes de salir de la página con cambios sin guardar, atajos de teclado adaptados al navegador, una plantilla Web de ventana completa y comprobaciones para Web en el validador de proyecto.
  • Los arrays de texturas de los tilesets se reconstruyen automáticamente antes de las builds del juego, para adaptarlos a la compresión de texturas de la build.
  • Sin URP 17.3, el paquete permanece inactivo y explica cómo instalar URP, en lugar de mostrar errores de compilación.

Integraciones

  • Mirror: transmisión del mapa con caché, unidades con servidor autoritativo, niebla de guerra por equipo, gestión de interés basada en la niebla de guerra.
  • A* Pathfinding Project Pro: grafos de cuadrícula a partir del mapa de pathing con actualizaciones incrementales, unidades AIPath y RVO.
  • Unity AI Navigation: NavMesh construido a partir del terreno y del mapa de pathing, actualizaciones incrementales, proveedor de rutas y modo NavMeshAgent.