Olá, pessoal, desculpem-me por ressuscitar esse tópico, mas é que também me envolvi nesse negócio de programar jogos em Java.
Poxa vida, Vini, eu que colocava minhas fichas em jogos desenvolvidos em Java, agora leio esse seu último post e fico perplexo !
Bem, depois de estudar os conceitos básicos e alguns avançados em Java, comecei a estudar desenvolvimento de jogos em Java.
Minha intenção é utilizar as técnicas de programação de jogos para programar simulações em física (sou profeesor de física!).
Deu uma pesquisada aqui no fórum e baixei o Killer Gaming Programing in java, mas confesso que fiquei bastante confuso com o livro, não me dou muito bem com o Inglês técnico, apesar de me comunicar bem em Inglês.
Resolvi então comprar um livro em Portugês de jogos em Java, comprei o Programação de Games com Java, editora Cengage Learning, autor Jonathan S. Harbour e confesso que gostei da didática do livro. a obra começa com o bem básico mesmo, o que eu quero! No capítulo 3 o autor já coloca o código de um joguinho bem simples baseado no jogo Asteroids, do Atari. Escrevi o código inteiro, ele deve rodar em uma applet, mas não obtive sucesso, há muitos erros, NullExceptionPointer e outros erros que não consigo resolver por causa de inexperiência com as técnicas de programação de jogos.
Se alguém pudesse me ajudar, ficaria muito grato.
Bem, o jogo é composto de 5 classes: BaseVectorShape (que é a classe mãe), Asteroid (que controla os asteroides), Bullet (que controla as balas), Ship (que controla a nave), e Asteroids (que é a applet que roda o jogo no navegador).
Os códigos das classes estão logo abaixo:
Classe BaseVectorShape:
package galactWarPackage;
import java.awt.Shape;
/*********************************************************************
* Base vector shape class for polygonal shapes
* @author valder
*********************************************************************/
public class BaseVectorShape
{
// Variáveis
private Shape shape;
private boolean alive;
private double x,y;
private double velX, velY;
private double moveAngle, faceAngle;
// Métodos
public Shape getShape()
{
return shape;
}
public boolean isAlive()
{
return alive;
}
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public double getVelX()
{
return velX;
}
public double getVelY()
{
return velY;
}
public double getMoveAngle()
{
return moveAngle;
}
public double getFaceAngle()
{
return faceAngle;
}
// Mutador and helper methods
public void setShape(Shape shape)
{
this.shape = shape;
}
public void setAlive(boolean alive)
{
this.alive = alive;
}
public void setX(double x)
{
this.x = x;
}
public void incX(double i)
{
this.x += i;
}
public void setY(double y)
{
this.y = y;
}
public void incY(double i)
{
this.y += i;
}
public void setVelX(double velX)
{
this.velX = velX;
}
public void incVelX(double i)
{
this.velX += i;
}
public void setVelY(double velY)
{
this.velY = velY;
}
public void incVelY(double i)
{
this.velY += i;
}
public void setFaceAngle(double angle)
{
this.faceAngle = angle;
}
public void incFaceAngle(double i)
{
this.faceAngle += i;
}
public void setMoveAngle(double angle)
{
this.moveAngle = angle;
}
public void incMoveAngle(double i)
{
this.moveAngle += i;
}
// Default constructor
BaseVectorShape()
{
setShape(null);
setAlive(false);
setX(0.0);
setY(0.0);
setVelX(0.0);
setVelY(0.0);
setMoveAngle(0.0);
setFaceAngle(0.0);
}
}
Classe Ship:
package galactWarPackage;
import java.awt.Polygon;
import java.awt.Rectangle;
/**************************************************************
* Ship class - Polygonal shape of the player's ship
* @author valder
**************************************************************/
public class Ship extends BaseVectorShape
{
// Define the ship polygon
private int[] shipx = {-6,-3,0,3,6,0};
private int[] shipy = {6,7,7,7,6,-7};
// Bounding Rectangle
public Rectangle getBounds()
{
Rectangle r;
r = new Rectangle((int)getX()-6, (int)getY()-6,12,12);
return r;
}
// Default Constructor
Ship()
{
setShape(new Polygon(shipx, shipy, shipx.length));
setAlive(true);
}
}
Classe Asteroid:
package galactWarPackage;
import java.awt.Polygon;
import java.awt.Rectangle;
/***********************************************************************
* Asteroid class - for polygonal asteroid shapes
* @author valder
***********************************************************************/
public class Asteroid extends BaseVectorShape
{
// Define the asteroid polygon shape
private int[] astx = {-20,-13,0,20,22,20,12,2,-10,-22,-16};
private int[] asty = {20,23,17,20,16,-20,-22,-14,-17,-20,-5};
// Rotation Speed
protected double rotVel;
public double getRotationVelocity()
{
return rotVel;
}
public void setRotationVelocity(double v)
{
rotVel = v;
}
//Bounding Rectangle
public Rectangle getBounds()
{
Rectangle r;
r = new Rectangle((int)getX()-20,(int)getY()-20, 40, 40);
return r;
}
// Default Constructor
Asteroid()
{
setShape(new Polygon(astx,asty,astx.length));
setAlive(true);
setRotationVelocity(0.0);
}
}
Classe Bullet:
package galactWarPackage;
import java.awt.*;
import java.awt.Rectangle;
/************************************************************
* Bullet class - Polygonal shape of a bullet
* @author valder
************************************************************/
public class Bullet extends BaseVectorShape
{
public Rectangle getBounds()
{
Rectangle r;
r = new Rectangle((int)getX(), (int)getY(),1,1);
return r;
}
// Default Constructor
Bullet()
{
// Create the bullet shape
setShape(new Rectangle(0,0,1,1));
setAlive(false);
}
}
E a classe Asteroids
package galactWarPackage;
/********************************************************************
* Chapter 3 - Asteroids Game
********************************************************************/
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
/********************************************************************
* Primary class for the game
********************************************************************/
public class Asteroids extends Applet implements Runnable, KeyListener
{
// The main thread becomes the game loop
Thread gameloop;
// Use this as a double buffer
BufferedImage backbuffer;
// The main drawing object for the back buffer
Graphics2D g2d;
// Toggle for drawing bounding boxes
boolean showbounds = false;
// Create the asteroid array
int ASTEROIDS = 20;
Asteroid[] ast = new Asteroid[ASTEROIDS];
// Create the bullet array
int BULLETS = 10;
Bullet[] bullet = new Bullet[BULLETS];
int currentBullet = 0;
// The player's ship
Ship ship = new Ship();
// Create a indentity transform (0,0)
AffineTransform identity = new AffineTransform();
// Create a random number generator
Random rand = new Random();
/********************************************************************
* Applet init event
********************************************************************/
public void init()
{
// Create the back buffer for smooth graphics
backbuffer = new BufferedImage(640,480,BufferedImage.TYPE_INT_RGB);
g2d = backbuffer.createGraphics();
//Set up the ship
ship.setX(320);
ship.setY(240);
// Set up the bullets
for(int n = 0; n < ASTEROIDS; n++)
{
ast[n] = new Asteroid();
ast[n].setRotationVelocity(rand.nextInt(3)+1);
ast[n].setX((double)rand.nextInt(600)+20);
ast[n].setY((double)rand.nextInt(440)+20);
ast[n].setMoveAngle(rand.nextInt(360));
double ang = ast[n].getMoveAngle()-90;
ast[n].setVelX(calcAngleMoveX(ang));
ast[n].setVelY(calcAngleMoveY(ang));
}
// Start the user input listener
addKeyListener(this);
}
/********************************************************************
* Applet update event to redraw the screen
********************************************************************/
public void update(Graphics g)
{
// Start off transforms at identity
g2d.setTransform(identity);
// Erase the background
g2d.setPaint(Color.BLACK);
g2d.fillRect(0, 0, getSize().width, getSize().height);
// Print some status information
g2d.setColor(Color.WHITE);
g2d.drawString("Ship:" + "," + Math.round(ship.getX())+ "," + Math.round(ship.getY()),5, 10);
g2d.drawString("Move angle: " + Math.round(ship.getMoveAngle())+90,5,25);
g2d.drawString("Face angle: " + Math.round(ship.getFaceAngle()),5,40);
// Draw the game graphics
drawShip();
drawBullets();
drawAsteroids();
// Repaint the applet window
paint(g);
}
/********************************************************************
* drawShip called by applet update event
********************************************************************/
public void drawShip()
{
g2d.setTransform(identity);
g2d.translate(ship.getX(),ship.getY());
g2d.rotate(Math.toRadians(ship.getFaceAngle()));
g2d.setColor(Color.ORANGE);
g2d.fill(ship.getShape());
}
/********************************************************************
* drawBullets called by applet update event
********************************************************************/
public void drawBullets()
{
// Iterate through the array of bullets
for(int n = 0; n < BULLETS; n++)
{
// is the bullet currently in use?
if(bullet[n].isAlive())
{
// Draw the bullet
g2d.setTransform(identity);
g2d.translate(bullet[n].getX(), bullet[n].getY());
g2d.setColor(Color.MAGENTA);
g2d.draw(bullet[n].getShape());
}
}
}
/********************************************************************
* drawAsteroids called by applet update event
********************************************************************/
public void drawAsteroids()
{
// Iterate through asteroids array
for(int n = 0; n < ASTEROIDS; n++)
{
// Is this asteroid being used?
if(ast[n].isAlive())
{
// Draw the asteroid
g2d.setTransform(identity);
g2d.translate(ast[n].getX(), ast[n].getY());
g2d.rotate(Math.toRadians(ast[n].getMoveAngle()));
g2d.setColor(Color.DARK_GRAY);
g2d.fill(ast[n].getShape());
}
}
}
/********************************************************************
* Applet window repaint event -- draw the back buffer
********************************************************************/
public void paint(Graphics g)
{
// Draw the back buffer onto the applet window
g.drawImage(backbuffer,0,0,this);
}
/********************************************************************
* Thread start event - start the game loop running
********************************************************************/
public void start()
{
// Create the gameloop thread for real - time update
gameloop = new Thread(this);
gameloop.start();
}
/********************************************************************
* Thread run event (game loop)
********************************************************************/
public void run()
{
// Acquire the current thread
Thread t = Thread.currentThread();
// Keep going as long as the thread is alive
while(t == gameloop)
{
try
{
// update the game loop
gameUpdate();
// Target framerate is 50 fps
Thread.sleep(20);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
}
/********************************************************************
* Thread stop event
********************************************************************/
public void stop()
{
// Kill the gameloop thread
gameloop = null;
}
/********************************************************************
* Move and animate the objects in the game
********************************************************************/
public void gameUpdate()
{
updateShip();
updateBullets();
updateAsteroids();
checkCollisions();
}
/********************************************************************
* Update the ship position based on velocity
********************************************************************/
public void updateShip()
{
// Update ship's X positions
ship.incX(ship.getVelX());
// Wrap around left/right
if(ship.getX()<-10)
ship.setX(getSize().width+10);
else if(ship.getX()>getSize().width+10)
ship.setX(-10);
//Update ship's Y position
ship.incY(ship.getVelY());
// Wrap around top/bottom
if(ship.getY()<-10)
ship.setY(getSize().height+10);
else if(ship.getY()>getSize().height+10)
ship.setY(-10);
}
/********************************************************************
* Update the bullets based on velocity
********************************************************************/
public void updateBullets()
{
// Move each of the bullets
for(int n = 0;n<BULLETS;n++)
{
// Is the bullet being used?
if(bullet[n].isAlive())
{
// Update bullet's x position
bullet[n].incX(bullet[n].getVelX());
// Bullet disappears at left/right edge
if(bullet[n].getX()<0||bullet[n].getX()>getSize().width)
bullet[n].setAlive(false);
// Update bullet's y position
bullet[n].incY(bullet[n].getVelY());
// Bullet disappears at top/bottom edge
if(bullet[n].getY()<0||bullet[n].getY()>getSize().height)
bullet[n].setAlive(false);
}
}
}
/********************************************************************
* Update the asteroids based on velocity
********************************************************************/
public void updateAsteroids()
{
// Move and rotate the asteroids
for(int n = 0; n<ASTEROIDS; n++)
{
// Is the asteroid being used?
if(ast[n].isAlive())
{
// Update the asteroid's X value
ast[n].incX(ast[n].getVelX());
// Warp the asteroid at screen edge
if(ast[n].getX()<20)
ast[n].setX(getSize().width+20);
else if(ast[n].getX()>getSize().width+20)
ast[n].setX(-20);
// Update the asteroid's Y value
ast[n].incY(ast[n].getVelY());
// Warp the asteroid at the screen edge
if(ast[n].getY()<-20)
ast[n].setY(getSize().height+20);
else if(ast[n].getY()>getSize().height+20)
ast[n].setY(-20);
// Update the asteroid's rotation
ast[n].incMoveAngle(ast[n].getRotationVelocity());
// Keep the angle within 0-359 degrees
if(ast[n].getMoveAngle()<0)
ast[n].setMoveAngle(360-ast[n].getRotationVelocity());
else if(ast[n].getMoveAngle()>360)
ast[n].setMoveAngle(ast[n].getRotationVelocity());
}
}
}
/********************************************************************
* Test asteroids for collisions with ship or bullets
********************************************************************/
public void checkCollisions()
{
// Iterate through the asteroids array
for(int m=0; m<ASTEROIDS; m++)
{
// Is this asteroid being used?
if(ast[m].isAlive())
{
/*
* Check for collision with bullet
*/
for(int n=0;n<BULLETS; n++)
{
// Is this bullet being used?
if(bullet[n].isAlive())
{
// Perform the collision test
if(ast[m].getBounds().contains(bullet[n].getX(), bullet[n].getY()))
{
bullet[n].setAlive(false);
ast[m].setAlive(false);
continue;
}
}
}
/*
* Check for collision with ship
*/
if(ast[m].getBounds().intersects(ship.getBounds()))
{
ast[m].setAlive(false);
ship.setX(320);
ship.setY(240);
ship.setFaceAngle(0);
ship.setVelX(0);
ship.setVelY(0);
continue;
}
}
}
}
/********************************************************************
* Key listener events
********************************************************************/
public void keyReleased(KeyEvent k){}
public void keyTyped(KeyEvent k){}
public void keyPressed(KeyEvent k)
{
int keyCode = k.getKeyCode();
switch(keyCode)
{
case KeyEvent.VK_LEFT:
// left arrow rotates ship left 5 degrees
ship.incFaceAngle(-5);
if(ship.getFaceAngle()<0)
ship.setFaceAngle(360-5);
break;
case KeyEvent.VK_RIGHT:
// Right arrow rotates ship 5 degrees
ship.incFaceAngle(5);
if(ship.getFaceAngle()>360)
ship.setFaceAngle(5);
break;
case KeyEvent.VK_UP:
// Up arrow add thrust to ship (1/10 normal speed)
ship.setMoveAngle(ship.getFaceAngle()-90);
ship.incVelX(calcAngleMoveX(ship.getMoveAngle())*0.1);
ship.incVelY(calcAngleMoveY(ship.getMoveAngle())*0.1);
break;
// Ctrl, Enter, or Space can be used to fire weapon
case KeyEvent.VK_CONTROL:
case KeyEvent.VK_ENTER:
case KeyEvent.VK_SPACE:
// Fire a bullet
currentBullet++;
if(currentBullet>BULLETS-1)
currentBullet = 0;
bullet[currentBullet].setAlive(true);
// Point bullet in same direction ship is facing
bullet[currentBullet].setX(ship.getX());
bullet[currentBullet].setY(ship.getY());
bullet[currentBullet].setMoveAngle(ship.getFaceAngle()-90);
// Fire bullet at angle of the ship
double angle = bullet[currentBullet].getMoveAngle();
double svx = ship.getVelX();
double svy = ship.getVelY();
bullet[currentBullet].setVelX(svx + calcAngleMoveX(angle)*2);
bullet[currentBullet].setVelY(svy + calcAngleMoveY(angle)*2);
break;
}
}
/********************************************************************
* Calculate X movement value based on direction angle
********************************************************************/
public double calcAngleMoveX(double angle)
{
return (double)(Math.cos(angle*Math.PI/180));
}
/********************************************************************
* Calculate Y movement value based on direction angle
********************************************************************/
public double calcAngleMoveY(double angle)
{
return (double)(Math.sin(angle*Math.PI/180));
}
}
Quando executo a applet, após criar o devido arquivo HTML, aparece um problema, quendo clico para ver a mensagem aparece a seguinte mensagem:
Detected from bootclasspath: C:\\PROGRA~1\\Java\\jre7\\lib\\deploy.jar
Plug-in Java 10.0.0.147
Usando versão JRE 1.7.0-b147 Java HotSpot(TM) Client VM
Diretório home do usuário = C:\Users\valder
----------------------------------------------------
c: limpar janela da console
f: concluir objetos da fila de finalização
g: coleta de lixo
h: exibir esta mensagem de ajuda
l: descartar lista de carregadores de classes
m: imprimir uso de memória
o: log do trigger
q: ocultar console
r: recarregar configuração da política
s: descartar propriedades do sistema e de implantação
t: descartar lista de threads
v: descartar pilha de threads
x: limpar cache do carregador de classes
0-5: definir nível de rastreamento como <n>
----------------------------------------------------
Daí resolvi rodar a aplicação dentro do Eclipse para ver o que dava, pois o Eclipse aponta melhor os erros. Rodei como um aplicativo Applet. A Applet é inicializada, é aberta uma tela preta pequena dizendo que a applet foi iniciada, mas aparece os erros abaixo:
Exception in thread "Thread-3" java.lang.NullPointerException
at galactWarPackage.Asteroids.updateBullets(Asteroids.java:271)
at galactWarPackage.Asteroids.gameUpdate(Asteroids.java:231)
at galactWarPackage.Asteroids.run(Asteroids.java:200)
at java.lang.Thread.run(Unknown Source)
Quando posiciono o ponteiro do mouse na tela da Applet e simpesmente teclo enter, aparecem os erros a seguir:
Exception in thread "Thread-3" java.lang.NullPointerException
at galactWarPackage.Asteroids.updateBullets(Asteroids.java:271)
at galactWarPackage.Asteroids.gameUpdate(Asteroids.java:231)
at galactWarPackage.Asteroids.run(Asteroids.java:200)
at java.lang.Thread.run(Unknown Source)
Exception in thread "AWT-EventQueue-1" java.lang.NullPointerException
at galactWarPackage.Asteroids.keyPressed(Asteroids.java:422)
at java.awt.Component.processKeyEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)