Follow up from General Game Loop
I have two interfaces. Entity for ticks. Drawable for rendering.
Drawable.java
import java.awt.Graphics;
public interface Drawable
{
public void draw (Graphics g);
}
Entity.java
public interface Entity
{
public void tick ();
public void second ();
}
I have a class to initialize the window that everything will be displayed to.
Window.java
import java.awt.Dimension;
import javax.swing.JFrame;
public class Window
{
public Window (int width, int height, String title, Game game) {
game.setPreferredSize(new Dimension (width, height));
game.setMaximumSize(new Dimension (width, height));
game.setMinimumSize(new Dimension (width, height));
JFrame frame = new JFrame (title);
frame.add (game);
frame.pack ();
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setResizable (false);
frame.setLocationRelativeTo (null);
frame.setVisible (true);
}
}
Then there is the main component.
Game.java
import java.util.ArrayList;
import java.util.List;
import Logging.*;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
public class Game extends Canvas implements Runnable {
/**
* Run, Tick, Render, Entities and Drawables
*/
private boolean isRunning = true;
private Thread thread;
private static final long NANOSECOND = 1000000000;
private static final double OPTIMAL_TICKS = 50.0;
private static final double OPTIMAL_TIME = NANOSECOND / OPTIMAL_TICKS;
private long lastLoopTime = System.nanoTime ();
private long currentTime;
private double deltaTime;
private long secondTimer = System.currentTimeMillis ();
private final List<Entity> entities = new ArrayList<> ();
private final List<Drawable> drawables = new ArrayList<> ();
/**
* Window Constants
*/
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final String TITLE = "Test Game";
/**
* Logging
*/
private final FPSViewer fpsViewer = new FPSViewer ();
// there are more here, stripped for this review
/**
* Data Contaienrs
*/
// there are more here, stripped for this review
/**
* Main Function, instantiating Game
*/
public static void main() {
new Game ();
}
/**
* Game constructor, initailized by main ()
*/
public Game () {
// fps tracking
entities.add (fpsViewer);
drawables.add (fpsViewer);
Window window = new Window (WIDTH, HEIGHT, TITLE, this);
thread = new Thread (this);
thread.start ();
}
/**
* Game Loop
*/
public void run () {
while (isRunning) {
// get delta time
currentTime = System.nanoTime ();
deltaTime += (currentTime - lastLoopTime) / OPTIMAL_TIME;
lastLoopTime = currentTime;
while (deltaTime >= 1) {
update ();
deltaTime--;
}
// update the game
render ();
// reset if a second has passed
if (System.currentTimeMillis () - secondTimer > 1000) {
updatePerSecond ();
secondTimer += 1000;
}
}
}
/**
* Game Updates for all entities
*/
private void update () {
for (Entity e : entities) {
e.tick ();
}
}
private void updatePerSecond () {
for (Entity e : entities) {
e.second ();
}
}
/**
* Renders all graphics for all drawables
*/
private void render () {
BufferStrategy bufferstrategy = getBufferStrategy ();
if (bufferstrategy == null) {
createBufferStrategy (3);
return;
}
Graphics g = bufferstrategy.getDrawGraphics();
g.setColor (Color.white);
g.fillRect (0, 0, getWidth (), getHeight ());
for (Drawable d : drawables) {
d.draw (g);
}
g.dispose ();
bufferstrategy.show ();
}
}
And finally, to test that everything is working.
FPSViewer.java
import java.awt.Color;
import java.awt.Graphics;
public class FPSViewer implements Drawable, Entity {
// executions and renderings per second
private int ticks;
private int frames;
private int averageTicks;
private int averageFrames;
/**
* From interface Drawable
*/
public void draw (Graphics g) {
frames++;
g.setColor(Color.black);
g.drawString("FPS: " + averageFrames + " Ticks: " + averageTicks, 680, 16);
}
/**
* From interface Entitiy
*/
public void tick () {
ticks ++;
}
public void second () {
averageFrames = frames / 60;
averageTicks = ticks;
frames = 0;
ticks = 0;
}
}
How will the actual game work?
- The game will consist mostly of 2D Buttons.
- Mouse and keyboard input.
- 99% of all events will directly be by the player. If the player goes away, the game stops, in most scenarios. (This made me remove the pause part from last iteration.)
- Turn-based combat later on.
Now to the feedback wanted: Everything! Be merciless!
Update
private static final long NANOSECOND = 1000000000
toprivate static final long NANOS_IN_SECOND = 1000000000L
FPSViewer
renamed toFPSTracker
removeddivision by 60
for frames.