Cenário
No Jplay existe uma classe chamada Scene que é utilizada para a construção de cenários.
1 - Antes de começar a construir o código
Antes de escrever qualquer linha de código pense em como o seu cenário é, quais são as imagens estáticas (não animadas), quais as imagens dinâmicas como sprites e animations. As imagens estáticas serão as que estarão inseridas no arquivo que compõe a cena.
Tenha em mente que todas as imagens do cenário devem ter a mesma dimensão, se você tem imagens que são retângulos então todas devem ser retângulos.
Feito isso, você está pronto para começar a construção do arquivo do seu cenário. As seções 2 e 3 cuidam dessa construção.
2 - Organização do arquivo do cenário
No Jplay as informações para a construção do cenário são quardadas em um arquivo com a extensão '.scn'.
O arquivo é organizado da seguinte forma:
linha 1 - número de imagens usadas para a construção do cenário (as imagens estáticas).
linha 2 - caminho da primeira imagem e sua extensão
linha 3 - caminho da segunda imagem e sua extensão
linha 4 - caminho da terceira imagem e sua extensão
linha n+1 - caminho da n-ésima imagem e sua extensão
linha n+2 - começo da construção da matriz
linha m - fim da construção da matriz que é representada pelo caracter '%'.
linha m+1 - caminho do backdrop e sua extensão.
Obs.: Não deixe nenhuma linha em branco pois, isso gerará um erro de execução.
Exemplo de uma arquivo '.scn'.
3 wall.jpg floor.jpg star.jpg 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 0,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0 0,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,0 0,1,2,1,2,2,2,1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,1,0 0,1,2,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,2,2,2,2,1,0 0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0 0,1,2,1,1,1,1,2,2,2,1,1,1,1,1,1,2,2,1,1,1,2,1,0 0,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,0 0,1,2,1,2,2,2,1,2,2,2,2,2,2,2,1,1,1,1,1,2,2,1,0 0,1,2,1,2,2,2,1,1,1,1,1,1,1,2,1,2,2,2,1,2,2,1,0 0,1,2,1,2,2,2,2,2,2,1,2,2,2,2,1,2,2,2,1,1,1,1,0 0,1,2,1,2,2,2,2,2,2,1,2,2,2,2,1,2,2,2,2,2,2,1,0 0,1,2,1,1,1,2,2,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,0 0,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,3,0 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 % backdrop.jpg
3 - Construindo a matriz
A matriz é composta por números inteiros separados por virgula e sem espaços entre eles.
O número 0 (zero) sempre corresponde ao backdrop (a imagem que será desenhada por baixo do cenário).
Já os outros número seguem a seguinte lógica:
1 - representa a imagem wall.jpg
2 - representa a imagem flor.jpg
3 - representa a imagem star.jpg
Colocamos o número 0 (zero) nas bordas sinalizando que essa é uma área do backdrop.
O número 1 é usado como 'wall' e delimita passagens.
O número 2 é usado como lugares pecorríveis.
O número 3 é usado como ponto final de chegada.
4 - Construindo o código para mostrar um cenário
Para construir o código nós usaremos os seguintes métodos da classe Scene:
//Construtor da classe public Scene(); //Ccarrega as informações armazenadas no arquivo '.scn'. public void loadFromFile(String fileName); //Informa ao Jplay quais são as coordenadas iniciais onde o cenário deverá ser desenhado inicialmente. public void setDrawStartPos(int drawStartX, int drawStartY); //Desenha o cenário na tela public void draw();
Exemplo: Mostrando um cenário na tela.
package cenario; import java.awt.Point; import java.util.Vector; import jplay.Keyboard; import jplay.Scene; import jplay.TileInfo; import jplay.Window; /** * @author Gefersom Cardoso Lima * Federal Fluminense University * Computer Science */ public class Cenario { private Window window; private Scene scene; public Cenario() { window = new Window(800,600); scene = new Scene(); scene.loadFromFile("scene.scn"); scene.setDrawStartPos(15, 30); } public void run() { while(true) { scene.draw(); window.update(); } } /** * @param args the command line arguments */ public static void main(String[] args) { Cenario cen = new Cenario(); cen.run(); } }
5 - Adicionando um objeto ao cenário.
Para adicionar um objeto que será controlado por nós ao cenário usamos o método abaixo:
//Adiciona qualquer GameObject do Jplay ao cenário mas, não modifica a //matriz que é usada para a construção do cenário. public void addOverlay(GameObject overlay);
Com o método acima podemos adicionar um GameImage, uma Animation ou um Sprite ao cenário.
6 - A classe auxiliar TileInfo.
Quando o Jplay lê o arquivo de entrada do cenário ele cria para cada valor inteiro da matriz armazenada no arquivo '.scn' um TileInfo.
As informações armazenadas no TileInfo são: 'id' - valor numérico que representa a imagem e uma referência para a imagem.
Todos os TileInfos são armazenados em um matriz usada internamente pela classe Scene.
Exemplo:
A imagem 'wall.jpg' é presentando pelo número 1.
O Jplay cria um TileInfo com a ID = 1 e associa esse TileInfo a imagem 'wall.jpg' que é armazenada em outra estrutura interna na classe Scene.
É por meio do TileInfo que podemos saber onde um determinado overlay está localizado e quais são as imagens sobrepostas por ele ou em volta dele.
7 - Delimitando áreas pecorríveis.
Método da classe Scene usados nessa seção:
//Retorna os objetos que estão entre os ponto passados como parâmetros. public Vector getTilesFromRect(Point min, Point max)
Se não delimitarmos áreas em que o GameObject (adicionado com o método addOverlay()) pode percorrer ele se moverá por todo o cenário.
Para fazer o GameObject se mover somente por algumas áreas seguimos os seguinte passos:
1 - Capturamos os valores mínimos e máximos para as coordenadas do GameObject.
//Lembre-se que as coordenadas de todo GameObject no Jplay são números no formato 'double' //logo, algumas vezes precisaremos fazer um cast para um número inteiro. //Posicao min é a posição (x,y) do GameObject Point playerMin = new Point ((int)gameObject.x, (int)gameObject.y); //Posicao max é a posição (x + largura, y + altura) do GameObject Point playerMax = new Point((int)(gameObject.x + player.width), (int)(gameObject.y + gameObject.height));
2 - Capturamos as imagens que estão na mesma área do GameObject.
//Retorna as imagens que estiverem na mesma área do GameObject (x,y), (x + largura, y + altura) Vector tiles = scene.getTilesFromRect(playerMin, playerMax);
3 - Para cada tile retornado verificamos qual é o tile a partir da sua id e se o player colidiu com ele.
Depois do teste de colisão e de verificação de id o que será feito é uma escolha do programador.
No trecho abaixo o GameObject somente é reposicionado alguns pixels. Já que para haver uma colisão alguns pixels têm que ser sobrepostos. Caso não fizessemos isso o GameObject ficaria colidindo infinitamente com o TileInfo.
//para todos os tiles for(int i = 0 ; i < tiles.size() ; i++) { TileInfo tile = (TileInfo)tiles.elementAt(i); //se o tile é parede e o player colidiu com ele if((tile.id == Constante.TILE_WALL) && player.collided(tile)) { //se o player está se movendo na diagonal if (player.moveuNaHorizontal()) { //o player está atrás da parede? if(player.x <= tile.x -1) player.x = tile.x - player.width;//Reposiciona o player else //o player está na frente da parede player.x = tile.x + tile.width;//Reposiciona o player } else { //o player está abaixo da parede? if(player.y >= tile.y + tile.height -1) player.y = tile.y + tile.height;//Reposiciona o player else //o player está acima da parede player.y = tile.y - player.height ;//Reposiciona o player } return false; } else //se o player encontrou a estrela if(tile.id == Constante.TILE_STAR) return true; } return false; }
Exemplo:
import jplay.Keyboard; import jplay.Sprite; import java.awt.Point; import java.util.Vector; import jplay.Keyboard; import jplay.Scene; import jplay.TileInfo; import jplay.Window; /*----------------------------------------------------------------------------------------------*/ public class Constante { public static int TILE_WALL = 1; public static int TILE_STAR = 3; } /*----------------------------------------------------------------------------------------------*/ public class Player extends Sprite { private boolean horizontalMoveKeyPressed; public Player() { super("player.png",8); } public boolean moveuNaHorizontal() { return horizontalMoveKeyPressed; } public void move(Keyboard keyboard) { horizontalMoveKeyPressed = true; if(keyboard.keyDown(Keyboard.LEFT_KEY) == true) { this.x -= 1; setCurrFrame(2); } else if(keyboard.keyDown(Keyboard.RIGHT_KEY) == true) { this.x += 1; setCurrFrame(3); } else horizontalMoveKeyPressed = false; if (!horizontalMoveKeyPressed) { if(keyboard.keyDown(Keyboard.UP_KEY) == true) { this.y -= 1; setCurrFrame(0); } else if(keyboard.keyDown(Keyboard.DOWN_KEY) == true) { this.y += 1; setCurrFrame(1); } } } } /*----------------------------------------------------------------------------------------------*/ public class Cenario { private Window window; private Keyboard keyboard; private Scene scene; private Player player; public Cenario() { window = new Window(800,600); keyboard = window.getKeyboard(); scene = new Scene(); scene.loadFromFile("scene.scn"); scene.setDrawStartPos(15, 30); player = new Player(); player.x = 454; player.y = 140; scene.addOverlay(player); } public void run() { boolean loop = true; while(loop) { draw(); controlarCaminho(); player.move(keyboard); if (keyboard.keyDown(Keyboard.ESCAPE_KEY)) loop = false; window.delay(10); } window.exit(); } public void draw() { scene.draw(); window.update(); } boolean controlarCaminho() { //Posicao min é a posição (x,y) do player //Posicao max é a posição (x + largura, y + altura) do player Point playerMin = new Point ((int)player.x, (int)player.y); Point playerMax = new Point((int)(player.x + player.width), (int)(player.y + player.height)); //Retorna as imagens que estiverem na mesma área do player (x,y), (x + largura, y + altura) Vector tiles = scene.getTilesFromRect(playerMin, playerMax); //para todos os tiles for(int i = 0 ; i < tiles.size() ; i++) { TileInfo tile = (TileInfo)tiles.elementAt(i); //se o tile é parede e o player colidiu com ele if((tile.id == Constante.TILE_WALL) && player.collided(tile)) { //se o player está se movendo na diagonal if (player.moveuNaHorizontal()) { //o player está atrás da parede? if(player.x <= tile.x -1) player.x = tile.x - player.width;//Reposiciona o player else //o player está na frente da parede player.x = tile.x + tile.width;//Reposiciona o player } else { //o player está abaixo da parede? if(player.y >= tile.y + tile.height -1) player.y = tile.y + tile.height;//Reposiciona o player else //o player está acima da parede player.y = tile.y - player.height ;//Reposiciona o player } return false; } else //se o player econtrou a estrela if(tile.id == Constante.TILE_EXIT) return true; } return false; } /** * @param args the command line arguments */ public static void main(String[] args) { Cenario cen = new Cenario(); cen.run(); } }
8 - Todos os métodos da classe Scene.
//Construtor da classe public Scene(); //Carrega as informações armazenadas no arquivo '.scn'. public void loadFromFile(String sceneFile); //Adiciona qualquer GameObject do Jplay ao cenário mas, não modifica a //matriz que é usada para a construção do cenário. public void addOverlay(GameObject overlay); //Informa ao Jplay quais são as coordenadas iniciais onde o cenário deverá ser desenhado inicialmente. public void setDrawStartPos(int drawStartX, int drawStartY); //Desenha o cenário na tela public void draw(); //Retorna um tile da matrix de tiles especificado pela sua linha e coluna na matrix, //caso a linha ou a coluna não existam retorn null. public TileInfo getTile(int row, int colunm); //Retorna os objetos que estão entre os ponto passados como parâmetros. public Vector getTilesFromRect(Point min, Point max); //Retorna os objetos que estão entre os ponto passados como parâmetros //(deve ser usado em substituição à função anterior, em jogos do tipo side scrolling). public Vector getTilesFromPosition(Point min, Point max); //Remove um tile da matrix de tiles especificado pela sua linha e coluna na matrix. public void removeTile(int row, int colunm); //Troca a ID do tile especificado pela sua linha e coluna pela ID de um outro tile. public void changeTile(int row, int colunm, int newID); //Salva o estado corrente da Scene em arquivo para posterior recuperação, os overlays não serão salvos. public void saveToFile(String fileName); //Move o cenario de forma a centralizar o object passado como parâmetro. public void moveScene(GameObject object); //Retorna a última movimentação do cenário no eixo x. public double getXOffset(); //Retorna a última movimentação do cenário no eixo y. public double getYOffset();
UFF - Universidade Federal Fluminense - Institudo de Computação - Ciência da Computação