当前位置: 代码迷 >> Java相关 >> 掷骰子游戏窗体实现-Java低级小项目
  详细解决方案

掷骰子游戏窗体实现-Java低级小项目

热度:28   发布时间:2016-04-22 19:29:03.0
掷骰子游戏窗体实现--Java初级小项目

掷骰子

**多线程&&观察者模式

题目要求:《掷骰子》窗体小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大,然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。

分析:这个题目要求灵活运用多线程的相关知识,达到点击开始按钮时,有3个线程启动,分别控制3颗骰子的转动,在3颗骰子全部转完以后,回到主线程计算游戏结果。

 1 //3个线程控制3颗骰子 2 Thread t1 = new Thread(); 3 Thread t2 = new Thread(); 4 Thread t3 = new Thread(); 5 //启动3个线程 6 t1.start(); 7 t2.start(); 8 t3.start(); 9 //将3个线程加入主线程10 t1.join();11 t2.join();12 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.窗体

  1 package com.sxt.dice;  2   3 import java.awt.Color;  4   5 public class DiceFrame extends JFrame implements ActionListener, Observer {  6   7     /**  8      * 《掷骰子》控制台小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,  9      * 以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大, 10      * 然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。 11      *  12      * 运用观察者模式 3个子线程分别控制3个骰子,都已经结束时,通知观察者窗体,窗体观察到所有子线程都结束时,计算游戏结果 13      *  14      */ 15  16     private static final long serialVersionUID = 1L; 17     private JTextField txtPut; 18     private JButton btnStart; 19     private JLabel labResult; 20     private JComboBox<String> comboBox; 21     private JLabel labBigOrSmall; 22     private JLabel labPut; 23     private JLabel labSumMoney; 24     private JLabel labDice3; 25     private JLabel labDice2; 26     private JLabel labDice1; 27     private JLabel labSum; 28     private JLabel labMes; 29  30     private static List<Icon> imgs = new ArrayList<Icon>(); 31  32     public static void main(String[] args) { 33         new DiceFrame(); 34     } 35  36     public DiceFrame() { 37         this.setLocationRelativeTo(null); 38         this.setBounds(200, 50, 380, 297); 39         this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 40         getContentPane().setLayout(null); 41         this.setResizable(false); 42  43         labDice1 = new JLabel(""); 44         labDice1.setIcon(new ImageIcon("img/dices.jpg")); 45         labDice1.setBounds(30, 50, 96, 96); 46         getContentPane().add(labDice1); 47  48         labSum = new JLabel("\u5269\u4F59\u91D1\u989D\uFF1A"); 49         labSum.setBounds(10, 10, 69, 23); 50         getContentPane().add(labSum); 51  52         labDice2 = new JLabel(""); 53         labDice2.setIcon(new ImageIcon("img/dices.jpg")); 54         labDice2.setBounds(136, 50, 96, 96); 55         getContentPane().add(labDice2); 56  57         labDice3 = new JLabel(""); 58         labDice3.setIcon(new ImageIcon("img/dices.jpg")); 59         labDice3.setBounds(242, 50, 96, 96); 60         getContentPane().add(labDice3); 61  62         labSumMoney = new JLabel("3000"); 63         labSumMoney.setForeground(Color.red); 64         labSumMoney.setBounds(86, 10, 63, 23); 65         getContentPane().add(labSumMoney); 66  67         labPut = new JLabel("\u672C\u6B21\u4E0B\u6CE8\uFF1A"); 68         labPut.setToolTipText("0.0"); 69         labPut.setBounds(10, 199, 69, 23); 70         getContentPane().add(labPut); 71  72         txtPut = new JTextField(); 73         txtPut.setBounds(80, 200, 69, 21); 74         getContentPane().add(txtPut); 75         txtPut.setColumns(10); 76  77         labBigOrSmall = new JLabel("\u62BC\uFF1A"); 78         labBigOrSmall.setBounds(45, 232, 34, 27); 79         getContentPane().add(labBigOrSmall); 80  81         comboBox = new JComboBox<String>(); 82         comboBox.setBounds(80, 234, 69, 23); 83         getContentPane().add(comboBox); 84         comboBox.addItem("大"); 85         comboBox.addItem("小"); 86  87         labResult = new JLabel(""); 88         labResult.setBounds(136, 156, 126, 27); 89         getContentPane().add(labResult); 90  91         btnStart = new JButton("START"); 92         btnStart.setBounds(263, 199, 88, 58); 93         getContentPane().add(btnStart); 94  95         labMes = new JLabel("<html><font size=5 color=red>*</font></html>"); 96         labMes.setBounds(152, 203, 101, 15); 97         getContentPane().add(labMes); 98  99         this.setVisible(true);100 101         imgs.add(new ImageIcon("img/1.png"));102         imgs.add(new ImageIcon("img/2.png"));103         imgs.add(new ImageIcon("img/3.png"));104         imgs.add(new ImageIcon("img/4.png"));105         imgs.add(new ImageIcon("img/5.png"));106         imgs.add(new ImageIcon("img/6.png"));107 108         btnStart.addActionListener(this);109     }110 111     @Override112     public void actionPerformed(ActionEvent e) {113         if (e.getSource() == btnStart) {114 115             // 清除上次游戏的结果116             labResult.setText("");117 118             // 获取当前下注金额,用户余额,用户押大还是押小119             String txt = txtPut.getText().trim();120             String remain = labSumMoney.getText().trim();121 122             // 余额不足,不能开始游戏,提示用户充值123             if (Integer.parseInt(remain) <= 0) {124                 JOptionPane.showMessageDialog(null, "当前余额不足,请充值!");125                 return;126             }127 128             // 下注金额合法性检查129             if (txt.length() == 0) {130                 // 提示用户输入131                 labMes.setText("*请输入下注金额");132                 labMes.setForeground(Color.RED);133                 return;134             }135             // 检查用户下注金额是否在有效范围内136             if (Integer.parseInt(txt) <= 0137                     || Integer.parseInt(txt) > Integer.parseInt(remain)) {138                 txtPut.setText("");139                 labMes.setText("下注金额应在0~" + remain + "之间");140                 return;141             }142 143             // 游戏开始后相关项不可更改144             txtPut.setEnabled(false);145             labMes.setText("");146             comboBox.setEnabled(false);147 148             //在主线程上开t1,t2,t3 3个子线程149             Thread t1 = new Thread() {150                 @Override151                 public void run() {152                     //每个子线程上再开子子线程,控制图标变换153                     IconThread t11 = new IconThread(labDice1, imgs);154                     //给t11添加观察者,即当前窗体155                     t11.addObserver(DiceFrame.this);156                     new Thread(t11).start();157                 }158             };159 160             Thread t2 = new Thread() {161                 @Override162                 public void run() {163                     IconThread t21 = new IconThread(labDice2, imgs);164                     t21.addObserver(DiceFrame.this);165                     new Thread(t21).start();166                 }167             };168 169             Thread t3 = new Thread() {170                 @Override171                 public void run() {172                     IconThread t31 = new IconThread(labDice3, imgs);173                     t31.addObserver(DiceFrame.this);174                     new Thread(t31).start();175                 }176             };177 178             t1.start();179             t2.start();180             t3.start();181         }182 183     }184 185     /**186      * 获取骰子点数和187      * 188      * @param lab189      * @return sum190      */191     private int result(JLabel lab) {192         // 获取当前骰子图片193         Icon icon = lab.getIcon();194         int sum = 0;195         for (int i = 0; i < imgs.size(); i++) {196             if (icon.equals(imgs.get(i))) {197                 sum += (i + 1);198                 break;199             }200         }201         return sum;202     }203 204     // 构建所有被观察者的集合205     Vector<Observable> allObservables = new Vector<Observable>();206 207     @Override208     public void update(Observable o, Object arg) {209         System.out.println(o + ".................");210         // 如果集合中不包含当前被观察者,将此被观察者加入集合211         if (allObservables.contains(o) == false) {212             allObservables.add(o);213         }214 215         // 如果集合中被观察者个数为3,说明3个骰子线程已经全部结束216         if (allObservables.size() == 3) {217             // 获取当前下注金额,用户余额,用户押大还是押小218             String txt = txtPut.getText().trim();219             String remain = labSumMoney.getText().trim();220             String bigOrSmall = comboBox.getSelectedItem().toString();221             // 获取每个骰子点数222             int sum1 = result(labDice1);223             int sum2 = result(labDice2);224             int sum3 = result(labDice3);225             System.out.println(sum1 + "-" + sum2 + "-" + sum3);226             int sum = sum1 + sum2 + sum3;227             System.out.println(sum);228 229             if (sum > 9 && "大".equals(bigOrSmall) || sum <= 9230                     && "小".equals(bigOrSmall)) {231 232                 // 奖励玩家相应金额233                 remain = String.valueOf(Integer.parseInt(remain)234                         + Integer.parseInt(txt));235                 labSumMoney.setText(remain);236 237                 // 显示游戏结果238                 labResult.setText("WIN");239                 labResult.setForeground(Color.GREEN);240                 labResult.setFont(new Font("宋体", Font.BOLD, 40));241 242             } else {243                 // 扣除玩家相应金额244                 remain = String.valueOf(Integer.parseInt(remain)245                         - Integer.parseInt(txt));246                 labSumMoney.setText(remain);247 248                 labResult.setText("FAIL");249                 labResult.setForeground(Color.red);250                 labResult.setFont(new Font("宋体", Font.BOLD, 40));251 252             }253             txtPut.setEnabled(true);254             comboBox.setEnabled(true);255             // 本次游戏结束后移除集合中所有线程256             allObservables.removeAll(allObservables);257         }258     }259 260 }

2.线程

 1 package com.sxt.dice; 2  3 import java.util.List; 4 import java.util.Observable; 5 import java.util.Random; 6  7 import javax.swing.Icon; 8 import javax.swing.JLabel; 9 10 public class IconThread extends Observable implements Runnable {11     /**12      * 运用观察者模式,将子线程作为被观察对象,一旦子线程运行完,发生改变,通知观察者13      */14     JLabel lab;15 16     Random random = new Random();17     List<Icon> imgs;18 19     public IconThread(JLabel lab, List<Icon> imgs) {20         this.lab = lab;21         this.imgs = imgs;22 23     }24 25     @Override26     public void run() {27         //设置每颗骰子转动30次28         int count = 30;29         while (count > 0) {30             31             //获取一个随机数[0~6)32             int index = random.nextInt(6);33             //从imgs集合中取相应图片放入lab中34             lab.setIcon(imgs.get(index));35             count--;36             37             try {38                 Thread.sleep(50);39             } catch (InterruptedException e) {40                 // TODO Auto-generated catch block41                 e.printStackTrace();42             }43         }44 45         this.setChanged();// 子线程运行完,发生改变46         this.notifyObservers();// 通知观察者47     }48 }

 

  相关解决方案