Começando Em Crafty: O Game Loop
() translation by (you can also view the original English article)
Até este ponto na série, você aprendeu como manipular diferentes entidades e usar o teclado para movê-las por aí. Nesta parte, você vai aprender como usar o game loop em Crafty para continuamente checar por vários eventos e animar diferentes entidades.
O game loop em Crafty é implementado em Crafty.timer.step, que usa eventos globais para comunicar-se com o resto dos motores. O loop é dirigido por requestAnimationFrame quando disponível. Cada loop consiste de uma ou mais chamadas para o evento EnterFrame
e uma única chamada para o RenderScene
que resulta no redesenhamento de cada camada.
O valor final de todas as propriedades e variáveis é resolvido antes da cena ser renderizada. Por exemplo, se você mover seu jogador 5 pixels para a direita dez vezes em um único evento EnterFrame
, ele vai ver desenhado diretamente 50 pixels para a direita saltando todos os desenhos intermediários.
EnterFrame e RenderScene
Tudo em seu jogo que precisa mudar sobre o tempo é finalmente ligado a um evento EnterFrame
. Você pode usar o método .bind()
para ligar diferentes entidades para este evento. Funções ligadas a este evento também são passadas a um objeto com propriedades como dt
que determina o número de mili segundos que passaram desde o último evento EnterFrame
.
Você pode usar a propriedade dt
para providenciar uma experiência de jogo suave determinando quão longe o estado de jogo deve avançar.
O evento RenderScene
é usado para garantir que tudo visível na tela corresponde ao atual estado de jogo no último evento EnterFrame
. Normalmente, você não irá precisar ligar este próprio evento a não ser que você decida implementar sua própria camada de renderização personalizada.
Usando Tween para Animar Propriedades 2D
Você pode usar o componente Tween
quando você apenas quer animar as propriedades 2D de uma entidade por um período específico de tempo. Você pode animar as propriedades x
, y
, w
, h
, rotation
, e alpha
usando este componente. Vamos animar o valor x e altura das caixas laranja e preta que você esteve criando nos dois últimos tutoriais.
Aqui está o código de que precisa:
1 |
blackBox.tween({x: 500}, 3000); |
2 |
|
3 |
orangeBox.tween({x: 50, h: 100, rotation: 360}, 3000); |
Você provavelmente notou que a caixa laranja não está girando em torno do seu centro mas sim em seu canto topo esquerdo. Você pode mudar o centro de rotação usando o método .origin()
. Ele pode aceitar dois argumentos inteiros, os quais determinam o deslocamento em pixels da origem dos eixos x e y.
Ele também aceita um valor string em seu argumento. O valor string pode ser uma combinação do centro, topo, fundo, meio, esquerda, e direita. Por exemplo, .origin("center")
vai girar a entidade ao redor do seu centro, e .origin("bottom right")
vai girar a entidade ao redor do seu canto direita baixo.
Você pode pausar ou recomeçar todos os tweens associados a uma dada entidade usando os métodos .pauseTweens()
e .resumeTweens()
. Da mesma forma, você também pode usar .cancelTween()
para cancelar um tween específico.
Entendendo o Timer do Crafty
O objeto Crafty.timer
lida com todos os tique-taques do jogo em Crafty. Você pode usar o método .FPS()
com esse objeto para pegar o frame rate do alvo. Mantenha em mente que isso não o frame rate verdadeiro.
Você pode também usar o método .simulateFrames(Numer frames[,Number timestep])
para avançar o estado do jogo um dado número de frames. O timestep
é a duração para passar cada frame. Se não especificado, o valor padrão de 20ms é usado.
Outro método útil é .step()
, o qual vai avançar o jogo executando um passo. Um único passo pode consistir de um ou mais frames seguidos de um render. O número de frames vai depender do steptype
do timer. Este método aciona uma variedade de eventos como EnterFrame
e ExitFrame
para cada frame e eventos PreRender
, RenderScene
, e PostRender
para cada render.
Há três modos diferentes de steptype
: fixed
, variable
, e semifixed
. No modo fixed
, cada frame em Crafty é enviado com o mesmo valor de dt
. Entretanto, este steptype
pode acionar múltiplos frames antes de cada render para alcançar a velocidade de jogo mirada.
Você pode também acionar apenas um frame antes de cada render usando o modo variable
. Neste caso, o valor de dt
é igual ao valor atual transcorrido desde o último frame.
Finalmente, o modo semifixed
aciona múltiplos frames por render, e o tempo desde o último frame é igualmente dividido entre eles.
Criando um Jogo Muito Básico
Se você leu todos os tutoriais nessa série, você deveria ter ganhado conhecimento suficiente por agora para criar um jogo bem básico. Nessa seção, você vai aprender como colocar tudo que você aprendeu em uso e criar um jogo onde o jogador principal tem que comer uma peça de comida.
A comida vai ser um quadrado vermelho girando. Assim que a comida entrar em contato com o jogador, ela desaparecerá da sua última localização e nascerá em uma nova localização aleatória. O jogador pode ser movido usando A, W, S, D, ou as setas.
Mais uma coisa que você precisará tomar cuidado é a posição do jogador. Ele deve supostamente estar entre os limites da fase do jogo.
Vamos escrever o código para a comida primeiro:
1 |
var foodBox = Crafty.e("2D, Canvas, Color, Food") |
2 |
.attr({x: 150, y: 250, w: 15, h: 15}) |
3 |
.color("red") |
4 |
.origin("center") |
5 |
.bind("EnterFrame", function(eventData) { |
6 |
this.rotation += 4; |
7 |
});
|
Por padrão, Crafty teria usado o canto topo esquerda da entidade da comida para girá-la. Configurando a origem para o centro dará a certeza que a entidade da comida girará ao redor do seu centro.
1 |
var playerBox = Crafty.e("2D, Canvas, Color, Fourway, Collision") |
2 |
.attr({x: 50, y: 360, w: 50, h: 50}) |
3 |
.color("black") |
4 |
.fourway(200) |
5 |
.bind("EnterFrame", function(eventData) { |
6 |
if(this.x < 0) { |
7 |
this.x = 0; |
8 |
}
|
9 |
if(this.y < 0) { |
10 |
this.y = 0; |
11 |
}
|
12 |
if(this.x > (stageWidth - this.w)) { |
13 |
this.x = stageWidth - this.w; |
14 |
}
|
15 |
if(this.y > (stageHeight - this.h)) { |
16 |
this.y = stageHeight - this.h; |
17 |
}
|
18 |
});
|
A entidade do jogador checa a localização atual do jogador em cada frame e redefine a localização se o jogador tenta ir para fora do estágio do jogo.
Você pode usar uma entidade Text
para observar a pontuação. A pontuação é mostrada no canto topo esquerdo. A variável gameScore
guarda o número de vezes que o jogador tocou a entidade comida.
1 |
var scoreText = Crafty.e('2D, DOM, Text') |
2 |
.attr({ x: 10, y: 10 }) |
3 |
.textFont({ size: '25px' }); |
4 |
|
5 |
scoreText.text(gameScore.toString()); |
Agora, você apenas deve escrever o código para mover a comida para um local diferente quando um toque é detectado. O código a seguir vai fazer exatamente isso.
1 |
playerBox.checkHits("Food").bind("HitOn", function(hitData) { |
2 |
foodBox.x = Math.random() * (stageWidth - foodBox.w); |
3 |
foodBox.y = Math.random() * (stageHeight - foodBox.h); |
4 |
gameScore += 1; |
5 |
scoreText.text(gameScore.toString()); |
6 |
});
|
Você deve manter em mente que você subtrai a largura e altura da sua entidade de comida da largura e altura do estágio respectivamente. Isso garante que a comida está sempre completamente dentro do estágio. Aqui está uma demonstração do jogo:
Pensamentos Finais
Com a ajuda do Crafty, você criou um jogo bem básico escrevendo algumas linhas de código. Agora, o jogo está em falta de algumas características que poderiam torná-lo mais interessante. Primeiro, não há nenhum som. Segundo, não há nenhuma maneira do jogador sair, e o nível de dificuldade também se mantem na mesma durante o jogo. Você vai aprender sobre som, sprites, eventos de mouse, e outras características da biblioteca na próxima série.
Se você teve qualquer problema ou dúvida enquanto percorria pelos exemplos na série, deixe-me saber nos comentários.