掷骰子游戏窗体实现--Java初级小项目
2015-11-26 12:10
519 查看
掷骰子
**多线程&&观察者模式
题目要求:《掷骰子》窗体小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大,然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。
分析:这个题目要求灵活运用多线程的相关知识,达到点击开始按钮时,有3个线程启动,分别控制3颗骰子的转动,在3颗骰子全部转完以后,回到主线程计算游戏结果。
But,,,写完代码以后发现,这样做虽然能够保证游戏能够正确运行,但是当我点击开始按钮时,由于3个骰子线程都是直接开在主线程上的,点击开始按钮时,按钮出现下沉情况,子线程一直在后台运行,我窗体中的图片根本不会发生改变,而是直接显示最后的结果,意思就是骰子一直在后台转动,不在前台的窗体中及时更新显示。后来在网上苦苦找寻,大神们说如果想要通过点击JButton使窗体中的JLabel/JTextFeild等其他组件及时更新,直接在JButton的监听事件的实现方法里面直接创建匿名线程,也就是说直接在actionPerformed()方法中修改代码即可,这样能保证你的组件中内容的及时变换,实现非常炫酷的效果。
代码如下:
But,,,But,,, 虽然非常炫酷了,能够实现图片的及时更新了,游戏结果却错了,每次我的骰子还在转动呢,我的游戏结果却早早的就出来了。
原因:3根骰子线程属于子线程,窗体线程属于主线程,问题就在于:子线程可以通过变成精灵线程来保持与主线程的同生死,但是主线程却无法控制子线程何时死亡,只有等待子线程执行完所属的run()方法,结束线程后才知道。
解决方法:在主线程(main)中开3个子线程(t1,t2,t3),在每个子线程上再开一个子子线程(t11,t21,t31)。
t1,t2,t3只运行一次,负责创建子子线程;t11,t21,t31每个线程运行多次,负责控制窗体中的图标及时更新。
这样主线程就不受子线程的影响,开始按钮也不回出现下沉的情况。
但是同样在此处使用join方法也是hold不住子线程的,毕竟t1,t2,t3只运行了一次,join对他们来说根本不起作用,想要掌控t11,t21,t31,最容易理解的办法,就是使用观察者模式了。
将窗体看做观察者,子线程看做被观察者。子线程运行完时,通知观察者我已经运行完成,当观察者观察到子线程全都运行完时,才开始运行后续步骤。
全部代码:
1.窗体
2.线程
**多线程&&观察者模式
题目要求:《掷骰子》窗体小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大,然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。
分析:这个题目要求灵活运用多线程的相关知识,达到点击开始按钮时,有3个线程启动,分别控制3颗骰子的转动,在3颗骰子全部转完以后,回到主线程计算游戏结果。
//3个线程控制3颗骰子 Thread t1 = new Thread(); Thread t2 = new Thread(); Thread t3 = new Thread(); //启动3个线程 t1.start(); t2.start(); t3.start(); //将3个线程加入主线程 t1.join(); t2.join(); t3.join();
But,,,写完代码以后发现,这样做虽然能够保证游戏能够正确运行,但是当我点击开始按钮时,由于3个骰子线程都是直接开在主线程上的,点击开始按钮时,按钮出现下沉情况,子线程一直在后台运行,我窗体中的图片根本不会发生改变,而是直接显示最后的结果,意思就是骰子一直在后台转动,不在前台的窗体中及时更新显示。后来在网上苦苦找寻,大神们说如果想要通过点击JButton使窗体中的JLabel/JTextFeild等其他组件及时更新,直接在JButton的监听事件的实现方法里面直接创建匿名线程,也就是说直接在actionPerformed()方法中修改代码即可,这样能保证你的组件中内容的及时变换,实现非常炫酷的效果。
代码如下:
public void actionPerformed(ActionEvent e) { new Thread(new Runnable() { @Override public void run() { //将外部线程类转移到窗体内部 } }).start(); }
But,,,But,,, 虽然非常炫酷了,能够实现图片的及时更新了,游戏结果却错了,每次我的骰子还在转动呢,我的游戏结果却早早的就出来了。
原因:3根骰子线程属于子线程,窗体线程属于主线程,问题就在于:子线程可以通过变成精灵线程来保持与主线程的同生死,但是主线程却无法控制子线程何时死亡,只有等待子线程执行完所属的run()方法,结束线程后才知道。
解决方法:在主线程(main)中开3个子线程(t1,t2,t3),在每个子线程上再开一个子子线程(t11,t21,t31)。
t1,t2,t3只运行一次,负责创建子子线程;t11,t21,t31每个线程运行多次,负责控制窗体中的图标及时更新。
这样主线程就不受子线程的影响,开始按钮也不回出现下沉的情况。
但是同样在此处使用join方法也是hold不住子线程的,毕竟t1,t2,t3只运行了一次,join对他们来说根本不起作用,想要掌控t11,t21,t31,最容易理解的办法,就是使用观察者模式了。
将窗体看做观察者,子线程看做被观察者。子线程运行完时,通知观察者我已经运行完成,当观察者观察到子线程全都运行完时,才开始运行后续步骤。
全部代码:
1.窗体
package com.sxt.dice; import java.awt.Color; public class DiceFrame extends JFrame implements ActionListener, Observer { /** * 《掷骰子》控制台小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小, * 以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大, * 然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。 * * 运用观察者模式 3个子线程分别控制3个骰子,都已经结束时,通知观察者窗体,窗体观察到所有子线程都结束时,计算游戏结果 * */ private static final long serialVersionUID = 1L; private JTextField txtPut; private JButton btnStart; private JLabel labResult; private JComboBox<String> comboBox; private JLabel labBigOrSmall; private JLabel labPut; private JLabel labSumMoney; private JLabel labDice3; private JLabel labDice2; private JLabel labDice1; private JLabel labSum; private JLabel labMes; private static List<Icon> imgs = new ArrayList<Icon>(); public static void main(String[] args) { new DiceFrame(); } public DiceFrame() { this.setLocationRelativeTo(null); this.setBounds(200, 50, 380, 297); this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); getContentPane().setLayout(null); this.setResizable(false); labDice1 = new JLabel(""); labDice1.setIcon(new ImageIcon("img/dices.jpg")); labDice1.setBounds(30, 50, 96, 96); getContentPane().add(labDice1); labSum = new JLabel("\u5269\u4F59\u91D1\u989D\uFF1A"); labSum.setBounds(10, 10, 69, 23); getContentPane().add(labSum); labDice2 = new JLabel(""); labDice2.setIcon(new ImageIcon("img/dices.jpg")); labDice2.setBounds(136, 50, 96, 96); getContentPane().add(labDice2); labDice3 = new JLabel(""); labDice3.setIcon(new ImageIcon("img/dices.jpg")); labDice3.setBounds(242, 50, 96, 96); getContentPane().add(labDice3); labSumMoney = new JLabel("3000"); labSumMoney.setForeground(Color.red); labSumMoney.setBounds(86, 10, 63, 23); getContentPane().add(labSumMoney); labPut = new JLabel("\u672C\u6B21\u4E0B\u6CE8\uFF1A"); labPut.setToolTipText("0.0"); labPut.setBounds(10, 199, 69, 23); getContentPane().add(labPut); txtPut = new JTextField(); txtPut.setBounds(80, 200, 69, 21); getContentPane().add(txtPut); txtPut.setColumns(10); labBigOrSmall = new JLabel("\u62BC\uFF1A"); labBigOrSmall.setBounds(45, 232, 34, 27); getContentPane().add(labBigOrSmall); comboBox = new JComboBox<String>(); comboBox.setBounds(80, 234, 69, 23); getContentPane().add(comboBox); comboBox.addItem("大"); comboBox.addItem("小"); labResult = new JLabel(""); labResult.setBounds(136, 156, 126, 27); getContentPane().add(labResult); btnStart = new JButton("START"); btnStart.setBounds(263, 199, 88, 58); getContentPane().add(btnStart); labMes = new JLabel("<html><font size=5 color=red>*</font></html>"); labMes.setBounds(152, 203, 101, 15); getContentPane().add(labMes); this.setVisible(true); imgs.add(new ImageIcon("img/1.png")); imgs.add(new ImageIcon("img/2.png")); imgs.add(new ImageIcon("img/3.png")); imgs.add(new ImageIcon("img/4.png")); imgs.add(new ImageIcon("img/5.png")); imgs.add(new ImageIcon("img/6.png")); btnStart.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == btnStart) { // 清除上次游戏的结果 labResult.setText(""); // 获取当前下注金额,用户余额,用户押大还是押小 String txt = txtPut.getText().trim(); String remain = labSumMoney.getText().trim(); // 余额不足,不能开始游戏,提示用户充值 if (Integer.parseInt(remain) <= 0) { JOptionPane.showMessageDialog(null, "当前余额不足,请充值!"); return; } // 下注金额合法性检查 if (txt.length() == 0) { // 提示用户输入 labMes.setText("*请输入下注金额"); labMes.setForeground(Color.RED); return; } // 检查用户下注金额是否在有效范围内 if (Integer.parseInt(txt) <= 0 || Integer.parseInt(txt) > Integer.parseInt(remain)) { txtPut.setText(""); labMes.setText("下注金额应在0~" + remain + "之间"); return; } // 游戏开始后相关项不可更改 txtPut.setEnabled(false); labMes.setText(""); comboBox.setEnabled(false); //在主线程上开t1,t2,t3 3个子线程 Thread t1 = new Thread() { @Override public void run() { //每个子线程上再开子子线程,控制图标变换 IconThread t11 = new IconThread(labDice1, imgs); //给t11添加观察者,即当前窗体 t11.addObserver(DiceFrame.this); new Thread(t11).start(); } }; Thread t2 = new Thread() { @Override public void run() { IconThread t21 = new IconThread(labDice2, imgs); t21.addObserver(DiceFrame.this); new Thread(t21).start(); } }; Thread t3 = new Thread() { @Override public void run() { IconThread t31 = new IconThread(labDice3, imgs); t31.addObserver(DiceFrame.this); new Thread(t31).start(); } }; t1.start(); t2.start(); t3.start(); } } /** * 获取骰子点数和 * * @param lab * @return sum */ private int result(JLabel lab) { // 获取当前骰子图片 Icon icon = lab.getIcon(); int sum = 0; for (int i = 0; i < imgs.size(); i++) { if (icon.equals(imgs.get(i))) { sum += (i + 1); break; } } return sum; } // 构建所有被观察者的集合 Vector<Observable> allObservables = new Vector<Observable>(); @Override public void update(Observable o, Object arg) { System.out.println(o + "................."); // 如果集合中不包含当前被观察者,将此被观察者加入集合 if (allObservables.contains(o) == false) { allObservables.add(o); } // 如果集合中被观察者个数为3,说明3个骰子线程已经全部结束 if (allObservables.size() == 3) { // 获取当前下注金额,用户余额,用户押大还是押小 String txt = txtPut.getText().trim(); String remain = labSumMoney.getText().trim(); String bigOrSmall = comboBox.getSelectedItem().toString(); // 获取每个骰子点数 int sum1 = result(labDice1); int sum2 = result(labDice2); int sum3 = result(labDice3); System.out.println(sum1 + "-" + sum2 + "-" + sum3); int sum = sum1 + sum2 + sum3; System.out.println(sum); if (sum > 9 && "大".equals(bigOrSmall) || sum <= 9 && "小".equals(bigOrSmall)) { // 奖励玩家相应金额 remain = String.valueOf(Integer.parseInt(remain) + Integer.parseInt(txt)); labSumMoney.setText(remain); // 显示游戏结果 labResult.setText("WIN"); labResult.setForeground(Color.GREEN); labResult.setFont(new Font("宋体", Font.BOLD, 40)); } else { // 扣除玩家相应金额 remain = String.valueOf(Integer.parseInt(remain) - Integer.parseInt(txt)); labSumMoney.setText(remain); labResult.setText("FAIL"); labResult.setForeground(Color.red); labResult.setFont(new Font("宋体", Font.BOLD, 40)); } txtPut.setEnabled(true); comboBox.setEnabled(true); // 本次游戏结束后移除集合中所有线程 allObservables.removeAll(allObservables); } } }
2.线程
package com.sxt.dice; import java.util.List; import java.util.Observable; import java.util.Random; import javax.swing.Icon; import javax.swing.JLabel; public class IconThread extends Observable implements Runnable { /** * 运用观察者模式,将子线程作为被观察对象,一旦子线程运行完,发生改变,通知观察者 */ JLabel lab; Random random = new Random(); List<Icon> imgs; public IconThread(JLabel lab, List<Icon> imgs) { this.lab = lab; this.imgs = imgs; } @Override public void run() { //设置每颗骰子转动30次 int count = 30; while (count > 0) { //获取一个随机数[0~6) int index = random.nextInt(6); //从imgs集合中取相应图片放入lab中 lab.setIcon(imgs.get(index)); count--; try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.setChanged();// 子线程运行完,发生改变 this.notifyObservers();// 通知观察者 } }
相关文章推荐
- java27
- java26
- 把应用服务写成系统服务
- java25
- java24
- java23
- 详解Java的Spring框架中的注解的用法
- [Storm] java.io.FileNotFoundException: File '../stormconf.ser' does not exist
- java实现发送手机短信
- spring AOP自定义注解方式实现日志管理
- springMVC 一些文章
- javaBean与Map相互转化
- 史上最全最强SpringMVC详细示例 实战
- [Java代码] java数学表达式计算 QLExpress
- java的Calendar和Date类
- java 四种线程池
- 用MyEclipse将java文件转换成UML类图
- java 遍历一个list的时候 然后修改 会报错
- Eclipse Kepler中安装Drools6插件
- java内存调优