Unidad: Cómo crear 2D Tilemap mediante programación
Para el tutorial de vídeo haga clic aquí
Si usted necesita para crear diferentes Tilemaps de diversos tamaños, echa un vistazo a la segunda parte de este tutorial
Tilemap componente fue introducido en la Unidad 2017.2 y facilitó de manera significativa el juego en 2D proceso de desarrollo. Con la versión 2018.3 se introdujo el mapa de bits isométrico que proporciona un gran soporte para juegos 2.5 D. Recientemente tuve la oportunidad de trabajar estrechamente con este componente y tuve el reto de crear mosaicos de forma programática. En mi último juego tengo muchos niveles y todos ellos tienen el mismo tablero basado en mapas de bits. Pero la placa en sí tiene una configuración única de nivel. Obviamente, al ser un desarrollador profesional, no quería crear 60 escenas de juego y pintar todos los niveles a mano, sino tener un mecanismo para llenar el tablero con los elementos adecuados dependiendo de la entrada dada. Si tienes curiosidad por saber cómo se ve el resultado final, aquí está el enlace al juego.
El código fuente está disponible en GitHub, por favor, encuentre el enlace al final de este tutorial.
Utilizo la última versión disponible de Unity 2019.2.0.1 f. Para trabajar con este tutorial, debe tener al menos la versión 2018.3. Utilizaremos cuadrícula isométrica, pero la técnica descrita es aplicable a cualquier tipo.
Antes de comenzar, sugiero leer esta brillante entrada de blog de Entornos Isométricos 2D con Mapas de elementos para obtener una comprensión básica del mapa de elementos isométricos.
Como estamos trabajando en un entorno 2D, debe configurar un nuevo proyecto 2D (consulte 2D y 3dmodesettings)
Hay 3 componentes principales necesarios para lograr nuestro objetivo: un tablero de juego donde se llevará a cabo el juego, un lugar donde guardar una descripción de nivel y algún código que se conecte entre sí.
Parte 1. Creación de la placa
Comencemos importando los activos de imagen necesarios. Usaré las mismas imágenes que usé para mi juego:
Mantendremos las imágenes en la carpeta Tiles, simplemente arrástrelas y suéltelas allí.
tenga en cuenta! Debe tener un tamaño correcto de «Píxel por Unidad» para cada imagen de mosaico (para más detalles, consulte Entornos Isométricos 2D con mapa de mosaico). Para esas imágenes el valor es 1096.
Es una buena práctica para separar las diferentes capas en una escena dedicados juego de objetos con nombres descriptivos. Imagine la pantalla de juego típica que podría contener una zona de juego, una interfaz de usuario, ubicaciones de anuncios, etc.
Siguiendo este enfoque, vamos a crear un nuevo objeto de juego vacío llamado GameZone
donde se pueden colocar el tablero y todos los elementos del juego.
Ahora es el momento de crear teselas reales de las imágenes que hemos importado anterior
Presione la Ventana -> 2D -> Mosaico Paleta
de Baldosas de la Paleta de vista se abrirá. Haga clic en «Crear nueva paleta» y cree una paleta:
Drag and drop tile images one by one to create an actual tiles.
With those tiles we can finally create the game board which will be filled with elements programmatically on a level startup later.
FindGameZone
En la vista de jerarquía, haga clic con el botón derecho del ratón → Objeto 2D → Mapa de elementos isométricos. Se creará un nuevo objeto de juego Grid
.
El tamaño del tablero será de 11×11 azulejos y podemos comenzar a pintar. Seleccione la herramienta Pincel de cuadro (4to elemento desde la izquierda) en la vista «Paleta de azulejos» y, a continuación, seleccione el azulejo «limpiar». Píntalo en la vista de escena.
Después de que haya terminado con la pintura debe comprimir manualmente tilemap límites. Para esto usted necesita para seleccionar Tilemap en la vista de Jerarquía, pulse en configuración (engranaje) botón en el Tilemap componente y seleccione «Comprimir Tilemap Límites»
Bien hecho, el tablero de juego está listo para ser utilizado! Volveremos a ello en la parte 3.
Parte 2. Datos de nivel
Dado que queremos reutilizar la misma escena y el mismo tablero de juego, los datos de nivel deben guardarse en algún lugar. Una solución sencilla para eso es un simple archivo json que describirá cómo se debe construir cada nivel.
Es importante entender lo que estamos tratando de lograr, por eso tengo que decir unas palabras sobre la mecánica. En el juego hay objetos que se mueven a lo largo del camino desde el principio hasta el final (casi como en Zuma) y el objetivo del jugador es destruirlos a todos. En este tutorial crearemos esta ruta que será única para cada nivel.
Bien, de vuelta al proyecto.
Hay varias formas de acceder a datos externos en un tiempo de ejecución desde un script. Aquí usaremos Carpetas de recursos
Vamos a crear una nueva carpeta — Archivos-y una subcarpeta Recursos. Ese es el lugar donde queremos guardar los datos, así que cree un nuevo nivel de archivo.json y colócalo ahí.
Para los fines del tutorial, solo tendremos dos campos para describir cada nivel:
- number — un int para identificar una ruta de nivel
- — la ruta basada en mosaicos que queremos crear programáticamente. Matriz de valores donde el primer valor es el punto de inicio y el último valor es el punto final.
Este es el archivo que usaré. No se preocupe por esos valores en path, lo abordaremos más adelante.
{
"levels":
},
{
"number": 2,
"path":
},
{
"number": 3,
"path":
}
]
}
Vamos a crear otra carpeta-Scripts – y ahora la codificación finalmente comienza.
Queremos acceder a los datos del archivo en el código, por lo que necesitamos una clase modelo para ello. Es el momento de crear nuestro primer script — LevelsData
. No está destinado a ser creado por Unity, por lo que los métodos MonoBehaviour
y Start
Update
deben eliminarse. Desde el archivo anterior podemos ver que el elemento raíz es unarray
de niveles donde cada nivel debe tener unint
número de campo y unint array
ruta de campo. Además, no olvide poner una anotación .
public class LevelsData
{
public LevelData levels;
public class LevelData
{
public int number;
public int path;
}
}
bueno, ahora que tenemos el archivo y el modelo. El siguiente paso es transformar uno en otro. Vamos a crear otro script – GameZone
div — – y adjuntarlo al objeto GameZone
en la escena. Este script se utilizará más tarde para configurar todo el tablero de juego.
Siga el principio de responsabilidad única vamos a crear otro script – LevelsDataLoader
div — – que hará toda la transformación. Adjúntelo al objeto GameZone
también.
public class LevelsDataLoader : MonoBehaviour
{
private const string LevelsPath = "Levels";
public Dictionary<int, LevelsData.LevelData> ReadLevelsData()
{
var jsonFile = Resources.Load(LevelsPath, typeof(TextAsset)) as TextAsset;
if (jsonFile == null)
{
throw new ApplicationException("Levels file is not accessible");
}
var loadedData = JsonUtility.FromJson<LevelsData>(jsonFile.text);
return loadedData.levels.ToDictionary(level => level.number, level => level);
}
}
Esta clase cargará los datos y los devolverá como un diccionario donde la clave es el número de nivel y los datos son los datos de nivel en sí.
Ahora deberíamos poder acceder a los datos en el script GameZone
.
public class GameZone : MonoBehaviour
{
private Dictionary<int, LevelsData.LevelData> _levelsData;
private LevelsDataLoader _dataLoader;private void Awake()
{
_dataLoader = GetComponent<LevelsDataLoader>();
}private void Start()
{
_levelsData = _dataLoader.ReadLevelsData();Debug.Log(_levelsData.Count + " levels have been stored in the dictionary!");
}
}
Vuelva a Unity, presione el botón de reproducción y verifique la consola; debería ver el mensaje » ¡Se han almacenado 3 niveles en el diccionario!»
Parte 3. Conectando el Tablero y los Datos
Felicitaciones, has llegado a la última y más interesante parte de este tutorial. ¿Cómo conectaremos realmente la placa y los datos? ¡Sigue leyendo para averiguarlo!
En primer lugar, vamos a colocar horizontal
y start_stop
teselas creadas en la primera parte del tutorial en la carpeta Recursos, debajo de la carpeta Teselas. A continuación, agregue un nuevo script — TilesResourcesLoader
— una clase auxiliar estática para cargar mosaicos de esa carpeta en tiempo de ejecución.
public static class TilesResourcesLoader
{
private const string PathHorizontal = "horizontal";
private const string StartStop = "start_stop"; public static Tile GetPathHorizontalTile()
{
return GetTileByName(PathHorizontal);
} public static Tile GetStartStopTile()
{
return GetTileByName(StartStop);
} private static Tile GetTileByName(string name)
{
return (Tile) Resources.Load(name, typeof(Tile));
}
}
Como último paso, debemos colocar esos mosaicos en el tablero en el inicio de la escena. Volvamos al script GameZone
. En primer lugar, tenemos que simular la selección de niveles, en el juego real, generalmente sucede cuando un usuario presiona un botón de nivel. En aras de la simplicidad, agreguemos un nivel de campo público a GameZone
y cambiemos su valor a 1 para comenzar. Primero te mostraré el guión final:
public class GameZone : MonoBehaviour
{
public int Level; private const int FieldLineSize = 11;
private const int FieldTotalTiles = FieldLineSize * FieldLineSize;
private Dictionary<int, LevelsData.LevelData> _levelsData; private void Start()
{
_levelsData = GetComponent<LevelsDataLoader>().ReadLevelsData();
SetupTiles();
} private void SetupTiles()
{
var baseLevel = GetComponentsInChildren<Tilemap>(); var localTilesPositions = new List<Vector3Int>(FieldTotalTiles);
foreach (var pos in baseLevel.cellBounds.allPositionsWithin)
{
Vector3Int localPlace = new Vector3Int(pos.x, pos.y, pos.z);
localTilesPositions.Add(localPlace);
} SetupPath(localTilesPositions, baseLevel);
} private void SetupPath(List<Vector3Int> localTilesPositions, Tilemap baseLevel)
{
var path = _levelsData.path;
var pathHorizontalTile = TilesResourcesLoader.GetPathHorizontalTile();
var first = path.First();
var last = path.Last();
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
} var startStopTile = TilesResourcesLoader.GetStartStopTile();
baseLevel.SetTile(localTilesPositions, startStopTile);
baseLevel.SetTile(localTilesPositions, startStopTile);
}
}
¡Vaya, eso es mucha acción! Déjame explicártelo.
En el método SetupTiles
primero debemos obtener el mapa de mosaico en sí porque necesitamos conocer las posiciones de los mosaicos para cambiarlo. Para lograr esto, estamos utilizando el método tilemap.cellBounds.allPositionsWithin
que devuelve todas las posiciones de los mosaicos a partir de la primera, en la configuración dada es un mosaico más abajo.
Consulte la siguiente imagen, donde cada número representa el índice en el localTilesPositions
lista.
¿Te acuerdas de los valores que utilizamos en la ruta de acceso en el Levels.json
? Como ya habrás adivinado, esos valores son los índices de los mosaicos de la matriz. Todo lo que necesitas hacer ahora es tener una imagen de cheatsheet para ayudarte a construir niveles. Ese es el que he estado usando durante el desarrollo:
En este ejemplo, estamos estableciendo una línea horizontal en la ruta de acceso, por favor refiérase a la etiqueta SetupPath
método. La parte clave es el siguiente bucle:
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
}
Aquí podemos iterar sobre localTilesPositions
para encontrar los para fijar el azulejo — horizontal en este caso.
¡Nota! GetRange
el método tiene dos parámetros: índice y recuento.
Para marcar las posiciones de inicio y fin de la ruta, se utiliza el mosaico start_stop
.
Aquí está el resultado de nuestro trabajo:
Ahora pruebe a cambiar el nivel del campo de número de la etiqueta GameZone
script de 1 a 2 o 3 y verás que el camino está cargado correctamente para el nivel dado.