Created
November 21, 2012 01:42
-
-
Save iiAtlas/4122531 to your computer and use it in GitHub Desktop.
Simple Double-Buffered, Delta Based Canvas [Java]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.atlas.fps; | |
import java.awt.Canvas; | |
import java.awt.Color; | |
import java.awt.Graphics; | |
import java.awt.image.BufferStrategy; | |
import javax.swing.JFrame; | |
public class AtlasFPSLimiter extends Canvas implements Runnable { | |
private JFrame frame; | |
private final int WIDTH = 640, HEIGHT = 480; | |
private int x = 20, y = 20, fps, tps; | |
private boolean running = false; | |
public AtlasFPSLimiter() { | |
frame = new JFrame("FPS: ~ TPS: ~"); | |
frame.setSize(WIDTH, HEIGHT); | |
frame.setResizable(false); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
setBackground(Color.black); | |
setForeground(Color.white); | |
frame.add(this); | |
frame.setVisible(true); | |
start(); | |
} | |
private void start() { | |
if(!running) { | |
running = true; | |
new Thread(this).start(); | |
} | |
} | |
private void stop() { | |
running = false; | |
} | |
private void preDraw() { //Method which prepares the screen for drawing | |
BufferStrategy bs = getBufferStrategy(); //Gets the buffer strategy our canvas is currently using | |
if(bs == null) { //True if our canvas has no buffer strategy (should only happen once when we first start the game) | |
createBufferStrategy(2); //Create a buffer strategy using two buffers (double buffer the canvas) | |
return; //Break out of the preDraw method instead of continuing on, this way we have to check again if bs == null instead of just assuming createBufferStrategy(2) worked | |
} | |
Graphics g = bs.getDrawGraphics(); //Get the graphics from our buffer strategy (which is connected to our canvas) | |
g.setColor(getBackground()); | |
g.fillRect(0, 0, WIDTH, HEIGHT); //Fill the screen with the canvas' background color | |
g.setColor(getForeground()); | |
draw(g); //Call our draw method, passing in the graphics object which we just got from our buffer strategy | |
g.dispose(); //Dispose of our graphics object because it is no longer needed, and unnecessarily taking up memory | |
bs.show(); //Show the buffer strategy, flip it if necessary (make back buffer the visible buffer and vice versa) | |
} | |
private void draw(Graphics g) { | |
g.fillRect(x, y, 25, 25); | |
} | |
private void tick(double delta) { | |
x += 1 * delta; //Multiply all movements by delta where the number before is your speed, this forces any movements to scale based on time passed since the last tick | |
y += 1 * delta; | |
} | |
@Override | |
public void run() { | |
int desiredTPS = 60; //Target ticks per second | |
long lastTime = System.currentTimeMillis(); //Time since we last looped (tick + draw), initialized here to the current time | |
long secondTime = lastTime + 1000; //Target time one second ahead of when we last updated fps/tps | |
double msPerTick = 1000 / desiredTPS; //Milliseconds expected in a single tick | |
int frames = 0, ticks = 0; //Used for counting frames and ticks while in between seconds, later used to set fps and tps | |
double delta = 0; //Represents the time passed since last tick | |
boolean needsRender = false; //True when the screen is dirty (when we have ticked) | |
while(running) { | |
long currentTime = System.currentTimeMillis(); //The time when we began our game loop | |
long timeDifference = currentTime - lastTime; //The difference in time since the last game loop (expressed as a negative value | |
lastTime = currentTime; //Reassign lastTime since we have used it, the last time is now the current time | |
delta += timeDifference / msPerTick; //A representation of the time passed, converted into a more manageable format | |
while(delta >= 1) { //Tick as many times as delta is greater then one | |
ticks++; //Increase temporary ticks variable, later used to set tps | |
tick(delta); //Call the tick method, pass it delta so it can alter any movement accordingly | |
delta--; //Decrease delta because we have used it | |
needsRender = true; //Informs us that the screen is dirty, must be rendered | |
} | |
if(needsRender) { | |
frames++; //Increase temporary frames variable, later used to set fps | |
preDraw(); //Draw the screen (or at least call the method which draws the screen) | |
} | |
if(System.currentTimeMillis() >= secondTime) { //True when the current time is equal to (or greater than) one second since we last updated fps/tps | |
fps = frames; //set fps to the frames variable which has been increasing over the last second | |
tps = ticks; //set tps to the frames variable which has been increasing over the last second | |
frames = 0; //reset frame count | |
ticks = 0; //reset tick count | |
frame.setTitle("FPS: " + fps + " TPS: " + tps); | |
secondTime = System.currentTimeMillis() + 1000; //Set the time which we must again update fps/tps (one second from the current time) | |
} | |
try { Thread.sleep(10); } catch(Exception e) { e.printStackTrace(); } //Attempt to sleep the thread for 10ms, not necessarry to run it nonstop even though our game will behave okay | |
} | |
} | |
public static void main(String[] args) { | |
new AtlasFPSLimiter(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@iiAtlas I think it is necessary reset the flag needsRender to false after a iteration.