我的一位学生设计的俄罗斯方块游戏

来源:互联网 发布:天行VPN软件下载 编辑:程序博客网 时间:2024/05/29 11:48

 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(
400480);
        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"116));
        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(
90040011030);
        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, 
00this);        
    }


    
/**
     * 画出游戏背景
     * 
@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;
    }

}

原创粉丝点击