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 }
  • 相关阅读:
    DC中为什么要用Uniquify?
    hdu 1596 find the safest road
    hdu2112 HDU Today
    hdu 2066 一个人的旅行
    poj 3026 Borg Maze
    poj 1979 Red and Black
    poj 1321 棋盘问题
    hdu 1010 Tempter of the Bone
    hdu 4861 Couple doubi
    codeforces584B Kolya and Tanya
  • 原文地址:https://www.cnblogs.com/yeshadow937/p/4997312.html
Copyright © 2011-2022 走看看