Páginas

quinta-feira, 30 de agosto de 2012

Criando animações de sprites

No post anterior eu expliquei como o GIMP pode ajudar a criar algumas imagens que servirão de efeitos no jogo.

E neste post eu expliquei como o Blender pode criar uma sequencia de imagens a serem utilizadas em nossos sprites.

Agora irei explicar como animar as imagens.
Para entendermos isso precisamos entender primeiro como funciona um jogo. Todo jogo tem o seu game loop, um loop que fica rodando o tempo todo enquanto o game executa.

A estrutura básica de um game simplificadamente pode ser assim:


carregandoImagens();//carrega as imagens do jogo
boolean gameRodando = true;
while(gameRodando){
  atualizaEstadoDoJogo();//Atualizamos as posições dos sprites
  renderizaTelaDoJogo();//pintamos na tela os sprites na nova posição
}


Podemos entender cada loop como um frame do jogo.

O método  carregandoImagens () carrega as imagens a serem usadas no jogo.
O método  atualizaEstadoDoJogo() atualiza a logica do game de acordo com os inputs e/ou outros fatores.
E o método renderizaTelaDoJogo() finalmente pinta na tela o estado do jogo no frame corrente.

É por causa do game loop que podemos fazer animações de images. Lembra de como se fazia um desenho animado antigamente? Pegava-se um caderno e em cada folha desenhava-se o personagem de um jeito. Então ao folhear o caderno tinha-se a ilusão da animação. Isso se aplica ao game loop, como?

Bom, podemos entender cada volta no loop como uma folha. Então em um frame (ou volta no loop) desenhamos a imagem de um jeito, e no proximo frame a mesma imagem levemente modificada, e assim até termos uma animação.

Veja novamente meu jogo em https://sites.google.com/site/asteroidsrain/ perceba os asteroids rodando, as explosões, a nave virando etc. Isso nada mais é do que imagens levemente diferentes em cada frame.

Mas como fazer isso na pratica?
Fiz um pequeno framework de animação. Ele nos dá alguns controles de animações interessantes.

Pense no seguinte: cada volta no loop é executada muito rapidamente, assim se em cada frame você colocar uma parte diferente de sua sequencia de imagens, a animação vai acabar num piscar de olhos. Você precisa ter um controle de delay.
Outro problema: E se a animação for constante? Como os asteroids que ficam rodando o tempo todo? Então ao chegar na ultima imagem deve-se voltar para a primeira

Essa pequena classe que criei ajuda nesses dois problemas. Vamos ao código:


import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
public class ImageAnimation {
    private List<BufferedImage> images = new ArrayList<BufferedImage>();
    private final int LOOP = 1;
    private int type = 0;
    private int delay = 0;
    private int currentdelay = 0;
    private int currentPosition = 0;
    public BufferedImage getCurrentImage(){
        if(currentPosition == 0){
            //ja pode retornar a primeira
        }else{
            if(delay > 0){
                //verifica se já é hora da proxima
                if(currentdelay == delay){
                    //chegou a hora, volta o delay para  zero
                    currentdelay = 0;
                    //proxima imagem
                    currentPosition++;
                }else{
                    //AINDA NÃO É A HORA
                    currentdelay++;
                }
            }else{
                //não tem delay, vai para a proxima
                currentPosition++;
            }
        }
        //verifica se saiu do tamanho da lista
        if(currentPosition >= images.size()){
            //passou do ultimo, verifica se é loop
            if(type == LOOP){
                //volta para a primeira imagem
                currentPosition = 0;
            }else{
                //acabou a animação
                return null;
            }
        }
        return images.get(currentPosition);
    }
    public void setImages(List<BufferedImage> images) {
        this.images = images;
    }
    public void setType(int type) {
        this.type = type;
    }
    public void setDelay(int delay) {
        this.delay = delay;
    }
}


Como podemos usar ela no jogo, veja abaixo:


List<BufferedImage> imagens = carregandoImagens ();//carrega as imagens do jogo
//configura a classe de animação
ImageAnimation animacao = new ImageAnimation();
animacao.setImages(imagens);
animacao.setDelay(30);
animacao.setType(ImageAnimation.LOOP);
//entra no game loop
boolean gameRodando = true;
while(gameRodando){            
   atualizaEstadoDoJogo();//Atualizamos as posições dos sprites
   //obtem aqui a proxima imagem da animacao
   BufferedImage imagem = animacao. getCurrentImage ();
   renderizaTelaDoJogo(imagem);//pintamos na tela a imagem corrente
}


Vamos entender esse código.
Na configuração da classe de animação eu passo as imagens que compõe minha animação utilizando o método animacao.setImages(imagens);.
Também informo que o delay será de 30 voltas no método animacao.setDelay(30), isso quer dizer que o framework de animação deverá esperar 30 voltas antes de entregar a próxima imagem. E finalmente informo que o tipo de animação será LOOP em  animacao.setType( ImageAnimation.LOOP ); , isso quer dizer que ao entregar a ultima imagem, o framework voltará seu ponteiro para primeira, reiniciando assim a animação. Se o parametro LOOP não fosse fornecido a animação acabaria na ultima imagem, retornando null após isso.

Espero que esse post tenha esclarecido o processo de animação.
No próximo post explicarei como pintar na tela imagens utilizando o java 2D

Att
Gustavo Marques.

Nenhum comentário:

Postar um comentário

Veja também

Related Posts Plugin for WordPress, Blogger...