750 likes | 984 Views
Games Programming with Java. 240-492, Special Topics in Comp. Eng. II Semester 1, 2002-2003. Objectives extend the basic Aliens game a background, explosion effects, scoring, a GUI interface, start and end dialog boxes. 9. Extended Alien Attack v.2 Attack of the Clones?. Overview.
E N D
Games Programming with Java 240-492, Special Topics in Comp. Eng. II Semester 1, 2002-2003 • Objectives • extend the basic Aliens game • a background, explosion effects, scoring, a GUI interface, start and end dialog boxes 9. Extended Alien Attack v.2 Attack of the Clones?
Overview 1. Extended Alien Attack 2. GameManager Extensions 3. GunManager Extensions 4. UFOManager Extensions 5. GunSprite 6. MissileSprite 7. UFOSprite 8. Extensions to ImagesSprite
1. Extended Alien Attack • Your mission: This dialog box appears when the game is started.
1.1.Playing the Game $ java GameManager
It’s all over: energy is gone, and a poor score :(
1.2. Game Restrictions • Most of the restriction are as before • but notice the changes to the key controls • The GUI reports the user’s current score, numbers of UFOs destroyed, and general messages. • When the player’s energy level reaches 0, the game terminates, and the score is judged.
1.3. Control Hierarchy the manager receives sprite status info for reporting creation andinitialisation Game Manager method calls updates Gun Manager UFO Manager redraws Gun Sprite Missile Sprite UFO Sprites
1.4. Sprite Interactions GameManager send msgs; report death; report landing moves from GunManager launch from GunManager report hit UFOSprite UFOSprite GunSprite MissileSprite UFOSprite UFOSprite get gun barrel position; tell gun that it has been hit by a UFO check if missile intersects a UFO; announce hit
1.5. Game Background • Many of the extensions are from: • "Black Art of Java Game Programming”Joel Fan et al., The Waite Group, 1996Chapter 6 • I have recoded them to use my sprite classes, and modern Java coding
2. GameManager Extensions • Pass GameManager references to the Gun and UFO managers. • New scoring methods and variables. • New GUI controls to display the game stats/score • textfields, progress bar • New gun movement keys • to avoid arrow keys problems continued
An unscaled skyline image • requires transparent sprite images • scaling slows redrawing • Loading of an explosion AudioClip. • Start and end dialog boxes • see earlier slides
2.2. GameManager.java (Extended) import java.applet.*;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.net.*;import java.io.File;public class GameManager extends JFrame { GameManagerPanel gmp; int totalScore, numKilled, numLanded, numGunHits; static final int MAX_ENERGY = 10; int energyLevel; JTextField jtfScore, jtfKilled, jtfMessage; JProgressBar jpEnergy; AudioClip explosionSound; : scoring GUI elements and variables
initialise vars and load sound public GameManager() { super( "Alien Attack!!" ); Container c = getContentPane(); c.setLayout( new BorderLayout() ); totalScore = 0; numKilled = 0; numLanded = 0; numGunHits = 0; energyLevel = MAX_ENERGY; try {explosionSound = Applet.newAudioClip( new File("Explosion.au").toURL() ); } catch(MalformedURLException e) { System.out.println( "Sound file Explosion.au not found");} gmp = new GameManagerPanel(this); c.add( gmp, "Center"); JPanel p = gameStatsPanel(); c.add( p, "South"); :
addKeyListener( new KeyAdapter() { public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_NUMPAD4) gmp.moveLeft(); else if (keyCode == KeyEvent.VK_NUMPAD6) gmp.moveRight(); else if (keyCode == KeyEvent.VK_NUMPAD8) gmp.fireMissile(); } }); : use the number pad for gun control
addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent ev) { gameOver(); } } ); pack(); setResizable(false); show();this.requestFocus(); // or key focus diappears } // end of GameManager()
place game info at bottom of JFrame private JPanel gameStatsPanel() { // build first row of status info JLabel jlScore = new JLabel("Score: "); jtfScore = new JTextField(5); jtfScore.setEditable(false); JLabel jlKilled = new JLabel("UFOs killed: "); jtfKilled = new JTextField(5); jtfKilled.setEditable(false); JLabel jlEnergy = new JLabel("Energy: ");jpEnergy = new JProgressBar(0, MAX_ENERGY); jpEnergy.setValue(MAX_ENERGY); jpEnergy.setStringPainted(true); :
JPanel p1 = new JPanel( new FlowLayout() ); p1.add(jlScore); p1.add(jtfScore); p1.add(jlKilled); p1.add(jtfKilled); p1.add(jlEnergy); p1.add(jpEnergy); // build second row of status info JLabel jlName = new JLabel("Your Name: "); jtfName = new JTextField(20); :
replaces stdout used in 1st version JLabel jlMesg = new JLabel("Game Messages: "); jtfMessage = new JTextField(20); jtfMessage.setEditable(false); JPanel p2 = new JPanel( new FlowLayout() ); p2.add(jlMesg); p2.add(jtfMessage); // put the rows together JPanel p = new JPanel(); p.setLayout( new BoxLayout(p, BoxLayout.Y_AXIS)); p.add(p1); p.add(p2); return p; } // end of gameStatsPanel()
public void setMessage(String mesg) { jtfMessage.setText(mesg); } // called by sprites to update score info public void ufoKilled() { numKilled++; jtfKilled.setText(""+numKilled);calcScore(); } public void ufoLanded() { numLanded++; calcScore(); energyLevel -= 1; // arbitrary reduction jpEnergy.setValue(energyLevel); }
public void gunHitByUFO() { numGunHits++; calcScore(); energyLevel -= 2; // arbitrary reduction jpEnergy.setValue(energyLevel); } private void calcScore() { totalScore = 4*numKilled - 2*numLanded - 2*numGunHits; // arbitrary jtfScore.setText(""+totalScore); } public int getEnergyLevel() { return energyLevel; }
public void playExplosion() { explosionSound.play(); } public void gameOver() { gmp.stopTimer(); // stops game updates String judgement; if (totalScore <= 0) judgement = "\nWere you asleep?"; else if (totalScore <=10) judgement = "\nMy pet goldfish could do better!"; else judgement = "\nNot too bad"; JOptionPane.showMessageDialog(this, "Your Score: " + totalScore + judgement, "Game Over", JOptionPane.INFORMATION_MESSAGE); System.exit(0); }
public static void main( String args[] ) { new GameManager(); } } // end of GameManager class
class GameManagerPanel extends JPanel implements ActionListener{ static final int PWIDTH = 500; static final int PHEIGHT = 500; private GameManager gameMan; private GunManager gm; private UFOManager um;private Image bgImage; // background image private Timer animatorTimer;private boolean atGameStart; // true at the game start :
public GameManagerPanel(GameManager gameMan) { setBackground(Color.black); setPreferredSize( new Dimension(PWIDTH, PHEIGHT));bgImage = new ImageIcon( "image/skyline.jpg").getImage(); gm = new GunManager(PWIDTH, PHEIGHT, gameMan); um = new UFOManager( gm.getGun(), PWIDTH, PHEIGHT, gameMan); gm.makeMissile( um.getUFOs() ); this.gameMan = gameMan;atGameStart = true; : for sprite comms back to manager
// create the timer animatorTimer = new Timer(50, this); //50ms animatorTimer.setInitialDelay(0); animatorTimer.setCoalesce(true); animatorTimer.start(); } // end of GameManagerPanel()
public void stopTimer() { animatorTimer.stop(); } public void moveLeft() { gm.moveGunLeft(); } public void moveRight() { gm.moveGunRight(); } public void fireMissile() { gm.fireMissile(); }
public void actionPerformed(ActionEvent e) // timer triggers execution of this method { if (atGameStart) { JOptionPane.showMessageDialog(gameMan, "The year is 2217, your mission is to\n" + "defend Earth from Alien Attack\n" + "Use the number pad keys '4' and '6'\n" + "to move your gun left and right,\n" + "and '8' to fire a missile.\n\n" + "When your energy drops to 0, it's goodbye.", "Alien Attack", JOptionPane.INFORMATION_MESSAGE);atGameStart = false; } : this dialog only appears at game start
if (gameMan.getEnergyLevel() <= 0) // game over gameMan.gameOver(); else { // continue game gm.updateSprites(); um.updateSprites(); repaint(); } } // end of actionPerformed()
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(bgImage, 0, 0, null); // no image scaling required gm.drawSprites(g); um.drawSprites(g); }} // end of GameManagerPanel class the redraw order is important: background fiirst, then sprites
3. GunManager Extensions • The GunManager passes a GameManager reference down to the gun sprite. • The gun sprite can then send information to the GameManager about being hit.
3.2. GunManager.java (Extended) import javax.swing.*;import java.awt.*;import java.util.*;public class GunManager { private GunSprite gun; private MissileSprite missile; private int pWidth, pHeight; static final int GUN_MOVE = 10; public GunManager(int w, int h, GameManager gm) { gun = new GunSprite(w, h, gm); pWidth = w; pHeight = h; } the only changes
public void makeMissile(ArrayList targets) { missile = new MissileSprite( targets, pWidth, pHeight); } public void moveGunLeft() { gun.move(-GUN_MOVE); } public void moveGunRight() { gun.move(GUN_MOVE); } public void fireMissile() { missile.launch( gun.getGunX() ); }
public GunSprite getGun() { return gun; } public void updateSprites() { gun.updateSprite(); missile.updateSprite(); } public void drawSprites(Graphics g) { gun.drawSprite(g); missile.drawSprite(g); }} // end of GunManager class
4. UFOManager Extensions • The UFOManager passes a GameManager reference down to each UFO sprite. • A UFO sprite can then send information to the GameManager.
4.2. UFOManager.java (Extended) import javax.swing.*;import java.awt.*;import java.util.*;public class UFOManager { private ArrayList ufos; static final int NUM_UFOS = 7; // the game has 7 ufos private JPanel parent; private int pWidth, pHeight; :
public UFOManager(GunSprite gun, int w, int h, GameManager gm) { int xPosn, yPosn; pWidth = w; pHeight = h; ufos = new ArrayList(); for (int i=0; i < NUM_UFOS; i++) { xPosn = getRand( pWidth ); yPosn = getRand( pHeight/3 ); ufos.add( new UFOSprite(xPosn, yPosn, pWidth, pHeight, gun, gm) ); } } // end of UFOManager() the only changes
public ArrayList getUFOs() { return ufos; } public void updateSprites() { UFOSprite ufo; for (int i=0; i < ufos.size(); i++) { ufo = (UFOSprite) ufos.get(i); if ( ufo.isActive()) ufo.updateSprite(); else { // reuse ufo in a new position initPosition(ufo); ufo.setActive(true); } } } // end of updateSprites()
private void initPosition(UFOSprite ufo) { int xPosn = getRand( pWidth ); int yPosn = getRand( pHeight/3 ); ufo.setPosition(xPosn, yPosn); } public void drawSprites(Graphics g) { UFOSprite ufo; for (int i=0; i < ufos.size(); i++) { ufo = (UFOSprite) ufos.get(i); ufo.drawSprite(g); } } // random number generator between 0-x private int getRand(int x) { return (int)(x * Math.random()); }} // end of UFOManager class
5. GunSprite GameManager moves from GunManager report hit UFOSprite UFOSprite GunSprite UFOSprite UFOSprite get gun barrel position; tell gun that it has been hit by a UFO
5.1. GunSprite Extensions • GunSprite has a revised hit() method which sends information to GameManager.
5.3. GunSprite.java (Extended) import java.awt.*;import javax.swing.*;import AndySprite.ImagesSprite;public class GunSprite extends ImagesSprite{ private GameManager gm; GunSprite(int w, int h, GameManager gm) { super(w, h);this.gm = gm; setStep(0,0); // no movement loadImage("image", "gun"); setPosition( getPWidth()/2 - getWidth()/2, getPHeight() - getHeight()); setActive(true); }
public void move(int xDist) { int newBPosn = locx + xDist + getWidth()/2; // position of gun barrel if (newBPosn < 0) // barrel off lhs locx = -getWidth()/2; else if (newBPosn > getPWidth()) // barrel off screen on rhs locx = getPWidth() - getWidth()/2; else // within screen locx += xDist; setPosition(locx, locy); } public int getGunX() // return central barrel x-coord position { return locx + getWidth()/2; }
the only major change public int getGunY() // return gun y-coord position { return locy; }public void hit() // gun is hit by a ufo { gm.setMessage("Gun is hit by a UFO!"); gm.gunHitByUFO(); } public void updateSprite() // no built-in movement behaviour, // and drawing area is fixed { }} // end of GunSprite class
6. MissileSprite • The MissileSprite class is unchanged from the one in the basic game.
6.1. MissileSprite.java completely unchanged import java.awt.*;import javax.swing.*;import java.util.*;import AndySprite.ImagesSprite;public class MissileSprite extends ImagesSprite{ static final int Y_SPEED = -27; // missile flies upwards ArrayList targets; :
MissileSprite(ArrayList ts, int w, int h) { super(w, h); setStep(0, Y_SPEED); // movement loadImage("image", "missile"); targets = ts; // sprites to hit // (x,y) loc is not set until missile fired setActive(false); // missile not enabled yet }