O que veremos aqui?
Neste post, mostrarei como criar o mapa do jogo. Ele é a continuação do terceiro post desta série onde adicionamos o inimgo no jogo.
Como construir o mapa?
O mapa do jogo nada mais é que definições de quais blocos visuais serão colocados em determinados lugares. Eventualmente podemos trabalhar com multicamadas e blocos específicos de objetos e imagens.
Neste post veremos o básico, ou seja, como criar um mapa de uma única camada e fazer o phaser interpretá-lo para que nosso personagem possa interagir com este mapa.
Para construção do mapa usaremos uma ferramenta muito usada e compatível com a maioria dos frameworks de jogos: Tiled.
O que é o Tiled Map Editor?
Tiled Map Editor é uma ferramenta open source usada para criar mapas em jogos 2d. Ele é fácil de usar como veremos neste tutorial. Baixe-o no site oficial e instale-o.
Observação: Se ao instalar o Tilled, você receber o erro que está faltando a dll msvcp100.dll, basta instalar o Windows Visual C++ runtime 2010 ou superior, aqui o link para windows 64 bits.
Tileset básico para começar
Um Tileset é uma imagem, normalmente formada por várias outras imagens, que contém blocos os quais serão usados para montar todo o cenário.
Baixe este titleset para fazermos este exemplo:
Como percebe-se, este tileset tem uma resolução baixa. Ele é constituído de blocos de 32px de altura por 32px de largura (32×32).
Você pode baixar centenas de outros tilesets no google procurando por “Tileset game 2d”, ou mesmo montar o seu próprio. Basta observar que os blocos devem ser do mesmo tamanho sempre, ok?
Iniciando o mapa
Com o Tiled Map Editor aberto, clique em File -> New. Crie um mapa seguindo os detalhes abaixo.
32 x 32 é a largura de cada bloco do nosso tileset. Width de 100 tiles significa que a largura do mapa terá 100 blocos, e 20 de altura, como visto. O cálculo em pixels do nosso mapa, será então, 3200 de largura por 640 de altura.
Caso queira um mapa maior, poderá mudar os valores de width e height. Isso também poderá ser feito depois de criar o mapa. A única coisa que não pode ser alterada depois é o tamanho de cada Tile (lajota).
Feito isso, você verá o seguinte mapa, com fundo cinza por padrão:
Adicionando Tileset
Após criar o mapa, agora vamos adicionar as imagens que usaremos para criar o visual do jogo, ou seja, os tilesets.
Para isso, clique no menu Map -> New Tileset. Selecione a imagem que deseja adicionar (neste caso tileset.png) e defina a largura e altura de cada bloco (por padrão é a mesma do cenário 32×32):
Veja agora, que no canto direito inferior, existe o tileset que adicionamos. Ele está pronto para ser usado:
Salvando o Trabalho
Antes de continuarmos o processo de construção é importante que salvemos o trabalho. Faça isso também durante toda a edição.
Aqui temos uma parte muito importante, que é o formato do nosso mapa. Todo o nosso mapa será construído num formato chamado JSON, muito usado em comunicações na internet para representação e troca de dados em javascript com outras linguagens.
Para salvar o mapa, siga estas regras:
- Salve o mapa em formato .JSON
- Ele deve ser salvo na mesma pasta que o tileset (assets)
- Salve o mapa com um nome que não possua espaço ou caracteres especiais (acentos e outros)
Para salvar, basta clicar no botão Save, bem grande na tela e salvar como mapacaverna.json:
Construindo o mapa
Basta agora clicar no bloco do tileset desejado, e clicar na área do mapa que deseja inserí-lo. Veja que defini aqui o nome da camada que estamos construindo. Teremos, por hora, somente uma camada, chamada cenario (sem acento):
Códifo fonte do jogo com Mapa completo
Uma vez que várias partes do jogo foram alteradas do tutorial anterior para este, segue o código fonte completo onde fazemos o personagem andar no cenário que criamos:
window.onload = function() { // Cria o cenário com 1220 x 600 px game = new Phaser.Game(1000, 640, Phaser.AUTO, '', { preload: carregaAssets, create: criaCenario, update: atualizaJogo }); }; /** * Carrega imagens, sons etc, para usar no jogo */ function carregaAssets() { game.load.image('inimigo', 'assets/inimigo.png'); game.load.spritesheet('dude', 'assets/dude.png', 32, 48); // Carrega imagem do novo mapa, // origem: http://www.ironstarmedia.co.uk/2010/10/free-game-assets-13-prototype-mine-tileset/ game.load.image('tilesCenario', 'assets/tileset.png'); // Carrega mapa em formato JSON game.load.tilemap('mapa', 'assets/mapacaverna.json', null, Phaser.Tilemap.TILED_JSON); } /** * Cria cenário do jogo */ function criaCenario() { // Define que vai usar a física ARCADE - fácil no jogo game.physics.startSystem(Phaser.Physics.ARCADE); // Adiciona mapa map = game.add.tilemap('mapa'); // Insere tileset map.addTilesetImage('tileset', 'tilesCenario'); // Define quais blocos do tileset serão de colisão //map.setCollision(1); map.setCollisionBetween(1,5); map.setCollisionBetween(8,12); layer = map.createLayer('cenario'); layer.resizeWorld(); // O grupo inimigos será usado para gerenciar todos os inimigos inimigos = game.add.group(); // Definimos aqui que qualquer inimigo terá um corpo, // ou seja, nosso personagem pode bater nele inimigos.enableBody = true; // Cria o jogador criaJogador(); // Chamaf unção que cria inimigo criaInimigo(); // Define os cursores do teclado para poder controlar o jogador cursors = game.input.keyboard.createCursorKeys(); } /** * Atualiza jogo. Esta função roda em torno de 60 vezes em 1 segundo, ou seja, * 60 FPS (FPS = Frames Por Segundo) */ function atualizaJogo() { aproximaInimigo(); game.physics.arcade.collide(jogador, layer); game.physics.arcade.collide(inimigos, layer); movimentaJogador(); verificaSeEncostouInimigo(); } function verificaSeEncostouInimigo(){ // Verifica colisão entre jogador e inimigos game.physics.arcade.overlap(jogador, inimigos, encostouInimigo); } function movimentaJogador(){ // Pára o movimento do jogador jogador.body.velocity.x = 0; if (cursors.left.isDown) { // Move to the left jogador.body.velocity.x = -250; jogador.animations.play('left'); } else if (cursors.right.isDown) { // Move to the right jogador.body.velocity.x = 250; jogador.animations.play('right'); } else { // Stand still jogador.animations.stop(); jogador.frame = 4; } // Permite jogador pular somente se está tocando algum chão if (cursors.up.isDown) { if (jogador.body.onFloor()) { jogador.body.velocity.y = -650; } } // Truque para jogador voar ao pressionar tecla T if(game.input.keyboard.isDown(Phaser.Keyboard.T) && cursors.up.isDown) { jogador.body.velocity.y = -150; } } /** * Função que cria o jogador */ function criaJogador(){ // Cria o player e o adiciona no jogo (x,y) jogador = game.add.sprite(50, game.world.height - 250, 'dude'); // É necessário adicionar a física no jogador game.physics.enable(jogador); // Propriedades da física do jogador. Dá a ele, um salto "normal". jogador.body.bounce.y = 0.2; jogador.body.gravity.y = 1600; jogador.body.linearDamping = 1; // Nâo deixa jogador "fugir" do mundo jogador.body.collideWorldBounds = true; // Define duas animações (esquerda e direita) para caminhar // 'nome', posições no quadro, quantas atualizações por segundo jogador.animations.add('left', [0, 1, 2, 3], 10, true); jogador.animations.add('right', [5, 6, 7, 8], 10, true); game.camera.follow(jogador); } /** * Função que faz inimigo se aproximar do jogador */ function aproximaInimigo(){ // Pega o primeiro elemento do grupo inimigos var inimigo = inimigos.children[0]; // Faz com que ele fique parado inimigo.body.velocity.x = 0; // Se o inimigo está mais para esquerda do jogador if (inimigo.position.x < jogador.body.position.x){ // faz ele ir para direita inimigo.body.velocity.x += 100; }else{ // Senão, faz ele ir para esquerda inimigo.body.velocity.x -= 100; } } /** * Função que cria o jogador */ function criaInimigo(){ // Cria inimigo dentro do grupo inimigos var inimigo = inimigos.create(700, 20, 'inimigo'); // Define gravidade do inimigo inimigo.body.gravity.y = 400; // Faz inimigos não fugirem do mundo inimigo.body.collideWorldBounds = true; } /** * Função que mata o jogador e informa que ele morreu */ function encostouInimigo (jogador, inimigo) { // Remove jogador do jogo jogador.kill(); // Mostra texto informando morte do jogador var posicaoJogador = jogador.body.position.x; var textoJogo = game.add.text(posicaoJogador - 150, game.camera.height / 2, "Você morreu", { font: "48px Arial", fill: "#ff0044", align: "center" }); }
Jogo em ação
Aqui você pode ver o código que implementamos funcionando. Neste link você pode baixar esta versão do jogo:
O que vem por aí?
No próximo post veremos como adicionar moedas ao jogo com este novo cenário criado usando o Tiled Map Editor!
Opa, tudo bom?
Estou com uma dificuldade aqui… na linha 45 do teu código:
layer.resizeWorld();
Está dando problemas com meu código. Podemos trocar e-mails?
Muito obrigado pela atenção.
Abraços.
Olá Heitor, podemos sim, me envia seu código por email que assim que possível lhe ajudarei.
Antecipando, você pode baixar o exemplo completo desse post abaixo e comparar com seu código para ver o que saiu errado.
Bons estudos!
No meu código diz que não pode encontrar a função resizeWorld()
Oi Danilo, verifique se a layer cenario realmente existe.
Paulo, excelente tutorial, meu de vdd, parabéns!
Eu tive também problema com a layer, recebia a mensagem:
Tilemap.createLayer: Invalid layer ID given: null
Isso ocorreu porque a função ta procurando uma layer chamada “cenario” dentro do arquivo json gerado pelo tiled, essa layer não existia no meu mapa porque esqueci de renomeá-la, então voltei no tiled e renomei a minha layer para “cenario”, funcionou perfeitamente.
Abraços!
Obrigado pelo retorno Wilkier!