Parte 4 – Criando o mapa

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.

Exemplo de mapa feito com tiled map editor
Exemplo de mapa feito com tiled map editor

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:

Tileset para usarmos neste tutorial
Tileset para usarmos neste tutorial

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

montando novo mapa

Feito isso, você verá o seguinte mapa, com fundo cinza por padrão:
novo mapa criado

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):
adicionando tileset mapa

Veja agora, que no canto direito inferior, existe o tileset que adicionamos. Ele está pronto para ser usado:
tileset adicionado

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:

salvando mapa

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):

construcao do cenario com tiled map editor

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!

7 Comments

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

    1. 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!

  2. 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!

Leave a Reply