home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / javafile / ch09 / InvaderApp.java < prev   
Encoding:
Java Source  |  1998-12-14  |  11.6 KB  |  533 lines

  1.  
  2. import java.awt.*;
  3. import java.awt.event.*;
  4. import java.applet.Applet;
  5. import java.awt.image.*;
  6.  
  7. /**
  8.  * A class that describes a target "invader"
  9.  * This hold a single invader
  10.  */
  11. class Invader {
  12.  
  13. /*
  14.  * this invader's position and velocity
  15.  */
  16. double x, y, dx, dy;
  17.  
  18. /*
  19.  * this invader's missile position and velocity
  20.  * only one missile allowed per invader
  21.  */
  22. int mx, my, mdy; 
  23.  
  24. /*
  25.  * The images for an invader
  26.  */
  27. Image img[] = new Image[4];
  28.  
  29. /*
  30.  * inplay is true if this invader has not been killed
  31.  */
  32. boolean inplay;
  33.  
  34. /*
  35.  * fired is set to true when this invader fires a missile
  36.  */
  37. boolean fired = false;
  38.  
  39. /*
  40.  * state is used to cycle through the four images
  41.  * of this invader
  42.  */
  43. double state;
  44.  
  45. /*
  46.  * value is the score value; it depends on the speed
  47.  */
  48. int value;
  49.  
  50. /*
  51.  * Initialize position and speed for this invader
  52.  */
  53. void random (int speed, int w, int h) {
  54.  
  55.     x = 10 + (w-20)*Math.random ();
  56.     y = 10 + ((h>>1)-20)*Math.random ();
  57.     dx = (speed>>1) - speed*Math.random ();
  58.     dy = (speed>>1) - speed*Math.random ();
  59.     inplay = true;
  60.     state = 3 * Math.random ();
  61.     fired = false;
  62.     mdy = 20;
  63.     value = speed * 10; 
  64. }
  65.  
  66. /*
  67.  * Calculate new invader and missile position
  68.  * Also fires a missile at random
  69.  * @param w    panel width
  70.  * @param h    panel height
  71.  */
  72. void compute (int w, int h) {
  73.  
  74.     if (x <= 0 || x > w) dx = -dx;
  75.     if (y <= 0 || y > h>>1) dy = -dy;
  76.     if (my > h-20) fired = false;
  77.     if (fired) my += mdy;
  78.     else my = 0;
  79.     if (inplay && !fired && Math.random () > 0.99) {
  80.         fired = true;
  81.         mx = (int) x; my = (int) y+25;
  82.     }
  83.     x += dx; y += dy;
  84. }
  85.  
  86. /*
  87.  * paint invader and missile (if it has been fired)
  88.  * @param g - destination graphics object
  89.  * @param obs - imageobserver associated 
  90.  * with this graphics context
  91. */
  92. public void paint (Graphics g, ImageObserver obs) {
  93.  
  94.     int whichImage; 
  95.  
  96.     if (inplay) {
  97.         whichImage = (int) state;
  98.         g.drawImage (img[whichImage & 0x3], (int) x-25,
  99.             (int) y-25, obs);
  100.         state += .25;
  101.     }
  102.     if (fired) {
  103.         g.setColor (Color.green);
  104.         g.drawLine ((int) mx, (int) my, (int) mx, (int) my-10);
  105.     }
  106. }
  107.  
  108. /*
  109.  * Tests whether the player's missile has hit this invader
  110.  * Returns true if invader is hit
  111.  * @param pmx - player's missile x position
  112.  * @param pmy - player's missile y position
  113.  */
  114. boolean killer (int pmx, int pmy) {
  115.  
  116.     int deltaX, deltaY;
  117.  
  118.     if (!inplay) return false;
  119.         deltaX = (int) Math.abs (x-pmx);
  120.         deltaY = (int) Math.abs (y-pmy);
  121.         if (deltaX < 20 && deltaY < 20) {
  122.             inplay = false;
  123.             return true;
  124.         }
  125.         return false;
  126. }
  127. }
  128.  
  129. /**
  130.  * A class to describe the player, very similar to Invader
  131.  * except in reverse
  132.  */
  133. class Player {
  134.  
  135. /*
  136.  * position of the player
  137.  */
  138. int x, y=-100;
  139.  
  140. /*
  141.  * position of the player's missile
  142.  */
  143. int mx, my, mdy = -20;
  144.  
  145. /*
  146.  * two different player images
  147.  */
  148. Image img1, img2;
  149.  
  150. /*
  151.  * fired is true if player has fired a missile
  152.  * inplay is true if the game is not over
  153.  */
  154. boolean fired = false, inplay=true;
  155.  
  156. /*
  157.  * called when a player fires a missile
  158.  */
  159. void fire () {
  160.  
  161.     if (fired || !inplay) return;
  162.     mx = x; my = y;
  163.     fired = true;
  164. }
  165.  
  166. /*
  167.  * Calculate next missile position
  168.  */
  169. void compute () {
  170.  
  171.     if (my < 0) fired = false;
  172.     if (fired) my += mdy;
  173.     else my = y;
  174. }
  175.  
  176. /**
  177.  * Paint player and missile
  178.  * @param g - destination graphics object
  179.  * @param obs - image observer
  180.  */
  181. public void paint (Graphics g, ImageObserver obs) {
  182.  
  183.     if (fired) {
  184.         if (inplay) g.drawImage (img2, x-25, y, obs);
  185.         g.setColor (Color.white);
  186.         g.drawLine (mx, my, mx, my+10);
  187.     } else if (inplay) g.drawImage (img1, x-25, y, obs);
  188. }
  189.  
  190. /**
  191.  * Returns true if the player has been killed
  192.  * @param bmx, bmy - position of enemy missile
  193.  */
  194. boolean killer (int bmx, int bmy) {
  195.  
  196.     int dx, dy;
  197.  
  198.     if (!inplay) return false;
  199.     dx = (int) Math.abs (x-bmx);
  200.     dy = (int) Math.abs (y-bmy);
  201.     if (dx < 20 && dy < 20) {
  202.         return true;
  203.     }
  204.     return false;
  205. }
  206. }
  207.  
  208. /*
  209.  * much of the game logic is here
  210.  */
  211. class Playfield extends Panel implements Runnable, MouseListener, MouseMotionListener
  212. {
  213.  
  214. static final int PLAYER_HIT = 1;
  215. static final int INVADER_HIT = 2;
  216.  
  217. InvaderApp invaderApp;
  218.  
  219. /*
  220.  * the number of invaders in play
  221.  */
  222. int NInvaders=0;
  223.  
  224. /*
  225.  * the maximum number of invaders possible
  226.  */
  227. final int MaxInvaders = 32;
  228.  
  229. /*
  230.  * array of invaders
  231.  */
  232. Invader invaders[] = new Invader[MaxInvaders];
  233. Player player;
  234.  
  235. /*
  236.  * offscreen image for double-buffering
  237.  */
  238. Image offscreen;
  239.  
  240. /*
  241.  * dimension of offscreen graphics image
  242.  */
  243. Dimension psize;
  244.  
  245. /*
  246.  * graphics object associated with offscreen image
  247.  */
  248. Graphics offgraphics;
  249.  
  250. /*
  251.  * game action thread
  252.  */
  253. Thread theThread;
  254.  
  255. /*
  256.  * the playfield background color
  257.  */
  258. Color bgcolor = new Color (51, 0, 153);
  259. int score, playerLives, playLevel;
  260. Font font;
  261.  
  262. /**
  263.  * constructor saves instance of the applet
  264.  * @param invaderApp - instance of the applet
  265.  */
  266. public Playfield (InvaderApp invaderApp) {
  267.  
  268.     this.invaderApp = invaderApp;
  269.     addMouseListener(this);
  270.     addMouseMotionListener(this); 
  271.  
  272. }
  273.  
  274. /*
  275.  * game action thread
  276.  */
  277. public void run() {
  278.  
  279.     psize = getSize();
  280.     offscreen = createImage (psize.width, psize.height);
  281.     offgraphics = offscreen.getGraphics ();
  282.     font = new Font ("TimesRoman", Font.BOLD, 18);
  283.     offgraphics.setFont (font);
  284.  
  285.     while (true) {
  286.         compute ();
  287.         repaint ();
  288.         try {
  289.             Thread.sleep(25);
  290.         } catch (InterruptedException e) { }
  291.     }
  292. }
  293.  
  294. /*
  295.  * calculate new positions for all objects
  296.  */
  297. synchronized void compute () {
  298.  
  299.     for (int i=0; i<NInvaders; i+=1) {
  300.         invaders[i].compute (psize.width, psize.height);
  301.         if (invaders[i].killer (player.mx, player.my)) {
  302.             invaderApp.hit (INVADER_HIT);
  303.             player.fired = false;
  304.             score += invaders[i].value;
  305.         }
  306.         if (player.killer (invaders[i].mx, invaders[i].my)) {
  307.             invaderApp.hit (PLAYER_HIT);
  308.             invaders[i].fired = false;
  309.             playerLives -= 1;
  310.             if (playerLives < 1) player.inplay = false;
  311.         }
  312.     }
  313.     player.compute ();
  314. }
  315.  
  316. /**
  317.  * override default update
  318.  * draw into offscreen image and then copy it to the screen
  319.  * @param g - destination graphics object
  320.  */
  321. public synchronized void update(Graphics g) {
  322.  
  323.     offgraphics.setColor (bgcolor);
  324.     offgraphics.fillRect (0, 0, psize.width, psize.height);
  325.  
  326.     for (int i = 0 ; i < NInvaders ; i++)
  327.         if (invaders[i].inplay) invaders[i].paint (offgraphics, this);
  328. player.paint (offgraphics, this);
  329.  
  330.     offgraphics.setColor (Color.green);
  331.     offgraphics.drawString ("Score", 10, 20);
  332.     offgraphics.drawString (Integer.toString (score), 60, 20);
  333.     offgraphics.drawString ("Level", psize.width>>1, 20);
  334.     offgraphics.drawString (Integer.toString (playLevel),
  335.         (psize.width>>1)+50, 20);
  336.     offgraphics.drawString ("Lives", psize.width-80, 20);
  337.     offgraphics.drawString (Integer.toString (playerLives),
  338.         psize.width-30, 20);
  339.     if (playerLives < 1) offgraphics.drawString ("Game Over",
  340.         (psize.width>>1)-30, psize.height>>1);
  341.     g.drawImage (offscreen, 0, 0, null); 
  342. }
  343.  
  344.  
  345. /*
  346.  * Start the game thread
  347.  */
  348. public void start() {
  349.  
  350.     theThread = new Thread (this);
  351.     theThread.start ();
  352. }
  353.  
  354. /*
  355.  * Stop the game thread
  356.  */
  357. public void stop() {
  358.  
  359.     if (theThread != null)
  360.        theThread = null;
  361. }
  362. public void mouseClicked(MouseEvent e){}
  363. public void mouseEntered(MouseEvent e){}
  364. public void mouseExited(MouseEvent e){}
  365.  
  366. public void mouseReleased(MouseEvent e){}
  367.  
  368. public void mouseMoved(MouseEvent e)
  369. {
  370.     int x =e.getX();
  371.     int y =e.getY();
  372.     player.x = x;
  373.     player.y = psize.height-45;
  374.     if (player.x < 20) player.x = 20;
  375.     if (player.x > psize.width-20) player.x = psize.width-20;
  376. }
  377.  
  378.  
  379. public void mousePressed(MouseEvent e)
  380. {
  381.     player.fire ();
  382. }
  383.  
  384. public void mouseDragged(MouseEvent e) 
  385. {
  386. }
  387.  
  388. }
  389.  
  390. /*
  391.  * the applet class
  392.  */
  393. ///////////////////////////////////////////////////////////////
  394. ///////////////////////////////////////////////////////////////
  395. ///////////////////////////////////////////////////////////////
  396. public class InvaderApp extends Applet implements ActionListener
  397. {
  398.  
  399. /*
  400.  * the playfield instance
  401.  */
  402. Playfield panel;
  403.  
  404. Button btnNewGame;
  405.  
  406. /*
  407.  * temporary storage for images
  408.  */
  409. Image img[] = new Image[4];
  410.  
  411. /*
  412.  * the speed of the game
  413.  * the number of invaders in this round
  414.  */
  415. int speed, NInvadersInPlay;
  416.  
  417. /*
  418.  * Called when the applet is loaded
  419.  
  420. * Load the images
  421.  */
  422. public void init() {
  423.  
  424.     int i;
  425.     MediaTracker tracker = new MediaTracker (this);
  426.  
  427.     setLayout(new BorderLayout());
  428.  
  429.     panel = new Playfield (this);
  430.     add("Center", panel);
  431.     Panel p = new Panel();
  432.     add("South", p);
  433.     btnNewGame = new Button("New Game");
  434.     p.add(btnNewGame);
  435.     btnNewGame.addActionListener(this);
  436.  
  437.  
  438.     showStatus ("Getting Invader images...");
  439.     for (i=0; i<4; i+=1) {
  440.         img[i] = getImage (getDocumentBase(), "T"+(i+1)+".gif");
  441.         tracker.addImage (img[i], 0);
  442.     }
  443.  
  444.     try {
  445.         tracker.waitForID(0);
  446.     } catch (InterruptedException e) { }
  447.  
  448.         for (i=0; i<panel.MaxInvaders; i+=1) {
  449.         panel.invaders[i] = new Invader ();
  450.         panel.invaders[i].inplay = false;
  451.         panel.invaders[i].img[0] = img[0];
  452.         panel.invaders[i].img[1] = img[1];
  453.         panel.invaders[i].img[2] = img[2];
  454.         panel.invaders[i].img[3] = img[3]; 
  455.     }
  456.     panel.player = new Player ();
  457.  
  458.     showStatus ("Getting player images...");
  459.     panel.player.img1 = getImage (getDocumentBase(),"Player1.gif");
  460. panel.player.img2 = getImage (getDocumentBase(),"Player2.gif");
  461.  
  462.     tracker.addImage (panel.player.img1, 1);
  463.     tracker.addImage (panel.player.img2, 1);
  464.     try {
  465.         tracker.waitForID (1);
  466.     } catch (InterruptedException e) { }
  467.     showStatus ("Ready to play!");
  468. }
  469.  
  470. /*
  471.  * Start the action thread
  472.  */
  473. public void start() {
  474.  
  475.     panel.start();
  476. }
  477.  
  478. /*
  479.  * Stop the action thread
  480.  */
  481. public void stop() {
  482.  
  483.     panel.stop();
  484. }
  485.  
  486. public void actionPerformed(ActionEvent ev)
  487. {
  488.         speed = 10;
  489.         panel.player.inplay = true;
  490.         panel.playerLives = 3;
  491.         panel.score = 0;
  492.         panel.playLevel = 1;
  493.         NInvadersInPlay = 2 * panel.playLevel + 1;
  494.         panel.NInvaders = NInvadersInPlay;
  495.         for (int i=0; i<panel.NInvaders; i+=1)
  496.             panel.invaders[i].random (speed,
  497.                 panel.psize.width, panel.psize.height);
  498.  
  499.         play (getCodeBase(), "gong.au");
  500.         if (NInvadersInPlay >= panel.MaxInvaders)
  501.             NInvadersInPlay = panel.MaxInvaders;
  502. }
  503.  
  504. /**
  505.  * Play the appropriate sound when something is hit
  506.  * @param which - which sound to play
  507.  */
  508. public void hit (int which) {
  509.  
  510.     switch (which) {
  511.         case Playfield.INVADER_HIT:
  512.         NInvadersInPlay -= 1;
  513.         if (NInvadersInPlay < 1) {
  514.             play (getCodeBase(), "gong.au");
  515.             panel.playLevel += 1;
  516.             NInvadersInPlay = 2 * panel.playLevel + 1;
  517.             speed += 4;
  518.             panel.NInvaders = NInvadersInPlay;
  519.             for (int i=0; i<panel.NInvaders; i+=1)
  520.             panel.invaders[i].random (speed,
  521.                 panel.psize.width, panel.psize.height);
  522.         } else {
  523.             play (getCodeBase(), "drip.au");
  524.         }
  525.         break;
  526.  
  527.         case Playfield.PLAYER_HIT:
  528.         play (getCodeBase(), "doh2.au");
  529.         break;
  530.     }
  531. }
  532. }
  533.