Space Shooter Applet
This applet uses much of the code from the application on the previous page, but you will find some of the code has moved into a different class. In order to take advantage of the inbuilt image-handling capabilities of an applet, we load all three images using the Applet class. We need image tracking code to ensure that the images are loaded before we obtain their dimensions. We added setWidth and setHeight methods to set the dimensions of the GameObject.
Whereas a JFrame allowed us to setDoubleBuffered in a single statement, in the applet we follow the method of our own tutorial page for writing to a back buffer. When the time is right to display the next image, we copy the prepared image to the applet.
We use a Thread instead of a Timer. (A Thread is regarded to perform better and we had sample code to adapt from our tutorial). The html file used to demonstrate the applet has a parameter for the delay that the applet reads. You can try changing the value for an easier or harder game.
The command line instruction jar ufv space_shooter_applet.jar alien.png craft.png missile.png added the three image files to the jar file. You can try using the applet and images in the jar file using this html page.
The code of RunSpaceShooterApplet.html
<html> <head> <title>SpaceShooter Applet</title> </head> <body> <center> <h3>SpaceShooter Applet</h2> <p>Java must be enabled.</p> <applet archive="space_shooter_applet.jar" code="space_shooter_applet/SpaceShooterApplet.class" width="400" height="300" > <param name="Delay" value="6"> </applet> </center> </body> </html>
The Code of SpaceShooterApplet.pas
namespace space_shooter_applet; interface uses java.awt.*, java.util.*, javax.swing.*, java.applet.*; type GameObject = public class(object) //Base class for craft, missiles and aliens protected //Available to derived classes x, y, width, height : Integer; visible : Boolean := true; public method getX : Integer; method getY : Integer; method setWidth(w : Integer); method setHeight(h : Integer); method setVisible(vis : Boolean); method isVisible : Boolean; method getBounds : Rectangle; end; Craft = public class(GameObject) private missiles : ArrayList; dx, dy : Integer; //Change in x and y public constructor; method move; method getMissiles : ArrayList; method keyPressed(e : KeyEvent); method keyReleased(e : KeyEvent); method fire; end; SpaceShooterApplet = public class(Applet, Runnable, KeyListener) private backbuffer : Image; backg : Graphics; t : Thread; threadSuspended : Boolean; craft : Craft; aliens : ArrayList; ingame : Boolean; pos : array [0 .. 26] of array [0 .. 1] of Integer := [ [2380, 29], [2500, 59], [1380, 89], [780, 109], [580, 139], [680, 239], [790, 259], [760, 50], [790, 150], [980, 209], [560, 45], [510, 70], [930, 159], [590, 80], [530, 60], [940, 59], [990, 30], [920, 200], [900, 259], [660, 50], [540, 90], [810, 220], [860, 20], [740, 180], [820, 128], [490, 170], [700, 30] ]; //Starting coordinates (see initAliens) boardWidth, boardHeight, alienWidth, alienHeight, missileWidth, missileHeight, craftWidth, craftHeight, delay : Integer; imgAlien, imgCraft, imgMissile : Image; public method init; override; method destroy; override; method start; override; method stop; override; method run; method initAliens; method paint(g : graphics); override; method update(g : graphics); override; method checkCollisions; method keyPressed(e : KeyEvent); method keyReleased(e : KeyEvent); method keyTyped(e : KeyEvent); end; Alien = public class(GameObject) public constructor (xpos, ypos : Integer); method move; end; Missile = public class(GameObject) private const MISSILE_SPEED = 2; public constructor(xpos, ypos : Integer); method move; end; implementation method GameObject.getX : Integer; begin result := x; end; method GameObject.getY : Integer; begin result := y; end; method GameObject.setWidth(w : Integer); begin width := w; end; method GameObject.setHeight(h : Integer); begin height := h; end; method GameObject.setVisible(vis : Boolean); begin visible := vis; end; method GameObject.isVisible : Boolean; begin result := visible; end; method GameObject.getBounds : Rectangle; begin result := new Rectangle(x, y, width, height); end; constructor Craft; begin missiles := new ArrayList; x := 40; y := 60; end; method Craft.move; begin x := x + dx; y := y + dy; if x < 1 then x := 1; if y < 1 then y := 1; end; method Craft.getMissiles : ArrayList; begin result := missiles; end; method Craft.keyPressed(e : KeyEvent); var key : Integer; begin key := e.getKeyCode; case key of KeyEvent.VK_SPACE : fire; KeyEvent.VK_LEFT : dx := -1; KeyEvent.VK_RIGHT : dx := 1; KeyEvent.VK_UP : dy := -1; KeyEvent.VK_DOWN : dy := 1; end; end; method Craft.fire; begin missiles.add(new Missile(x + width, y + height / 2)); end; method Craft.keyReleased(e : KeyEvent); var key : Integer; begin key := e.getKeyCode; case key of KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT : dx := 0; KeyEvent.VK_UP, KeyEvent.VK_DOWN : dy := 0; end; end; method SpaceShooterApplet.init; var mt : MediaTracker; begin delay := Integer.valueOf(getParameter('Delay')); //Param from HTML boardWidth := getSize.width; boardHeight := getSize.height; mt := new MediaTracker(self); backbuffer := createImage(boardWidth, boardHeight); backg := backbuffer.getGraphics; backg.setColor(Color.BLACK); imgCraft := getImage(getDocumentBase, 'craft.png'); imgAlien := getImage(getDocumentBase, 'alien.png'); imgMissile := getImage(getDocumentBase, 'missile.png'); mt.addImage(imgCraft, 0); mt.addImage(imgAlien, 1); mt.addImage(imgMissile, 2); try mt.waitForAll; except on e : Exception do e.printStackTrace; end; while not mt.checkAll(true) do begin end; //Three images loaded craftWidth := imgCraft.getWidth(self); craftHeight := imgCraft.getHeight(self); alienWidth := imgAlien.getWidth(self); alienHeight := imgAlien.getHeight(self); missileWidth := imgMissile.getWidth(self); missileHeight := imgMissile.getHeight(self); ingame := true; craft := new Craft; craft.setWidth(craftWidth); craft.setHeight(craftHeight); addKeyListener(self); setFocusable(true); initAliens; end; //Executed when the applet is destroyed method SpaceShooterApplet.destroy; begin end; //Executed after the applet is created and on return of browser method SpaceShooterApplet.start; begin if (t = nil) then begin t := new Thread(self); threadSuspended := false; t.start; end else begin if threadSuspended then begin threadSuspended := false; locking self do begin notify; end; end; end; end; //Executed whenever the browser leaves the page containing the applet method SpaceShooterApplet.stop; begin threadSuspended := true; end; //Executed within the thread that this applet created method SpaceShooterApplet.run; var ms : ArrayList; i : Integer; m : Missile; a : Alien; msg : String; small : Font; metr : FontMetrics; begin try while (true) do begin backg.setColor(Color.BLACK); backg.fillRect(0, 0, boardWidth, boardHeight); i := 0; if aliens.size = 0 then ingame := false; ms := craft.getMissiles; while i < ms.size do begin m := Missile(ms.get(i)); //Cast from item in ArrayList to Missile if m.isVisible then begin m.move; backg.drawImage(imgMissile, m.getX, m.getY, self); end else ms.remove(i); inc(i); end; i := 0; while i < aliens.size do begin a := Alien(aliens.get(i)); if a.isVisible then begin a.move; backg.drawImage(imgAlien, a.getX, a.getY, self); end else aliens.remove(i); inc(i); end; craft.move; backg.drawImage(imgCraft, craft.getX, craft.getY, self); checkCollisions; if ingame then begin backg.setColor(Color.WHITE); backg.drawString('Aliens left: ' + aliens.size, 5, 15); end else begin msg := 'Game Over'; small := new Font('Helvetica', Font.BOLD, 14); metr := self.getFontMetrics(small); backg.setColor(Color.black); backg.fillRect(0, 0, boardWidth, boardHeight); backg.setColor(Color.white); backg.setFont(small); //Output 'Game Over' near centre of the window. backg.drawString(msg, (boardWidth - metr.stringWidth(msg)) / 2, boardHeight / 2); end; repaint; //Thread checks to see if it should suspend itself if threadSuspended then begin locking self do begin while threadSuspended do begin wait; end; end; end; repaint; t.sleep(delay); //Interval given in milliseconds end; except on e : InterruptedException do begin end; end; end; method SpaceShooterApplet.initAliens; var i : Integer; begin aliens := new ArrayList; for i := 0 to length(pos) - 1 do aliens.add(new Alien(pos[i][0], pos[i][1])); end; method SpaceShooterApplet.paint(g : Graphics); begin update(g); end; method SpaceShooterApplet.checkCollisions; var r1, r2, r3 : Rectangle; i, j : Integer; ms : ArrayList; m : Missile; a : Alien; begin r3 := craft.getBounds; for j := 0 to aliens.size - 1 do begin a := Alien(aliens.get(j)); //Cast from item in ArrayList to Alien r2 := new Rectangle(a.getX, a.getY, alienWidth, alienHeight); if r3.intersects(r2) then begin craft.setVisible(false); a.setVisible(false); ingame := false; end; end; ms := craft.getMissiles; for i := 0 to ms.size - 1 do begin m := Missile(ms.get(i)); //Cast from item in ArrayList to Missile r1 := new Rectangle(m.getX, m.getY, missileWidth, missileHeight); for j := 0 to aliens.size - 1 do begin a := Alien(aliens.get(j)); //Cast from item in ArrayList to Alien r2 := new Rectangle(a.getX, a.getY, alienWidth, alienHeight); if r1.intersects(r2) then begin m.setVisible(false); a.setVisible(false); end; end; end; end; method SpaceShooterApplet.update(g : Graphics); begin g.drawImage(backbuffer, 0, 0, self); end; method SpaceShooterApplet.keyReleased(e : KeyEvent); begin craft.keyReleased(e); end; method SpaceShooterApplet.keyPressed(e : KeyEvent); begin craft.keyPressed(e); end; method SpaceShooterApplet.keyTyped(e : KeyEvent); //Must provide implementation even if empty begin end; constructor Alien(xpos, ypos : Integer); begin x := xpos; y := ypos; end; method Alien.move; begin if x < 0 then x := 400; dec(x); end; constructor Missile(xpos, ypos : Integer); begin x := xpos; y := ypos; end; method Missile.move; begin x := x + MISSILE_SPEED; if x > 400 then visible := false; end; end.