Tutorial 1 - javaPlay em 10 passos

download Tutorial 1 - javaPlay em 10 passos

If you can't read please download the document

description

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.

Transcript of Tutorial 1 - javaPlay em 10 passos

  • 1. SENAI So Jos Curso Tcnico em Informtica Tutorial 1 Introduo a Criao de Jogos com javaPlay em 10 passospor Kalu Caminha 10 de maro de 2012.IntroduoEste tutorial apresenta o uso bsico 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 informaes do texto e efetivamente tentar. Programar parecido com aprender uma lnguaestrangeira, s se aprende de verdade quando tentamos falar nessa lngua, em Programao Orientada a Objetos, voc s aprender secriar muitas classes e continuamente revisar os conceitos. Bom trabalho.1 O mtodo 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 necessrio apenas rodar a GameEngine.Iremos iniciar rodando um jogo sem nenhuma fase. Crie uma classe com o mtodo main (o mtodo que inicia todo projeto em Java).Configure o nmero de frames por segundo e rode a engine com o comando run.Cdigo 1 Mtodo que inicia a Enginepublic 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, chefo, tela final, tela de configurao, etc) so tambm chamados deestados do jogo. No javaPlay um estado representado por uma classe que implementa a interface GameStateController.Classes interface so classes que apenas definem a assinatura dos mtodos, sem nenhuma implementao. As classes queimplementam a interface que so responsveis por dizer como cada mtodo se comporta. Em OO, muito til criar primeiro asinterfaces que voc precisar e s depois, criar as classes que definem essa interface.Cdigo 2 Criando sua primeira fasepublic 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 mtodos que so utilizados durante o ciclo de vida de uma fase. Por enquanto, apenas osmtodos load, step e draw so importantes para ns. Deixe os outros trs vazios.Desenhamos um texto no mtodo draw apenas para testar nosso controlador no prximo 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 umainstncia 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.Instncia uma outra forma de se referir a um objeto. Considere uma classe Professor. Esta classe poderia ter em umdeterminado sistema diversas instncias (ou objetos), como por exemplo: new Professor(Kalu); new Professor(Maichel);Cada novo objeto criado pode tambm ser chamado instncia.Cdigo 3 Adicionando um novo estado para a GameEnginepublic static void main(String args[]) {GameEngine.getInstance().addGameStateController( 100 , newFase1());GameEngine.getInstance().setStartingGameStateController( 100 );GameEngine.getInstance().setFramesPerSecond(60);GameEngine.getInstance().run();}Execute e voc ver o texto que digitamos no mtodo draw do nosso cenrio.4 Step by StepTodo jogo baseado em loop infinito (at que o jogo acabe, claro). A GameEngine ir executar dentro deste loop o mtodo step do estadoativo de acordo com o nmero de frames por segundo.Se voc informou 60 no mtodo setFramePerSecond, o mtodo step da fase ser executado 60 vezes em um segundo. Para testar, omtodo abaixo apresenta um nmero que aumenta em uma unidade a cada execuo do loop. Este nmero tambm foi desenhado nomtodo draw.Frames por Segundo. Repare se a contagem est mesmo representando 60 frames por segundo. Provavelmente no. Se oprocessador do computador no for excepcional, este nmero de frames no ser processado. Um nmero relativamente seguronos meus testes entre 20 e 30 frames por segundo. Se for necessrio, voc pode criar animaes e processamento baseadosem tempo. Para isso, utilize o atributo elapsedTime no mtodo step para executar algo apenas quando uma determinadaquantidade de tempo tiver passado.Cdigo 4 Contagem de frames pela fase.public class Fase1 implements GameStateController { 3. private int numeroExecucoesStep; //cria atributopublic 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 retngulo pretog.setColor(Color.red); //configurar a cor de desenho para vermelho//Pinta o nmero de execues calculados.g.drawString("Execues: "+this.numeroExecucoesStep, 350, 290);}Execute e observe que cada vez que o nmero muda na tela, foi decorrente de uma execuo do mtodo step seguido pelo mtodo draw.o mtodo load executado apenas uma vez, quando a janela inicializada, voc pode pensar no load como um construtor.5 GameObjectsAgora vamos iniciar a construo 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 bnus que aumentam a velocidade so 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 crculo verde e suas nicas so aes sero se movimentarpara as quatro direes.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 mtodos de acesso (getters e setters) e dois mtodos abstratos, step e draw.Cdigo 5 GameObject representa o jogadorpublic class Jogador extends GameObject {private int velocidade = 5; //atributo que controla a velocidadepublic Jogador(){//O Construtor inicia o jogador em uma posio fixathis.x = 50;this.y = 50;}public void step(long timeElapsed) {//pega o objeto responsvel pelo tecladoKeyboard k = GameEngine.getInstance().getKeyboard();//Verifica se uma tecla direcional est pressionado//Em caso positivo, altera a posio do jogadorif(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 crculo com a cor verde.g.setColor(Color.GREEN);g.fillOval(this.x, this.y, 20, 20);}}Observe que o mtodo step, o responsvel, por verificar se alguma tecla foi pressionada e movimentar o jogador. O mtodo draw apenasdesenha o jogador na posio x e y atual sem se preocupar em moviment-lo. Se voc precisa dar movimento a algum GameObject, essemovimento deve ser gerenciado pelo mtodo step.6 Adicionando nosso primeiro GameObject ao Cenrio.Se voc adicionar a classe Jogador ao projeto, ele ainda no 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 mtodos step e draw doobjeto. Veja o exemplo e preste ateno nas linhas em negrito, elas tero sempre o mesmo padro para adicionar um novo objeto fase:Cdigo 6 Adicionando o jogador Fase1public class Fase1 implements GameStateController {private int numeroExecucoesStep;private Jogador jogador; //cria atributo que representa o jogadorpublic void load() {this.numeroExecucoesStep = 0;this.jogador = new Jogador(); //inicializa uma instncia Jogador}public void step(long timeElapsed) {this.numeroExecucoesStep++;this.jogador.step(timeElapsed); //executa o mtodo step do jogador}public void draw(Graphics g) { 5. g.fillRect(0, 0, 800, 600);g.setColor(Color.red);g.drawString("Execues: "+this.numeroExecucoesStep, 350, 290);this.jogador.draw(g); //executa o mtodo draw do jogador}...Execute e movimente seu personagem pelo cenrio. Agora, vamos criar alguns inimigos.7 Nosso Primeiro InimigoUm inimigo tambm um GameObject. Neste primeiro exemplo nosso inimigo ser apenas um GameObject que se move para frente epara trs continuamente. A lgica de andar de um lado para o outro implementada pelo mtodo step e o desenho pelo mtodo draw. Umadas grandes diferenas que um inimigo dever receber no construtor sua posio x e y inicial.Cdigo 7 Nosso primeiro inimigopublic 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:Cdigo 8 Incluso de Inimigos na Fasepublic 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("Execues: "+this.numeroExecucoesStep, 350, 290);this.jogador.draw(g);this.inimigo1.draw(g);this.inimigo2.draw(g);}8 Objetos interagindoBem, estes inimigos no so muito desafiadores. Vamos tentar criar um inimigo especial que assim como no pacman consegue perseguirnosso jogador.O perseguidor precisa conhecer a posio do objeto perseguido, ou seja o perseguidor precisar ter alguma forma de acessar os atributos xe y do jogador.Existem vrias formas de fazer isso em OO, eu utilizarei a mais simples neste momento, dentro de cada step da fase, chamarei um mtododo perseguidor passando como parmetro as posies x e y do nosso jogador.O Perseguidor tambm inicializar na parte de baixo do cenrio e dever ser um pouco mais lento que o nosso jogador para que adificuldade seja menor.Cdigo 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 embaixothis.x = 750;this.y = 550;}public void step(long timeElapsed) {//No faz nada aqui.}public void draw(Graphics g) {//Nosso inimigo ser um crculo laranjag.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 lgica mais interessanteif(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.Cdigo 10 Incluindo o inimigo perseguidor na faseprivate 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 ColisoPara que nosso programa seja realmente um jogo, ele vai precisar de coliso. Uma coliso ocorre quando dois corpos ocupam a mesmaposio, em computao, a matemtica que nos ensina como encontrar essa coliso verificando se um polgono tem interseco comoutro.A forma mais simples de realizar deteco de coliso utilizando retngulos. Como nosso objetivo agora trabalhar Orientao a Objetos eno matemtica, iremos utilizar a classe Rectangle do pacote awt que j nos fornece um mtodo que verifica a interseco entreretngulos.Para que isso funcione, o primeiro passo adicionar aos nossos GameObjects um mtodo que retorna o retngulo correspondente a cadaum deles:Cdigo 11 Mtodo que retorna um retngulo no Jogadorpublic class Jogador extends GameObject {...public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20);}}Cdigo 12 Mtodo que retorna um retngulo no Inimigopublic class Inimigo extends GameObject {...public Rectangle getRectangle(){ return new Rectangle(this.x, this.y, 20, 20);} 9. }Cdigo 13 Mtodo que retorna um retngulo no InimigoPerseguidorpublic class InimigoPerseguidor extends GameObject { ... public Rectangle getRectangle(){return new Rectangle(this.x, this.y, 20, 20); }}Como voc pode observar o mtodo igual em todas as classes pois todos os nossos objetos tem um retngulo semelhante. A partir deagora, sempre que precisar de colises, utilize retngulos. Colises com Elipses ou utilizando a tcnica PixelPerfect so mais complexas evoc no deve se preocupar com elas agora.Agora, basta verificar se o retngulo do jogador tem interseco com algum outro da tela. Para isso, criamos na classe Jogador o mtodotemColisao. Este mtodo apenas verifica se o retngulo do jogador faz interseco com algum outro retngulo passado como parmetro.Cdigo 14 Incluindo o mtodo temColisao classe Jogadorpublic 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 mtodo step da fase se existe coliso do jogador com algum dos inimigos, em caso positivo, voltamos o jogadorpara a posio inicial. Neste ponto poderia ser inserida a lgica de troca de fase, perda de pontos, etc.Cdigo 15 Verificando colises dentro da fasepublic 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.getRectangle() )){ this.jogador.setX(50); this.jogador.setY(50); } }10 Chegando ao DestinoPara termos um jogo com as funcionalidades bsicas falta apenas o estado de vitria. Para isso, criamos um GameObject que desenhauma estrela representando o local onde o jogo termina. A API Java2D permite o uso de diversos recursos grficos como desenho de curvas, polgonos, gradientes, imagens e muito mais. Procure por API Java 2D e sinta-se livre para testar seus recursos com os objetos do seu jogo.Cdigo 17 GameObject que representa a chegada do jogopublic 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 coresthis.cor = new Color(this.vermelhoEVerde, this.vermelhoEVerde, 100);}public void draw(Graphics g) {//Este um exemplo de uso da API Java2D com um polgonosPolygon 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 verificao se houve coliso com a chegada:Cdigo 18 Verifica coliso do jogador com a chegadapublic 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);...//Novidadeif( this.jogador.temColisao( this.chegada.getRectangle() )){JOptionPane.showMessageDialog(null, "Voc venceu, parabns.");System.exit(0);}}Execute e impressione-se com o seu poder.!E srio, se ver isso funcionando no te impressiona, voc no tem emoo.Bnus, listas e diverso.O importante em um jogo encontrar o equilbrio entre dificuldade e a capacidade do jogador de venc-lo.Um dos elementos de programao que podem lhe ajudar so as listas/arrays, com eles possvel ter ma grandequantidade de objetos interagindo durante o jogo sem que voc precise adicionar um cdigo especfico para cadaum deles.Observe o cdigo 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 lgica para cada lista j estimplementada nos mtodos step e draw. Aproveite e veja se existe algo que voc no consegue entender nesrasimplementaes. Pense um pouco e, se a dvida persistir, chame o professor.Agora, o cdigo da fase1 com algumas novidades Cdigo 19 Fase 1 utilizando listas public class Fase1 implements GameStateController {private int numeroExecucoesStep;private Jogador jogador;private ArrayList inimigos;private ArrayList inimigosPerseguidores;private Chegada chegada;public void load() {this.numeroExecucoesStep = 0;this.jogador = new Jogador(); 13. this.inimigos = new ArrayList(); 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(); 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, parabns."); 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.