/**
*===========================================================================
* CSCI298c-java
* Programmer: Ping Wang
* Lab 4: Tank.java is to show Producer/Consumer and monitor concept for
* the threads.
* Description:
* There are 4 classes in this project.
* Tank: As a interface for game control and applet will show up in
* the screen.
* Producer: Put new enemy position to monitor.
* Consumer: Get the enemy position from the monitor, put new tank position
* into the monitor.
* Monitor: Provide synchronized methods, put and get methods.
*============================================================================
*/
import java.awt.*;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.event.*;
/**
*===========================================================================
* Class Name:
* Tank
* Purpose:
* This is an interface for applet
* Import:
* None
* Export:
* Web page shows the applet and animation.
*
*============================================================================
*/
public class Tank extends Applet implements Runnable
{
// Locations for tank and enemy
private Point tankPoint, enemyPoint;
private Point tankStartingPoint, enemyStartingPoint;
// Audit sound
private AudioClip beginSound, bgSound, fireSound, explodeSound;
// Thread declaration
private volatile Thread tank = null;
private volatile Thread producer, consumer;
// Object declaration
private Monitor monitor;
// Image objects
private Image tankImage, enemyImage, enemyImage2;
// Flags for this program
private boolean b_start = false, b_fire = false, b_stop = false;
// Panel objects used to contain button and graphics
private Panel canvas, buttonPanel;
/**
*Method: init()
*Purpose:
* Initialization, and load into images and audio sounds.
*Pre-Condition:
* None
*Post-Condition:
* Initialization is done
*/
public void init()
{
// Set layout format
setLayout(new BorderLayout(10, 5));
canvas = new Panel();
add(canvas, "Center");
buttonPanel = new Panel();
// Add buttons onto buttonPanel
addButton(buttonPanel, "Start",
new ActionListener()
{
/**
*Method: actionPerformed()
*Purpose:
* Method generated by Button Event
*Pre-Condition:
* None
*Post-Condition:
* Event method is performaed
*/
public void actionPerformed(ActionEvent e)
{
if (b_start == false)
{
start(); // Start this thread
producer.start(); // Start producer thread
consumer.start(); // Start consumer thread
b_start = true;
b_stop = false;
}
}
});
addButton(buttonPanel, "Stop",
new ActionListener()
{
/**
*Method: actionPerformed()
*Purpose:
* Method generated by Button Event
*Pre-Condition:
* None
*Post-Condition:
* Event method is performaed
*/
public void actionPerformed(ActionEvent e)
{
if (b_stop == false)
{
b_stop = true;
b_start = false;
stop(); // Stop all threads
}
}
});
add(buttonPanel, "South");
// Get image files
tankImage = getImage(getCodeBase(), "tankImage.gif");
enemyImage = getImage(getCodeBase(), "enemyImage.gif");
enemyImage2 = getImage(getCodeBase(), "enemyImage2.gif");
// Get sounds files
beginSound = getAudioClip(getCodeBase(), "beginSound.au");
bgSound = getAudioClip(getCodeBase(), "bgSound.au");
fireSound = getAudioClip(getCodeBase(), "fireSound.au");
explodeSound = getAudioClip(getCodeBase(), "explodeSound.au");
}
/**
*Method: addButton()
*Purpose:
* Add buttons onto comtainer
*Pre-Condition:
* Panel p, String s, ActionListener a
*Post-Condition:
* Button is added
*/
public void addButton(Panel p, String s, ActionListener a)
{
Button b = new Button(s); // Create new Button object
p.add(b); // Add button to corresponding container
b.addActionListener(a); // Registration of Listener
}
/**
*Method: pause()
*Purpose:
* Set Thread sleep time
*Pre-Condition:
* int time
*Post-Condition:
* Thread sleep time is set
*/
public void pause(int time)
{
try{
tank.sleep(time); // Thread sleep for some time
}catch(InterruptedException e){}
}
/**
*Method: stop()
*Purpose:
* Stop Threads
*Pre-Condition:
* None
*Post-Condition:
* Threads are stop
*/
public void stop()
{
tank = null; // Stop tank thread
producer = null; // Stop producer thread
consumer = null; // Stop consumer thread
bgSound.stop(); // Stop playing background sound
}
/**
*Method: run()
*Purpose:
* Run Threads
*Pre-Condition:
* None
*Post-Condition:
* Threads run
*/
public void run()
{
Graphics g;
bgSound.loop(); // Play background sound
while (b_start == false)
{
// Display blink sentence to prompt user to click button
g = canvas.getGraphics();
g.drawString("Click the start button to begin", 225, 225);
pause(1500);
g.clearRect(200, 200, 300, 40);
pause(500);
}
// Play beginning sound
beginSound.play();
g = canvas.getGraphics();
g.clearRect(0, 0, 600, 600);
while(tank != null)
{
g.drawRect(0, 0, 590, 450);
try
{
// Draw tank image
drawTank(tankImage, monitor.getTankPoint());
// Draw enemy image
drawEnemy(enemyImage, monitor.getEnemyPoint());
tank.sleep(100);
if (monitor.getFireStatus())
{
fire(); // Handle fire
tank.sleep(1000);
// Set fire status back to false
monitor.setFireStatus(false);
// Produce random location for the enemy
int temp_x = (int)(Math.random()*500+50);
int temp_y = 10;
Point enemyStartingPoint = new Point(temp_x, temp_y);
monitor.setEnemyPoint(enemyStartingPoint);
}
g = canvas.getGraphics();
// Clean this enmey image before new one being drawn
g.clearRect(monitor.getEnemyPoint().x,
monitor.getEnemyPoint().y-20, 40, 80);
// Clean this tank image before new one being drawn
g.clearRect(monitor.getTankPoint().x-20,
monitor.getTankPoint().y, 90, 50);
}catch(Exception e)
{
e.printStackTrace(); // Print out any stack traces
}
}
}
/**
*Method: start()
*Purpose:
* Thread start. Draw the scence.
*Pre-Condition:
* None
*Post-Condition:
* Thread run
*/
public void start()
{
Point tempPoint;
// Produce random starting position of enemy
int temp_x = (int)(Math.random() * 500 + 50);
int temp_y = 10;
tempPoint = new Point(temp_x, temp_y);
enemyStartingPoint = tempPoint;
// Produce starting position of tank
tankStartingPoint = new Point(250, 400);
if (tank == null)
{
monitor = new Monitor(); // Create new Monitor object
tank = new Thread(this); // Create new Tank thread
monitor.setEnemyPoint(enemyStartingPoint);
monitor.setTankPoint(tankStartingPoint);
producer = new Producer(monitor); // Create new Producer thread
consumer = new Consumer(monitor); // Create new Consumer thread
tank.start();
}
}
/** Method Name: drawTank()
* Purpose: draw graphics
* Pre-Condition: Graphics g, Image aTank, Point aPoint.
* Post-Condition: Graphics is painted
*/
public void drawTank(Image aTank, Point aPoint)
{
Image tankImage = aTank;
Point tankPoint = aPoint;
Graphics g = canvas.getGraphics();
// Draw input tankImage
g.drawImage(tankImage, tankPoint.x, tankPoint.y, this);
}
/** Method Name: drawEnemy()
* Purpose: draw graphics
* Pre-Condition: Graphics g, Image aEnemy, Point aPoint.
* Post-Condition: Graphics is painted
*/
public void drawEnemy(Image aEnemy, Point aPoint)
{
Image enemyImage = aEnemy;
Point enemyPoint = aPoint;
Graphics g = canvas.getGraphics();
// Draw input enemyImage
g.drawImage(enemyImage, enemyPoint.x, enemyPoint.y, this);
}
/** Method Name: drawBullet()
* Purpose: draw bullets shooting and explosion
* Pre-Condition: Point aTankPoint
* Post-Condition: Bullets and explosion graphics is drawnk
*/
public void drawBullet(Point aTankPoint)
{
int temp_tank_x = aTankPoint.x;
int temp_tank_y = aTankPoint.y;
int temp_enemy_x, temp_enemy_y;
Graphics g = canvas.getGraphics();
do
{
// draw bullets
g.fillOval(temp_tank_x + 25, temp_tank_y - 5, 4, 4);
g.fillOval(temp_tank_x + 25, temp_tank_y - 30, 6, 6);
g.fillOval(temp_tank_x + 25, temp_tank_y - 60, 8, 8);
pause(50);
// clear bullets of last loop
g = canvas.getGraphics();
g.clearRect(temp_tank_x + 25, temp_tank_y - 60, 10, 60);
temp_tank_y = temp_tank_y - 30;
// get coordinates of enemy tank
temp_enemy_x = monitor.getEnemyPoint().x;
temp_enemy_y = monitor.getEnemyPoint().y;
}while((temp_tank_y - 60) > monitor.getEnemyPoint().y);
// clean bullets from screen
g = canvas.getGraphics();
g.clearRect(temp_tank_x + 25, temp_tank_y - 30, 10,
temp_tank_y - temp_enemy_y);
// draw explosion around the enemy
explode(temp_enemy_x, temp_enemy_y);
}
/** Method Name: fire()
* Description: Handle fire sound playing and call drawBullet() method.
* Pre-Condition:
* None.
* Post-Condition:
* Fire sound is played three times, bullets and explosion drawn.
*/
public void fire()
{
// loop for playing fire sound
for(int i = 0; i < 3; i++)
{
// play fire sound three times
fireSound.play();
}
// draw bullets
drawBullet(monitor.getTankPoint());
}
//The method below was modified from the explodeSequnce method presented
//in Greg Murray's past year 298java program.
/** Method Name: explode()
* Description: Handle explosion sequence.
* Pre-Condition: int x, int y.
* Post-Condition: Bullets and explosion graphics is drawn
*/
public void explode(int x, int y)
{
int destroy_x = x + 15;
int destroy_y = y;
int counter, temp1, temp2;
Graphics g = canvas.getGraphics();
pause(100);
explodeSound.play();
for (counter = 0; counter < 20; counter++)
{
// Produce random numbers
temp1 = (int)(Math.random()*counter*4);
temp2 = (int)(Math.random()*counter*4);
// draw fragments of explosion ;
g.setColor(Color.red);
g.fillRect(destroy_x + temp1, destroy_y + temp2, counter/3, counter/3);
pause(45);
g.fillRect(destroy_x + temp1, destroy_y - temp2, counter/3, counter/3);
pause(30);
g.fillRect(destroy_x - temp1, destroy_y + temp2, counter/3, counter/3);
pause(15);
g.fillRect(destroy_x - temp1, destroy_y - temp2, counter/3, counter/3);
}
pause(1000);
g.clearRect(destroy_x - 100, destroy_y - 100, 200, 200);
}
/** Method Name: paint()
* Description: Do nothing
* Pre-Condition: None
* Post-Condition: None
*/
public void paint(Graphics g)
{
}
} // end of Tank class
//******************************************************************************
/**
*===========================================================================
* Class Name:
* Producer
* Purpose:
* This is a producer to produce enemy in the screen. It provides new enemy
* locations
* Import:
* None
* Export:
* Enemy information is produced
*
*============================================================================
*/
class Producer extends Thread
{
private Monitor monitor; // create monitor object
private Point enemyPoint; // enemy information
/**
*Method: Producer()
*Purpose:
* constructor
*Pre-Condition:
* Monitor aMonitor
*Post-Condition:
* Initialization is done
*/
public Producer(Monitor aMonitor)
{
monitor = aMonitor;
}
/**
*Method: run()
*Purpose:
* Producer thread runs
*Pre-Condition:
* None
*Post-Condition:
* Thread runs
*/
public void run()
{
int temp_x;
int temp_y;
while(this != null)
{
try
{
enemyPoint = monitor.getEnemyPoint(); // Get enemy position
temp_x = enemyPoint.x;
temp_y = enemyPoint.y;
temp_y++;
enemyPoint = new Point(temp_x, temp_y);
// Set new enemy position
monitor.setEnemyPoint(enemyPoint);
// This thread sleep for some time
this.sleep((int)(Math.random() * 50));
}catch(InterruptedException e){}
}
}
} // end of Produder class
//******************************************************************************
/**
*===========================================================================
* Class Name:
* Consumer
* Purpose:
* This is a consumer to retrive enemy information from monitor . Then,
* put new tank location into monitor.
* Import:
* None
* Export:
* Enemy information are retrived and tank location is modified.
*
*============================================================================
*/
class Consumer extends Thread
{
private Monitor monitor; // create monitor object
private Point enemyPoint, tankPoint; // enemy information
/**
*Method: Consumer()
*Purpose:
* Constructor
*Pre-Condition:
* None
*Post-Condition:
* Initialization is done
*/
public Consumer(Monitor aMonitor)
{
// Create Monitor object
monitor = aMonitor;
}
/**
*Method: run()
*Purpose:
* Consumer thread runs
*Pre-Condition:
* None
*Post-Condition:
* Thread runs
*/
public void run()
{
int temp_enemy_x;
int temp_enemy_y;
int temp_tank_x;
int temp_tank_y;
while(this != null)
{
try
{
enemyPoint = monitor.getEnemyPoint(); // Get enemy position
temp_enemy_x = enemyPoint.x;
tankPoint = monitor.getTankPoint(); // Get tank position
temp_tank_x = tankPoint.x;
temp_tank_y = tankPoint.y;
if (temp_tank_x < temp_enemy_x - 13)
{
temp_tank_x++;
}
else if (temp_tank_x > temp_enemy_x - 13)
{
temp_tank_x--;
}
else
{
monitor.setFireStatus(true);
}
tankPoint = new Point(temp_tank_x, temp_tank_y);
monitor.setTankPoint(tankPoint); // Set new tank position
// Thread sleeps sometime
this.sleep((int)(Math.random()*30));
}catch(InterruptedException e){}
}
}
} // end of Consumer class
//******************************************************************************
/**
*===========================================================================
* Class Name:
* Monitor
* Purpose:
* This is a monitor to provided synchronized methods for producer and
* consumer.
* Import:
* None
* Export:
* Locations of enemy and tank images.
*
*============================================================================
*/
class Monitor
{
// Enemy and tank position information
private Point enemyPoint;
private Point tankPoint;
private boolean b_fire = false;
/**
*Method: setFireStatus()
*Purpose:
* Set new value of boolean variable
*Pre-Condition:
* boolean bFire
*Post-Condition:
* boolean variable is set with a value
*/
public synchronized void setFireStatus(boolean bFire)
{
b_fire = bFire;
notifyAll();
}
/**
*Method: getFireStatus()
*Purpose:
* Get value of boolean variable
*Pre-Condition:
* None
*Post-Condition:
* Value of boolean variable b_fire is returned
*/
public synchronized boolean getFireStatus()
{
notifyAll();
return b_fire;
}
/**
*Method: setEnemyPoint()
*Purpose:
* Set new position value of enemy
*Pre-Condition:
* Point aPoint
*Post-Condition:
* enemy postion variable is set with a new value
*/
public synchronized void setEnemyPoint(Point aPoint)
{
enemyPoint = aPoint;
notifyAll();
}
/**
*Method: setTankPoint()
*Purpose:
* Set new position value of Tank
*Pre-Condition:
* Point aPoint
*Post-Condition:
* tank postion variable is set with a new value
*/
public synchronized void setTankPoint(Point aPoint)
{
tankPoint = aPoint;
notifyAll();
}
/**
*Method: getEnemyPoint()
*Purpose:
* Get position value of enemy
*Pre-Condition:
* None
*Post-Condition:
* Value of position variable enemyPoint is returned
*/
public synchronized Point getEnemyPoint()
{
notifyAll();
return enemyPoint;
}
/**
*Method: getTankPoint()
*Purpose:
* Get position value of tank
*Pre-Condition:
* None
*Post-Condition:
* Value of position variable tankPoint is returned
*/
public synchronized Point getTankPoint()
{
notifyAll();
return tankPoint;
}
} // end of Monitor class