
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