掷骰子游戏窗体实现--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每个线程运行多次,负责控制窗体中的图标及时更新。

 

掷骰子游戏窗体实现--Java初级小项目_第1张图片

这样主线程就不受子线程的影响,开始按钮也不回出现下沉的情况。

但是同样在此处使用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     @Override
112     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) <= 0
137                     || 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                 @Override
151                 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                 @Override
162                 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                 @Override
171                 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 lab
189      * @return sum
190      */
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     @Override
208     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 <= 9
230                     && "小".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     @Override
26     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 block
41                 e.printStackTrace();
42             }
43         }
44 
45         this.setChanged();// 子线程运行完,发生改变
46         this.notifyObservers();// 通知观察者
47     }
48 }

 

你可能感兴趣的:(掷骰子游戏窗体实现--Java初级小项目)