public class Explosion extends VisibleObject {
static DrawAnim _drawProto;
protected DrawAnim _drawer;
// number of frames in the explosion animation
public static final int count = 16;
protected int _next;
/** create an explosion at position (x,y) */
public Explosion(int nx, int ny) throws IOException {
initDrawer();
x=nx;
y=ny;
}
/** create explosion at the center of "exploding" */
public Explosion(Rectangle exploding) throws IOException {
initDrawer();
x=exploding.x+(exploding.width-width)/2;
y=exploding.y+(exploding.width-width)/2;
}
protected void initDrawer() throws IOException {
Explosion.preload();
try {
_drawer = (DrawAnim) _drawProto.clone();
} catch (CloneNotSupportedException oops) {
// this should never happen
throw new RuntimeException("Bug in Explosion",oops);
}
_next = 0;
width = _drawer.getWidth();
height = _drawer.getHeight();
}
/** the explosion does not move **/
public void move() { };
/** draw the next frame; _alive set to false at the end **/
public void draw(Graphics g) {
if (_drawer.getLoopCount()>0) {
_alive = false;
return;
}
_drawer.draw(g,x,y);
}
/** load the explosion pictures that will be used in all instances
* of Explosion
*/
protected static synchronized void preload() throws IOException {
if (null!=_drawProto) return;
Vector imageNames = new Vector();
for (int i=0; i
The heart of this code is in preload() and in draw(). In preload(), we create the list of picture files (imageNames), and then create _drawProto with the list of images. The third argument (true) indicates that we want the background color to be transparent (the code will use the color of the top-left pixel of the image as the background color). In draw(), we just delegate drawing to the _drawer. We use the getLoopCount() method to determine whether we've gone through all of the pictures in the explosion sequence. If we have, we set _alive to false so that the animation will go away.
The other thing that's going on here is that we have a single static instance for the DrawAnim (_drawProto) and we make a clone every time we need a new Explosion (_drawer). Because of the way clone() is written, we end up keeping only one copy of the pictures used in the animation, but each _drawer object keeps its own image counter. This means that each animation will proceed independently of the others while at the same time only using the minimal necessary amount of memory.
The next step is to modify the main loop of the program to make room for explosions. We create a list of VisibleObject for the explosions.
Collection _animations;
We call move() even though we don't have to. This is so that we don't have bad surprises if we create moving animations later, and also so animations that are not _alive are properly removed.
public void playLevel() {
(...)
// move both kinds of bullets, plus animations
for (Iterator<VisibleObject> it = new UnionIterator<VisibleObject>(_bullets,_enemyBullets,_animations);it.hasNext();) {
VisibleObject b = it.next();
b.move();
if (!(b._alive)) it.remove();
}
(...)
}
We call draw() in the main drawing routine.
/** draw what the player sees **/
public void drawWorld() {
if (null==_dbImage) return;
synchronized(_dbImage) {
(...)
for (VisibleObject e : _animations) {
e.draw(_dbg);
}
(...)
}
}
And then, of course, we add the explosion when we kill an enemy. The only tricky part here is that we have to handle the case where the explosion graphics are missing. The proper way to do this would be to call Explosion.preload() before starting the actual game, but here we cheat and just do nothing at all if we can't load the file.
public void playLevel() {
(...)
Pair<List<VisibleObject>> collision;
collision = com.jpemartin.jgt.CollisionDetection.findCollisionListSlow(_bullets,_enemies);
if (null!=collision) {
for (VisibleObject bullet : collision._a) {
bullet._alive = false;
}
for (VisibleObject enemy : collision._b) {
Enemy3 e = (Enemy3)enemy;
if (e.hitKills()) {
enemy._alive=false;
try {
_animations.add( new Explosion(e) );
} catch (IOException e1) {
// no explosion for you because (probably)
// we couldn't load the picture files.
e1.printStackTrace();
}
}
}
} // of collision
(...)
}
And there you go! It really is that simple.
Next: 11 - Formation Flying