Articles

a Unidade: Como criar 2D Tilemap programaticamente

Para o vídeo tutorial clique aqui

Se você precisar criar diferentes Tilemaps de vários tamanhos confira a segunda parte deste tutorial

Tilemap componente foi introduzido na Unidade 2017.2 e facilitou significativamente o jogo em 2D processo de desenvolvimento. Com a versão 2018.3 Isometric Tilemap foi introduzido fornecendo grande suporte para jogos 2.5 D. Recentemente eu tive uma oportunidade de trabalhar com este componente de perto e foi desafiado com a tarefa de criar azulejos programaticamente. No meu último jogo eu tenho um monte de níveis e todos eles têm a mesma placa baseada em Tilemap. Mas o próprio tabuleiro tem um nível de configuração único. Obviamente, sendo um desenvolvedor profissional eu não queria criar 60 cenas de jogo e pintar todos os níveis à mão, mas sim ter um mecanismo para encher o tabuleiro com os elementos adequados, dependendo da entrada dada. Se você está curioso como o resultado final parece aqui é link para o jogo.

código fonte está disponível no GitHub, por favor, Encontre o link no final deste tutorial.

I use the latest available Unity 2019.2.0.1 F. Para trabalhar com este tutorial você deve ter pelo menos a versão 2018.3. Usaremos grade isométrica, mas a técnica descrita é aplicável a qualquer tipo.

Antes de começar, sugiro fortemente ler através deste brilhante ambiente isométrico 2D com entrada no Blog Tilemap para obter uma compreensão básica do Tilemap isométrico.como estamos trabalhando em um ambiente 2D você deve criar um novo projeto 2D (veja 2DAnd3DModeSettings)

Existem 3 componentes principais necessários para alcançar o nosso objetivo: um tabuleiro de jogo onde o jogo terá lugar, um lugar onde manter uma descrição de nível e algum código que se conecta um ao outro.

Parte 1. Criando o tabuleiro

vamos começar por importar os ativos de imagem necessários. Vou usar as mesmas imagens que usei para o meu jogo.:

Used to create a base tilemap level

Used as a path

Used to mark the start and end points of a localização

iremos manter as imagens na pasta de peças, simplesmente arrastá-las e largá-las lá.

Telhas de configuração

Nota! Você deve ter um conjunto de tamanho “Pixel por unidade” correto por cada imagem de azulejo (para mais detalhes veja ambientes isométricos 2D com Tilemap). Para essas imagens o valor é 1096.

configuração da Imagem

É uma boa prática para os diferentes camadas em uma cena em jogo dedicado objetos com nomes descritivos. Imagine a tela típica do jogo que pode conter uma zona de jogo, UI, anúncios e assim por diante.

seguindo esta abordagem vamos criar um novo objeto de jogo vazio chamado GameZone onde o tabuleiro e todos os elementos do jogo poderiam ser colocados.

Agora é a hora de criar a real telhas de imagens importadas anterior

Prima a Janela de> 2D> Bloco Paleta

Bloco Paleta

Bloco Paleta de exibição será aberta. Carregue em “criar uma nova paleta” e crie uma paleta:

New Palette

Drag and drop tile images one by one to create an actual tiles.

Tiles setup

With those tiles we can finally create the game board which will be filled with elements programmatically on a level startup later.

Find GameZone In Hierarchy view, right mouse click → 2D Object → Isometric Tilemap. Um novo objeto de jogo Grid será criado.

O tamanho da placa será 11×11 peças e podemos começar a pintar. Seleccione a ferramenta de pincel da caixa (4º elemento à esquerda) na área “paleta de azulejos” e seleccione depois a opção “Limpar”. Pinta – o na vista da cena.

Placa

Depois que você é feito com a pintura, você deve comprimir manualmente tilemap limites. Para isso, você precisa selecionar Tilemap na Hierarquia vista, prima em configurações (engrenagem) botão o Tilemap componente e selecione “Comprimir Tilemap Limites”

Comprimir Tilemap Limites

Bem feito, o tabuleiro do jogo está pronto para ser usado! Voltaremos à parte 3.

Parte 2. Dados de nível

Uma vez que queremos reutilizar a mesma cena e o mesmo tabuleiro de jogo, os dados de nível devem ser mantidos em algum lugar. Uma solução simples para isso é um arquivo json simples que irá descrever como cada nível deve ser construído.

é importante entender o que estamos tentando alcançar, é por isso que eu tenho que dizer algumas palavras sobre a mecânica. No jogo existem objetos que estão se movendo ao lado do caminho desde o início até o fim (praticamente como em Zuma) e o objetivo do jogador é destruir todos eles. Neste tutorial vamos criar este caminho que será único para cada nível.ok, de volta ao projecto.

