Your SlideShare is downloading. ×
Tutorial 1 - javaPlay em 10 passos
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Tutorial 1 - javaPlay em 10 passos

615
views

Published on

Introdução ao framework javaPlay em 10 passos utilizado no curso de programação orientada a objetos do SENAI SC de São José no primeiro semestre de 2012. …

Introdução ao framework javaPlay em 10 passos utilizado no curso de programação orientada a objetos do SENAI SC de São José no primeiro semestre de 2012.

O framework javaPlay aucilia o desenvolvimento de jogos em Java.

Published in: Education

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
615
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
25
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. SENAI – São José – Curso Técnico em Informática Tutorial 1 – Introdução a Criação de Jogos com javaPlay em 10 passos por Kaléu Caminha <http://kaleucaminha.com> 10 de março de 2012.IntroduçãoEste tutorial apresenta o uso básico do framework javaPlay em 10 passos. Você pode baixá-lo do GitHub, adicioná-lo a um novo projeto doNetBeans e seguir o tutorial abaixo.O mais importante é estar atento as informações do texto e efetivamente tentar. Programar é parecido com aprender uma línguaestrangeira, só se aprende de verdade quando tentamos falar nessa língua, em Programação Orientada a Objetos, você só aprenderá secriar muitas classes e continuamente revisar os conceitos. Bom trabalho.1 – O método mainIniciar um projeto com javaPlay é simplesmente um processo de criar fases, adicionar objetos para as fases e adicionar as fases para aGameEngine. Por fim, será necessário apenas rodar a GameEngine.Iremos iniciar rodando um jogo sem nenhuma fase. Crie uma classe com o método main (o método que inicia todo projeto em Java).Configure o número de frames por segundo e rode a engine com o comando run. Código 1 – Método que inicia a Engine public class MeuJogo2D { public static void main( String args[] ) { GameEngine.getInstance().setFramesPerSecond( 60 ); GameEngine.getInstance().run(); } }Execute e você verá uma tela vazia. Perfeito. Agora vamos adicionar nossa primeira fase.2 – Estados/Fases do JogoTodas fases ou telas do jogo (tela de abertura, fase 1, fase 2, chefão, tela final, tela de configuração, etc) são também chamados deestados do jogo. No javaPlay um estado é representado por uma classe que implementa a interface GameStateController. Classes interface são classes que apenas definem a assinatura dos métodos, sem nenhuma implementação. As classes que implementam a interface é que são responsáveis por dizer como cada método se comporta. Em OO, é muito útil criar primeiro as interfaces que você precisará e só depois, criar as classes que definem essa interface. Código 2 – Criando sua primeira fase public class Fase1 implements GameStateController { public void load() { } public void step(long timeElapsed) { } public void draw(Graphics g) { g.drawString("Fase 1 - Are you ready?", 350, 290); } public void start() { } public void unload() { }
  • 2. public void stop() { } }A interface GameStateController define 6 métodos que são utilizados durante o ciclo de vida de uma fase. Por enquanto, apenas osmétodos load, step e draw são importantes para nós. Deixe os outros três vazios.Desenhamos um texto no método draw apenas para testar nosso controlador no próximo passo.3 – Conectando as fases com a GameEngineAgora iremos executar a GameEngine informando que a Fase1 será o estado inicial. Fazemos isso passando para a GameEngine umainstância da nossa Fase e identificando-a com um ID de valor 100. Depois, dizemos que o estado inicial do jogo será o estado com o ID100. Mais tarde utilizaremos estes IDs para alterar as fases. Instância é uma outra forma de se referir a um objeto. Considere uma classe Professor. Esta classe poderia ter em um determinado sistema diversas instâncias (ou objetos), como por exemplo: new Professor(“Kaléu”); new Professor(“Maichel”); Cada novo objeto criado pode também ser chamado instância. Código 3 – Adicionando um novo estado para a GameEngine public static void main(String args[]) { GameEngine.getInstance().addGameStateController( 100 , new Fase1()); GameEngine.getInstance().setStartingGameStateController( 100 ); GameEngine.getInstance().setFramesPerSecond(60); GameEngine.getInstance().run(); }Execute e você verá o texto que digitamos no método draw do nosso cenário.4 – Step by StepTodo jogo é baseado em loop infinito (até que o jogo acabe, claro). A GameEngine irá executar dentro deste loop o método step do estadoativo de acordo com o número de frames por segundo.Se você informou 60 no método setFramePerSecond, o método step da fase será executado 60 vezes em um segundo. Para testar, ométodo abaixo apresenta um número que aumenta em uma unidade a cada execução do loop. Este número também foi desenhado nométodo draw. Frames por Segundo. Repare se a contagem está mesmo representando 60 frames por segundo. Provavelmente não. Se o processador do computador não for excepcional, este número de frames não será processado. Um número relativamente seguro nos meus testes é entre 20 e 30 frames por segundo. Se for necessário, você pode criar animações e processamento baseados em tempo. Para isso, utilize o atributo elapsedTime no método step para executar algo apenas quando uma determinada quantidade de tempo tiver passado. Código 4 – Contagem de frames pela fase. public class Fase1 implements GameStateController {
  • 3. private int numeroExecucoesStep; //cria atributo public void load() { this.numeroExecucoesStep = 0; //inicializa valor do atributo } public void step(long timeElapsed) { this.numeroExecucoesStep++; //faz contagem } public void draw(Graphics g) { g.fillRect(0, 0, 800, 600); //pinta um retângulo preto g.setColor(Color.red); //configurar a cor de desenho para vermelho //Pinta o número de execuções calculados. g.drawString("Execuções: "+this.numeroExecucoesStep, 350, 290); } …Execute e observe que cada vez que o número muda na tela, foi decorrente de uma execução do método step seguido pelo método draw.o método load é executado apenas uma vez, quando a janela é inicializada, você pode pensar no load como um construtor.5 – GameObjectsAgora vamos iniciar a construção dos diversos objetos que constituem um jogo. Tomando como exemplo um jogo de corrida. Cada carro éum objeto, o painel de velocidade é um objeto, os bônus que aumentam a velocidade são objetos. Cada objeto é modelados em uma oumais classes.Vamos iniciar com um jogo simples cujo objetivo é desviar de inimigos com movimentos de teclado. Nosso primeiro objeto de jogo será aclasse que representa o Jogador. Nosso jogador será representado por um círculo verde e suas únicas são ações serão se movimentarpara as quatro direções.No javaPlay cada classe que representa um objeto do jogo deve estender a classe abstrata GameObject. Esta classe define dois atributos(x e y), os métodos de acesso (getters e setters) e dois métodos abstratos, step e draw. Código 5 – GameObject representa o jogador public class Jogador extends GameObject { private int velocidade = 5; //atributo que controla a velocidade public Jogador(){ //O Construtor inicia o jogador em uma posição fixa this.x = 50; this.y = 50; } public void step(long timeElapsed) { //pega o objeto responsável pelo teclado Keyboard k = GameEngine.getInstance().getKeyboard(); //Verifica se uma tecla direcional está pressionado //Em caso positivo, altera a posição do jogador if(k.keyDown(Keyboard.DOWN_KEY)){ this.y += this.velocidade;
  • 4. } if(k.keyDown(Keyboard.UP_KEY)){ this.y -= this.velocidade; } if(k.keyDown(Keyboard.LEFT_KEY)){ this.x -= this.velocidade; } if(k.keyDown(Keyboard.RIGHT_KEY)){ this.x += this.velocidade; } } public void draw(Graphics g) { //Desenha um círculo com a cor verde. g.setColor(Color.GREEN); g.fillOval(this.x, this.y, 20, 20); } }Observe que o método step, é o responsável, por verificar se alguma tecla foi pressionada e movimentar o jogador. O método draw apenasdesenha o jogador na posição x e y atual sem se preocupar em movimentá-lo. Se você precisa dar movimento a algum GameObject, essemovimento deve ser gerenciado pelo método step.6 – Adicionando nosso primeiro GameObject ao Cenário.Se você adicionar a classe Jogador ao projeto, ele ainda não aparecerá na tela. Quem gerencia nosso jogo é o controle. Se quisermos queo nosso objeto participe, ele deve estar dentro do controle.Isto é simples, basta adicioná-lo como atributo da classe que representa uma fase do jogo, inicializá-lo e rodar os métodos step e draw doobjeto. Veja o exemplo e preste atenção nas linhas em negrito, elas terão sempre o mesmo padrão para adicionar um novo objeto à fase: Código 6 – Adicionando o jogador à Fase1 public class Fase1 implements GameStateController { private int numeroExecucoesStep; private Jogador jogador; //cria atributo que representa o jogador public void load() { this.numeroExecucoesStep = 0; this.jogador = new Jogador(); //inicializa uma instância Jogador } public void step(long timeElapsed) { this.numeroExecucoesStep++; this.jogador.step(timeElapsed); //executa o método step do jogador } public void draw(Graphics g) {
  • 5. g.fillRect(0, 0, 800, 600); g.setColor(Color.red); g.drawString("Execuções: "+this.numeroExecucoesStep, 350, 290); this.jogador.draw(g); //executa o método draw do jogador } ...Execute e movimente seu personagem pelo cenário. Agora, vamos criar alguns inimigos.7 – Nosso Primeiro InimigoUm inimigo também é um GameObject. Neste primeiro exemplo nosso inimigo será apenas um GameObject que se move para frente epara trás continuamente. A lógica de andar de um lado para o outro é implementada pelo método step e o desenho pelo método draw. Umadas grandes diferenças é que um inimigo deverá receber no construtor sua posição x e y inicial. Código 7 – Nosso primeiro inimigo public class Inimigo extends GameObject { private int passos; private int direcao; public Inimigo(int xInicial, int yInicial){ this.x = xInicial; this.y = yInicial; this.passos = 0; this.direcao = 1; //para frente } public void step(long timeElapsed) { if(this.passos < 15){ this.x += 2 * this.direcao; this.passos++; } else { this.direcao *= -1; this.passos = 0; } } public void draw(Graphics g) { g.setColor(Color.red); g.fillOval(this.x, this.y, 20, 20); } }
  • 6. Para ver o GameObject em atividade, basta adicioná-lo à fase: Código 8 – Inclusão de Inimigos na Fase public class Fase1 implements GameStateController { private int numeroExecucoesStep; private Jogador jogador; private Inimigo inimigo1; private Inimigo inimigo2; public void load() { this.numeroExecucoesStep = 0; this.jogador = new Jogador(); this.inimigo1 = new Inimigo(100, 100); this.inimigo2 = new Inimigo(400, 400); } public void step(long timeElapsed) { this.numeroExecucoesStep++; this.jogador.step(timeElapsed); this.inimigo1.step(timeElapsed); this.inimigo2.step(timeElapsed); } public void draw(Graphics g) { g.fillRect(0, 0, 800, 600); g.setColor(Color.red); g.drawString("Execuções: "+this.numeroExecucoesStep, 350, 290); this.jogador.draw(g); this.inimigo1.draw(g); this.inimigo2.draw(g); }8 – Objetos interagindoBem, estes inimigos não são muito desafiadores. Vamos tentar criar um inimigo especial que assim como no pacman consegue perseguirnosso jogador.O perseguidor precisa conhecer a posição do objeto perseguido, ou seja o perseguidor precisará ter alguma forma de acessar os atributos xe y do jogador.Existem várias formas de fazer isso em OO, eu utilizarei a mais simples neste momento, dentro de cada step da fase, chamarei um métododo perseguidor passando como parâmetro as posições x e y do nosso jogador.O Perseguidor também inicializará na parte de baixo do cenário e deverá ser um pouco mais lento que o nosso jogador para que adificuldade seja menor. Código 9 – Classe representando um inimigo perseguidor
  • 7. public class InimigoPerseguidor extends GameObject { private int velocidade = 2; //se a velocidade for 5, existira chance? public InimigoPerseguidor(){ //Inicializa no canto direito embaixo this.x = 750; this.y = 550; } public void step(long timeElapsed) { //Não faz nada aqui. } public void draw(Graphics g) { //Nosso inimigo será um círculo laranja g.setColor(Color.ORANGE); g.fillOval(this.x, this.y, 20, 20); } public void persegue(int xPerseguido, int yPerseguido){ //Este é o segredo, você pode criar uma lógica mais interessante if(this.x < xPerseguido){ this.x += this.velocidade; } else { this.x -= this.velocidade; } if(this.y < yPerseguido){ this.y += this.velocidade; } else { this.y -= this.velocidade; } } }Para vermos o funcionamento, adicionamos este novo tipo de inimigo na nossa fase. Código 10 – Incluindo o inimigo perseguidor na fase … private Inimigo inimigo2; private InimigoPerseguidor inimigoPerseguidor;
  • 8. public void load() { this.numeroExecucoesStep = 0; this.jogador = new Jogador(); this.inimigo1 = new Inimigo(100, 100); this.inimigo2 = new Inimigo(400, 400); this.inimigoPerseguidor = new InimigoPerseguidor(); } public void step(long timeElapsed) { this.numeroExecucoesStep++; this.jogador.step(timeElapsed); this.inimigo1.step(timeElapsed); this.inimigo2.step(timeElapsed); this.inimigoPerseguidor.persegue( this.jogador.getX(), this.jogador.getY() ); }Execute e fuja.!9 – ColisãoPara que nosso programa seja realmente um jogo, ele vai precisar de colisão. Uma colisão ocorre quando dois corpos ocupam a mesmaposição, em computação, é a matemática que nos ensina como encontrar essa colisão verificando se um polígono tem intersecção comoutro.A forma mais simples de realizar detecção de colisão é utilizando retângulos. Como nosso objetivo agora é trabalhar Orientação a Objetos enão matemática, iremos utilizar a classe Rectangle do pacote awt que já nos fornece um método que verifica a intersecção entreretângulos.Para que isso funcione, o primeiro passo é adicionar aos nossos GameObjects um método que retorna o retângulo correspondente a cadaum deles: Código 11 – Método que retorna um retângulo no Jogador public class Jogador extends GameObject { ... public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20); } } Código 12 – Método que retorna um retângulo no Inimigo public class Inimigo extends GameObject { ... public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20); }
  • 9. } Código 13 – Método que retorna um retângulo no InimigoPerseguidor public class InimigoPerseguidor extends GameObject { ... public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20); } }Como você pode observar o método é igual em todas as classes pois todos os nossos objetos tem um retângulo semelhante. A partir deagora, sempre que precisar de colisões, utilize retângulos. Colisões com Elipses ou utilizando a técnica PixelPerfect são mais complexas evocê não deve se preocupar com elas agora.Agora, basta verificar se o retângulo do jogador tem intersecção com algum outro da tela. Para isso, criamos na classe Jogador o métodotemColisao. Este método apenas verifica se o retângulo do jogador faz intersecção com algum outro retângulo passado como parâmetro. Código 14 – Incluindo o método temColisao à classe Jogador public class Jogador extends GameObject { ... public boolean temColisao( Rectangle r2 ){ return this.getRectangle().intersects( r2 ); } public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20); } }Finalizamos verificando no método step da fase se existe colisão do jogador com algum dos inimigos, em caso positivo, voltamos o jogadorpara a posição inicial. Neste ponto poderia ser inserida a lógica de troca de fase, perda de pontos, etc.Código 15 – Verificando colisões dentro da fase public class Fase1 implements GameStateController { ... public void step(long timeElapsed) { this.numeroExecucoesStep++; this.jogador.step(timeElapsed); this.inimigo1.step(timeElapsed);
  • 10. this.inimigo2.step(timeElapsed); this.inimigoPerseguidor.persegue( this.jogador.getX(), this.jogador.getY() ); //Daqui para baixo está a novidade... if( this.jogador.temColisao( this.inimigo1.getRectangle() ) ){ this.jogador.setX(50); this.jogador.setY(50); } if( this.jogador.temColisao( this.inimigo2.getRectangle() ) ){ this.jogador.setX(50); this.jogador.setY(50); } if(this.jogador.temColisao( this.inimigoPerseguidor.getRectang le() )){ this.jogador.setX(50); this.jogador.setY(50); } }10 – Chegando ao DestinoPara termos um jogo com as funcionalidades básicas falta apenas o estado de vitória. Para isso, criamos um GameObject que desenhauma estrela representando o local onde o jogo termina. A API Java2D permite o uso de diversos recursos gráficos como desenho de curvas, polígonos, gradientes, imagens e muito mais. Procure por API Java 2D e sinta-se livre para testar seus recursos com os objetos do seu jogo. Código 17 – GameObject que representa a chegada do jogo public class Chegada extends GameObject { private Color cor; private int vermelhoEVerde = 230; public Chegada(int x, int y){ this.x = x; this.y = y; this.cor = new Color(this.vermelhoEVerde, this.vermelhoEVerde, 20);
  • 11. } public void step(long timeElapsed) { this.vermelhoEVerde += 5; if(this.vermelhoEVerde > 250){ this.vermelhoEVerde = 180; } //tentei fazer a estrela parecer estar piscando apenas com as cores this.cor = new Color(this.vermelhoEVerde, this.vermelhoEVerde, 100); } public void draw(Graphics g) { //Este é um exemplo de uso da API Java2D com um polígonos Polygon estrela = new Polygon(); estrela.addPoint(this.x, this.y); estrela.addPoint(this.x+10, this.y+20); estrela.addPoint(this.x-10, this.y+20); estrela.addPoint(this.x, this.y); Polygon estrela2 = new Polygon(); int x2 = this.x; int y2 = this.y+25; estrela2.addPoint(x2, y2); estrela2.addPoint(x2+10, y2-20); estrela2.addPoint(x2-10, y2-20); estrela2.addPoint(x2, y2); g.setColor(this.cor); g.fillPolygon(estrela); g.fillPolygon(estrela2); } public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 25); } }Para saber se o jogador chegou até na estrela, basta fazermos verificação se houve colisão com a chegada: Código 18 – Verifica colisão do jogador com a chegada public class Fase1 implements GameStateController { ... private Chegada chegada; public void load() { ...
  • 12. this.chegada = new Chegada(750, 550); //Novidade } public void step(long timeElapsed) { ... this.chegada.step(timeElapsed); ... //Novidade if( this.jogador.temColisao( this.chegada.getRectangle() )){ JOptionPane.showMessageDialog(null, "Você venceu, parabéns."); System.exit(0); } }Execute e impressione-se com o seu poder.!E sério, se ver isso funcionando não te impressiona, você não tem emoção.Bônus, listas e diversão.O importante em um jogo é encontrar o equilíbrio entre dificuldade e a capacidade do jogador de vencê-lo.Um dos elementos de programação que podem lhe ajudar são as listas/arrays, com eles é possível ter ma grandequantidade de objetos interagindo durante o jogo sem que você precise adicionar um código específico para cadaum deles.Observe o código da Fase1 abaixo, ele foi modificado para ter duas listas, uma de inimigos e outra de inimigosperseguidores.Para ter mais inimigos, basta adicionar novos objetos às listas e pronto, a lógica para cada lista já estáimplementada nos métodos step e draw. Aproveite e veja se existe algo que você não consegue entender nesrasimplementações. Pense um pouco e, se a dúvida persistir, chame o professor.Agora, o código da fase1 com algumas novidades Código 19 – Fase 1 utilizando listas public class Fase1 implements GameStateController { private int numeroExecucoesStep; private Jogador jogador; private ArrayList<Inimigo> inimigos; private ArrayList<InimigoPerseguidor> inimigosPerseguidores; private Chegada chegada; public void load() { this.numeroExecucoesStep = 0; this.jogador = new Jogador();
  • 13. this.inimigos = new ArrayList<Inimigo>(); this.inimigos.add( new Inimigo(100, 100) ); this.inimigos.add( new Inimigo(600, 400) ); this.inimigos.add( new Inimigo(300, 550) ); this.inimigos.add( new Inimigo(200, 200) ); this.inimigosPerseguidores = newArrayList<InimigoPerseguidor>(); this.inimigosPerseguidores.add( new InimigoPerseguidor() ); this.chegada = new Chegada(750, 550); } public void step(long timeElapsed) { this.numeroExecucoesStep++; this.jogador.step(timeElapsed); this.chegada.step(timeElapsed); for(Inimigo i : this.inimigos ){ i.step(timeElapsed); if(this.jogador.temColisao(i.getRectangle())){ this.jogador.setX(50); this.jogador.setY(50); } } for(InimigoPerseguidor i : this.inimigosPerseguidores ){ i.persegue( this.jogador.getX(), this.jogador.getY() ); if(this.jogador.temColisao(i.getRectangle())){ this.jogador.setX(50); this.jogador.setY(50); } } if( this.jogador.temColisao( this.chegada.getRectangle() )){ JOptionPane.showMessageDialog(null, "Você venceu, parabéns."); System.exit(0);
  • 14. } Random sorteador = new Random(); if( sorteador.nextInt(40) == 1){ this.inimigosPerseguidores.add( new InimigoPerseguidor() ); } } public void draw(Graphics g) { g.setColor(Color.BLACK); g.fillRect(0, 0, 800, 600); this.chegada.draw(g); this.jogador.draw(g); for(Inimigo i : this.inimigos ){ i.draw(g); } for(InimigoPerseguidor i : this.inimigosPerseguidores ){ i.draw(g); } } public void start() { } public void unload() { } public void stop() { }} A partir daqui, explore sua criatividade livremente.