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(); } }