俄罗斯方块 Java 版

   1 package ersblockgamedemo;
   2 
   3 /**
   4  *
   5  * @author jiangnan314
   6  */
   7 import java.applet.Applet;
   8 import java.applet.AudioClip;
   9 import java.awt.*;
  10 import java.awt.event.*;
  11 import java.text.DateFormat;
  12 import java.text.SimpleDateFormat;
  13 import java.util.Date;
  14 import javax.swing.*;
  15 import javax.swing.border.Border;
  16 import javax.swing.border.EtchedBorder;
  17 
  18 /**
  19  * 游戏主类,继承自JFrame类,负责游戏的全局控制。 内含: 1.一个GameCanvas画布类的实例对象,
  20  * 2.一个保存当前活动块(ErsBlock)实例的对象; 3.一个保存当前控制面板(ControlPanel)实例的对象;
  21  */
  22 public class ErsBlocksGame extends JFrame {
  23 
  24     /**
  25      * 每填满一行计多少分
  26      */
  27     public final static int PER_LINE_SCORE = 100;
  28     /**
  29      * 积多少分以后能升级
  30      */
  31     public final static int PER_LEVEL_SCORE = PER_LINE_SCORE * 20;
  32     /**
  33      * 最大级数是10级
  34      */
  35     public final static int MAX_LEVEL = 10;
  36     /**
  37      * 默认级数是2
  38      */
  39     public final static int DEFAULT_LEVEL = 2;
  40     private GameCanvas canvas;
  41     private ErsBlock block;
  42     private FileDialog dialog = new FileDialog(this);
  43     private boolean playing = false;
  44     private AudioClip audioclip;
  45     private ControlPanel ctrlPanel;
  46     private JMenuBar bar = new JMenuBar();
  47     private JMenu mGame = new JMenu(" 游戏"),
  48             mControl = new JMenu(" 控制"),
  49             mWindowStyle = new JMenu("游戏风格"),
  50             mMusic = new JMenu("设置游戏音乐"),
  51             mInfo = new JMenu("帮助");
  52     private JMenuItem miNewGame = new JMenuItem("新游戏"),
  53             miSetBlockColor = new JMenuItem("设置方块颜色..."),
  54             miSetBackColor = new JMenuItem("设置背景颜色..."),
  55             miTurnHarder = new JMenuItem("升高游戏难度"),
  56             miTurnEasier = new JMenuItem("降低游戏难度"),
  57             miExit = new JMenuItem("退出"),
  58             miPlay = new JMenuItem("开始"),
  59             miPause = new JMenuItem("暂停"),
  60             miResume = new JMenuItem("恢复"),
  61             miOpen = new JMenuItem("打开"),
  62             playMusic = new JMenuItem("播放"),
  63             stopMusic = new JMenuItem("停止"),
  64             miStop = new JMenuItem("终止游戏"),
  65             loopMusic = new JMenuItem("循环"),
  66             miAuthor = new JMenuItem("关于本游戏");
  67     private JCheckBoxMenuItem miAsWindows = new JCheckBoxMenuItem("Windows"),
  68             miAsMotif = new JCheckBoxMenuItem("Motif"),
  69             miAsMetal = new JCheckBoxMenuItem("Metal", true);
  70 
  71     /**
  72      * 主游戏类的构造方法
  73      *
  74      * @param title String ,窗口标题
  75      */
  76     public ErsBlocksGame(String title) {
  77         super(title);                                          //设置标题
  78         setSize(328, 405);                                     //设置窗口大小                  
  79         setLocationRelativeTo(null);                             //设置窗口居中
  80 
  81         creatMenu();
  82         Container container = getContentPane();
  83         container.setLayout(new BorderLayout(6, 0));              //设置窗口的布局管理器
  84         canvas = new GameCanvas(20, 15);                          //新建游戏画布
  85         ctrlPanel = new ControlPanel(this);                        //新建控制面板
  86         container.add(canvas, BorderLayout.CENTER);                //左边加上画布
  87         container.add(ctrlPanel, BorderLayout.EAST);               //右边加上控制面板
  88 
  89         addWindowListener(new WindowAdapter() {                    //注册窗口事件。当点击关闭按钮时,结束游戏,系统退出。
  90             @Override
  91             public void windowClosing(WindowEvent we) {
  92                 stopGame();
  93                 System.exit(0);
  94             }
  95         });
  96         addComponentListener(new ComponentAdapter() {
  97             @Override
  98             public void componentResized(ComponentEvent ce) {
  99                 canvas.fanning();
 100             }
 101         });
 102 
 103         setVisible(true);
 104         canvas.fanning();
 105 
 106 
 107     }
 108 
 109     /**
 110      * 让游戏复位
 111      */
 112     public void reset() {                            //画布复位,控制面板复位
 113         ctrlPanel.setPlayButtonEnable(true);
 114         ctrlPanel.setPauseButtonEnable(false);
 115         ctrlPanel.setPauseButtonLabel(true);
 116         ctrlPanel.setStopButtonEnable(false);
 117         ctrlPanel.setTurnLevelDownButtonEnable(true);
 118         ctrlPanel.setTurnLevelUpButtonEnable(true);
 119         miPlay.setEnabled(true);
 120         miPause.setEnabled(false);
 121         miResume.setEnabled(false);
 122         miStop.setEnabled(false);
 123         ctrlPanel.reset();
 124         canvas.reset();
 125     }
 126 
 127     /**
 128      * 判断游戏是否还在进行
 129      *
 130      * @return boolean,true -还在运行,false-已经停止
 131      */
 132     public boolean isPlaying() {
 133         return playing;
 134     }
 135 
 136     /**
 137      * 得到当前活动的块
 138      *
 139      * @return ErsBlock,当前活动块的引用
 140      */
 141     public ErsBlock getCurBlock() {
 142         return block;
 143     }
 144 
 145     /**
 146      * 得到当前画布
 147      *
 148      * @return GameCanvas,当前画布的引用
 149      */
 150     public GameCanvas getCanvas() {
 151         return canvas;
 152     }
 153 
 154     /**
 155      * 开始游戏
 156      */
 157     public void playGame() {
 158         play();
 159         ctrlPanel.setPlayButtonEnable(false);
 160         ctrlPanel.setPauseButtonEnable(true);
 161         ctrlPanel.setPauseButtonLabel(true);
 162         ctrlPanel.setStopButtonEnable(true);
 163         ctrlPanel.setTurnLevelDownButtonEnable(false);
 164         ctrlPanel.setTurnLevelUpButtonEnable(false);
 165         miPlay.setEnabled(false);
 166         miPause.setEnabled(true);
 167         miResume.setEnabled(false);
 168         miStop.setEnabled(true);
 169         miTurnHarder.setEnabled(false);
 170         miTurnEasier.setEnabled(false);
 171         ctrlPanel.requestFocus();              //设置焦点
 172     }
 173 
 174     /**
 175      * 游戏暂停
 176      */
 177     public void pauseGame() {
 178         if (block != null) {
 179             block.pauseMove();
 180         }
 181         ctrlPanel.setPlayButtonEnable(false);
 182         ctrlPanel.setPauseButtonLabel(false);
 183         ctrlPanel.setStopButtonEnable(true);
 184         miPlay.setEnabled(false);
 185         miPause.setEnabled(false);
 186         miResume.setEnabled(true);
 187         miStop.setEnabled(true);
 188     }
 189 
 190     /**
 191      * 让暂停中的游戏继续
 192      */
 193     public void resumeGame() {
 194         if (block != null) {
 195             block.resumeMove();
 196         }
 197         ctrlPanel.setPlayButtonEnable(false);
 198         ctrlPanel.setPauseButtonEnable(true);
 199         ctrlPanel.setPauseButtonLabel(true);
 200         miPause.setEnabled(true);
 201         miResume.setEnabled(false);
 202         ctrlPanel.requestFocus();
 203     }
 204 
 205     /**
 206      * 用户停止游戏
 207      */
 208     public void stopGame() {
 209         playing = false;
 210         if (block != null) {
 211             block.stopMove();
 212         }
 213         ctrlPanel.setPlayButtonEnable(true);
 214         ctrlPanel.setPauseButtonEnable(false);
 215         ctrlPanel.setPauseButtonLabel(true);
 216         ctrlPanel.setStopButtonEnable(false);
 217         ctrlPanel.setTurnLevelDownButtonEnable(true);
 218         ctrlPanel.setTurnLevelUpButtonEnable(true);
 219         miPlay.setEnabled(true);
 220         miPause.setEnabled(false);
 221         miResume.setEnabled(false);
 222         miStop.setEnabled(false);
 223         miTurnHarder.setEnabled(true);
 224         miTurnEasier.setEnabled(true);
 225     }
 226 
 227     public AudioClip getAudioClip() {
 228         return audioclip;
 229     }
 230 
 231     public void setAudioClip(AudioClip clip) {
 232         this.audioclip = clip;
 233     }
 234 
 235     public void playAudio() {
 236         if (audioclip != null) {
 237             audioclip.play();
 238         }
 239     }
 240 
 241     public void loop() {
 242         if (audioclip != null) {
 243             loop();
 244         }
 245     }
 246 
 247     public void stop() {
 248         if (audioclip != null) {
 249             stop();
 250         }
 251     }
 252 
 253     /**
 254      * 得到游戏者设置的难度
 255      *
 256      * @return int ,游戏难度1-MAX_LEVEL
 257      */
 258     public int getLevel() {
 259         return ctrlPanel.getLevel();
 260     }
 261 
 262     /**
 263      * 用户设置游戏难度
 264      *
 265      * @param level int ,游戏难度1-MAX_LEVEL
 266      */
 267     public void setLevel(int level) {
 268         if (level < 11 && level > 0) {
 269             ctrlPanel.setLevel(level);
 270         }
 271     }
 272 
 273     /**
 274      * 得到游戏积分
 275      *
 276      * @return int,积分
 277      */
 278     public int getScore() {
 279         if (canvas != null) {
 280             return canvas.getScore();
 281         }
 282         return 0;
 283     }
 284 
 285     /**
 286      * 得到自上次升级以来的游戏积分,升级以后,此积分清零
 287      *
 288      * @return int,积分
 289      */
 290     public int getScoreForLevelUpdate() {
 291         if (canvas != null) {
 292             return canvas.getScoreForLevelUpdate();
 293         }
 294         return 0;
 295     }
 296 
 297     /**
 298      * 当积分累积到一定数值时,升一次级
 299      *
 300      * @return Boolean,true-update succeed,false-update fail
 301      */
 302     public boolean levelUpdate() {
 303         int curLevel = getLevel();
 304         if (curLevel < MAX_LEVEL) {
 305             setLevel(curLevel + 1);
 306             canvas.resetScoreForLevelUpdate();
 307             return true;
 308         }
 309         return false;
 310     }
 311 
 312     /**
 313      * 游戏开始
 314      */
 315     private void play() {
 316         reset();
 317         playing = true;
 318         Thread thread = new Thread(new Game());
 319         thread.start();
 320     }
 321 
 322     /**
 323      * 报告游戏结束了
 324      */
 325     private void reportGameOver() {
 326         new gameOverDialog(this, "俄罗斯方块", "游戏结束,您的得分为" + canvas.getScore());
 327     }
 328 
 329     /**
 330      * 建立并设置窗口菜单
 331      */
 332     private void creatMenu() {
 333         bar.add(mGame);
 334         bar.add(mControl);
 335         bar.add(mWindowStyle);
 336         bar.add(mMusic);
 337         bar.add(mInfo);
 338         mGame.add(miNewGame);
 339         mGame.addSeparator();
 340         mGame.add(miSetBlockColor);
 341         mGame.add(miSetBackColor);
 342         mGame.addSeparator();
 343         mGame.add(miTurnHarder);
 344         mGame.add(miTurnEasier);
 345         mGame.addSeparator();
 346         mGame.add(miExit);
 347         mControl.add(miPlay);
 348         miPlay.setEnabled(true);
 349         mControl.add(miPause);
 350         miPause.setEnabled(false);
 351         mControl.add(miResume);
 352         miResume.setEnabled(false);
 353         mControl.add(miStop);
 354         miStop.setEnabled(false);
 355         mWindowStyle.add(miAsWindows);
 356         mWindowStyle.add(miAsMotif);
 357         mWindowStyle.add(miAsMetal);
 358         mMusic.add(miOpen);
 359         mMusic.add(playMusic);
 360         mMusic.add(stopMusic);
 361         mMusic.add(loopMusic);
 362         mInfo.add(miAuthor);
 363         setJMenuBar(bar);
 364 
 365         miPause.setAccelerator(KeyStroke.getKeyStroke(
 366                 KeyEvent.VK_P, KeyEvent.CTRL_MASK));
 367         miResume.setAccelerator(KeyStroke.getKeyStroke(
 368                 KeyEvent.VK_R, KeyEvent.CTRL_MASK));
 369 
 370         miNewGame.addActionListener(new ActionListener() {
 371             @Override
 372             public void actionPerformed(ActionEvent e) {
 373                 stopGame();
 374                 reset();
 375                 setLevel(DEFAULT_LEVEL);
 376             }
 377         });
 378         miSetBlockColor.addActionListener(new ActionListener() {
 379             @Override
 380             public void actionPerformed(ActionEvent e) {
 381                 Color newFrontColor =
 382                         JColorChooser.showDialog(ErsBlocksGame.this, "设置方块颜色", canvas.getBlockColor());
 383                 if (newFrontColor != null) {
 384                     canvas.setBlockColor(newFrontColor);
 385                 }
 386             }
 387         });
 388         miSetBackColor.addActionListener(new ActionListener() {
 389             @Override
 390             public void actionPerformed(ActionEvent e) {
 391                 Color newBackColor =
 392                         JColorChooser.showDialog(ErsBlocksGame.this, "设置背景颜色", canvas.getBackgroundColor());
 393                 if (newBackColor != null) {
 394                     canvas.setBackgroundColor(newBackColor);
 395                 }
 396             }
 397         });
 398         miAuthor.addActionListener(new ActionListener() {                        //定义菜单栏"关于"的功能,弹出确认框。
 399             @Override
 400             public void actionPerformed(ActionEvent e) {
 401                 JOptionPane.showMessageDialog(null, "© Copyright 姜楠-张卓\n" + "  All Rights Reserved.", "关于俄罗斯方块 - 2013", 1);
 402             }
 403         });
 404         miTurnHarder.addActionListener(new ActionListener() {
 405             @Override
 406             public void actionPerformed(ActionEvent e) {
 407                 int curLevel = getLevel();
 408                 if (!playing && curLevel < MAX_LEVEL) {
 409                     setLevel(curLevel + 1);
 410                 }
 411             }
 412         });
 413         miTurnEasier.addActionListener(new ActionListener() {
 414             @Override
 415             public void actionPerformed(ActionEvent e) {
 416                 int curLevel = getLevel();
 417                 if (!playing && curLevel > 1) {
 418                     setLevel(curLevel - 1);
 419                 }
 420             }
 421         });
 422         miExit.addActionListener(new ActionListener() {
 423             @Override
 424             public void actionPerformed(ActionEvent e) {
 425                 System.exit(0);
 426             }
 427         });
 428         miPlay.addActionListener(new ActionListener() {
 429             @Override
 430             public void actionPerformed(ActionEvent e) {
 431                 playGame();
 432             }
 433         });
 434         miPause.addActionListener(new ActionListener() {
 435             @Override
 436             public void actionPerformed(ActionEvent e) {
 437                 pauseGame();
 438             }
 439         });
 440         miResume.addActionListener(new ActionListener() {
 441             @Override
 442             public void actionPerformed(ActionEvent e) {
 443                 resumeGame();
 444             }
 445         });
 446         miStop.addActionListener(new ActionListener() {
 447             @Override
 448             public void actionPerformed(ActionEvent e) {
 449                 stopGame();
 450             }
 451         });
 452         miOpen.addActionListener(new ActionListener() {
 453             @Override
 454             public void actionPerformed(ActionEvent e) {
 455                 dialog.setVisible(true);
 456                 if (dialog.getFile() != null) {
 457                     String filename = dialog.getDirectory() + dialog.getFile();
 458                     try {
 459                         setAudioClip(Applet.newAudioClip((new java.io.File(filename)).toURI().toURL()));
 460                         playAudio();
 461                     } catch (Exception ex) {
 462                         ex.printStackTrace();
 463                     }
 464                 }
 465             }
 466         });
 467         playMusic.addActionListener(new ActionListener() {
 468             @Override
 469             public void actionPerformed(ActionEvent e) {
 470                 playAudio();
 471                 playMusic.setEnabled(false);
 472                 stopMusic.setEnabled(true);
 473             }
 474         });
 475         stopMusic.addActionListener(new ActionListener() {
 476             @Override
 477             public void actionPerformed(ActionEvent e) {
 478                 stop();
 479             }
 480         });
 481         loopMusic.addActionListener(new ActionListener() {
 482             @Override
 483             public void actionPerformed(ActionEvent e) {
 484                 loop();
 485             }
 486         });
 487         miAsWindows.addActionListener(new ActionListener() {                    //改变游戏风格,Windows风格,motif风格,metal风格
 488             @Override
 489             public void actionPerformed(ActionEvent ae) {
 490                 String plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
 491                 setWindowStyle(plaf);
 492                 canvas.fanning();                                               //以新的界面风格的重新绘制画布和控制面板
 493                 ctrlPanel.fanning();
 494                 miAsWindows.setState(true);                                     //
 495                 miAsMetal.setState(false);
 496                 miAsMotif.setState(false);
 497             }
 498         });
 499         miAsMotif.addActionListener(new ActionListener() {
 500             @Override
 501             public void actionPerformed(ActionEvent e) {
 502                 String plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
 503                 setWindowStyle(plaf);
 504                 canvas.fanning();
 505                 ctrlPanel.fanning();
 506                 miAsWindows.setState(false);
 507                 miAsMetal.setState(false);
 508                 miAsMotif.setState(true);
 509             }
 510         });
 511         miAsMetal.addActionListener(new ActionListener() {
 512             @Override
 513             public void actionPerformed(ActionEvent e) {
 514                 String plaf = "javax.swing.plaf.metal.MetalLookAndFeel";
 515                 setWindowStyle(plaf);
 516                 canvas.fanning();
 517                 ctrlPanel.fanning();
 518                 miAsWindows.setState(false);
 519                 miAsMetal.setState(true);
 520                 miAsMotif.setState(false);
 521             }
 522         });
 523 
 524 
 525     }
 526 
 527     /**
 528      * 根据字串设置窗口外观
 529      *
 530      * @param plaf String,窗口外观的描述
 531      */
 532     private void setWindowStyle(String plaf) {
 533         try {
 534             UIManager.setLookAndFeel(plaf);
 535             SwingUtilities.updateComponentTreeUI(this);
 536         } catch (Exception e) {
 537         }
 538     }
 539 
 540     /**
 541      * 一轮游戏过程,实现了Runnable接口 一轮游戏是一个大循环,在这个循环中,每隔100毫秒, 检查游戏中的当前块是否已经到底了,如果没有,
 542      * 就继续等待。如果到底了,就看有没有全填满的行, 如果有就删除它,并为游戏者加分,同时随机产生一个 新的当前块人 让它自动落下。
 543      * 当新产生一个块时,先检查画布最顶上的一行是否已经 被占了,如果是,可以判断Game Over 了。
 544      */
 545     private class Game implements Runnable {
 546 
 547         @Override
 548         public void run() {
 549             int col = (int) (Math.random() * (canvas.getCols() - 3));
 550             int style = ErsBlock.STYLES[ (int) (Math.random() * 7)][(int) (Math.random() * 4)];
 551 
 552             while (playing) {
 553                 if (block != null) {   //第一次循环时,block为空
 554                     if (block.isAlive()) {
 555                         try {
 556                             Thread.currentThread().sleep(500);
 557                         } catch (InterruptedException ie) {
 558                             ie.printStackTrace();
 559                         }
 560                         continue;
 561                     }
 562                 }
 563 
 564                 checkFullLine();    //检查是否有全填满的行
 565 
 566                 if (isGameOver()) {
 567                     reportGameOver();
 568                     miPlay.setEnabled(true);
 569                     miPause.setEnabled(false);
 570                     miResume.setEnabled(false);
 571                     miStop.setEnabled(false);
 572                     ctrlPanel.setPlayButtonEnable(true);
 573                     ctrlPanel.setPauseButtonLabel(false);
 574                     ctrlPanel.setStopButtonEnable(false);
 575                     return;
 576                 }
 577 
 578                 block = new ErsBlock(style, -1, col, getLevel(), canvas);
 579                 block.start();
 580 
 581                 col = (int) (Math.random() * (canvas.getCols() - 3));
 582                 style = ErsBlock.STYLES[ (int) (Math.random() * 7)][(int) (Math.random() * 4)];
 583 
 584                 ctrlPanel.setTipStyle(style);
 585             }
 586         }
 587 
 588         //检查画布中是否有全填满的行,如果有就删之
 589         public void checkFullLine() {
 590             for (int i = 0; i < canvas.getRows(); i++) {
 591                 int row = -1;
 592                 boolean fullLineColorBox = true;
 593                 for (int j = 0; j < canvas.getCols(); j++) {
 594                     if (!canvas.getBox(i, j).isColorBox()) {
 595                         fullLineColorBox = false;
 596                         break;
 597                     }
 598                 }
 599                 if (fullLineColorBox) {
 600                     row = i--;
 601                     canvas.removeLine(row);
 602                 }
 603             }
 604         }
 605 
 606         //根据最顶行是否被占,判断游戏是否已经结束了
 607         //@return boolean ,true-游戏结束了,false-游戏未结束
 608         private boolean isGameOver() {
 609             for (int i = 0; i < canvas.getCols(); i++) {
 610                 ErsBox box = canvas.getBox(0, i);
 611                 if (box.isColorBox()) {
 612                     return true;
 613                 }
 614             }
 615             return false;
 616         }
 617     }
 618 
 619     /**
 620      * 定义GameOver对话框。
 621      */
 622     private class gameOverDialog extends JDialog implements ActionListener {
 623 
 624         private JButton againButton, exitButton;
 625         private Border border = new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140));
 626 
 627         public gameOverDialog(JFrame parent, String title, String message) {
 628             super(parent, title, true);
 629             if (parent != null) {
 630                 setSize(240, 120);
 631                 this.setLocationRelativeTo(parent);
 632                 JPanel messagePanel = new JPanel();
 633                 messagePanel.add(new JLabel(message));
 634                 messagePanel.setBorder(border);
 635                 Container container = this.getContentPane();
 636                 container.setLayout(new GridLayout(2, 0, 0, 10));
 637                 container.add(messagePanel);
 638                 JPanel choosePanel = new JPanel();
 639                 choosePanel.setLayout(new GridLayout(0, 2, 4, 0));
 640                 container.add(choosePanel);
 641                 againButton = new JButton("再玩一局");
 642                 exitButton = new JButton("退出游戏");
 643                 choosePanel.add(new JPanel().add(againButton));
 644                 choosePanel.add(new JPanel().add(exitButton));
 645                 choosePanel.setBorder(border);
 646             }
 647             againButton.addActionListener(this);
 648             exitButton.addActionListener(this);
 649             this.setVisible(true);
 650         }
 651 
 652         @Override
 653         public void actionPerformed(ActionEvent e) {
 654             if (e.getSource() == againButton) {
 655                 this.setVisible(false);
 656                 reset();
 657             } else if (e.getSource() == exitButton) {
 658                 stopGame();
 659                 System.exit(0);
 660 
 661             }
 662         }
 663     }
 664 
 665     /**
 666      * 程序入口函数
 667      *
 668      * @param args String[],附带的命令行参数
 669      */
 670     public static void main(String[] args) {
 671         new ErsBlocksGame("俄罗斯方块:ZJUT-CS&T");
 672     }
 673 }
 674 
 675 /**
 676  * 画布类,内有<行数>*<列数> 个方格类实例。 继承自JPanel类。 ErsBlock线程类动态改变画布类的方格颜色,画布类通过
 677  * 检查方格颜色来体现ErsBlock块的移动情况。
 678  */
 679 class GameCanvas extends JPanel {
 680 
 681     private Color backColor = Color.LIGHT_GRAY, frontColor = Color.orange;
 682     private int rows, cols, score = 0, scoreForLevelUpdate = 0;
 683     private ErsBox[][] boxes;
 684     private int boxWidth, boxHeight;
 685 
 686     /**
 687      * 画布类的构造函数
 688      *
 689      * @param rows int,画布的行数
 690      * @param cols int,画布的列数 行数和列数决定着画布拥有方格的数目
 691      */
 692     public GameCanvas(int rows, int cols) {
 693         this.rows = rows;
 694         this.cols = cols;
 695 
 696         boxes = new ErsBox[rows][cols];
 697         for (int i = 0; i < boxes.length; i++) {
 698             for (int j = 0; j < boxes[i].length; j++) {
 699                 boxes[i][j] = new ErsBox(false);
 700             }
 701         }
 702 
 703         setBorder(new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)));
 704     }
 705 
 706     /**
 707      * 画布类的构造函数
 708      *
 709      * @param rows
 710      * @param cols
 711      * @param backColor
 712      * @param frontColor
 713      */
 714     public GameCanvas(int rows, int cols,
 715             Color backColor, Color frontColor) {
 716         this(rows, cols);
 717         this.backColor = backColor;
 718         this.frontColor = frontColor;
 719     }
 720 
 721     /**
 722      * 设置游戏背景色彩
 723      *
 724      * @param backColor Color,背景色彩
 725      */
 726     public void setBackgroundColor(Color backColor) {
 727         this.backColor = backColor;
 728     }
 729 
 730     /**
 731      * 取得游戏背景色彩
 732      *
 733      * @return Color ,背景色彩
 734      */
 735     public Color getBackgroundColor() {
 736         return backColor;
 737     }
 738 
 739     /**
 740      * 设置游戏方块颜色
 741      *
 742      * @param frontColor Color,方块颜色
 743      */
 744     public void setBlockColor(Color frontColor) {
 745         this.frontColor = frontColor;
 746     }
 747 
 748     /**
 749      * 取得游戏方块色彩
 750      *
 751      * @return Color,方块颜色
 752      */
 753     public Color getBlockColor() {
 754         return frontColor;
 755     }
 756 
 757     /**
 758      * 取得画布中方格的列数
 759      *
 760      * @return
 761      */
 762     public int getRows() {
 763         return rows;
 764     }
 765 
 766     /**
 767      * 取得画布中方格的行数
 768      *
 769      * @return int,方格的行数
 770      */
 771     public int getCols() {
 772         return cols;
 773     }
 774 
 775     /**
 776      * 取得游戏成绩
 777      *
 778      * @return int, 分数
 779      */
 780     public int getScore() {
 781         return score;
 782     }
 783 
 784     /**
 785      * 取得自上一次升级后的积分
 786      *
 787      * @return int ,上一次升级后的积分
 788      */
 789     public int getScoreForLevelUpdate() {
 790         return scoreForLevelUpdate;
 791     }
 792 
 793     /**
 794      * 升级后,将上一次升级以来的积分清零
 795      */
 796     public void resetScoreForLevelUpdate() {
 797         scoreForLevelUpdate -= ErsBlocksGame.PER_LEVEL_SCORE;
 798     }
 799 
 800     /**
 801      * 得到某一行某一列的方格引用
 802      *
 803      * @return row int ,要引用的方格所在的行
 804      * @param col int, 要引用的方格所在的行
 805      * @return ErsBox,在row行col列的方格的引用
 806      */
 807     public ErsBox getBox(int row, int col) {
 808         if (row < 0 || row > boxes.length - 1 || col < 0 || col > boxes[0].length - 1) {
 809             return null;
 810         }
 811         return (boxes[row][col]);
 812     }
 813 
 814     /**
 815      * 覆盖JComponent类的函数,画组件。
 816      *
 817      * @param g 图形设备环境
 818      */
 819     @Override
 820     public void paintComponent(Graphics g) {
 821         super.paintComponent(g);
 822 
 823         g.setColor(frontColor);
 824         for (int i = 0; i < boxes.length; i++) {
 825             for (int j = 0; j < boxes[i].length; j++) {
 826                 g.setColor(boxes[i][j].isColorBox() ? frontColor : backColor);
 827                 g.fill3DRect(j * boxWidth, i * boxHeight,
 828                         boxWidth, boxHeight, true);
 829             }
 830         }
 831     }
 832 
 833     /**
 834      * 根据窗口大小,自动调节方格的尺寸
 835      */
 836     public void fanning() {
 837         boxWidth = getSize().width / cols;
 838         boxHeight = getSize().height / rows;
 839     }
 840 
 841     /**
 842      * 当一行被游戏者叠满后,将此行清除,并为游戏者加分
 843      *
 844      * @param row int,要清除的行,是由ErsBoxesGame类计算的
 845      */
 846     public synchronized void removeLine(int row) {
 847         for (int i = row; i > 0; i--) {
 848             for (int j = 0; j < cols; j++) {
 849                 boxes[i][j] = (ErsBox) boxes[i - 1][j].clone();                 //将上一行的方块颜色克隆下来,
 850             }                                                                   //即消去一行方块
 851         }
 852 
 853         score += ErsBlocksGame.PER_LEVEL_SCORE;
 854         scoreForLevelUpdate += ErsBlocksGame.PER_LEVEL_SCORE;
 855         repaint();
 856     }
 857 
 858     /**
 859      * 重置画布,置积分为零
 860      */
 861     public void reset() {
 862         score = 0;
 863         scoreForLevelUpdate = 0;
 864         for (int i = 0; i < boxes.length; i++) {
 865             for (int j = 0; j < boxes[i].length; j++) {
 866                 boxes[i][j].setColor(false);
 867             }
 868         }
 869 
 870         repaint();
 871     }
 872 }
 873 
 874 /**
 875  * 方格类,是组成块的基本元素,用自己的颜色来表示块的外观
 876  */
 877 class ErsBox implements Cloneable {
 878 
 879     private boolean isColor;
 880     private Dimension size = new Dimension();
 881 
 882     /**
 883      * 方格类的构造函数,
 884      *
 885      * @param isColor 是不是用前景色来为此方格着色 true前景色,false 用背景色
 886      */
 887     public ErsBox(boolean isColor) {
 888         this.isColor = isColor;
 889     }
 890 
 891     /**
 892      * 此方格是不是用前景色表现
 893      *
 894      * @return boolean ,true用前景色表现,false 用背景色表现
 895      */
 896     public boolean isColorBox() {
 897         return isColor;
 898     }
 899 
 900     /**
 901      * 设置方格的颜色,
 902      *
 903      * @param isColor boolean ,true用前景色表现,false 用背景色表现
 904      */
 905     public void setColor(boolean isColor) {
 906         this.isColor = isColor;
 907     }
 908 
 909     /**
 910      * 得到此方格的尺寸
 911      *
 912      * @return Dimension ,方格的尺寸
 913      */
 914     public Dimension getSize() {
 915         return size;
 916     }
 917 
 918     /**
 919      * 设置方格的尺寸,
 920      *
 921      * @param size Dimension ,方格的尺寸
 922      */
 923     public void setSize(Dimension size) {
 924         this.size = size;
 925     }
 926 
 927     /**
 928      * 覆盖Object的Object clone(),实现克隆
 929      *
 930      * @return Object,克隆的结果
 931      */
 932     @Override
 933     public Object clone() {
 934         Object cloned = null;
 935         try {
 936             cloned = super.clone();
 937         } catch (Exception ex) {
 938             ex.printStackTrace();
 939         }
 940 
 941         return cloned;
 942     }
 943 }
 944 
 945 /**
 946  * 块类,继承自线程类(Thread) 由4 × 4个方块(ErsBox)构成一个方块, 控制块的移动·下落·变形等
 947  */
 948 class ErsBlock extends Thread {
 949 
 950     /**
 951      * 一个块占的行数是4行
 952      */
 953     public final static int BOXES_ROWS = 4;
 954     /**
 955      * 一个块占的列数是4列
 956      */
 957     public final static int BOXES_COLS = 4;
 958     /**
 959      * 让升级变化平滑的因子,避免最后几级之间的速度相差近一倍
 960      */
 961     public final static int LEVEL_FLATNESS_GENE = 3;
 962     /**
 963      * 相近的两级之间,块每下落一行的时间差别为多少(毫秒)
 964      */
 965     public final static int BETWEEN_LEVELS_DEGRESS_TIME = 50;
 966     /**
 967      * 方块的样式数目为7
 968      */
 969     public final static int BLOCK_KIND_NUMBER = 7;
 970     /**
 971      * 每一个样式的方块的反转状态种类为4
 972      */
 973     public final static int BLOCK_STATUS_NUMBER = 4;
 974     /**
 975      * 分别对应7种模型的28种状态
 976      */
 977     public final static int[][] STYLES = { //共28种状态
 978         {0x0f00, 0x4444, 0x0f00, 0x4444}, //长条型的四种状态
 979         {0x04e0, 0x0464, 0x00e4, 0x04c4}, //T型的四种状态
 980         {0x4620, 0x6c00, 0x4620, 0x6c00}, //反Z型的四种状态
 981         {0x2640, 0xc600, 0x2640, 0xc600}, //Z型的四种状态
 982         {0x6220, 0x1700, 0x2230, 0x0740}, //7型的四种状态
 983         {0x6440, 0x0e20, 0x44c0, 0x8e00}, //反7型的四种状态   
 984         {0x0660, 0x0660, 0x0660, 0x0660}, //方块的四种状态
 985     };
 986     private GameCanvas canvas;
 987     private ErsBox[][] boxes = new ErsBox[BOXES_ROWS][BOXES_COLS];
 988     private int style, y, x, level;
 989     private boolean pausing = false, moving = true;
 990 
 991     /**
 992      * 构造函数,产生一个特定的块
 993      *
 994      * @param style 块的样式,对应STYLES的28个值中的一个
 995      * @param y 起始位置,左上角在canvas中的坐标行
 996      * @param x 起始位置,左上角在canvas中的坐标lie
 997      * @param level 游戏等级,控制块的下落速度
 998      * @param canvas 画板
 999      */
1000     public ErsBlock(int style, int y, int x, int level, GameCanvas canvas) {
1001         this.style = style;
1002         this.y = y;
1003         this.x = x;
1004         this.level = level;
1005         this.canvas = canvas;
1006 
1007         int key = 0x8000;
1008         for (int i = 0; i < boxes.length; i++) {
1009             for (int j = 0; j < boxes[i].length; j++) {
1010                 boolean isColor = ((style & key) != 0);
1011                 boxes[i][j] = new ErsBox(isColor);
1012                 key >>= 1;
1013             }
1014         }
1015 
1016         display();
1017     }
1018 
1019     /**
1020      * 线程类的run()函数覆盖,下落块,直到块不能再下落
1021      */
1022     @Override
1023     public void run() {
1024         while (moving) {
1025             try {
1026                 sleep(BETWEEN_LEVELS_DEGRESS_TIME
1027                         * (ErsBlocksGame.MAX_LEVEL - level + LEVEL_FLATNESS_GENE));
1028             } catch (InterruptedException ie) {
1029                 ie.printStackTrace();
1030             }
1031             //后边的moving是表示在等待的100毫秒间,moving没有被改变
1032             if (!pausing) {
1033                 moving = (moveTo(y + 1, x) && moving);
1034             }
1035         }
1036     }
1037 
1038     /**
1039      * 块向左移动一格
1040      */
1041     public void moveLeft() {
1042         moveTo(y, x - 1);
1043     }
1044 
1045     /**
1046      * 块向右移动一格
1047      */
1048     public void moveRight() {
1049         moveTo(y, x + 1);
1050     }
1051 
1052     /**
1053      * 块向下移动一格
1054      */
1055     public void moveDown() {
1056         moveTo(y + 1, x);
1057     }
1058 
1059     /**
1060      * 块变型
1061      */
1062     public void turnNext() {
1063         for (int i = 0; i < BLOCK_KIND_NUMBER; i++) {
1064             for (int j = 0; j < BLOCK_STATUS_NUMBER; j++) {
1065                 if (STYLES[i][j] == style) {
1066                     int newStyle = STYLES[i][(j + 1) % BLOCK_STATUS_NUMBER];
1067                     turnTo(newStyle);
1068                     return;
1069                 }
1070             }
1071         }
1072     }
1073 
1074     public void startMove() {
1075         pausing = false;
1076         moving = true;
1077     }
1078 
1079     /**
1080      * 暂停块的下落,对应游戏暂停
1081      */
1082     public void pauseMove() {
1083         pausing = true;
1084         //   moving = false;
1085     }
1086 
1087     /**
1088      * 继续块的下落,对应游戏继续
1089      */
1090     public void resumeMove() {
1091         pausing = false;
1092         moving = true;
1093     }
1094 
1095     /**
1096      * 停止块的下落,对应游戏停止
1097      */
1098     public void stopMove() {
1099         pausing = false;
1100         moving = false;
1101     }
1102 
1103     /**
1104      * 将当前块从画布的对应位置移除,要等到下次重画画布时才能反映出来
1105      */
1106     private void erase() {
1107         for (int i = 0; i < boxes.length; i++) {
1108             for (int j = 0; j < boxes[i].length; j++) {
1109                 if (boxes[i][j].isColorBox()) {
1110                     ErsBox box = canvas.getBox(i + y, j + x);
1111                     if (box == null) {
1112                         continue;
1113                     }
1114                     box.setColor(false);
1115                 }
1116             }
1117         }
1118     }
1119 
1120     /**
1121      * 让当前块放置在画布的对因位置上,要等到下次重画画布时才能看见
1122      */
1123     private void display() {
1124         for (int i = 0; i < boxes.length; i++) {
1125             for (int j = 0; j < boxes[i].length; j++) {
1126                 if (boxes[i][j].isColorBox()) {
1127                     ErsBox box = canvas.getBox(i + y, j + x);
1128                     if (box == null) {
1129                         continue;
1130                     }
1131                     box.setColor(true);
1132                 }
1133             }
1134         }
1135     }
1136 
1137     /**
1138      * 当前块能否移动到newRow/newCol 所指定的位置
1139      *
1140      * @param newRow int,目的地所在行
1141      * @param newCol int,目的地所在列
1142      * @return boolean,true-能移动,false-不能移动
1143      */
1144     public boolean isMoveAble(int newRow, int newCol) {
1145         erase();
1146         for (int i = 0; i < boxes.length; i++) {
1147             for (int j = 0; j < boxes[i].length; j++) {
1148                 if (boxes[i][j].isColorBox()) {
1149                     ErsBox box = canvas.getBox(i + newRow, j + newCol);
1150                     if (box == null || (box.isColorBox())) {
1151                         display();
1152                         return false;
1153                     }
1154                 }
1155             }
1156         }
1157         display();
1158         return true;
1159     }
1160 
1161     /**
1162      * 将当前块移动到newRow/newCol 所指定的位置
1163      *
1164      * @param newRow int,目的地所在行
1165      * @param newCol int,目的地所在列
1166      * @return boolean,true-移动成功,false-移动失败
1167      */
1168     private synchronized boolean moveTo(int newRow, int newCol) {
1169         if (!isMoveAble(newRow, newCol) || !moving) {
1170             return false;
1171         }
1172 
1173         erase();
1174         y = newRow;
1175         x = newCol;
1176 
1177         display();
1178         canvas.repaint();
1179 
1180         return true;
1181     }
1182 
1183     /**
1184      * 当前块能否变成newStyle所指定的块样式,主要是考虑 边界以及被其他块挡住,不能移动的情况
1185      *
1186      * @param newSytle int,希望改变的块样式,对应STYLES的28个值中的一个
1187      * @return boolean,true-能改变,false-不能改变
1188      */
1189     private boolean isTurnAble(int newStyle) {
1190         int key = 0x8000;
1191         erase();
1192         for (int i = 0; i < boxes.length; i++) {
1193             for (int j = 0; j < boxes[i].length; j++) {
1194                 if ((newStyle & key) != 0) {
1195                     ErsBox box = canvas.getBox(i + y, j + x);
1196                     if (box == null || (box.isColorBox())) {
1197                         display();
1198                         return false;
1199                     }
1200                 }
1201                 key >>= 1;
1202             }
1203         }
1204         display();
1205         return true;
1206     }
1207 
1208     /**
1209      * 将当前块变成newStyle所指定的块样式
1210      *
1211      * @param newStyle int,希望改变的块样式,对应STYLES的28个值中的一个
1212      * @return true-改变成功,false-改变失败
1213      */
1214     private boolean turnTo(int newStyle) {
1215         if (!isTurnAble(newStyle) || !moving) {
1216             return false;
1217         }
1218 
1219         erase();
1220         int key = 0x8000;
1221         for (int i = 0; i < boxes.length; i++) {
1222             for (int j = 0; j < boxes[i].length; j++) {
1223                 boolean isColor = ((newStyle & key) != 0);
1224                 boxes[i][j].setColor(isColor);
1225                 key >>= 1;
1226             }
1227         }
1228         style = newStyle;
1229 
1230         display();
1231         canvas.repaint();
1232 
1233         return true;
1234     }
1235 }
1236 
1237 /**
1238  * 控制面板类,继承自JPanel。 上边安放预显窗口,等级,得分,控制按钮 主要用来控制游戏进程。
1239  */
1240 class ControlPanel extends JPanel {
1241 
1242     private JTextField tfLevel = new JTextField("" + ErsBlocksGame.DEFAULT_LEVEL),
1243             tfScore = new JTextField(" 0"),
1244             tfTime = new JTextField(" ");
1245     private JButton btPlay = new JButton(" 开始"),
1246             btPause = new JButton(" 暂停"),
1247             btStop = new JButton("终止游戏"),
1248             btTurnLevelUp = new JButton(" 增加难度"),
1249             btTurnLevelDown = new JButton(" 降低难度");
1250     private JPanel plTip = new JPanel(new BorderLayout());
1251     private TipPanel plTipBlock = new TipPanel();
1252     private JPanel plInfo = new JPanel(new GridLayout(4, 1));
1253     private JPanel plButton = new JPanel(new GridLayout(6, 1));
1254     private Timer timer;
1255     private ErsBlocksGame game;
1256     private Border border = new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140));
1257 
1258     /**
1259      * 控制面板类的构造函数
1260      *
1261      * @param game ErsBlocksGame,ErsBlocksGame 类的一个实例引用 方便直接控制ErsBlocksGame类的行为。
1262      */
1263     public ControlPanel(final ErsBlocksGame game) {
1264         setLayout(new GridLayout(3, 1, 0, 2));
1265         this.game = game;
1266 
1267         plTip.add(new JLabel(" 下一个方块"), BorderLayout.NORTH);               //添加组件
1268         plTip.add(plTipBlock);
1269         plTip.setBorder(border);
1270 
1271         plInfo.add(new JLabel(" 难度系数"));
1272         plInfo.add(tfLevel);
1273         plInfo.add(new JLabel(" 得分"));
1274         plInfo.add(tfScore);
1275         plInfo.setBorder(border);
1276 
1277         plButton.add(btPlay);
1278         btPlay.setEnabled(true);
1279         plButton.add(btPause);
1280         btPause.setEnabled(false);
1281         plButton.add(btStop);
1282         btStop.setEnabled(false);
1283         plButton.add(btTurnLevelUp);
1284         plButton.add(btTurnLevelDown);
1285         plButton.add(tfTime);
1286         plButton.setBorder(border);
1287 
1288         tfLevel.setEditable(false);
1289         tfScore.setEditable(false);
1290         tfTime.setEditable(false);
1291 
1292         add(plTip);
1293         add(plInfo);
1294         add(plButton);
1295 
1296         addKeyListener(new KeyAdapter() {
1297             @Override
1298             public void keyPressed(KeyEvent ke) {
1299                 if (!game.isPlaying()) {
1300                     return;
1301                 }
1302 
1303                 ErsBlock block = game.getCurBlock();
1304                 switch (ke.getKeyCode()) {
1305                     case KeyEvent.VK_DOWN:
1306                         block.moveDown();
1307                         break;
1308                     case KeyEvent.VK_LEFT:
1309                         block.moveLeft();
1310                         break;
1311                     case KeyEvent.VK_RIGHT:
1312                         block.moveRight();
1313                         break;
1314                     case KeyEvent.VK_UP:
1315                         block.turnNext();
1316                         break;
1317                     default:
1318                         break;
1319                 }
1320             }
1321         });
1322 
1323         btPlay.addActionListener(new ActionListener() {                         //开始游戏
1324             @Override
1325             public void actionPerformed(ActionEvent ae) {
1326                 game.playGame();
1327             }
1328         });
1329         btPause.addActionListener(new ActionListener() {                        //暂停游戏
1330             @Override
1331             public void actionPerformed(ActionEvent ae) {
1332                 if (btPause.getText().equals(" 暂停")) {
1333                     game.pauseGame();
1334                 } else {
1335                     game.resumeGame();
1336                 }
1337             }
1338         });
1339         btStop.addActionListener(new ActionListener() {                         //停止游戏
1340             @Override
1341             public void actionPerformed(ActionEvent ae) {
1342                 game.stopGame();
1343             }
1344         });
1345         btTurnLevelUp.addActionListener(new ActionListener() {                  //升高难度
1346             @Override
1347             public void actionPerformed(ActionEvent ae) {
1348                 try {
1349                     int level = Integer.parseInt(tfLevel.getText());
1350                     if (level < ErsBlocksGame.MAX_LEVEL) {
1351                         tfLevel.setText("" + (level + 1));
1352                     }
1353                 } catch (NumberFormatException e) {
1354                 }
1355                 requestFocus();
1356             }
1357         });
1358         btTurnLevelDown.addActionListener(new ActionListener() {                //降低游戏难度
1359             @Override
1360             public void actionPerformed(ActionEvent ae) {
1361                 try {
1362                     int level = Integer.parseInt(tfLevel.getText());
1363                     if (level > 1) {
1364                         tfLevel.setText("" + (level - 1));
1365                     }
1366                 } catch (NumberFormatException e) {
1367                 }
1368                 requestFocus();
1369             }
1370         });
1371 
1372         addComponentListener(new ComponentAdapter() {
1373             @Override
1374             public void componentResized(ComponentEvent ce) {
1375                 plTipBlock.fanning();
1376             }
1377         });
1378 
1379         timer = new Timer(1000, new ActionListener() {
1380             @Override
1381             public void actionPerformed(ActionEvent ae) {
1382                 DateFormat format = new SimpleDateFormat("时间:HH:mm:ss");      //系统获得时间
1383                 Date date = new Date();
1384                 tfTime.setText(format.format(date));
1385 
1386                 tfScore.setText("" + game.getScore());
1387                 int ScoreForLevelUpdate = //判断当前分数是否能升级
1388                         game.getScoreForLevelUpdate();
1389                 if (ScoreForLevelUpdate >= ErsBlocksGame.PER_LEVEL_SCORE
1390                         && ScoreForLevelUpdate > 0) {
1391                     game.levelUpdate();
1392                 }
1393             }
1394         });
1395         timer.start();
1396     }
1397 
1398     /**
1399      * 设置预显窗口的样式
1400      *
1401      * @param style int,对应ErsBlock类的STYLES中的28个值
1402      */
1403     public void setTipStyle(int style) {
1404         plTipBlock.setStyle(style);
1405     }
1406 
1407     /**
1408      * 取得用户设置的游戏等级。
1409      *
1410      * @return int ,难度等级,1-ErsBlocksGame.MAX_LEVEL
1411      */
1412     public int getLevel() {
1413         int level = 0;
1414         try {
1415             level = Integer.parseInt(tfLevel.getText());
1416         } catch (NumberFormatException e) {
1417         }
1418         return level;
1419     }
1420 
1421     /**
1422      * 让用户修改游戏难度等级。
1423      *
1424      * @param level 修改后的游戏难度等级
1425      */
1426     public void setLevel(int level) {
1427         if (level > 0 && level < 11) {
1428             tfLevel.setText("" + level);
1429         }
1430     }
1431 
1432     /**
1433      * 设置“开始”按钮的状态。
1434      */
1435     public void setPlayButtonEnable(boolean enable) {
1436         btPlay.setEnabled(enable);
1437     }
1438 
1439     public void setPauseButtonEnable(boolean enable) {
1440         btPause.setEnabled(enable);
1441     }
1442 
1443     public void setPauseButtonLabel(boolean pause) {
1444         btPause.setText(pause ? " 暂停" : " 继续");
1445     }
1446 
1447     public void setStopButtonEnable(boolean enable) {
1448         btStop.setEnabled(enable);
1449     }
1450 
1451     public void setTurnLevelUpButtonEnable(boolean enable) {
1452         btTurnLevelUp.setEnabled(enable);
1453     }
1454 
1455     public void setTurnLevelDownButtonEnable(boolean enable) {
1456         btTurnLevelDown.setEnabled(enable);
1457     }
1458 
1459     /**
1460      * 重置控制面板
1461      */
1462     public void reset() {
1463         tfScore.setText(" 0");
1464         plTipBlock.setStyle(0);
1465     }
1466 
1467     /**
1468      * 重新计算TipPanel里的boxes[][]里的小框的大小
1469      */
1470     public void fanning() {
1471         plTipBlock.fanning();
1472     }
1473 
1474     /**
1475      * 预显窗口的实现细节类
1476      */
1477     private class TipPanel extends JPanel {                                    //TipPanel用来显示下一个将要出现方块的形状
1478 
1479         private Color backColor = Color.LIGHT_GRAY, frontColor = Color.ORANGE;
1480         private ErsBox[][] boxes = new ErsBox[ErsBlock.BOXES_ROWS][ErsBlock.BOXES_COLS];
1481         private int style, boxWidth, boxHeight;
1482         private boolean isTiled = false;
1483 
1484         /**
1485          * 预显示窗口类构造函数
1486          */
1487         public TipPanel() {
1488             for (int i = 0; i < boxes.length; i++) {
1489                 for (int j = 0; j < boxes[i].length; j++) {
1490                     boxes[i][j] = new ErsBox(false);
1491                 }
1492             }
1493         }
1494 
1495         /**
1496          * 预显示窗口类构造函数
1497          *
1498          * @param backColor Color,窗口的背景色
1499          * @param frontColor Color,窗口的前景色
1500          */
1501         public TipPanel(Color backColor, Color frontColor) {
1502             this();
1503             this.backColor = backColor;
1504             this.frontColor = frontColor;
1505         }
1506 
1507         /**
1508          * 设置预显示窗口的方块样式
1509          *
1510          * @param style int,对应ErsBlock类的STYLES中的28个值
1511          */
1512         public void setStyle(int style) {
1513             this.style = style;
1514             repaint();
1515         }
1516 
1517         /**
1518          * 覆盖JComponent类的函数,画组件。
1519          *
1520          * @param g 图形设备环境
1521          */
1522         @Override
1523         public void paintComponent(Graphics g) {
1524             super.paintComponent(g);
1525 
1526             if (!isTiled) {
1527                 fanning();
1528             }
1529 
1530             int key = 0x8000;
1531             for (int i = 0; i < boxes.length; i++) {
1532                 for (int j = 0; j < boxes[i].length; j++) {
1533                     Color color = ((key & style) != 0 ? frontColor : backColor);
1534                     g.setColor(color);
1535                     g.fill3DRect(j * boxWidth, i * boxHeight,
1536                             boxWidth, boxHeight, true);
1537                     key >>= 1;
1538                 }
1539             }
1540         }
1541 
1542         /**
1543          * g根据窗口的大小,自动调整方格的尺寸
1544          */
1545         public void fanning() {
1546             boxWidth = getSize().width / ErsBlock.BOXES_COLS;
1547             boxHeight = getSize().height / ErsBlock.BOXES_ROWS;
1548             isTiled = true;
1549         }
1550     }
1551 }

 

你可能感兴趣的:(java)