package gjt.animation; import java.awt.*; import java.util.Enumeration; import java.util.Vector; import gjt.Util; /** * A surface upon which Sprites are animated. Playfields are * responsible for animating the sprites.

* * Each Playfield comes complete with two collision detectors: * an edge collision detector and a sprite collision detector. * * Playfield is an abstract class: extensions must implement * the following methods: *

*
void paintBackground(Graphics) *
void void spriteCollision(Sprite sprite, Sprite other) *
void void edgeCollision (Sprite sprite, Sprite other) *
* * @version 1.0, Apr 1 1996 * @author David Geary * @see CollisionArena * @see Sprite * @see SpriteCollisionDetector * @see EdgeCollisionDetector * @see gjt.test.SimpleAnimationTest * @see gjt.test.BumpAnimationTest * @see gjt.test.TwoDrinkersAnimationTest */ public abstract class Playfield extends Canvas implements Runnable, CollisionArena { protected Vector sprites = new Vector(); private boolean running = false; private Insets insets = new Insets(0,0,0,0); private Thread animationThread; private Image bgoffscreen, workplaceBuffer; private Dimension offscreenSize; private EdgeCollisionDetector edgeCollisionDetector; private SpriteCollisionDetector spriteCollisionDetector; abstract public void paintBackground(Graphics g); public Playfield() { edgeCollisionDetector = new EdgeCollisionDetector(this); spriteCollisionDetector = new SpriteCollisionDetector(this); } public void stop () { running = false; } public boolean running () { return running; } public Dimension getSize () { return size(); } public Insets getInsets () { return insets; } public Vector getSprites() { return sprites; } public void addSprite(Sprite sprite) { sprites.addElement(sprite); } public void setInsets(Insets insets) { this.insets = insets; } public void start() { animationThread = new Thread(this); running = true; animationThread.start(); } public void paint(Graphics g) { if(needNewOffscreenBuffer()) { workplaceBuffer = createOffscreenImage(size()); bgoffscreen = createOffscreenImage(size()); paintBackground(bgoffscreen.getGraphics()); } g.drawImage(bgoffscreen, 0, 0, this); paintSprites(); } public void reshape(int x, int y, int w, int h) { super.reshape(x,y,w,h); repaint(); } public void run() { while(running == true) { edgeCollisionDetector.detectCollisions (); spriteCollisionDetector.detectCollisions(); animateSprites(); Thread.currentThread().yield(); } animationThread = null; } private boolean needNewOffscreenBuffer() { return (workplaceBuffer == null || bgoffscreen == null || size().width != offscreenSize.width || size().height != offscreenSize.height); } private Image createOffscreenImage(Dimension size) { Image image = createImage(size.width, size.height); Util.waitForImage(this, image); offscreenSize = size; return image; } protected void animateSprites() { Sprite nextSprite; Enumeration e = sprites.elements(); while(e.hasMoreElements()) { nextSprite = (Sprite)e.nextElement(); nextSprite.animate(); } } protected void paintSprites() { Sprite nextSprite; Enumeration e = sprites.elements(); while(e.hasMoreElements()) { nextSprite = (Sprite)e.nextElement(); paintSprite(nextSprite); } } protected void paintSprite(Sprite sprite) { Graphics g = getGraphics(); Graphics wpg = workplaceBuffer.getGraphics(); Rectangle clip = sprite.clipRect(); wpg.clipRect(clip.x, clip.y, clip.width, clip.height); wpg.drawImage(bgoffscreen, 0, 0, this); sprite.paint(wpg); g.clipRect (clip.x, clip.y, clip.width, clip.height); g.drawImage(workplaceBuffer, 0, 0, this); g.dispose(); wpg.dispose(); } }