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.

Programming - a skill for life!

How to write games in Oxygene for Java