Existem várias maneiras de acessar um dado externo em um tempo de execução de um script. Aqui estaremos usando pastas de recursos

vamos criar uma nova pasta-arquivos-e um sub-pasta Recursos. Esse é o lugar onde queremos manter os dados, então crie um novo nível de arquivos.json e coloque-o lá.

Para o tutorial fins de nós tem apenas dois campos para descrever cada nível:

  • número — um int para identificar um nível
  • caminho — o tile-based caminho que queremos criar programaticamente. Array of values where first value is the start point and the last value is the end point.

Este é o ficheiro que vou usar. Não se preocupe com esses valores no caminho, vamos chegar a ele mais tarde.

{
"levels":
},
{
"number": 2,
"path":
},
{
"number": 3,
"path":
}
]
}

let’s create another folder — Scripts — and now the coding finally starts.

queremos acessar os dados do arquivo no código, portanto, precisamos de uma classe de modelo para ele. É hora de criar o nosso primeiro script – LevelsData. Ele não serve para ser instanciado por Unidade, portanto, MonoBehaviour e StartUpdate métodos devem ser removidos. A partir do arquivo acima, podemos ver que o elemento raiz é uma array de níveis, onde cada nível deve ter uma int número do campo e uma int array caminho de campo. Além disso, não se esqueça de colocar anotação.


public class LevelsData
{
public LevelData levels;
public class LevelData
{
public int number;
public int path;
}
}

Nice, agora temos o arquivo e o modelo. O próximo passo é transformar um em outro. Vamos criar outro script – GameZone — e anexá-lo ao GameZone objeto na cena. Este script será usado mais tarde para configurar todo o tabuleiro de jogo.

siga o princípio de Responsabilidade Única vamos criar mais um script – LevelsDataLoader — que fará toda a transformação. Anexá-lo ao objeto GameZone também.

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 classe irá carregar os dados e devolvê-los como um dicionário onde a chave é o número de nível e os dados são os próprios dados de nível.

agora devemos ser capazes de acessar os dados em GameZone script.

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

mude de volta para a unidade, carregue no botão tocar e verifique a consola — você deve ver a mensagem “3 níveis foram armazenados no dicionário!”

Parte 3. Conectando a placa e os dados

Parabéns, você alcançou a última e mais interessante parte deste tutorial. Como vamos realmente conectar a placa e os dados? Continua a ler para descobrir!

Em Primeiro Lugar, vamos colocar horizontal e start_stop ladrilhos criados na primeira parte do tutorial em pasta de recursos sob a pasta de azulejos. Em seguida, adicione um novo script — TilesResourcesLoader — uma classe auxiliar estática para carregar as peças dessa pasta em tempo de execução.

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 passo, devemos colocar essas peças no tabuleiro no arranque da cena. Vamos voltar para oGameZone script. Em primeiro lugar, precisamos simular a seleção de nível, no jogo real geralmente acontece sempre que um usuário pressiona um botão Nível. Por uma questão de simplicidade, adicionemos um nível de campo público a GameZone e alteremos o seu valor para 1 para o início. Eu vou mostrar-lhe o script final primeiro:

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

Wow, isso é muita ação! Deixa-me explicar-te.

no método SetupTiles devemos primeiro obter o próprio tilemap porque precisamos saber as posições dos azulejos para mudá-lo. Para isso, estamos usando o método que retorna todas as posições das peças começando a partir do primeiro — na configuração dada é uma peça para baixo.

refere-se à figura seguinte, onde cada número representa o índice na lista localTilesPositions.

Numeradas tilemap

Você se lembra dos valores que usamos no caminho Levels.json? Como você já deve ter adivinhado, esses valores são os índices das peças na matriz. Tudo o que você precisa fazer agora é ter uma imagem cheatsheet para ajudá-lo a construir níveis. É o que tenho usado durante o desenvolvimento.:

Meu feio numeradas tilemap

neste exemplo estamos criando uma linha horizontal no caminho, por favor, consulte o SetupPath método. A peça chave é o seguinte loop:

foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
}

Aqui podemos iterar sobre localTilesPositions para encontrar os para definir o desejado de telha — horizontal neste caso.Nota! GetRange method has two parameters — index and count.

A fim de marcar as posições de início e fim do caminho é usado o start_stop ladrilho.

Aqui está o resultado do nosso trabalho:

resultado Final

Agora tente alterar o número do nível do campo de GameZone script de 1 para 2 ou 3 e você verá que o caminho está carregado corretamente para determinado nível.

depois