import java.awt.*; import java.util.Vector; import java.lang.Thread; /** A SpriteWorld is the onscreen Component in which the sprites are drawn, and the object responsible for tracking their movements and collisions. In practice, you create a SpriteWorld, create sprites and add them to the SpriteWorld, then start the SpriteWorld. */ public class SpriteWorld extends Canvas implements Runnable { public Dimension size; protected Vector spriteList; private HardlySprite scratchSprite, otherScratchSprite; protected Thread paintThread; protected long timeSlice, lastSlice; protected CollisionHandler collider; private int scratchInt; protected Color bg; protected Image db; public Graphics dbg; //change back to protected!!!!!!! public final String getInfo() { return ("(c) 1997 Chris Adamson, plucky@mindspring.com, http://www.mindspring.com/~plucky/"); } /** Creates a new SpriteWorld. @param collider the CollisionHandler object (ie, the object that implements the two collision() methods) to be used when sprites in this world collide. */ public SpriteWorld(CollisionHandler collider) { spriteList = new Vector (50); this.collider = collider; } /** Calls parent class' resize method, then notes new size and creates double-buffer. @param d The re-sized Dimension (hey, it's Sun's routine... blame them) */ public void resize (Dimension d) { super.resize (d); this.size = this.size(); makeNewDoubleBuffer(); } // overridden resize -- just makes sure we note our new size /** Calls parent class' resize method, then notes new size and creates double-buffer. @param width The re-sized width @param height The re-sized height */ public void resize (int width, int height) { super.resize (width, height); this.size = this.size(); makeNewDoubleBuffer(); } // overridden resize -- just makes sure we note our new size /** Creates a new double-buffer for flicker-free animation */ private void makeNewDoubleBuffer() { if (db != null) { db = null; } // guess we resized - kill old buffer if (dbg != null) { dbg = null; } // ditto db = createImage (size.width, size.height); dbg = db.getGraphics(); bg=this.getBackground(); dbg.setColor (bg); dbg.fillRect (0, 0, size.width, size.height); } /** Adds a sprite to the SpriteWorld. @param sprite The HardlySprite to be added */ public void add (HardlySprite sprite) { spriteList.addElement (sprite); } /** Removes a sprite from the SpriteWorld. @param sprite The HardlySprite to be removed */ public void remove (HardlySprite sprite) { sprite.unpaintLast(dbg, bg); sprite.unpaintCurrent (dbg, bg); // kludge, but we don't know if we're being called from updatePosition or outside spriteList.removeElement (sprite); } public final int getWidth () { return size.width; } // getX public final int getHeight () { return size.height; } // getY /** Implements start() from Runnable interface. Creates a new double buffer, then creates an animation thread and runs it. Usually called after you've added your first set of sprites to the SpriteWorld. You must add the SpriteWorld to your applet's layout and validate it before you call start(), or the world may have zero width and height and bad things will ensue. */ public void start () { this.size = this.size(); // kludgy, but needs to be AFTER validation! makeNewDoubleBuffer(); if (paintThread == null) { paintThread = new Thread(this); lastSlice = System.currentTimeMillis(); paintThread.start(); } } // start /** Implements stop() from Runnable interface. Stops animation thread if one exists */ public void stop () { if (paintThread != null) { paintThread.stop(); paintThread = null; } } // stop /** Implements run() from Runnable interface. Loops through each sprite, testing first for wall collision, then collisions with other sprites, calling CollisionHandler's collision() methods in each case. May only detect one sprite-sprite collision, if collision() returns false, indicating a sprite has been moved or deleted as a result of the collision. */ public final void run () { int counter1, counter2; boolean spriteSurvives; while (true) { for (counter1=0; counter1 < spriteList.size(); counter1++) { // test for collisions and call CollisionHandler // NOTE!! THIS IS THE VERSION THAT TESTS FOR SPRITE TOTALLY OFFSCREEN (NOT JUST TOUCHING EDGE) scratchSprite = (HardlySprite) spriteList.elementAt(counter1); if (scratchSprite.getX() > size.width) { collider.collision (scratchSprite, CollisionHandler.EAST); } if (scratchSprite.getX() < 0 - scratchSprite.getWidth()) { collider.collision (scratchSprite, CollisionHandler.WEST); } if (scratchSprite.getY() > size.height) { collider.collision (scratchSprite, CollisionHandler.SOUTH); } if (scratchSprite.getY() < 0 - scratchSprite.getHeight()) { collider.collision (scratchSprite, CollisionHandler.NORTH); } for (counter2 = counter1 + 1; counter2 < spriteList.size(); counter2++) { otherScratchSprite = (HardlySprite) spriteList.elementAt(counter2); if (scratchSprite.hotBox.intersects (otherScratchSprite.hotBox)) { spriteSurvives = collider.collision (scratchSprite, otherScratchSprite); if (! spriteSurvives) { break; }; } /* if collision */ } /* for */ } /* for */ repaint(); paintThread.yield(); } // while } // run /** Overrides Component.Canvas.update(g). Calls superclass' update() if the double-buffer background isn't available (not a pretty thought). Otherwise calls overrider paint(). */ public final void update (Graphics g) { if (dbg == null) { super.update(g); } else { paint (g); } } /** Overrides Component.Canvas.paint(g). Updates the position of each sprite, unpaints it, then paints all the sprites. Double-buffered for your sanitary pleasure. */ public final void paint (Graphics g) { boolean spriteAlive; // int listSize = spriteList.size(); // need to put this in loop header and recalc each time, since multi-sprites may die and thus change list size if (dbg==null) { super.paint(g); } else { for (int counter=0; counter < spriteList.size(); counter++) { scratchSprite = (HardlySprite) spriteList.elementAt(counter); spriteAlive = scratchSprite.updatePosition(); if (spriteAlive) { scratchSprite.unpaintLast(dbg, bg); } } // for // listSize = spriteList.size(); // need to get it again in case sprites died first time through for (int counter=0; counter < spriteList.size(); counter++) { scratchSprite = (HardlySprite) spriteList.elementAt(counter); scratchSprite.paint(dbg); } // for g.drawImage (db, 0, 0, this); } // else } // paint /** Erases the entire Canvas. */ public final void clearScreen() { dbg.fillRect(0,0,size.width,size.height); }//clearScreen protected void removeAll () { int last = spriteList.size(); for (int counter=0; counter < last; counter++) { scratchSprite = (HardlySprite) spriteList.elementAt(counter); scratchSprite.unpaintCurrent(dbg, bg); } spriteList.removeAllElements(); }//removeAll }