Game.java package net.handson.russianBox; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; /** */ /** * * @author 汪燕飞 * @version 1.0 * time: 2008-3 * */ public class Game extends JFrame ... { private JPanel bgPanel; private int score = 0; JMenuItem startMenuItem,quitMenuItem,stopMenuItem; private JLabel scoreLabel;// 显示分数的标签 private boolean isKeyEnable = false; private Shape shape = null;; private RunThread runThread; private GamePanel gamePanel; private BackgroundMatrix bgMatrix; public static final int UP = 1; public static final int LEFT = 2; public static final int RIGHT = 3; public static final int DOWN = 4; private Object lock = new Object(); private String aboutMessage = "<html><p>Author: 汪燕飞</p><p>copy right: Anhui HandsOn training company</p></html>"; private String helpMessage = "<html><b>游戏控制参考</b></p><p>向上方向键为变形</p><p>向下方向键为下移</p><p>向左方向键为左移</p><p>向右方向键为右移</p><p>游戏进行过程中可以选择暂停,暂停后可以选择继续游戏</p></html>"; public Game() ...{ super("俄罗斯方块"); runThread = new RunThread(); setBackground(Color.cyan); setLayout(new BorderLayout()); bgPanel =(JPanel)getContentPane(); initBgPanel(); initMenuBar(); setSize(400, 480); addKeyListener(new MyKeyListener()); addWindowListener(new WindowAdapter() ...{ public void windowClosing(WindowEvent e) ...{ System.exit(0); } }); setToScreenCenter(); setResizable(false); setVisible(true); } private void initBgPanel() ...{ bgMatrix = new BackgroundMatrix(); bgMatrix.init(); gamePanel = new GamePanel(bgMatrix); gamePanel.setVisible(true); bgPanel.add(gamePanel,BorderLayout.CENTER); scoreLabel = new JLabel("得分:" + score); scoreLabel.setFont(new Font("1", 1, 16)); scoreLabel.setBackground(new Color(55550255)); JPanel eastPanel=new JPanel(new FlowLayout(FlowLayout.CENTER)); eastPanel.add(scoreLabel); bgPanel.add(new JPanel(),BorderLayout.NORTH); bgPanel.add(new JPanel(),BorderLayout.SOUTH); bgPanel.add(new JPanel(),BorderLayout.WEST); bgPanel.add(eastPanel,BorderLayout.EAST); bgPanel.addKeyListener(new MyKeyListener()); } private void initMenuBar()...{ JMenuBar menuBar=new JMenuBar(); MyActionListener actionListener=new MyActionListener(); JMenu controlMenu = new JMenu("控制(c)"); controlMenu.setMnemonic('c'); startMenuItem=new JMenuItem("开始"); startMenuItem.setBackground(Color.LIGHT_GRAY); startMenuItem.setActionCommand("start"); startMenuItem.addActionListener(actionListener); controlMenu.add(startMenuItem); stopMenuItem=new JMenuItem("停止"); stopMenuItem.setBackground(Color.LIGHT_GRAY); stopMenuItem.setActionCommand("stop"); stopMenuItem.setEnabled(false); stopMenuItem.addActionListener(actionListener); controlMenu.add(stopMenuItem); quitMenuItem=new JMenuItem("退出"); quitMenuItem.setBackground(Color.LIGHT_GRAY); quitMenuItem.setActionCommand("quit"); quitMenuItem.addActionListener(actionListener); controlMenu.add(quitMenuItem); JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H'); JMenuItem helpItem = new JMenuItem("help"); helpItem.setActionCommand("help"); helpItem.addActionListener(actionListener); JMenuItem aboutItem = new JMenuItem("about"); aboutItem.setActionCommand("about"); aboutItem.addActionListener(actionListener); helpMenu.add(helpItem); helpMenu.add(aboutItem); menuBar.add(controlMenu); menuBar.add(helpMenu); this.setJMenuBar(menuBar); } private void setToScreenCenter()...{ Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); int locationX = (dimension.width-this.getSize().width)/2; int locationY = (dimension.height-this.getSize().height)/2; this.setLocation(locationX, locationY); } /** *//** * 开始游戏 */ private void gameStart()...{ startMenuItem.setText("暂停"); stopMenuItem.setEnabled(true); isKeyEnable = true; shape = new Shape(); gamePanel.setShape(shape); runThread.start(); } /** *//** * 将游戏暂停 */ private void gamePause()...{ startMenuItem.setText("继续"); runThread.end(); isKeyEnable = false; } /** *//** * 将暂停的游戏继续下去 */ private void gameResume()...{ startMenuItem.setText("暂停"); runThread.restart(); isKeyEnable = true; } /** *//** * 游戏重新开始时进行的一些处理 */ public void gameRestart() ...{ startMenuItem.setText("暂停"); bgMatrix.init(); gamePanel.setBgMatrix(bgMatrix); isKeyEnable = true; score = 0; scoreLabel.setText("得分: " + score); shape = new Shape(); gamePanel.setShape(shape); runThread = new RunThread(); runThread.start(); } /** *//** * 游戏结束的一些处理 */ public void gameOver() ...{ isKeyEnable = false; runThread.end(); startMenuItem.setBounds(900, 400, 110, 30); startMenuItem.setText("重新开始"); } private void quit()...{ boolean isRun=runThread.isRun; runThread.end(); isKeyEnable = false; if (JOptionPane.showConfirmDialog(null, "确定退出游戏?") == JOptionPane.OK_OPTION) ...{ System.exit(0); }else...{ if(isRun)...{ gameResume(); } } } /** *//** * 消除一行时同时进行加分 * @param rows */ private void addScore(int rows) ...{ if (rows != 0) ...{ switch (rows) ...{ case 1: score += 10; break; case 2: score += 30; break; case 3: score += 60; break; case 4: score += 100; break; default: break; } } } public class MyActionListener implements ActionListener ...{ public void actionPerformed(ActionEvent e) ...{ String command = e.getActionCommand(); if (command.equals("start")) ...{ stopMenuItem.setEnabled(true); if (startMenuItem.getText().equals("开始")) ...{ gameStart(); } else if (startMenuItem.getText().equals("暂停")) ...{ gamePause(); } else if (startMenuItem.getText().equals("继续")) ...{ gameResume(); } else if (startMenuItem.getText().equals("重新开始")) ...{ gameRestart(); } }else if(command.equals("stop"))...{ gameOver(); stopMenuItem.setEnabled(false); }else if (command.equals("quit")) ...{ quit(); }else if(command.equals("help"))...{ showWindow(helpMessage); }else if(command.equals("about"))...{ showWindow(aboutMessage); } } } private void showWindow(String message)...{ JOptionPane.showMessageDialog(this, message); } /** *//** * 保持当前形状的一个副本,然后可以对副本进行尝试移动,如果不能移动就放弃当前副本,如果可以移动,则将当前形状变换为当前副本形状 * @param shape 当前形状 * @param tempShape 一个临时形状,做为当前形状的一个副本 */ private void copyPreShape(Shape shape, Shape tempShape) ...{ tempShape.setLeft(shape.getLeft()); tempShape.setStatus(shape.getStatus()); tempShape.setTop(shape.getTop()); } /** *//** * 当形状不可再往下移动时调用此方法,将形状merge到背景中,并消除可以消除的行 */ private void mergeShapeToBg() ...{ bgMatrix.mergeShape(shape); int cancelRows = bgMatrix.cancelRows(shape); if (cancelRows > 0) ...{ addScore(cancelRows); scoreLabel.setText("得分: " + score); } shape = new Shape(); if (bgMatrix.isOverlap(shape)) ...{ gameOver(); } else ...{ gamePanel.setShape(shape); } } /** *//** * 判断当前形状是否可以向指定的方向移动 * @param shape 当前形状 * @param direction 需要移动的方向 <br> * Game.UP, Game.LEFT, Game.RIGHT, Game.DOWN * @return 如果可以移动返回true, 否则返回false */ public boolean isMoveAble(Shape shape, int direction) ...{ Shape tempShape = new Shape(); copyPreShape(shape, tempShape); switch (direction) ...{ case UP: tempShape.rotate(); return !bgMatrix.isOverlap(tempShape); case LEFT: tempShape.moveLeft(); return !bgMatrix.isOverlap(tempShape); case RIGHT: tempShape.moveRight(); return !bgMatrix.isOverlap(tempShape); case DOWN: tempShape.moveDown(); return !bgMatrix.isOverlap(tempShape); default: return false; } } private class MyKeyListener implements KeyListener ...{ public void keyPressed(KeyEvent e) ...{ synchronized (lock) ...{ if (isKeyEnable) ...{ switch (e.getKeyCode()) ...{ case KeyEvent.VK_UP: if (isMoveAble(shape, UP)) ...{ shape.rotate(); } break; case KeyEvent.VK_DOWN: if (isMoveAble(shape, DOWN)) ...{ shape.moveDown(); } break; case KeyEvent.VK_LEFT: if (isMoveAble(shape, LEFT)) ...{ shape.moveLeft(); } break; case KeyEvent.VK_RIGHT: if (isMoveAble(shape, RIGHT)) ...{ shape.moveRight(); } break; default: break; } } } } public void keyReleased(KeyEvent e) ...{ } public void keyTyped(KeyEvent e) ...{ } } /** *//** * 重画游戏界面及将形状定时往下移动 * @author smm * */ private class RunThread extends Thread ...{ boolean isRun = false; /** *//** * end or pause game */ public void end() ...{ isRun = false; } /** *//** * restart game */ public void restart() ...{ isRun = true; } public void run() ...{ final int repaintGap = 640; isRun = true; gamePanel.setGameRunningStatus(true); int sleepedMills=0; while (true) ...{ try ...{ sleep(80); } catch (Exception e) ...{ e.printStackTrace(); } if (! isRun) ...{ continue; } synchronized (lock) ...{ sleepedMills += 80; gamePanel.repaint(); if (!isMoveAble(shape, DOWN)) ...{ sleepedMills = 0; mergeShapeToBg(); } else if(sleepedMills == repaintGap)...{ shape.moveDown(); sleepedMills=0; } } } } } public static void main(String[] args) ...{ new Game(); }} GamePanel.java package net.handson.russianBox; import java.awt. * ; import java.awt.Graphics; import java.awt.Image; import javax.swing.JPanel; /** */ /** * 绘制游戏的面板 * @author 汪燕飞 * */ public class GamePanel extends JPanel ... { private Shape shape; private BackgroundMatrix bgMatrix; private int[][] bgM; private Image img; private Graphics bg; private boolean gameIsRunning; /** *//** * 绘制游戏的面板 * @param bgMatrix */ public GamePanel(BackgroundMatrix bgMatrix)...{ this.bgMatrix = bgMatrix; bgM=bgMatrix.getValues(); setSize(bgMatrix.WIDTH,bgMatrix.HEIGHT); setVisible(true); } /** *//** * 设置背景的形状 * @param bgMatrix */ public void setBgMatrix(BackgroundMatrix bgMatrix)...{ this.bgMatrix=bgMatrix; } /** *//** * 设置移动的形状 * @param shape */ public void setShape(Shape shape)...{ this.shape=shape; } public void addNotify()...{ super.addNotify(); img=createImage(bgMatrix.WIDTH, bgMatrix.HEIGHT); } /** *//** * 通过设置游戏是否在进行来决定是否画出移动形状 * @param isRun */ public void setGameRunningStatus(boolean isRun)...{ gameIsRunning=isRun; } public void paintComponent(Graphics g)...{ super.paintComponent(g); bg = img.getGraphics(); paintBgMatrix(bg); if(gameIsRunning)...{ paintShape(bg); } bg.dispose(); g.drawImage (img, 0, 0, this); } /** *//** * 画出游戏背景 * @param g */ private void paintBgMatrix(Graphics g)...{ for(int i = 0; i < bgM.length; i ++)...{ for(int j = 0; j < bgM[0].length; j ++)...{ if(bgM [i][j] == 1)...{ g.setColor(Color.black); g.fillRect( bgMatrix.cellDistance *j , bgMatrix.cellDistance *i, bgMatrix.cellWidth, bgMatrix.cellHeight); } else...{ g.setColor(Color.lightGray); g.fillRect( bgMatrix.cellDistance *j , bgMatrix.cellDistance *i, bgMatrix.cellWidth, bgMatrix.cellHeight); } } } } /** *//** * 画出游戏中移动的形状 * @param g */ private void paintShape(Graphics g)...{ for(int k=0;k<Shape.POINT_NUMBER;k++)...{ int i=shape.getLeft()+shape.getPoint(k).x; int j=shape.getTop()+shape.getPoint(k).y; g.setColor(Color.black); g.fillRect( bgMatrix.cellDistance *i , bgMatrix.cellDistance *j, bgMatrix.cellWidth, bgMatrix.cellHeight); } } } Sharp.java package net.handson.russianBox; import java.awt.Point; /** */ /** * 该类用于表示游戏中运动的形状,一个形状可理解为4×4的数组,实际只是由四点表示而成。类中top, left描述形状在背景中的x,y坐标,而point数组中的值则 * 依次表示形状中的四个点相对于形状左上角的偏移量 * @author 汪燕飞 * */ public class Shape ... { public static final int POINT_NUMBER = 4; private Point point[]; private int top, left; private int status; /** *//** * 随机得到一个形状,形状默认位于背景的顶部中间位置 */ public Shape()...{ top=0; left=7; status=(int)(Shapes.SHAPE_NUMBER*Math.random()); point = Shapes.shapes[status]; } /** *//** * * @param index * @return 返回相应的index指定的Point值, index应该>=0, <4. Point代表该点相对形状左上角的偏移量 */ public Point getPoint(int index)...{ return point[index]; } /** *//** * * @return 返回表示形状的四个点 */ public Point[] getPoints()...{ return point; } /** *//** * * @return 返回用以表示当前形状的一个状态 */ public int getStatus()...{ return this.status; } /** *//** * 通过设置status来设置形状 * @param status */ public void setStatus(int status)...{ this.status=status; point = Shapes.shapes[status]; } /** *//** * 当前形状往下移动 */ public void moveDown()...{ top += 1; } /** *//** * 当前形状往左移动 */ public void moveLeft()...{ left -= 1; } /** *//** * 当前形状往右移动 */ public void moveRight()...{ left+=1; } /** *//** * 将形状进行旋转 */ public void rotate()...{ status += 1; if(status % 4 == 0)...{ status -= 4; } point = Shapes.shapes[status]; } /** *//** * * @return 得到当前形状在背景中的x坐标,及最左边的坐标值 */ public int getLeft() ...{ return left; } /** *//** * 设置当前形状的x值,及最左边的坐标值 * @param left */ public void setLeft(int left) ...{ this.left = left; } /** *//** * * @return 得到当前形状在背景中的y坐标,及最顶上的坐标值 */ public int getTop() ...{ return top; } /** *//** * 设置当前形状的y值,及最顶部的坐标值 * @param top */ public void setTop(int top) ...{ this.top = top; }} /** */ /** * 用来描述所有的形状,在shape类中通过设置status来与具体形状进行关联 * @author smm * @see Shape * */ interface Shapes ... { int SHAPE_NUMBER = 20; Point[][] shapes = new Point[][]...{ // shape 田 ...{new Point(0,0), new Point(0,1), new Point(1,0), new Point(1,1)}, ...{new Point(0,0), new Point(0,1), new Point(1,0), new Point(1,1)}, ...{new Point(0,0), new Point(0,1), new Point(1,0), new Point(1,1)}, ...{new Point(0,0), new Point(0,1), new Point(1,0), new Point(1,1)}, // shape T ...{new Point(0,1), new Point(1,0), new Point(1,1), new Point(1,2)}, ...{new Point(0,0), new Point(1,0), new Point(1,1), new Point(2,0)}, ...{new Point(0,0), new Point(0,1), new Point(0,2), new Point(1,1)}, ...{new Point(0,1), new Point(1,0), new Point(1,1), new Point(2,1)}, //shape l ...{new Point(0,1), new Point(1,1), new Point(2,1), new Point(3,1)}, ...{new Point(1,0), new Point(1,1), new Point(1,2), new Point(1,3)}, ...{new Point(0,1), new Point(1,1), new Point(2,1), new Point(3,1)}, ...{new Point(1,0), new Point(1,1), new Point(1,2), new Point(1,3)}, //shape L ...{new Point(0,0), new Point(1,0), new Point(2,0), new Point(2,1)}, ...{new Point(0,0), new Point(0,1), new Point(0,2), new Point(1,0)}, ...{new Point(0,0), new Point(0,1), new Point(1,1), new Point(2,1)}, ...{new Point(0,2), new Point(1,0), new Point(1,1), new Point(1,2)}, //shape 7 ...{new Point(0,1), new Point(1,1), new Point(2,0), new Point(2,1)}, ...{new Point(0,0), new Point(1,0), new Point(1,1), new Point(1,2)}, ...{new Point(0,0), new Point(0,1), new Point(1,0), new Point(2,0)}, ...{new Point(0,0), new Point(0,1), new Point(0,2), new Point(1,2)}, };} BackgroundMatrix.java package net.handson.russianBox; import java.awt.Point; public class BackgroundMatrix ... { public final int ROWS = 20; public final int COLUMNS = 15; private int[][] values=new int[ROWS][COLUMNS]; public final int WIDTH=300; public final int HEIGHT=400; public final int cellDistance =20;//单元块之间的距离 public final int cellWidth=18 , cellHeight=18;//单元块的宽和高 /** *//** * 将当前移动的形状合并到背景中,成为背景的一部分 * @param shape */ public void mergeShape(Shape shape)...{ int left = shape.getLeft(); int top = shape.getTop(); Point point = null; for(int i=0; i<Shape.POINT_NUMBER; i++)...{ point = shape.getPoint(i); values[top + point.y][left + point.x] = 1; } } /** *//** * * @return 得到表示背景状态的二维数组,数组元素中1表示该格占用,0表示该格为空 */ public int[][] getValues()...{ return values; } /** *//** * 背景初始化 */ public void init()...{ for(int i=0;i<ROWS;i++)...{ for(int j=0;j<COLUMNS;j++)...{ values[i][j]=0; } } } /** *//** * 判断当前移动的形状是否跟背景形状有冲突 * @param shape 进行检测是否可以移动的shape * @return 如果shape跟背景有重叠的地方,则返回true, 否则返回false, 可以用此方法来判断shape是否可以进行移动 */ public boolean isOverlap(Shape shape)...{ int left = shape.getLeft(); int top = shape.getTop(); Point point = null; for(int i=0; i<Shape.POINT_NUMBER; i++)...{ point = shape.getPoint(i); if(left + point.x <0 || left+point.x>COLUMNS-1)...{ return true; } if(top + point.y <0 || top+point.y>ROWS-1)...{ return true; } if(values[top + point.y][left + point.x] == 1)...{ return true; } } return false; } /** *//** * * @param shape 需要进行计算的形状 * @return 返回shape中最大的y坐标,该坐标是指相到形状左上角的坐标,而不是在背景中的坐标,在判断当前shape是否可以下移时需要用到 */ public int getMaxY(Shape shape)...{ int max = 0; for(int i = 0; i < Shape.POINT_NUMBER; i ++)...{ if(shape.getPoint(i).y > max)...{ max = shape.getPoint(i).y ; } } return max; } /** *//** * * @param values 需要进行判断的某行的值 * @return 如果该行可以被消除则返回true, 否则返回false */ private boolean isRowCancelAble(int values[])...{ for(int i=0; i<COLUMNS; i++)...{ if(values[i]==0)...{ return false; } } return true; } /** *//** * 消除rowIndex表示的行。具体是依次先上一行的值下移,然后将顶行值置为0, * @param rowIndex */ private void clearRow(int rowIndex)...{ for(int y = rowIndex; y >= 1 ; y--)...{ for(int x = 0; x < COLUMNS; x ++)...{ values[y][x] = values[y-1][x]; } } //消除最顶上一行 for(int x = 0; x<COLUMNS; x ++)...{ values[0][x] = 0; } } /** *//** * 当形状不可再下移时,需要将其与当前背景合并,并消除相应的已满足消除条件的行 * @param shape 当前形状 * @return 返回有多少行被消除 */ public int cancelRows(Shape shape)...{ int rows=0; for(int i=shape.getTop(); i<=getMaxY(shape)+shape.getTop(); i++)...{ if(! isRowCancelAble(values[i]))...{ continue; } clearRow(i); rows++; } return rows; }}