zoukankan      html  css  js  c++  java
  • 掷骰子游戏窗体实现--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("u5269u4F59u91D1u989DuFF1A");
     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("u672Cu6B21u4E0Bu6CE8uFF1A");
     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("u62BCuFF1A");
     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 }
  • 相关阅读:
    ruoyi管理系统建立子项目,卡住
    JSON
    各类求自然数幂和方法
    一个关于序列区间gcd的小trick
    【JZOJ6654】【2020.05.27省选模拟】数据结构
    【JZOJ6569】【GDOI2020模拟】夕张的改造 (kaisou)
    拉格朗日插值法
    【JZOJ1914】【2011集训队出题】最短路
    【JZOJ4817】【NOIP2016提高A组五校联考4】square
    【JZOJ4816】【NOIP2016提高A组五校联考4】label
  • 原文地址:https://www.cnblogs.com/yeshadow937/p/4997312.html
Copyright © 2011-2022 走看看