Billiards
<< Bounce | FinalProjectsTrailIndex | DuckyLove >>
In Carom (or Spanish) Billiards there is a cue ball and 2 other balls, and no pockets. The idea is to hit three cushions with the cue ball call before hitting the last ball. This is a little like that game, but there is only one target ball and one cue ball. Try to hit three or more cushions before hitting the red ball.
This demonstrates a more complex user interface using both MouseMotionEvents and MouseEvents
Here is a Link to a working copy
You'll need these sound files:
Ball.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
public class Ball {
private int x;
private int y;
private int radius;
private int rise;
private int run;
private int speed;
private Color color;
private boolean visable;
public Ball(int radius){
x=randInt(350,450);
y=randInt(250,350);
this.radius=radius;
rise=randInt(1,8);
run=randInt(-8,8);
setVisable(false);
setColor(Color.BLUE);
setSpeed(1);
}
public int randInt(int min, int max){
return min+(int)((max-min)*Math.random());
}
public void move(){
x+=speed*run;
y+=speed*rise;
}
public void bounceVertical(){
rise*=-1;
}
public void bounceHorizontal(){
run*=-1;
}
public void draw(Graphics g){
if (visable){
Color c=g.getColor();
g.setColor(color);
g.fillOval(x-radius, y-radius, 2*radius, 2*radius);
g.setColor(c);
}
}
public void setVisable(boolean visable) {
this.visable = visable;
}
public boolean isVisable() {
return visable;
}
public Rectangle2D area(){
return new Rectangle2D.Double(x-radius, y-radius, 2*radius, 2*radius);
}
public void setRise(int rise){ this.rise=rise;}
public void setRun(int run){ this.run=run;}
public int getRise(){return rise;}
public int getRun(){return run;}
public void setX(int x){ this.x=x;}
public void setY(int y){ this.y=y;}
public int getX(){return x;}//center x
public int getY(){return y;}//center y
public int getRadius(){return radius;}
public void setColor(Color c){ this.color=c;}
public Color getColor(){return this.color;}
public void setSpeed(int speed) {this.speed = speed;}
public int getSpeed() {return speed;}
}
PoolBall.java
import java.awt.Color;
public class PoolBall extends Ball
{
private int energy;
public PoolBall(int radius) {
super(radius);
energy=0;
}
public PoolBall(Color color, int radius) {
super(radius);
energy=0;
setColor(color);
}
/** a passive hit from a stick or another ball
*
* @param angle
* @param force
*/
public void hit(double angle, double force){
energy=(int)(0.5*force);
setRise((int)(5*Math.sin(angle)));
setRun((int)(5*Math.cos(angle)));
setSpeed(3);
}
public void strike(PoolBall b){
double run=b.getX()-this.getX();
double rise=b.getY()-this.getY();
double relationAngle =Math.atan2(rise, run);
//double pathAngle=Math.atan2(getRise(), getRun());
double theta=0.5*Math.PI-relationAngle;
setRise((int)(5*Math.sin(theta)));
setRun((int)(5*Math.cos(theta)));
b.hit(relationAngle, energy);
}
public void move(){
if (energy<=0)
return;
energy=995*energy/1000;
if (energy< 50)
setSpeed(1);
super.move();
}
public void lowerSpeed(){
int s=getSpeed();
s--;
if (s>0)
setSpeed(s);
}
public int getEnergy(){ return energy;}
public void setEnergy(int energy){ this.energy=energy;}
public boolean touches(PoolBall target) {
return this.area().intersects(target.area());
}
}
PoolTable.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
public class PoolTable {
private int top;
private int bottom;
private int left;
private int right;
private Color color;
private Color bgColor;
public PoolTable(int left, int right, int top, int bottom){
this.top=top;
this.bottom=bottom;
this.left=left;
this.right=right;
this.bgColor=Color.black;
this.color=Color.GREEN;
}
public PoolTable(){
this(100,700,100,400);
}
public void draw(Graphics g){
Color old=g.getColor();
g.setColor(bgColor);
g.fillRect(0, 0, 800, 600);
g.setColor(color);
g.fillRect(left, top, right-left, bottom-top);
g.setColor(old);
}
/**
* this will test if ball is close enough to a rail
* tell the ball to change direction
* and return whether or not there were changes made
* @param b
* @return
*/
public boolean touches(PoolBall b){
boolean bounce=false;
Rectangle2D r=b.area();
double bRt=r.getWidth()+r.getX();
double bBtm=r.getHeight()+r.getY();
double bLt=r.getX();
double bTop=r.getY();
if (bRt>=right){
b.setX(right-b.getRadius());
b.setRun(-1*(int)Math.abs(b.getRun()));
bounce=true;
}
if (bLt<=left){
b.setX(left+b.getRadius());
b.setRun((int)Math.abs(b.getRun()));
bounce=true;
}
if (bBtm>=bottom){
b.setY(bottom-b.getRadius());
b.setRise(-1*(int)Math.abs(b.getRise()));
bounce=true;
}
if (bTop<=top){
b.setY(top+b.getRadius());
b.setRise((int)Math.abs(b.getRise()));
bounce=true;
}
if (bounce)
b.lowerSpeed();
return bounce;
}
}
PoolStick.java
import java.awt.Color;
import java.awt.Graphics;
public class PoolStick {
private boolean visible;
private Color color;
private int[] xPoints;
private int[] yPoints;
private int rise;
private int run;
private double angle;
private double force;
public PoolStick(Color c){
color=c;
setVisible(false);
xPoints=new int[3];
yPoints=new int[3];
for(int i=0;i<3;i++){
xPoints[i]=0;
yPoints[i]=0;
}
rise=0;run=0;
}
public void setVisible(boolean visable) {
this.visible = visable;
}
public boolean isVisible() {
return visible;
}
public void setTip(int x, int y){
xPoints[0]=x;
yPoints[0]=y;
}
public void setEnd(int x, int y){
rise = (yPoints[0]-y);
run = (xPoints[0]-x);
double a=Math.atan2(run, rise);
angle=Math.atan2(rise, run);
force=Math.sqrt(rise*rise+run*run);
xPoints[1]=x-(int)(15*Math.cos(a));
yPoints[1]=y+(int)(15*Math.sin(a));
xPoints[2]=x+(int)(15*Math.cos(a));
yPoints[2]=y-(int)(15*Math.sin(a));
}
public void draw(Graphics g){
if(visible){
Color old=g.getColor();
g.setColor(color);
g.fillPolygon(xPoints, yPoints, 3);
g.setColor(old);
}
}
public int getRise(){return rise;}
public int getRun(){return run;}
public int getAngleDeg(){return (int)(180*angle/Math.PI);}
public double getAngle(){return angle;}
public double getForce(){return force;}
}
Sound.java
import java.applet.*;
import java.net.URL;
public class Sound{
private AudioClip clip;
public Sound(String fileName, Applet a){
URL soundToPlay = getClass().getResource(fileName);
clip = a.getAudioClip(soundToPlay);
}
public void play(){
clip.play();
}
public void loop(){
clip.loop();
}
}
Billiards.java
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class Billiards extends Applet implements MouseMotionListener, MouseListener, ActionListener
{
private PoolStick stick;
private PoolBall cueBall, target1;
private PoolTable table;
Timer timer;
int frame;
int count;
int score;
Sound hit, cue, bounce;
boolean target1Hit;
Image virtualMem;
Graphics gBuffer;
public void init(){
stick = new PoolStick(Color.red);
cueBall = new PoolBall(Color.white, 10);
target1 = new PoolBall(Color.red, 10);
target1.setVisable(true);
timer = new Timer(20, this);
frame=0;
count=0;
score=0;
target1Hit=false;
cueBall.setVisable(true);
this.addMouseListener(this);
this.addMouseMotionListener(this);
table = new PoolTable();
virtualMem = createImage(800,600);
gBuffer = virtualMem.getGraphics();
hit = new Sound("hit.wav",this);
cue = new Sound("cue.wav", this);
bounce = new Sound("bounce.wav",this);
}
public void paint(Graphics g){
gBuffer.setColor(Color.green);
gBuffer.fillRect(0,0,getWidth(), getHeight());
table.draw(gBuffer);
cueBall.draw(gBuffer);
target1.draw(gBuffer);
gBuffer.setColor(Color.white);
gBuffer.drawString("Click and drag to make pool stick to hit the ball", 20, 20);
gBuffer.drawString("Strike 3 or more cushions before hitting red Ball", 20, 445);
gBuffer.drawString("angle="+(-1*stick.getAngleDeg())+" force="+stick.getForce(), 20, 50);
String result="Red ball ";
if (!target1Hit)
result+="not ";
gBuffer.drawString(result+"hit after "+count+" bounces", 20, 460);
gBuffer.drawString("Score: "+score, 20, 475);
stick.draw(gBuffer);
//Now we send the result to the screen
g.drawImage(virtualMem,0,0,this);
}
public void update(Graphics g)
{
paint(g); //get rid of flicker with this method
}
@Override
public void mouseDragged(MouseEvent e) {
//Needed to implement MouseMotionListener
int x=e.getX();
int y=e.getY();
stick.setEnd(x, y);
repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
// Needed to implement MouseMotionListener
}
@Override
public void mouseClicked(MouseEvent e) {
// Needed to implement MouseListener
}
@Override
public void mouseEntered(MouseEvent e) {
// Needed to implement MouseListener
}
@Override
public void mouseExited(MouseEvent e) {
// Needed to implement MouseListener
}
@Override
public void mousePressed(MouseEvent e) {
// Needed to implement MouseListener
timer.stop();
stick.setTip(cueBall.getX(), cueBall.getY());
stick.setEnd(e.getX(), e.getY());
stick.setVisible(true);
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
// Needed to implement MouseListener
stick.setVisible(false);
cueBall.hit(stick.getAngle(), stick.getForce());
timer.start();
count=0;
target1Hit=false;
cue.play();
//repaint();
}
@Override
public void actionPerformed(ActionEvent e) {
// Needed to implement the Timer which generates ActionEvents
if (e.getSource()==timer){
frame++;
frame%=100;
cueBall.move();
if (table.touches(cueBall)&&!target1Hit){
count++;
bounce.play();
}
if (cueBall.touches(target1)){
cueBall.strike(target1);
target1Hit=true;
hit.play();
if (count>2)
score+=10*count;
}
target1.move();
if (table.touches(target1))
bounce.play();
}
repaint();
}
}
