All pastes #912558 Raw Edit

grom358

public java v1 · immutable
·published 2008-02-21 13:38 UTC
rendered paste body
import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.RenderingHints;import java.awt.Toolkit;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import javax.swing.JComponent;import java.text.DecimalFormat;import java.util.List;import java.util.ArrayList;import java.util.LinkedList;/** * @author grom */public class Animation extends JComponent implements Runnable {	private static final long serialVersionUID = 9214955127889918259L;	/**	 * Number of frames with a delay of 0 ms before the animation thread yields	 * to other running threads.	 */	private static final int NO_DELAYS_PER_YIELD = 16;	/**	 * Number of frames that can be skipped in any one animation loop. i.e the games	 * state is updated but not rendered	 */	private static final int MAX_FRAME_SKIPS = 5;	private Ball[] balls;	private long period; // The amount of time in nanoseconds allocated to each frame	public Animation(int width, int height, long period) {		setOpaque(true);		setBackground(Color.WHITE);		setSize(width, height);		setPreferredSize(new Dimension(width, height));		this.period = period;		List<Ball> ballList = new ArrayList<Ball>();		ballList.add(new Ball(this, 10, 10, 0.5, 0.2, Color.BLUE));		ballList.add(new Ball(this, 30, 30, 0.3, 0.1, Color.GREEN));		ballList.add(new Ball(this, 150, 150, -0.2, -0.3, Color.RED));		ballList.add(new Ball(this, 300, 440, -0.3, -0.4, Color.CYAN));		ballList.add(new Ball(this, 600, 10, 0.3, 0.3, Color.GRAY));		ballList.add(new Ball(this, 10, 460, 0.1, 0.2, Color.MAGENTA));		ballList.add(new Ball(this, 50, 50, 0.3, 0.2, Color.ORANGE));		ballList.add(new Ball(this, 620, 30, -0.4, -0.2, Color.YELLOW));		ballList.add(new Ball(this, 10, 330, 0.2, 0.3, Color.PINK));		balls = (Ball[]) ballList.toArray(new Ball[0]);		initStats();		setFocusable(true);		requestFocus();		readyForTermination();	}		 private void readyForTermination() {		addKeyListener(new KeyAdapter() {			// listen for esc, q, end, ctrl-c on the canvas to			// allow a convenient exit from the full screen configuration			public void keyPressed(KeyEvent e) {				int keyCode = e.getKeyCode();				if ((keyCode == KeyEvent.VK_ESCAPE)						|| (keyCode == KeyEvent.VK_Q)						|| (keyCode == KeyEvent.VK_END)						|| ((keyCode == KeyEvent.VK_C) && e.isControlDown())) {					isRunning = false;				}			}		});	}	 	private Thread animator; // Thread for performing the animation	 	public void start() {		// initialise and start the thread		if (animator == null || !isRunning) {			animator = new Thread(this);			animator.start();		}	}	private volatile boolean isPaused = false;	private volatile boolean isRunning = false;	public void pause() {		isPaused = true;	}	public void resume() {		isPaused = false;	}	public void stop() {		isRunning = false;	}	public void run() {		long beforeTime, afterTime, timeDiff, sleepTime, lastRunTime, delta;		long overSleepTime = 0L;		int noDelays = 0;		long excess = 0L;		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);		isRunning = true;		gameStartTime = System.nanoTime();		prevStatsTime = gameStartTime;		beforeTime = gameStartTime;		lastRunTime = gameStartTime;		while (isRunning) {			delta = System.nanoTime() - lastRunTime; // How long since we last execute the main game loop			lastRunTime = System.nanoTime();			nextFrame(delta/1000000L);			updateDisplay();			afterTime = System.nanoTime();			timeDiff = afterTime - beforeTime;			sleepTime = (period - timeDiff) - overSleepTime;			if (sleepTime > 0) {				if (sleepTime > 1000000L) { // Don't try to sleep for any less then 1ms					try {						Thread.sleep(sleepTime / 1000000L /* nanosecs -> ms */);					} catch (InterruptedException ex) {						// do nothing					}					overSleepTime = (System.nanoTime() - afterTime) - sleepTime;				} else {					overSleepTime = 0L;				}			} else { // sleepTime <= 0; the frame took longer than the period				excess -= sleepTime;  // store excess time value				overSleepTime = 0L;				if (++noDelays >= NO_DELAYS_PER_YIELD) {					Thread.yield();   // give another thread a chance to run					noDelays = 0;				}			}			beforeTime = System.nanoTime();			/*			 * If frame animation is taking too long, update the game state			 * without rendering it, to get the updates/sec nearer to the			 * required FPS.			 */			int skips = 0;			while ((excess > period) && (skips < MAX_FRAME_SKIPS)) {				excess -= period;				nextFrame(period/1000000L); // update state but don't render				skips++;			}			framesSkipped += skips;			updateStats();		}		cleanup();	}		public void cleanup() {		isRunning = false;		System.exit(0);	}	/**	 * Creates the next frame	 */	public void nextFrame(double t) {		if (isPaused) return;		long startTime = System.nanoTime();		double rt = t; // remaining frame time		boolean isCollision = false;		do {			isCollision = false;			// t is the time of the earliest collision.			// if t == rt then there are either no collisions			// or collision occured at time rt			// Since only the earliest collision needs to be found,			// the collision test are performed for the current			// time of the earliest collision (t).			// Test for wall collisions			for (int i = 0; i < balls.length; i++) {				t = Math.min(t, balls[i].testWallCollision(t));			}			// Test for ball collisions			// collision are added to the head of this list			List<Collision> collisions = new LinkedList<Collision>();			Ball a, b;			for (int i = 0; i < balls.length; i++) {				a = balls[i];				for (int j = i+1; j < balls.length; j++) {					b = balls[j];					t = Math.min(t, a.testBallCollision(b, t, collisions));				}			}			// Move the balls			for (int i = 0; i < balls.length; i++) {				balls[i].move(t);			}			// Handle the collisions that occured			// NOTE: It is possible that two collision occurred at the			// same time. Therefore the collision list is iterated			// until we get to a collision that occured after the			// time of the first collision			for (Collision c : collisions) {				if (c.t == t) {					c.handle();					isCollision = true;				} else {					break;				}			}			// calculate frame time renaming			t = rt - t;			rt = t;		// continue updating frame if there was a collision and the frame period has not expired		} while(isCollision && rt > 0 && System.nanoTime() - startTime < period);	}	private void renderFrame(Graphics2D g2d) {		// clear screen in background		g2d.setColor(getBackground());		g2d.fillRect(0, 0, this.getWidth(), this.getHeight());		g2d.setRenderingHint(			RenderingHints.KEY_ANTIALIASING,			RenderingHints.VALUE_ANTIALIAS_ON);		for (int i = 0; i < balls.length; i++) {			balls[i].paint(g2d);		}		drawStats(g2d);	}	// off screen rendering	private Graphics dbg;	private Image dbImage = null;	private void updateDisplay() {	    if (dbImage == null) {	    	// Create buffer image			dbImage = createImage(getWidth(), getHeight());			if (dbImage == null) {				System.out.println("dbImage is null");				return;			} else {				dbg = dbImage.getGraphics();			}		}	    // Render frame to off-screen buffer	    renderFrame((Graphics2D) dbg);		// Use active rendering	    Graphics g;		try {			g = this.getGraphics();			if ((g != null) && (dbImage != null)) {				g.drawImage(dbImage, 0, 0, null);				Toolkit.getDefaultToolkit().sync();  // sync the display on some systems				g.dispose();			}		} catch (Exception e) {			System.out.println("Graphics context error: " + e);		}	}	// used for gathering statistics	private static final long MAX_STATS_INTERVAL = 1000000000L; // record (roughly) every 1 second	private static final int NUM_FPS = 10; // number of FPS values stored to get an average	private long statsInterval = 0L;    // in ns	private long prevStatsTime;	private long totalElapsedTime = 0L;	private long gameStartTime;	private long frameCount = 0;	private double fpsStore[];	private long statsCount = 0;	private double averageFPS = 0.0;	private long framesSkipped = 0L;	private long totalFramesSkipped = 0L;	private double upsStore[];	private double averageUPS = 0.0;	private void initStats() {		fpsStore = new double[NUM_FPS];		upsStore = new double[NUM_FPS];		for (int i = 0; i < NUM_FPS; i++) {			fpsStore[i] = 0.0;			upsStore[i] = 0.0;		}	}	/**	 * The statistics: - the summed periods for all the iterations in this	 * interval (period is the amount of time a single frame iteration should	 * take), the actual elapsed time in this interval, the error between these	 * two numbers; - the total frame count, which is the total number of calls	 * to run(); - the frames skipped in this interval, the total number of	 * frames skipped. A frame skip is a game update without a corresponding	 * render; - the FPS (frames/sec) and UPS (updates/sec) for this interval,	 * the average FPS & UPS over the last NUM_FPSs intervals.	 *	 * The data is collected every MAX_STATS_INTERVAL (1 sec).	 */	private void updateStats() {		frameCount++;		statsInterval += period;		if (statsInterval >= MAX_STATS_INTERVAL) { // record stats every			// MAX_STATS_INTERVAL			long timeNow = System.nanoTime();			long realElapsedTime = timeNow - prevStatsTime; // time since last			// stats collection			totalElapsedTime += realElapsedTime;			totalFramesSkipped += framesSkipped;			double actualFPS = 0; // calculate the latest FPS and UPS			double actualUPS = 0;			if (totalElapsedTime > 0) {				actualFPS = (((double) frameCount / totalElapsedTime) * 1000000000L);				actualUPS = (((double) (frameCount + totalFramesSkipped) / totalElapsedTime) * 1000000000L);			}			// store the latest FPS and UPS			fpsStore[(int) statsCount % NUM_FPS] = actualFPS;			upsStore[(int) statsCount % NUM_FPS] = actualUPS;			statsCount = statsCount + 1;			double totalFPS = 0.0; // total the stored FPSs and UPSs			double totalUPS = 0.0;			for (int i = 0; i < NUM_FPS; i++) {				totalFPS += fpsStore[i];				totalUPS += upsStore[i];			}			if (statsCount < NUM_FPS) { // obtain the average FPS and UPS				averageFPS = totalFPS / statsCount;				averageUPS = totalUPS / statsCount;			} else {				averageFPS = totalFPS / NUM_FPS;				averageUPS = totalUPS / NUM_FPS;			}			framesSkipped = 0;			prevStatsTime = timeNow;			statsInterval = 0L; // reset		}	}	private DecimalFormat df = new DecimalFormat("0.##");	private void drawStats(Graphics g) {	    // report frame count & average FPS and UPS at top left		g.setColor(Color.GRAY);		g.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", "				+ df.format(averageUPS), 20, 25);		g.drawString("Frames skipped: " + totalFramesSkipped, 20, 50);	}	// Overrides for performance	public boolean isOpaque() {		return true;	}	public void paint(Graphics g) {	}	public void paintComponent(Graphics g) {	}	public void paintImmediately(int x, int y, int w, int h) {	}	public void paintImmediately(java.awt.Rectangle r) {	}	public void validate() {	}	public void revalidate() {	}	public void repaint(long tm, int x, int y, int width, int height) {	}	public void repaint(java.awt.Rectangle r) {	}	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {	}	public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {	}	public void updateUI() {	}}