📄 billiardball.java
字号:
pTarget = new Point(51, 51); // 先设为一个初始值.
setCanFire(true); // 设置fire 为true, 表示能发射
addMouseMotionListener(new MouseMotionMonitor());
addMouseListener(new MouseMonitor());
}
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.GREEN);
g.fillRect(0, 0, getSize().width - 1, getSize().height - 1);
g.drawImage(table, 0, 0, null);
// mBall.draw(g);
for ( int i = 0; i < balls.size(); i++ ) {
balls.get(i).draw(g);
}
if ( isCanFire() == true && isCanPlace() == false ) {
g.setColor(Color.PINK);
g.drawLine((int)(mBall.x + 0.5), (int)(mBall.y + 0.5), pTarget.x, pTarget.y);
g.fillOval(pTarget.x - 5, pTarget.y - 5, 10, 10);
// g.drawLine((int)(mBall.x + 1.5), (int)(mBall.y + 1.5), pTarget.x + 1, pTarget.y + 1);
}
synchronized(this) {
for ( int i = 0; i < flashs.size(); i++ ) {
flashs.get(i).draw(g);
}
Iterator<Tnode> iter = flashs.iterator();
while ( iter.hasNext() ) {
Tnode item = iter.next();
if ( item.isLive() == false )
iter.remove();
}
}
g.setColor(c);
}
public boolean isState() {
return state;
}
public void setState(boolean state) {
this.state = state;
}
public boolean isCanFire() {
return canFire;
}
public void setCanFire(boolean fire) {
canFire = fire;
}
public boolean isCanPlace() {
return canPlace;
}
public void setCanPlace(boolean canPlace) {
this.canPlace = canPlace;
}
public void initial() {
mBall = new Ball();
balls.add(mBall);
for ( int i = 0; i < 10; i++ )
balls.add(new Ball());
}
public void gameStart() {
double rad = Math.sqrt(3.0) * 10.0;
double ix = 450, iy = 150;
int index = 0;
balls.get(0).setProperty(150, 150, Color.WHITE);
balls.get(0).setLive(true);
for ( int i = 0; i < 4; i++ ) {
for ( int j = 0; j <= i; j++ ) {
balls.get(++index).setProperty(ix + i * rad, iy + (j - i / 2.0) * 20, Color.RED);
balls.get(index).setLive(true);
}
}
setCanFire(true);
setCanPlace(true);
parent.initScore();
synchronized(this) {
flashs.clear();
}
}
public void addFlash(Tnode item) {
synchronized(this) {
flashs.add(item);
}
}
public void move() { // 主要负责运动的处理.
// 把一个单位, 划分为很小时间片, 然后进行模拟. 碰撞, 就是这样的.
int ctype = 0;
int step = 0, record = 0, mstep;
int number = 0, slice, request; //balls.size();
Ball sb1 = null, sb2 = null;
while ( step < SLICE ) { // time limited
mstep = SLICE - step;
ctype = CWNULL;
sb1 = sb2 = null;
for ( int i = 0; i < balls.size(); i++ ) {
Ball b1 = balls.get(i);
if ( b1.isLive() == false ) continue;
slice = b1.collidesHole(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
if ( mstep > slice ) {
mstep = slice;
ctype = CWHOLE;
sb1 = b1;
}
for ( int j = i + 1; j < balls.size(); j++ ) {
Ball b2 = balls.get(j);
if ( b2.isLive() == false ) continue;
slice = b1.collides(b2);
if ( mstep > slice ) {
mstep = slice;
ctype = CWBALL; // 1 表示两球碰撞.
sb1 = b1; sb2 = b2;
}
}
slice = b1.collidesWall(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
if ( mstep > slice ) {
mstep = slice;
ctype = CWWALL; // 2 表示球与墙壁发生碰撞.
sb1 = b1;
}
}
for ( int i = 0; i < balls.size(); i++ ) {
if ( balls.get(i).isLive() == true )
balls.get(i).update(mstep);
}
if ( ctype == CWBALL ) {
sb1.changes(sb2);
new Thread(new MusicThread("Collide.wav")).start();
} else if ( ctype == CWWALL ) {
sb1.changesWall(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
new Thread(new MusicThread("CollideEdge.wav")).start();
} else if ( ctype == CWHOLE ) {
Point tp = sb1.changesHole(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
addFlash(new Tnode(tp.x, tp.y));
if ( sb1 != mBall ) {
parent.addScore();
}
new Thread(new Mp3Encode("sounds/Pocket.wav")).start();
}
step += mstep;
}
int counter = 0;
for ( int i = 0; i < balls.size(); i++ ) {
if ( balls.get(i).isStayed() == true || balls.get(i).isLive() == false )
counter++;
}
if ( counter == balls.size() ) {
if ( isCanFire() == false )
parent.initialPower();
setCanFire(true);
if ( balls.get(0).isLive() == false ) {
balls.get(0).setData(150, 150, 0, 0); // bug still exsit. 可能存在重叠
balls.get(0).setLive(true);
setCanPlace(true);
}
}
}
public boolean judge() {
Ball b1 = balls.get(0);
for ( int i = 1; i < balls.size(); i++ ) {
Ball b2 = balls.get(i);
if ( b1.isTouched(b1.x, b1.y, b2.x, b2.y) == true )
return false;
}
if ( b1.y <= Ball.OFFSET + b1.radian || b1.y >= TABLE_HEIGHT - Ball.OFFSET - b1.radian )
return false;
return true;
}
private class MouseMotionMonitor extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e) {
if ( isCanFire() == true ) {
pTarget = e.getPoint();
}
if ( isCanPlace() == true ) {
balls.get(0).x = 150;
balls.get(0).y = e.getPoint().y;
}
}
public void mouseMoved(MouseEvent e) {
if ( isCanFire() == true ) {
pTarget = e.getPoint();
}
if ( isCanPlace() == true ) {
balls.get(0).x = 150;
balls.get(0).y = e.getPoint().y;
}
}
}
private class MouseMonitor extends MouseAdapter { // 这边涉及一个同步问题.
public PowerChooser chooser = null;
public void mousePressed(MouseEvent e) {
if ( e.getButton() == MouseEvent.BUTTON1 && isCanFire() == true && isCanPlace() == false ) {
chooser = new PowerChooser();
chooser.start();
}
}
public void mouseReleased(MouseEvent e) {
if ( e.getButton() == MouseEvent.BUTTON1 && isCanFire() == true ) {
if ( isCanPlace() == false ) {
setCanFire(false);
mBall.setData(parent.getPower(), e.getPoint());
chooser.setLive(false);
} else if ( judge() == true ) {
setCanPlace(false);
}
}
}
}
private class Tnode {
public int x, y;
public int index = 0;
public Tnode(int x, int y) {
this.x = x;
this.y = y;
}
public void draw(Graphics g) { // 0 1 2 3 2 1 0
BufferedImage image = null;
if ( index <= 3 )
image = ImageLoader.getHitedImage(index);
else
image = ImageLoader.getHitedImage(6 - index);
g.drawImage(image, x - image.getWidth() / 2, y - image.getHeight() / 2, null);
index++;
}
public boolean isLive() {
return index <= 6;
}
}
private class PowerChooser extends Thread {
public boolean live = true;
public void setLive(boolean live) {
this.live = live;
}
public void run() {
parent.initialPower();
while ( live == true ) {
try {
Thread.sleep(80);
parent.updatePower();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
// 使用一个隐藏组件, 来回避JProcessBar 这个高级Swing组件的model问题;
class ProcessPanel extends JPanel {
public static final int PROCESS_WIDTH = 600;
public static final int PROCESS_HEIGHT = 50;
public JLabel lblScore = null;
public JLabel lblPower = null;
public JTextField tfScore = null;
public JButton btnStart = null;
private int prec = 0;
private int changedUnit = 2;
public ProcessPanel() {
lblScore = new JLabel("得分");
lblPower = new JLabel("力量条");
tfScore = new JTextField("0", 5);
tfScore.setEditable(false);
btnStart = new JButton("开始");
deploy();
}
public void deploy() {
setSize(PROCESS_WIDTH, PROCESS_HEIGHT);
setLayout(null);
lblScore.setBounds(50, 10, 60, 30);
tfScore.setBounds(120, 10, 60, 30);
btnStart.setBounds(200, 10, 60, 30);
lblPower.setBounds(300, 10, 60, 30);
add(lblScore);
add(lblPower);
add(btnStart);
add(tfScore);
}
public void paint(Graphics g) {
super.paint(g);
Color c = g.getColor();
g.setColor(Color.PINK);
g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);
g.drawRect(360, 10, 200, 30);
g.setColor(Color.LIGHT_GRAY);
g.fillRect(360, 10, 200 * (prec - 10) / 90, 30);
g.setColor(c);
}
public int getPower() {
return prec;
}
public synchronized void initialPower() {
prec = 10;
changedUnit = 2;
}
public synchronized void updatePower() {
if ( prec >= 100 ) {
changedUnit = -2;
} else if ( prec <= 2 ) {
changedUnit = 2;
}
prec += changedUnit;
}
public void addStartActionListener(ActionListener act) {
btnStart.addActionListener(act);
}
public void addScore() {
int value = Integer.parseInt(tfScore.getText());
tfScore.setText(new Integer(value + 1).toString());
}
public void initScore() {
tfScore.setText("0");
}
}
// singleton pattern
class ImageLoader {
public static final int NPICTURE = 18;
public static final int NFLASH = 4;
public static BufferedImage[] images = new BufferedImage[NPICTURE];
public static BufferedImage cursor = null;
public static BufferedImage background = null;
public static BufferedImage[] flashs = new BufferedImage[NFLASH];
public synchronized static BufferedImage getIndexOf(int index) { // check index, judge whether index skip the range;
if ( images[index] == null ) {
URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
URL url = loader.findResource("images/" + index + ".png");
try {
images[index] = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
return images[index];
}
public synchronized static BufferedImage getCursor() {
if ( cursor == null ) {
URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
URL url = loader.findResource("images/cursor.gif");
try {
cursor = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
return cursor;
}
public synchronized static BufferedImage getBackground() {
if ( background == null ) {
URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
URL url = loader.findResource("images/table.png");
try {
background = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
return background;
}
public synchronized static BufferedImage getHitedImage(int index) {
if ( flashs[index] == null ) {
URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
URL url = loader.findResource("images/" + index + ".gif");
try {
flashs[index] = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
return flashs[index];
}
}
// 显然, 用AudioClip 也可以来播放wav格式的文件
// Applet 中定义的AudioClip: getAudioClip();
class MusicThread implements Runnable {
public AudioClip clip = null;
public String filename = null;
public MusicThread(String filename) {
this.filename = filename;
}
public void run() {
try {
URLClassLoader loader = (URLClassLoader)getClass().getClassLoader();
URL url = loader.findResource("sounds/" + filename);
clip = Applet.newAudioClip(url); // newAudioClip(url) 与 getAudioClip() 的区别是,一个是静态另一个是成员函数.
clip.play();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 知识点: (1) 函数mouseMoved(MouseEvent e) 和mouseDragged(MouseEvent e) 两者都监控鼠标的移动, 但是, 前者为无按键按下时,
// 而后者则是按下某个组件时进行跟踪.
// 在监视mouseDragged(MouseEvent e) {} 这个函数时, 要用到MouseMotionAdapter类,
// 因为mouseAdapter() 中可能不发送mouseDragged事件, 这与具体的实现有关.
// Thread ---> java.lang.Thread;
// (1)先搭框架.
// (2)先完成, 球碰台球桌面, 的碰撞检测, 以及碰撞反应.
// (3)简单的两个球的碰撞, 以及多个球的碰撞.
//
// (4) 安置力量条:
// (5) 对碰撞的时候, 添加背景音乐.
// 今天的任务: 完成
// 给球贴上图.(option: 制作球滚动的效果).
// 书写 碰撞的原理 + 实现碰撞的过程.
//
//
// 对Timer类的认识, Timer在java中, 很有多种定义. 在不同的包中
// 我们一般用java.util.timer;
// timer.schedule(task, delay, interval);
//
// 撑到800行吧
//
//
//
// today task:
// (1) 球进洞, + 显示进球的洞, 制作动画效果 \/
// (2) 对力量条的选择, 应该更加主动. \/
// (3) 白球进洞后, 位置的重新选择. \/
// (4) 开始, 即重新开始 \/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -