import java.awt.Canvas; import java.awt.Graphics2D; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import java.awt.event.MouseListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelListener; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferStrategy; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; public abstract class Scene extends JPanel implements Runnable, KeyListener, MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener { protected final long DESIRED_FPS = 60; protected final long DESIRED_DELTA_LOOP = (1000*1000*1000)/DESIRED_FPS; protected boolean running; protected boolean paused; protected Object pauseLock; protected BufferStrategy buffer; protected Dimension canvasSize; protected Canvas canvas; // TODO: make accessible from user settings protected int guiSize = 2; protected Scene() { this.canvasSize = GameWindow.WINDOW_SIZE; this.canvas = new Canvas(); this.canvas.setBounds(0, 0, this.canvasSize.width, this.canvasSize.height); this.canvas.setIgnoreRepaint(true); this.canvas.addKeyListener(this); this.canvas.addMouseListener(this); this.canvas.addMouseMotionListener(this); this.canvas.addMouseWheelListener(this); } private void initCanvasBuffer() { this.canvas.createBufferStrategy(2); this.buffer = this.canvas.getBufferStrategy(); this.canvas.requestFocus(); } public void run() { long beginLoopTime; long endLoopTime; long currentUpdateTime = System.nanoTime(); long lastUpdateTime; long deltaLoop; this.initCanvasBuffer(); this.running = true; this.paused = false; pauseLock = new Object(); while (running) { beginLoopTime = System.nanoTime(); synchronized (this.pauseLock) { if (this.paused) { try { this.pauseLock.wait(); } catch (InterruptedException ex) { ex.printStackTrace(); break; } } } this.render(); lastUpdateTime = currentUpdateTime; currentUpdateTime = System.nanoTime(); this.update(currentUpdateTime - lastUpdateTime); endLoopTime = System.nanoTime(); deltaLoop = endLoopTime - beginLoopTime; if (deltaLoop > this.DESIRED_DELTA_LOOP) { // TODO: late => skip frame } else { try { Thread.sleep((this.DESIRED_DELTA_LOOP - deltaLoop)/(1000*1000)); } catch (InterruptedException e ) { } } } } public synchronized void stop() { this.running = false; // stop thread even if paused this.resume(); } public synchronized void pause() { this.paused = true; } public synchronized void resume() { if (!paused) return; this.paused = false; this.pauseLock.notifyAll(); } // automagically set the canvas size to the parent's size public synchronized void updateCanvasSize() { this.canvasSize = this.getParent().getSize(); } public synchronized void setCanvasSize(Dimension newSize) { this.canvasSize = newSize; } protected abstract void render(); protected abstract void update(long deltaNanoTime); @Override public void keyTyped(KeyEvent e) {} public void keyPressed(KeyEvent e) {} public void keyReleased(KeyEvent e) {} @Override public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseDragged(MouseEvent e) {} public void mouseMoved(MouseEvent e) {} public void mouseWheelMoved(MouseWheelEvent e) {} @Override public void componentResized(ComponentEvent e) { this.updateCanvasSize(); } public void componentMoved(ComponentEvent e) {} public void componentShown(ComponentEvent e) {} public void componentHidden(ComponentEvent e) {} }