package com.knutejohnson.games.asteroids;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.sound.sampled.*;
import javax.swing.*;
public class Asteroids extends JPanel implements ActionListener {
final Path2D.Double ship = new Path2D.Double();
final Path2D.Double largeRock = new Path2D.Double();
final Path2D.Double smallRock;
final ArrayList<Bullet> bullets = new ArrayList<Bullet>();
final ArrayList<Rock> rocks = new ArrayList<Rock>();
final Random random = new Random(new Date().getTime());
final Font font = new Font("Monospaced",Font.BOLD,18);
final javax.swing.Timer displayTimer;
final javax.swing.Timer rockTimer;
double x,y,theta,deltaX,deltaY;
int points,ammo = 1000, fuel = 10000;
int shipDestroyed;
boolean leftKeyIsPressed,rightKeyIsPressed;
boolean upKeyIsPressed,spaceKeyIsPressed;
boolean firstDisplay = true;
public Asteroids() {
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent ke) {
int keyCode = ke.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
if (!spaceKeyIsPressed && shipDestroyed == 0 && ammo > 0) {
spaceKeyIsPressed = true;
Bullet bullet = new Bullet(x,y,theta,deltaX,deltaY);
bullets.add(bullet);
--ammo;
playSound("sounds/bullet.wav");
}
} else if (keyCode == KeyEvent.VK_LEFT) {
leftKeyIsPressed = true;
} else if (keyCode == KeyEvent.VK_RIGHT) {
rightKeyIsPressed = true;
} else if (keyCode == KeyEvent.VK_UP) {
upKeyIsPressed = true;
} else if (keyCode == KeyEvent.VK_ENTER) {
x = getWidth() / 2.0;
y = getHeight() / 2.0;
deltaX = 0;
deltaY = 0;
theta = 0;
points = 0;
fuel = 10000;
ammo = 1000;
shipDestroyed = 0;
}
}
public void keyReleased(KeyEvent ke) {
int keyCode = ke.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE)
spaceKeyIsPressed = false;
else if (keyCode == KeyEvent.VK_LEFT)
leftKeyIsPressed = false;
else if (keyCode == KeyEvent.VK_RIGHT)
rightKeyIsPressed = false;
else if (keyCode == KeyEvent.VK_UP)
upKeyIsPressed = false;
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
x = getWidth() / 2.0;
y = getHeight() / 2.0;
deltaX = 0;
deltaY = 0;
theta = 0;
points = 0;
fuel = 10000;
ammo = 1000;
shipDestroyed = 0;
requestFocusInWindow();
}
});
ship.moveTo(0,-30);
ship.lineTo(-15,20);
ship.lineTo(0,5);
ship.lineTo(15,20);
ship.closePath();
largeRock.moveTo(-32,8);
largeRock.lineTo(-36,-12);
largeRock.lineTo(-26,-24);
largeRock.lineTo(-18,-34);
largeRock.lineTo(-10,-36);
largeRock.lineTo(0,-40);
largeRock.lineTo(8,-38);
largeRock.lineTo(24,-28);
largeRock.lineTo(34,-26);
largeRock.lineTo(38,-12);
largeRock.lineTo(38,14);
largeRock.lineTo(26,22);
largeRock.lineTo(12,38);
largeRock.lineTo(-8,40);
largeRock.lineTo(-16,32);
largeRock.lineTo(-26,28);
largeRock.lineTo(-34,18);
largeRock.closePath();
smallRock = new Path2D.Double(largeRock,
AffineTransform.getScaleInstance(0.25,0.25));
displayTimer = new javax.swing.Timer(15,this);
displayTimer.setInitialDelay(250);
displayTimer.setActionCommand("Display");
rockTimer = new javax.swing.Timer(2500,this);
rockTimer.setInitialDelay(250);
rockTimer.setActionCommand("Rock");
}
public void start() {
displayTimer.start();
rockTimer.start();
}
public void stop() {
displayTimer.stop();
rockTimer.stop();
}
void playSound(final String fname) {
Runnable r = new Runnable() {
public void run() {
try {
URL url = getClass().getResource(fname);
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
AudioFormat af = ais.getFormat();
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af);
line.start();
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = ais.read(buf)) != -1)
line.write(buf,0,bytesRead);
line.drain();
line.stop();
line.close();
} catch (Exception e) {
System.out.println(e);
}
}
};
new Thread(r).start();
}
public void actionPerformed(ActionEvent ae) {
String ac = ae.getActionCommand();
if (ac.equals("Display")) {
if (firstDisplay) {
firstDisplay = false;
x = getWidth() / 2.0;
y = getHeight() / 2.0;
requestFocusInWindow();
}
if (leftKeyIsPressed && !rightKeyIsPressed && fuel > 0 &&
shipDestroyed == 0) {
theta -= 0.035;
--fuel;
} else if (rightKeyIsPressed && !leftKeyIsPressed && fuel > 0 &&
shipDestroyed == 0) {
theta += 0.035;
--fuel;
}
if (upKeyIsPressed && fuel > 0 && shipDestroyed == 0) {
deltaX += Math.sin(theta) * 0.01;
deltaY += Math.cos(theta + Math.PI) * 0.01;
fuel -= 10;
}
x += deltaX;
y += deltaY;
if (x <= 0 || x >= getWidth())
deltaX = -deltaX;
if (y <= 0 || y >= getHeight())
deltaY = -deltaY;
ListIterator<Bullet> bIter = bullets.listIterator();
while (bIter.hasNext()) {
Bullet bullet = bIter.next();
bullet.x += bullet.deltaX;
bullet.y += bullet.deltaY;
if (bullet.x < 0 || bullet.x > getWidth() ||
bullet.y < 0 || bullet.y > getHeight())
bIter.remove();
}
ListIterator<Rock> rIter = rocks.listIterator();
while (rIter.hasNext()) {
Rock rock = rIter.next();
rock.x += rock.deltaX;
rock.y += rock.deltaY;
rock.theta += rock.deltaTheta;
int rockSize = rock.large ? 40 : 10;
if (rock.x < -rockSize || rock.x > getWidth() + rockSize ||
rock.y < -rockSize || rock.y > getHeight() + rockSize)
rIter.remove();
}
AffineTransform at = AffineTransform.getRotateInstance(theta);
rIter = rocks.listIterator();
loop: while (rIter.hasNext()) {
double[] coords = new double[6];
Rock rock = rIter.next();
AffineTransform at2 =
AffineTransform.getRotateInstance(rock.theta);
PathIterator pi = ship.getPathIterator(at);
while (!pi.isDone()) {
pi.currentSegment(coords);
Shape shape;
if (rock.large)
shape = largeRock.createTransformedShape(at2);
else
shape = smallRock.createTransformedShape(at2);
if (shape.contains(rock.x-x-coords[0],rock.y-y-coords[1])) {
if (shipDestroyed == 0) {
shipDestroyed = 1;
playSound("sounds/explode.wav");
}
break loop;
}
pi.next();
}
}
rIter = rocks.listIterator();
while (rIter.hasNext()) {
Rock rock = rIter.next();
at = AffineTransform.getRotateInstance(rock.theta);
bIter = bullets.listIterator();
while (bIter.hasNext()) {
Bullet bullet = bIter.next();
Shape shape;
if (rock.large) {
shape = largeRock.createTransformedShape(at);
if (shape.contains(rock.x-bullet.x,rock.y-bullet.y)) {
playSound("sounds/implosion2.wav");
bIter.remove();
rIter.remove();
points += 10;
for (int i=0; i<5; i++) {
double offset = random.nextDouble() * 1.25644;
double deltaDeltaX =
Math.sin(rock.theta+i*1.25644+offset) * 0.2;
double deltaDeltaY =
Math.cos((rock.theta + i * 1.25644 + offset) +
Math.PI) * 0.2;
Rock r = new Rock(
rock.x,rock.y,rock.theta,
rock.deltaX + deltaDeltaX,
rock.deltaY + deltaDeltaY,
rock.deltaTheta,false);
rIter.add(r);
}
break;
}
} else {
shape = smallRock.createTransformedShape(at);
if (shape.contains(rock.x-bullet.x,rock.y-bullet.y)) {
playSound("sounds/implosion2.wav");
bIter.remove();
rIter.remove();
points += 25;
break;
}
}
}
}
paintImmediately(0,0,getWidth(),getHeight());
} else if (ac.equals("Rock")) {
Rock rock = new Rock(
random.nextInt(getWidth()),
random.nextInt(getHeight()),
random.nextInt(62822) / 10000.0,
random.nextDouble() * (random.nextBoolean() ? 1.0 : -1.0),
random.nextDouble() * (random.nextBoolean() ? 1.0 : -1.0),
random.nextDouble() / (random.nextBoolean() ? 200.0 : -200.0),
true);
rocks.add(rock);
}
}
public void paintComponent(Graphics g2D) {
Graphics2D g = (Graphics2D)g2D;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.WHITE);
g.setFont(font);
g.drawString(String.format(
"AMMO:%5d FUEL:%7d POINTS:%7d",ammo,fuel,points),10,20);
for (Rock rock : rocks) {
AffineTransform at = g.getTransform();
g.translate(rock.x,rock.y);
g.rotate(rock.theta);
if (rock.large)
g.draw(largeRock);
else
g.draw(smallRock);
g.setTransform(at);
}
for (Bullet b : bullets)
g.fillRect((int)b.x-1,(int)b.y-1,3,3);
if (shipDestroyed < 100) {
g.translate(x,y);
g.rotate(theta,0,0);
if ((shipDestroyed & 1) == 0)
g.draw(ship);
else
g.fill(ship);
if (shipDestroyed > 0)
++shipDestroyed;
} else
g.drawString("GAME OVER",getWidth()/2-40,getHeight()/2);
}
static class Bullet {
double x,y,deltaX,deltaY;
public Bullet(double x, double y, double theta, double shipDeltaX,
double shipDeltaY) {
this.x = x;
this.y = y;
deltaX = shipDeltaX + Math.sin(theta) * 4.0; deltaY = shipDeltaY + Math.cos(theta + Math.PI) * 4.0;
}
}
static class Rock {
double x,y,theta,deltaX,deltaY,deltaTheta;
boolean large;
public Rock(double x, double y, double theta,
double deltaX, double deltaY, double deltaTheta, boolean large) {
this.x = x;
this.y = y;
this.theta = theta;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.deltaTheta = deltaTheta;
this.large = large;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Asteroids");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Asteroids asteroids = new Asteroids();
asteroids.setPreferredSize(new Dimension(800,600));
asteroids.start();
f.add(asteroids);
f.pack();
f.setVisible(true);
}
});
}
}