Enquanto estudava desenvolvimento de jogos em HTML5 com Phaser acabei criando um jogo muito simples e muito bom para ser usado como introdução ao desenvolvimento com a engine.
Porque a Phaser?
Apesar de existirem muitas outras engines para o desenvolvimento de jogos multiplataforma, como a Unity, a ferramenta Phaser continua sendo a melhor opção para o desenvolvimento de um jogo que será executado em diversos dispositivos. A justificativa esta no fato da Phaser não necessita de nenhum complemento adicional, além de funcionar sem problemas em navegadores mobile.
Caso queira saber mais veja nesta outra postagem: Engine Phaser
O jogo que vamos usar será um lago onde peixes famintos tentam se alimentar de insetos que caem na água, lembrando que é apenas para estudo da engine, não esperam grandes efeitos ;D. O jogo que vamos criar pode ser acessado clicando aqui.
Preparando o ambiente.
A primeira coisa que vamos fazer é preparar o ambiente.
- Pasta Lago: É o diretório principal do jogo
- Pasta assets: Nesta pasta vamos armazenas as imagens e efeitos do jogo
- Pasta css: Onde vamos armazenar os CSS “Só pra não ter duvidas!”
- Pasta js: Onde iremos armazenar a engine e os scripts do jogo
- Phaser.js ou phaser.min.js: É a engine responsável pelo controle das ações e fisica do jogo
- Game.js: Aqui estarão os códigos que darão vida ao jogo
- Game.html: Página onde o jogo será executado.
Agora que temos toda a estrutura de pastas montadas e os arquivos criados vamos começas o desenvolvimento.
Game.html:
A página onde o jogo será executado não precisa de muita implementação, basta usar um html bem simples.
No código abaixo a div <div id=”game” class=”div-game”> será onde o jogo realmente vai ser executado.
<html> <head> <meta charset="UTF-8"> <title>Lago</title> <script src="js/phaser.min.js"></script> <script src="js/game.js"></script> <style type="text/css"> body, html{ margin: 0; border: 0; } div-game { background-color:black; width:100%; height: 100%; min-height: 90%; display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; } </style> </head> <body style="overflow: hidden;"> <div id="game" class="div-game"></div> <div id="orientation"></div> </body> </html>
Assets:
Antes de entrar em mais detalhes quanto ao script game.js vamos a pasta assets, nela vão estar todos os arquivos de imagens que vamos usar no jogo.
- Imagem de fundo: um bom exemplo disso é a imagem de fundo, para manter uma agonização ela está armazenada na pasta assets/maps.
- Sprites: A segunda pasta dentro de assets é a pasta sprite, nela vamos armazenar as imagens dos personagens, tanto herói quanto vilão ou NPC, dando uma pesquisada rápida no Google imagens podemos achar uma infinidade de sprites prontos.
Sprites:
Com o phase criar a animação de um personagem é muito simples, primeiro carregamos a imagem com a sequencia de sprites que desejamos:
Criamos um objeto spritesheet que é uma sequencia de sprites informando a altura e largura de cada sprit dentro da imagem e a quantidade de sprites que vamos carregar: game.load.spritesheet(“inseto”, “assets/sprites/butterfly.png”, 70, 70, 14); os parâmetros 70, 70 representam a altura e largura de cada borboleta.
Depois adicionamos os sprites ao nosso personagem: var inseto = game.add.sprite(100, 150, “inseto”); os parâmetros 100 e 150 informam a posição em x, y que vai ser criado na tela.
Para dar movimento ao personagem criamos uma animação informando a sequencia dos sprites e tempo a serem executados e para finalizar executamos a animação.
// criamos uma animação com a cadeia de sprite inseto.animations.add('fly', [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ], 20, true); inseto.play('fly');
Para o peixe carreguei uma imagens com vários sprites diferente e no hora da animação selecionei apenas os sprit que desejava:
// além do arquivo passamos como parametro a largura, altura e quantidade de sprites dentro da imagem. game.load.spritesheet(“peixe”, “assets/sprites/animales2.png”, 32, 33, 24); peixe[i] = game.add.sprite(randomPoint.x, randomPoint.y, “peixe”) // Como queremos apenas o peixe azul vamos usar apenas o 12,13 e 14 na animação peixe[i].animations.add(‘flyx’, [ 12, 13, 14], 20, true); peixe[i].play(‘flyx’);
Dessa forma podemos usar uma unica imagem para ter quantos sprites desejar.
game.js
O coração de tudo, no game.js vamos iniciar o jogo, carregar os sprites, criar as animações e eventos. Como o código do script está todo comentado não vou entrar em mais detalhes.
var game = new Phaser.Game(700, 680, Phaser.AUTO, 'game', { preload: preload, create: create, update: update }); /* 700, 680 - é a area do jogo 'game' - é a div onde o jogo vai ser executado */ var cardume = 15; var peixe = []; var inseto; // Carrega os objetos a serem usados durante o jogo. function preload(){ // game.load.image - são objetos para imagens estáticas game.load.image('oceano', 'assets/maps/oceano-azul.jpeg'); // game.load.spritesheet - são para objetos animados, // além do arquivo passamos como parametro a largura, altura e quantidade de sprites dentro da imagem. game.load.spritesheet("peixe", "assets/sprites/animales2.png", 32, 33, 24); game.load.spritesheet("inseto", "assets/sprites/butterfly.png", 70, 70, 14); } // cria os objetos do jogo e as instancias function create(){ // Aplicando a imagem de fundo do jogo bg = game.add.tileSprite(0, 0, 700, 680, 'oceano'); // vamos criar o inseto no jogo // inserimos ele no centro do jogo pegando a largura e altura dividido por 2 inseto = game.add.sprite(game.width / 2, game.height / 2, "inseto"); inseto.anchor.set(0.5); // Ativamos o sprite inseto.inputEnabled = true; // Permitimos arrastar com o mouse inseto.input.enableDrag(); // criamos uma animação com a cadeia de sprite inseto.animations.add('fly', [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ], 20, true); inseto.play('fly'); for(var i = 0; i < cardume; i++){ // randomPoint - usado para inserir o peixe em um ponto aleatório da tela var randomPoint = new Phaser.Point(game.rnd.between(0, game.width - 1), game.rnd.between(0, game.height - 1)); peixe[i] = game.add.sprite(randomPoint.x, randomPoint.y, "peixe") peixe[i].anchor.set(0.5); // Velocidade do peixe peixe[i].speed = game.rnd.between(50, 150); // Força do peixe peixe[i].force = game.rnd.between(5, 25); // Aplica fisica ao peixe game.physics.enable(peixe[i], Phaser.Physics.ARCADE); // Não permite o peixe girar sobre o proprio eixo, o obriga a fazer a curva peixe[i].body.allowRotation = false; // Cria uma animação peixe[i].animations.add('flyx', [ 12, 13, 14], 20, true); peixe[i].play('flyx'); // O sprit esta ao contrario, esta fazendo o peixe nada de ré, vamos inverter a imagem peixe[i].scale.x = -1; // corrige o angulo do peixe peixe[i].angle = 30; } } // atualiza todo o jogo frame a frame function update(){ for(var i = 0; i < cardume; i++){ // direção vector é a direção em linha reta do peixe ao alvo var direction = new Phaser.Point(inseto.x, inseto.y); // agora nós subtrair a posição peixe atual direction.subtract(peixe[i].x, peixe[i].y); // então nós normalizá-lo. Um vector normalizado tem o seu comprimento é 1, mas mantém a mesma direcção direction.normalize(); // tempo para definir magnitude (comprimento) para evitar velocidade direction.setMagnitude(peixe[i].speed); // agora nós subtrair a velocidade peixe atual direction.subtract(peixe[i].body.velocity.x, peixe[i].body.velocity.y); // normalizar novamente direction.normalize(); // finalmente, definir a magnitude de anular a força, que deve ser a maneira mais baixa do que a sua velocidade direction.setMagnitude(peixe[i].force); // Agora vamos adicionar direção peixe a atual velocidade peixe peixe[i].body.velocity.add(direction.x, direction.y); // normalizar a velocidade peixe[i].body.velocity.normalize(); // vamos definir a magnitude para construir a velocidade peixe[i].body.velocity.setMagnitude(peixe[i].speed); // Se estiver distante muda de direção if (peixe[i].position.distance(inseto.position) > 2){ peixe[i].angle = 180 + Phaser.Math.radToDeg(Phaser.Point.angle(peixe[i].position, new Phaser.Point(peixe[i].x + peixe[i].body.velocity.x, peixe[i].y + peixe[i].body.velocity.y))); } // Se o peixe pegar o inseto o inseto recebe uma nova posição na tela, da a impressão de ser outro. if(peixe[i].position.distance(inseto.position) < 2){ inseto.x = game.rnd.between(10, game.width - 10); inseto.y = game.rnd.between(10, game.height - 10); } } }
Clique aqui para ver o jogo rodando.