擲骰子游戲窗體實現--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     @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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM