Java編寫擲骰子游戲
廢話不多說了,直接奔主題。
**多線程&&觀察者模式
題目要求:《擲骰子》窗體小游戲,在該游戲中,玩家初始擁有1000的金錢,每次輸入押大還是押小,以及下注金額,隨機3個骰子的點數(shù),如果3個骰子的總點數(shù)小于等于9,則開小,否則開大,然后判斷玩家是否押對,如果未押對則扣除下注金額,如果押對則獎勵和玩家下注金額相同的金錢。
分析:這個題目要求靈活運用多線程的相關(guān)知識,達到點擊開始按鈕時,有3個線程啟動,分別控制3顆骰子的轉(zhuǎn)動,在3顆骰子全部轉(zhuǎn)完以后,回到主線程計算游戲結(jié)果。
//個線程控制顆骰子 Thread t = new Thread(); Thread t = new Thread(); Thread t = new Thread(); //啟動個線程 t.start(); t.start(); t.start(); //將個線程加入主線程 t.join(); t.join(); t.join();
But,,,寫完代碼以后發(fā)現(xiàn),這樣做雖然能夠保證游戲能夠正確運行,但是當我點擊開始按鈕時,由于3個骰子線程都是直接開在主線程上的,點擊開始按鈕時,按鈕出現(xiàn)下沉情況,子線程一直在后臺運行,我窗體中的圖片根本不會發(fā)生改變,而是直接顯示最后的結(jié)果,意思就是骰子一直在后臺轉(zhuǎn)動,不在前臺的窗體中及時更新顯示。后來在網(wǎng)上苦苦找尋,大神們說如果想要通過點擊JButton使窗體中的JLabel/JTextFeild等其他組件及時更新,直接在JButton的監(jiān)聽事件的實現(xiàn)方法里面直接創(chuàng)建匿名線程,也就是說直接在actionPerformed()方法中修改代碼即可,這樣能保證你的組件中內(nèi)容的及時變換,實現(xiàn)非常炫酷的效果。
代碼如下:
public void actionPerformed(ActionEvent e) { new Thread(new Runnable() { @Override public void run() { //將外部線程類轉(zhuǎn)移到窗體內(nèi)部 } }).start(); }
But,,,But,,, 雖然非常炫酷了,能夠?qū)崿F(xiàn)圖片的及時更新了,游戲結(jié)果卻錯了,每次我的骰子還在轉(zhuǎn)動呢,我的游戲結(jié)果卻早早的就出來了。
原因:3根骰子線程屬于子線程,窗體線程屬于主線程,問題就在于:子線程可以通過變成精靈線程來保持與主線程的同生死,但是主線程卻無法控制子線程何時死亡,只有等待子線程執(zhí)行完所屬的run()方法,結(jié)束線程后才知道。
解決方法:在主線程(main)中開3個子線程(t1,t2,t3),在每個子線程上再開一個子子線程(t11,t21,t31)。
t1,t2,t3只運行一次,負責(zé)創(chuàng)建子子線程;t11,t21,t31每個線程運行多次,負責(zé)控制窗體中的圖標及時更新。
這樣主線程就不受子線程的影響,開始按鈕也不回出現(xiàn)下沉的情況。
但是同樣在此處使用join方法也是hold不住子線程的,畢竟t1,t2,t3只運行了一次,join對他們來說根本不起作用,想要掌控t11,t21,t31,最容易理解的辦法,就是使用觀察者模式了。
將窗體看做觀察者,子線程看做被觀察者。子線程運行完時,通知觀察者我已經(jīng)運行完成,當觀察者觀察到子線程全都運行完時,才開始運行后續(xù)步驟。
全部代碼:
1.窗體
package com.sxt.dice; import java.awt.Color; public class DiceFrame extends JFrame implements ActionListener, Observer { /** * 《擲骰子》控制臺小游戲,在該游戲中,玩家初始擁有的金錢,每次輸入押大還是押小, * 以及下注金額,隨機個骰子的點數(shù),如果個骰子的總點數(shù)小于等于,則開小,否則開大, * 然后判斷玩家是否押對,如果未押對則扣除下注金額,如果押對則獎勵和玩家下注金額相同的金錢。 * * 運用觀察者模式 個子線程分別控制個骰子,都已經(jīng)結(jié)束時,通知觀察者窗體,窗體觀察到所有子線程都結(jié)束時,計算游戲結(jié)果 * */ private static final long serialVersionUID = L; private JTextField txtPut; private JButton btnStart; private JLabel labResult; private JComboBox<String> comboBox; private JLabel labBigOrSmall; private JLabel labPut; private JLabel labSumMoney; private JLabel labDice; private JLabel labDice; private JLabel labDice; 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(, , , ); this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); getContentPane().setLayout(null); this.setResizable(false); labDice = new JLabel(""); labDice.setIcon(new ImageIcon("img/dices.jpg")); labDice.setBounds(, , , ); getContentPane().add(labDice); labSum = new JLabel("\u\uF\uD\uD\uFFA"); labSum.setBounds(, , , ); getContentPane().add(labSum); labDice = new JLabel(""); labDice.setIcon(new ImageIcon("img/dices.jpg")); labDice.setBounds(, , , ); getContentPane().add(labDice); labDice = new JLabel(""); labDice.setIcon(new ImageIcon("img/dices.jpg")); labDice.setBounds(, , , ); getContentPane().add(labDice); labSumMoney = new JLabel(""); labSumMoney.setForeground(Color.red); labSumMoney.setBounds(, , , ); getContentPane().add(labSumMoney); labPut = new JLabel("\uC\uB\uEB\uCE\uFFA"); labPut.setToolTipText("."); labPut.setBounds(, , , ); getContentPane().add(labPut); txtPut = new JTextField(); txtPut.setBounds(, , , ); getContentPane().add(txtPut); txtPut.setColumns(); labBigOrSmall = new JLabel("\uBC\uFFA"); labBigOrSmall.setBounds(, , , ); getContentPane().add(labBigOrSmall); comboBox = new JComboBox<String>(); comboBox.setBounds(, , , ); getContentPane().add(comboBox); comboBox.addItem("大"); comboBox.addItem("小"); labResult = new JLabel(""); labResult.setBounds(, , , ); getContentPane().add(labResult); btnStart = new JButton("START"); btnStart.setBounds(, , , ); getContentPane().add(btnStart); labMes = new JLabel("<html><font size= color=red>*</font></html>"); labMes.setBounds(, , , ); getContentPane().add(labMes); this.setVisible(true); imgs.add(new ImageIcon("img/.png")); imgs.add(new ImageIcon("img/.png")); imgs.add(new ImageIcon("img/.png")); imgs.add(new ImageIcon("img/.png")); imgs.add(new ImageIcon("img/.png")); imgs.add(new ImageIcon("img/.png")); btnStart.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == btnStart) { // 清除上次游戲的結(jié)果 labResult.setText(""); // 獲取當前下注金額,用戶余額,用戶押大還是押小 String txt = txtPut.getText().trim(); String remain = labSumMoney.getText().trim(); // 余額不足,不能開始游戲,提示用戶充值 if (Integer.parseInt(remain) <= ) { JOptionPane.showMessageDialog(null, "當前余額不足,請充值!"); return; } // 下注金額合法性檢查 if (txt.length() == ) { // 提示用戶輸入 labMes.setText("*請輸入下注金額"); labMes.setForeground(Color.RED); return; } // 檢查用戶下注金額是否在有效范圍內(nèi) if (Integer.parseInt(txt) <= || Integer.parseInt(txt) > Integer.parseInt(remain)) { txtPut.setText(""); labMes.setText("下注金額應(yīng)在~" + remain + "之間"); return; } // 游戲開始后相關(guān)項不可更改 txtPut.setEnabled(false); labMes.setText(""); comboBox.setEnabled(false); //在主線程上開t,t,t 個子線程 Thread t = new Thread() { @Override public void run() { //每個子線程上再開子子線程,控制圖標變換 IconThread t = new IconThread(labDice, imgs); //給t添加觀察者,即當前窗體 t.addObserver(DiceFrame.this); new Thread(t).start(); } }; Thread t = new Thread() { @Override public void run() { IconThread t = new IconThread(labDice, imgs); t.addObserver(DiceFrame.this); new Thread(t).start(); } }; Thread t = new Thread() { @Override public void run() { IconThread t = new IconThread(labDice, imgs); t.addObserver(DiceFrame.this); new Thread(t).start(); } }; t.start(); t.start(); t.start(); } } /** * 獲取骰子點數(shù)和 * * @param lab * @return sum */ private int result(JLabel lab) { // 獲取當前骰子圖片 Icon icon = lab.getIcon(); int sum = ; for (int i = ; i < imgs.size(); i++) { if (icon.equals(imgs.get(i))) { sum += (i + ); break; } } return sum; } // 構(gòu)建所有被觀察者的集合 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); } // 如果集合中被觀察者個數(shù)為,說明個骰子線程已經(jīng)全部結(jié)束 if (allObservables.size() == ) { // 獲取當前下注金額,用戶余額,用戶押大還是押小 String txt = txtPut.getText().trim(); String remain = labSumMoney.getText().trim(); String bigOrSmall = comboBox.getSelectedItem().toString(); // 獲取每個骰子點數(shù) int sum = result(labDice); int sum = result(labDice); int sum = result(labDice); System.out.println(sum + "-" + sum + "-" + sum); int sum = sum + sum + sum; System.out.println(sum); if (sum > && "大".equals(bigOrSmall) || sum <= && "小".equals(bigOrSmall)) { // 獎勵玩家相應(yīng)金額 remain = String.valueOf(Integer.parseInt(remain) + Integer.parseInt(txt)); labSumMoney.setText(remain); // 顯示游戲結(jié)果 labResult.setText("WIN"); labResult.setForeground(Color.GREEN); labResult.setFont(new Font("宋體", Font.BOLD, )); } else { // 扣除玩家相應(yīng)金額 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, )); } txtPut.setEnabled(true); comboBox.setEnabled(true); // 本次游戲結(jié)束后移除集合中所有線程 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 { /** * 運用觀察者模式,將子線程作為被觀察對象,一旦子線程運行完,發(fā)生改變,通知觀察者 */ 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() { //設(shè)置每顆骰子轉(zhuǎn)動次 int count = ; while (count > ) { //獲取一個隨機數(shù)[~) int index = random.nextInt(); //從imgs集合中取相應(yīng)圖片放入lab中 lab.setIcon(imgs.get(index)); count--; try { Thread.sleep(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.setChanged();// 子線程運行完,發(fā)生改變 this.notifyObservers();// 通知觀察者 } }
以上所述就是關(guān)于Java編寫擲骰子游戲的全部內(nèi)容,希望大家喜歡。
相關(guān)文章
java實現(xiàn)操作系統(tǒng)中的最佳置換Optimal算法
這篇文章主要介紹了java實現(xiàn)操作系統(tǒng)中的最佳置換Optimal算法 ,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Java實現(xiàn)Excel導(dǎo)入導(dǎo)出數(shù)據(jù)庫的方法示例
這篇文章主要介紹了Java實現(xiàn)Excel導(dǎo)入導(dǎo)出數(shù)據(jù)庫的方法,結(jié)合實例形式分析了java針對Excel的讀寫及數(shù)據(jù)庫操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2017-08-08SpringBoot任務(wù)調(diào)度器的實現(xiàn)代碼
SpringBoot自帶了任務(wù)調(diào)度器,通過注解的方式使用。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12intelliJ idea 2023 配置Tomcat 8圖文教程
這篇文章主要介紹了intelliJ idea 2023 配置Tomcat 8教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06Java使用新浪微博API開發(fā)微博應(yīng)用的基本方法
這篇文章主要介紹了Java使用新浪微博API開發(fā)微博應(yīng)用的基本方法,文中還給出了一個不使用任何SDK實現(xiàn)Oauth授權(quán)并實現(xiàn)簡單的發(fā)布微博功能的實現(xiàn)方法,需要的朋友可以參考下2015-11-11