Hi, I’m new. Looks like a great community. I was considering posting this in the newbies/problems section but I get the idea this one might be more suitable. Would appreciate a move if it’s wrong.
So, I’m looking to create a relatively basic game in Java2D. I know, I’ve looked into Slick/Pulp a bit and it may make it easier, but I want to try Java2D and my own classes first. Some may call it a waste of time, I see it more as an academic persuit. If I max out Java2D’s capabilities, then great I’ve understood it, but I don’t aim on getting near that point anyway.
At first I was using a basic buffered image as a back buffer and using “passive” rendering, and a very basic game loop. I then read up a bit on active rendering techniques (including buffer strategies, which I didn’t even realise could be used in applets!) and using a more sophisicated way of controlling game speed. Before, the inaccuracies of the timing method became more and more pronounced as you wanted the game to speed up. There is a danger of it becoming totally out of sync with the rendering at high speeds.
This forum has been a great help, especially Gudradain and ra4king with the loop/rendering (I hope you don’t mind ;)).
I’d be grateful for any feedback/criticism of this basic functionality before I get deeper into the game’s development and find I may have to go back and alter a lot of stuff (though I’m sure this will inevitably happen anyway…). I’ve just commented on some stuff to help me try to understand what’s going on. Maybe it’ll be useful for other newbies as well!
package games;
import java.applet.Applet;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class GameApplet extends Applet implements Runnable, MouseListener{
//<editor-fold defaultstate="collapsed" desc="class declarations">
static int width = 800;
static int height = 600;
Canvas canvas;
BufferStrategy bufferStrategy;
boolean running = true;
//game loop control
long desiredFPS = 60;
long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
Thread gameloop;
ImageEntity background; //a class that loads images for use
int frameCount = 0;
int frameRate = 0;
long startTime = System.currentTimeMillis();
//</editor-fold>
@Override
public void init() {
background = new ImageEntity(this); //uses external class as an image loader
background.load("bg.jpg");
System.out.println("loaded background"); //testing
if (bufferStrategy == null) {
setPreferredSize(new Dimension(width, height));
this.setSize(new Dimension(width, height));
this.setIgnoreRepaint(true); //ensure os does not interfere in painting
canvas = new Canvas();
canvas.setBounds(0, 0, width, height);
canvas.setIgnoreRepaint(true);
add(canvas);
canvas.createBufferStrategy(2); //create front+back buffer
bufferStrategy = canvas.getBufferStrategy(); //link the canvas to that strategy
canvas.requestFocus();
}
}
@Override
public void start() {
gameloop = new Thread(this);
gameloop.start();
running = true;
}
@Override
public void run() {
// thread run event (game loop)
long beginLoopTime;
long endLoopTime;
long currentUpdateTime = System.nanoTime();
long lastUpdateTime;
long deltaLoop;
while (!isActive()) {
Thread.yield();//wait on the thread if applet is not active/visible
}
while (running) {
beginLoopTime = System.nanoTime(); //time before render
render(); //draw objects onto the canvas/buffer
lastUpdateTime = currentUpdateTime;
currentUpdateTime = System.nanoTime();
update((int) ((currentUpdateTime - lastUpdateTime) / (1000 * 1000))); //update the game logic with this speed
endLoopTime = System.nanoTime();
deltaLoop = endLoopTime - beginLoopTime;
//I know this may be inaccurate due to Milliseconds, but it's good enough for me for now
//<editor-fold defaultstate="collapsed" desc="fps calculation">
frameCount++;
if (System.currentTimeMillis() > startTime + 1000) {//if a second passes
startTime = System.currentTimeMillis();//update to this second
frameRate = frameCount;
System.out.println(frameRate);
frameCount = 0;
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="synchronisation">
if (deltaLoop > desiredDeltaLoop) {
//Do nothing. We are already late.
} else {
try {
Thread.sleep((desiredDeltaLoop - deltaLoop) / (1000 * 1000));
} catch (InterruptedException e) {
}
}
if (!isActive()) {
System.out.println("Inactive"); //testing
return;
}
//</editor-fold>
}
}
@Override
public void stop() {
running = false;//is this really necessary or would the thread cease on its own if running is already false elsewhere?
//volatile?
}
public void render() {
try {
do {
do {
Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics(); //creates a g2d object, takes graphics context from strategy
render(g); //draw
g.dispose(); //manually free up resources
} while (bufferStrategy.contentsRestored()); //buffer contents restored since being lost
bufferStrategy.show();
} while (bufferStrategy.contentsLost()); //has the buffer been restored?
} catch (Exception exc) {
exc.printStackTrace();
}
}
public void update(int deltaTime){
//update game logic
}
protected void render(Graphics2D g) {
g.drawImage(background.getImage(), 0, 0, this); //returns an image and draws it onto the context
}
@Override
public void mouseClicked(MouseEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void mousePressed(MouseEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void mouseReleased(MouseEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void mouseEntered(MouseEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void mouseExited(MouseEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
}