Vamos agora iniciar o desenvolvimento de um jogo simples usando alguns dos conceitos que aprendemos hoje. Preparamos para você um projeto pré-configurado com tudo que você vai precisar para construir e publicar seu jogo na internet.
No final desse capítulo você terá esse jogo implementado:
O código final se encontra nesse link:
OBS: Usaremos o framework Phaser para construção do jogo
Uma vez que você já tem o nodejs instalado e já baixou o projeto pré-configurado, já podemos iniciar a instalação das dependências do projeto, para isso, abra o terminal no diretório do projeto pré-configurado e digite o comando:
$ npm install
Após a instalação das dependências, já podemos iniciar o servidor http, utilizando o comando:
$ npm run dev
Após o comando acesse: http://localhost:8000
Nós escreveremos o código dentro do arquivo main.js, ele deve estar dentro do diretório assets/js
Nele você deverá escrever o seguinte código:
{% code-tabs %} {% code-tabs-item title="main.js" %}
function preload () {}
function create () {}
function update () {}
function principal () {}
// Estamos adicionando uma função para o evento OnLoad da janela
window.onload = principal
{% endcode-tabs-item %} {% endcode-tabs %}
preload, create, update são as nossas cenas do jogo que serão criadas.
- preload: É a nossa primeira cena, aonde carregará todos os recursos visuais e sonoros que poderão vir a ser implementados no jogo.
- create: É a nossa segunda cena, aonde os elementos do jogo são configurados, essa cena é importante pois é nela que se encontra toda a lógica do jogo.
- update: É a nossa terceira cena, nessa cena podemos sempre atualizar o estado do jogo, como por exemplo a movimentação dos personagens.
A função principal vocês verão no próximo tópico.
Para iniciarmos um jogo precisamos informar algumas configurações básicas. O nosso framework exige que essas configurações sejam feitas para que ele possa entender o que você vai tentar implementar. Então vamos voltar ao código, olhando agora para a função principal:
{% code-tabs %} {% code-tabs-item title="main.js" %}
function principal () {
var largura = ''
var altura = ''
// cria uma variável com as configurações do jogo
var conf = {
type: Phaser.AUTO,
width: largura,
height: altura,
pixelArt: true,
backgroundColor: '',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 200 }
}
},
scene: {
preload: null,
create: null,
update: null
}
}
var game = new Phaser.Game(conf)
}
{% endcode-tabs-item %} {% endcode-tabs %}
Vamos começar a configurar o exemplo anterior alterando os valores, primeiro definiremos uma largura e uma altura para a nossa tela do jogo:
{% code-tabs %} {% code-tabs-item title="main.js" %}
var largura = 500
var altura = 500
{% endcode-tabs-item %} {% endcode-tabs %}
Se você deseja que a tela do jogo seja o tamanho da tela da janela do seu navegador, ou seja, que ocupe toda a tela, você pode fazer assim:
{% code-tabs %} {% code-tabs-item title="main.js" %}
var largura = window.innerWidth
var altura = window.innerHeight
{% endcode-tabs-item %} {% endcode-tabs %}
Agora, vamos definir uma cor para o fundo da tela do jogo, procure por backgroundColor no exemplo acima e adicione uma cor de sua preferencia:
backgroundColor: '#b3e6ff'
As cores são em hexadecimal, você pode procurar uma cor nesse link
Por fim, vamos definir as nossas cenas preload, create e update. Por padrão o framework necessita que essas cenas sejam criadas e informadas na configuração. Então basta apenas adicionar a sua assinatura dessa forma:
{% code-tabs %} {% code-tabs-item title="main.js" %}
scene: {
preload: preload,
create: create,
update: update
}
{% endcode-tabs-item %} {% endcode-tabs %}
Agora que já temos o nosso jogo configurado, podemos visualizar no navegador como está ficando a primeira cara do jogo.
Como sabemos os jogos possuem vários recursos gráficos e sonoros, são esses recursos que melhoram a experiência do jogador. No nosso jogo não será diferente, dentro do diretório assets/sprites temos algumas imagens que vão nos acompanhar durante esse capitulo. Essas imagens são chamadas de sprites. Agora vamos importar elas para o nosso jogo.
{% code-tabs %} {% code-tabs-item title="main.js" %}
// funcao de carregar as imagens e outros recursos
function preload () {
// Carrega os recursos de imagens para o jogo
this.load.atlas('hamtaro_atlas', 'assets/sprites/hamtaro/hamham.png', 'assets/sprites/maps/hamtaro.json')
this.load.atlas('comida_atlas', 'assets/sprites/assest/food.png', 'assets/sprites/maps/food.json')
}
{% endcode-tabs-item %} {% endcode-tabs %}
Vamos apenas comparar essas linhas de código e entender o que é cada coisa que estamos escrevendo:
{% code-tabs %} {% code-tabs-item title="main.js" %}
// this.load.atlas(identificacao-do-sprite, endereco-da-imagem, endereco-do-mapeamento-json)
this.load.atlas('hamtaro_atlas', 'assets/sprites/hamtaro/hamham.png', 'assets/sprites/maps/hamtaro.json')
{% endcode-tabs-item %} {% endcode-tabs %}
OBS: A primeira linha é um exemplo, a segunda linha é o nosso recurso importado anteriormente
- A identificacao-do-sprite vai ser o nome que vou informar ao framework que eu quero dar a imagem que estou carregando. Escolhi como nome: hamtaro_atlas
- O endereco-da-imagem é aonde se encontra as imagens do recurso que quero usar. No caso, elas estão dentro de: assets/sprites/hamtaro/hamham.png
- O endereco-do-mapeamento-json é aonde se encontra o mapeamento da imagem. Usamos o JSON para guardar informações de tamanho e posição de cada sprite contido na imagem. Então esse arquivo se encontra em: assets/sprites/maps/hamtaro.json
Vamos adicionar algum elemento de texto na nossa tela, utilizando uma função do framework add.text():
{% code-tabs %} {% code-tabs-item title="main.js" %}
function create () {
// Adiciona um texto para informar o score a jogadora
pontuacao = this.add.text(10, 10, 'SCORE: 0', {
fontFamily: 'Arial',
fontSize: 20,
fontWeight: 'bold',
color: '#000000'
})
}
{% endcode-tabs-item %} {% endcode-tabs %}
Assim como fizemos no exemplo anterior, vamos analisar o que está sendo feito:
this.add.text( x , y , texto , configuracao )
Chamamos de parâmetros todas essas informações que ficam separadas entre virgulas e entre parenteses. Os nossos parâmetros são:
- x e y, que serão usados para determinar a posição que ficará o texto na tela. Usamos o plano cartesiano para orientar os elementos visuais na tela do computador. Então x e y esperam valores numéricos e inteiros.
- texto, é propriamente o texto que queremos escrever na tela
- configuração, será todo o estilo que iremos aplicar no nosso texto. Podemos informar qual fonte (fontFamily) queremos usar, o seu tamanho (fontSize), o seu peso (fontWeight) e a cor (color)
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Agora vamos para a parte mais divertida! Vamos adicionar o nosso personagem! Ainda na função create, vamos adicionar mais algumas linhas de código.
OBS: Para te orientar usaremos: " ... " Isso vai significar a existência um código que já foi adicionado em uma etapa anterior.
{% code-tabs %} {% code-tabs-item title="main.js" %}
function create () {
...
// Cria um hamtaro que será controlado pela participante
hamtaro = this.physics.add.sprite(150, 150, 'hamtaro_atlas')
}
{% endcode-tabs-item %} {% endcode-tabs %}
Analisando o comando:
this.physics.add.sprite( x , y , identificacao-do-sprite )
Já vimos nos exemplos anteriores o que cada uma dessas informações significam. A identificacao-do-sprite foi definida no momento que fiz o carregamento dos recursos. Chamamos o recurso de hamtaro_atlas
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Percebemos que o sprite apareceu e logo em seguida sofreu um efeito de queda. Isso acontece devido as nossas configurações de jogo. No inicio desse capítulo fizemos algumas configurações, procure por esse bloco de código e altere o valor de gravity: { y: 200 } para gravity: { y: 0 }. Ficando assim:
{% code-tabs %} {% code-tabs-item title="main.js" %}
...
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 }
}
},
...
{% endcode-tabs-item %} {% endcode-tabs %}
Essa alteração remove a gravidade do mundo que criamos no nosso jogo.
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Já temos um personagem em jogo, agora precisamos movimentar ele de alguma forma. E podemos utilizar o teclado para fazer isso! O framework nos proporciona muitas facilidades e uma delas é o comando para capturar as teclas do nosso teclado. O input.keyboard.createCursorKeys() nos retorna as teclas direcionais.
{% code-tabs %} {% code-tabs-item title="main.js" %}
function create () {
...
// Captura todas as teclas do teclado
cursors = this.input.keyboard.createCursorKeys()
}
{% endcode-tabs-item %} {% endcode-tabs %}
Uma vez que já temos essas teclas guardadas em uma variável, vamos até a função de update para criar um comportamento no nosso personagem de acordo com a tecla que for usada. Veja:
{% code-tabs %} {% code-tabs-item title="main.js" %}
// funcao para atualizar o jogo
function update () {
// Controle pelas setas esquerda direita cima e baixo do teclado
if (cursors.left.isDown) {
hamtaro.x -= 3
} else if (cursors.right.isDown) {
hamtaro.x += 3
} else if (cursors.up.isDown) {
hamtaro.y -= 2
} else if (cursors.down.isDown) {
hamtaro.y += 2
}
}
{% endcode-tabs-item %} {% endcode-tabs %}
- cursors.left, cursors.right, cursors.up, cursors.down são nossas teclas direcionais, respectivamente: esquerda, direita, cima e baixo.
- cursors.left.isDown nos indica se a tecla foi acionada pela jogadora.
- hamtaro.x e hamtaro.y recebem sempre um valor atualizado devido ao += ou -= que incrementa e decrementa a posição do personagem, fazendo assim, a movimentação do nosso personagem.
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Para criar animações precisamos primeiro entender como está disposta a nossa imagem que contem os sprites do nosso personagem. Vamos abrir a imagem assets/sprites/hamtaro/hamham.png e verificar o que temos.
Você vai ver que algumas imagens são complementares como na Figura 1, elas dão uma sensação de animação, e elas vão dar vida ao nosso personagem.
Mas vamos voltar até função create...
{% code-tabs %} {% code-tabs-item title="main.js" %}
function create () {
...
// Cria as animações
this.anims.create({
key: 'direita',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 1,
end: 3
}),
repeat: -1,
duration: 300
});
}
{% endcode-tabs-item %} {% endcode-tabs %}
O comando anims.create recebe um parâmetro de configuração, onde poderemos definir com quais sprites da nossa imagem (hamham.png), nós iremos criar uma animação.
1) Primeiro passo definimos um nome para essa animação key: 'direita', o nome será direita. Vamos utilizar esse nome futuramente.
2) Vamos definir os frames, cada frame é um sprite da imagem que queremos usar para montar a nossa animação.
O anims.generateFrameNames cria esses frames para nós.
Mas antes, abra o arquivo assets/sprites/maps/hamtaro.json.
{% code-tabs %} {% code-tabs-item title="hamtaro.js" %}
{
"frames":{
"hamtaro_1":{
"frame":{
"x":259,
"y":98,
"w":38,
"h":29
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":38,
"h":29
},
"sourceSize":{
"w":38,
"h":29
}
},
...
}
}
{% endcode-tabs-item %} {% endcode-tabs %}
Essa é a aparência do nosso hamtaro.json. Dentro desse arquivo existem todas as configurações que o comando anims.generateFrameNames precisa para identificar a imagem que precisamos. Passamos para o comando as seguintes informações: prefix, start e end. Isso quer dizer que na configuração passada no exemplo o comando está criando frames dos sprites hamtaro_1, hamtaro_2 e hamtaro_3
3) repeat e duration, o valor -1 em repeat significa que a animação será infinita e a duração de 300ms
Uma vez que a animação foi criada uma animação, podemos fazer uso da mesma.
O ****Leshy SpriteSheet Tool é uma ferramenta online que pode te ajudar a criar o seu próprio arquivo json para mapeamento de sprites. Basta você carregar a imagem para a área indicada e escolher um padrão de mapeamento. O nosso framework usa o padrão JSON-TP-Array.
Volte para a função update e atualize para ficar dessa forma:
{% code-tabs %} {% code-tabs-item title="main.js" %}
function update () {
if (cursors.left.isDown) {
hamtaro.x -= 3
} else if (cursors.right.isDown) {
hamtaro.x += 3
hamtaro.anims.play('direita', true)
} else if (cursors.up.isDown) {
hamtaro.y -= 2
} else if (cursors.down.isDown) {
hamtaro.y += 2
}
}
{% endcode-tabs-item %} {% endcode-tabs %}
Adicionamos a linha hamtaro.anims.play('direita', true)
. Esse comando faz a animação ser iniciada sempre que teclarmos o direcional para direita.
Tente criar as demais animações com base no que foi explicado nesse tópico.
Abaixo o resultado de como as animações devem ficar:
{% tabs %} {% tab title="Direita" %}
// Cria as animações
this.anims.create({
key: 'direita',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 1,
end: 3
}),
repeat: -1,
duration: 300
});
{% endtab %}
{% tab title="Esquerda" %}
// Cria as animações
this.anims.create({
key: 'esquerda',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 4,
end: 6
}),
repeat: -1,
duration: 300
});
{% endtab %}
{% tab title="Cima" %}
// Cria as animações
this.anims.create({
key: 'cima',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 7,
end: 8
}),
repeat: -1,
duration: 300
});
{% endtab %}
{% tab title="Baixo" %}
// Cria as animações
this.anims.create({
key: 'baixo',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 9,
end: 10
}),
repeat: -1,
duration: 300
});
{% endtab %}
{% tab title="Parado" %}
// Cria as animações
this.anims.create({
key: 'parado',
frames: this.anims.generateFrameNames('hamtaro_atlas', {
prefix: 'hamtaro_',
start: 11,
end: 12
}),
repeat: -1,
duration: 300
});
{% endtab %} {% endtabs %}
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Chamamos de colisões o momento onde os elementos do jogo se encontram. Uma colisão pode encadear uma série de ações ou simplesmente impedir um personagem de ultrapassar um desafio. Para fazer os objetos se tocarem podemos usar o comando physics.add.collider, assim informamos para o framework quais elementos são passíveis de colisão, assim o jogo saberá que determinados objetos não podem ocupar o mesmo espaço ou acionarão algum evento. Segue o código:
{% code-tabs %} {% code-tabs-item title="main.js" %}
function create () {
...
// Cria um sprite de comida
comida = this.physics.add.sprite(10, 60, 'comida_atlas', 'sprite92')
// Informa que o hamtaro e a comida são passíveis de colisão
this.physics.add.collider(hamtaro, comida)
}
{% endcode-tabs-item %} {% endcode-tabs %}
Adicionamos um sprite novo e após isso informamos que o nosso personagem hamtaro vai colidir com a comida
Após adicionar as novas linhas salve as alterações e atualize o seu navegador, veja o que aconteceu
Agora vamos criar um evento que acontecerá sempre que o nosso personagem colidir com uma comida, adicione o seguinte código:
function create () {
...
// Cria o evento que acontecerá quando o hamtaro colidir com uma comida
this.physics.add.overlap(hamtaro, comida, function(){
// vamos implementar uma logica para essa colisãoo!
}, null, this);
}
O physics.add.overlap faz a função de criar esse evento, sempre que o hamtaro e a comida colidirem, uma rotina dentro desse escopo irá ser iniciada. Nós próximos passos iremos implementar:
1) A funcionalidade de mudar aleatoriamente a localização da comida na tela
2) A funcionalidade de mudar o sprite da comida aleatoriamente
3) A funcionalidade de adicionar pontos
Vamos implementar a funcionalidade que irá trocar a posição da comida na tela aleatoriamente.
function create () {
...
// Cria o evento que acontecera quando o hamtaro colidir com uma comida
this.physics.add.overlap(hamtaro, comida, function(){
// Escolhe randomicamente a nova posicao da comida
comida.x = randomNumber(50, window.innerWidth - 50)
comida.y = randomNumber(50, window.innerHeight - 50)
}, null, this);
}
O comando randomNumber que está no bloco a seguir vai nos retornar sempre um número aleatório entre 50 ~ ( Tamanho da Tela - 50 ). Adicione essa função no inicio do arquivo main.js
// Funcao para retornar um valor randomico
function randomNumber (start, end) { return Phaser.Math.Between(start, end) }
Estamos quase lá! Agora iremos implementar a nossa segunda funcionalidade.
Criaremos uma lista de números e cada número representará uma dessas comidinhas:
// cenoura = 92
// pao = 88
// limao = 87
// banana = 86
// rabanete = 85
// cogumelo = 81
// laranja = 78
// figo = 77
// pera = 76
OBS: você pode ir em assets/sprites/maps/food.json e escolher outros números que estejam mapeados e adicionar a nossa lista.
function create () {
...
// Cria o evento que acontecera quando o hamtaro colidir com uma comida
this.physics.add.overlap(hamtaro, comida, function(){
...
// Cada numero indica uma imagem para uma comida diferente
let number = [92, 88, 87, 86, 85, 81, 78, 77, 76]
// Escolhe um numero da lista acima
number = random(number)
// Troca a imagem da comida de acordo com o numero escolhido
comida.setTexture('comida_atlas', `sprite${number}`)
}, null, this);
}
O comando random que está no bloco a seguir vai nos retornar sempre um número aleatório que exista dentro de uma lista. Adicione essa função no inicio do arquivo main.js
// Funcao para retornar um valor randomico em um array
function random (array) { return array[Math.floor(Math.random() * array.length)] }
E por fim, vamos adicionar uma pontuação para cada comida que o personagem tocar.
function create () {
...
// Variavel para guardar a pontuação
this.score = 0;
// Cria o evento que acontecera quando o hamtaro colidir com uma comida
this.physics.add.overlap(hamtaro, comida, function(){
...
// Adiciona pontuação ao score
this.score += 3
// Adiciona a informação ao texto da tela
pontuacao.setText(`SCORE: ${this.score}`)
}, null, this);
}
Uma vez que criamos todas essas funcionalidades já temos um pequeno jogo pronto, outras funcionalidades podem ser criadas o que vale agora é a criatividade!
Mas essas melhorias no jogo podem ser feitas posteriormente, o que queremos agora é distribuir esse jogo na internet!
Se o seu servidor estiver funcionando, faça CTRL + C para derrubar-lo e digite o seguinte comando:
$ npm run deploy
Siga os passos que forem pedidos nesse momento e acesse o seu site na url escolhida! =D