代码地址:src · master · 小肖在路上 / ider · GitCode
直接克隆就好了
我来说一下大致思路:
1.利用swing创建窗口,开始按钮,复原按钮,乌龟图片、兔子图片
2.点击开始进行游戏,再次点击暂停游戏,再次点击继续游戏,点击复原按钮回到起点
2.获取到兔子和乌龟的x坐标,创建两个线程每次加0~5随机,每加完一次休眠0~500毫秒
3.当有其中一个到达终点时,停止游戏,点击复原再点击开始可以再次开始
创建窗口代码如下:
private void initialize() {
frame = new JFrame();
frame.setBounds(200, 100, 500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setTitle("龟兔赛跑");
//乌龟图片
wuGui = new JLabel(new ImageIcon("src\\wg1.png"));
wuGui.setBounds(0, 102, 100, 100);
frame.getContentPane().add(wuGui);
//兔子图片
tuZi = new JLabel(new ImageIcon("src\\tz1.png"));
tuZi.setBounds(0, 271, 100, 100);
frame.getContentPane().add(tuZi);
Thread wg = null;
initializationWgThread(wg, false);
Thread tz = null;
initializationTzThread(tz, false);
//开始按钮
JButton kaiShi = new JButton("开始");
kaiShi.setFont(new Font("宋体", Font.BOLD, 30));
kaiShi.setBounds(150, 10, 100, 54);
kaiShi.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (buttons) {
buttons = false;
isVictory = false;
initializationWgThread(wg, true);
initializationTzThread(tz, true);
} else {
String t = kaiShi.getText();
if (t.equals("开始")) {
state = true;
kaiShi.setText("暂停");
} else {
state = false;
continues();
kaiShi.setText("开始");
}
}
}
});
frame.getContentPane().add(kaiShi);
//复原按钮
JButton fuYuan = new JButton("复原");
fuYuan.setFont(new Font("宋体", Font.BOLD, 30));
fuYuan.setBounds(260, 10, 100, 54);
frame.add(fuYuan);
fuYuan.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
wuGui.setBounds(0, 102, 100, 100);
tuZi.setBounds(0, 271, 100, 100);
}
});
}
代码也很好理解,主要是开始按钮这里:
我给了一个标识量: buttons
作用于表示本次点击是开始游戏还是暂停游戏
点击时标识量为true:开始游戏,调用初始化乌龟、兔子的线程方法
点击时标识量为false:
判断当前按钮的汉字,为“开始”时,改变为“暂停”,调用暂停游戏的方法,原理就是利用Object的 wait()方法,暂停该线程,等待解开。
按钮名字为“暂停”时,调用继续游戏方法,利用Object的notifyAll()方法解开被暂停的线程,由于时暂停了两个线程,所以要调用notifyAll()方法,而不是notify()方法,因为notify()方法只能一次解开一个线程,所以会导致一个在动,另一个不动的bug。
继续游戏方法:
private void continues() {
synchronized (lock) {
lock.notifyAll();
}
}
暂停游戏方法:
private void pause() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
复原按钮:
点击复原乌龟、兔子的位置,但是不停止游戏、
我认为我这个程序最值得注意的一点是:
当游戏结束时,我们点击复原,再点击开始还能继续游戏,本来我原始时直接调用乌龟和兔子的开始线程方法,不过很显然是错的,因为一个线程不允许调用启动两次,所以我就把他们封装为一个初始化方法,每次新游戏是创建两个新的线程,这样就实现的二次游戏的目的。
private void initializationTzThread(Thread tz, boolean isYes) {
tz = new Thread() {
@Override
public void run() {
while (!isVictory) {
if (state) {
pause();
}
double x = tuZi.getBounds().getX();
tuZi.setBounds(((int) x) + r.nextInt(11) + testSpeed, 271, 100, 100);
if (tuZi.getBounds().getX() >= 400) {
synchronized (r) {
if (isVictory == false) {
isVictory = true;
JOptionPane.showMessageDialog(null, "兔子胜利", "游戏结束", JOptionPane.YES_NO_OPTION);
buttons = true;
}
}
break;
}
try {
//距离大于50时,兔子会睡觉5秒
int s = (int) (tuZi.getBounds().getX() - wuGui.getBounds().getX());
if (s >= 50) {
Thread.sleep(5000);
}
Thread.sleep((r.nextInt(6)) * 100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
if (isYes) {
tz.start();
}
}
/**
* @initializationWgThread 乌龟线程初始化
*/
private void initializationWgThread(Thread wg, boolean isYes) {
wg = new Thread() {
@Override
public void run() {
while (!isVictory) {
if (state) {
pause();
}
double x = wuGui.getBounds().getX();
wuGui.setBounds(((int) x) + r.nextInt(6) + testSpeed, 102, 100, 100);
if (wuGui.getBounds().getX() >= 400) {
synchronized (r) {
if (isVictory == false) {
isVictory = true;
JOptionPane.showMessageDialog(null, "乌龟胜利", "游戏结束", JOptionPane.YES_NO_OPTION);
buttons = true;
}
}
break;
}
try {
Thread.sleep(150);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
if (isYes) {
wg.start();
}
}