[苦逼程序员成长之路]2、俄罗斯方块游戏

这个是学完面向对象之后写的,也是跟着老师写的,跟飞扬小鸟不同的是,写这个的时候,对java的语法结构有了很多的了解,对java有了一定的语感,对java的基础知识和数据类型也有了系统的认识。

写的时候,首先跟着老师分析了俄罗斯方块的业务需求,然后根据业务需求设计出大致的类和方法,在写游戏的时候,根据具体的需求继续添加设计需要的方法,下面是我自己根据业务需求文档写的程序设计大致结构:

俄罗斯方块
    对象:
        Cell   //组成俄罗斯方块的基础一个一个小格子
            属性:
                int row;//格子所在行
                int col;//格子所在列
                BufferedImage image;//格子所在位置填充的图片
            方法:
                /*get set方法*/
                getRow()
                getCol()
                getImage()
                setRow(row) 
                setCol(col)
                setImage(Image)
                /*格子下落方法*/
                drop()    
                /*格子左移*/
                moveLeft(){
                /*格子右移*/    
                moveRight(){
                    
                    
                
            
        Tetromino         //抽象类,定义4格方块
            属性:
                Cell[] cells //组成4格方块的格子
                state    //变形用数组
                type    //数组列长
            方法:
                Tetromino tetromino()//工厂方法:生成随机一种4格方块
                softDrop()//软下落
                
                moveLeft()//四格方块左移
                moveRight()//四格方块右移
                rotate()//四格方块变形
                
                
                
        (T S J L I Z O 类继承于Tetromino类)    

        Tetris //俄罗斯方块类,整个游戏的运行和操作
            属性:tetromino//正在下落的4格方块
                nextOne//即将进场的4个方块
                score//分数
                lines//总共消除的行数
                wall[][]//游戏场地
                ROWS//游戏场地的总行数
                COLS//游戏场地的总列数
                
            
            方法:    /*四格方块的运动方法*/
                softDropAction()
                hardDropAction()
                moveLeftAction()
                moveRightAction()
                /*消除方法 及 消除一行的方法*/
                destroy()
                deletLine()
                /*判断方法*/
                fullLines()    //判断一行满了没有
                outOfWall()//判断4格方块出界了没有
                coincide()//判断4个方块有没有重合
                isgameOver()//判断游戏结束了没有
                canDrop()//判断4个方块还能不能下落

下面就是具体的代码了

首先是Cell类:

package com.tarena.tetris;

import java.awt.image.BufferedImage;
/**
 * 组成俄罗斯方块的格子
 * 
 * @author 
 *
 */
 class Cell {
    /**
     * 格子所在的列、行、图片
     */
    public int col;
    public int row;
    BufferedImage image;
    
    public Cell(int row, int col, BufferedImage image) {
        super();
        this.col = col;
        this.row = row;
        this.image = image;
    }
    public int getCol() {
        return col;
    }
    public void setCol(int col) {
        this.col = col;
    }
    public int getRow() {
        return row;
    }
    public void setRow(int row) {
        this.row = row;
    }
    public BufferedImage getImage() {
        return image;
    }
    public void setImage(BufferedImage image) {
        this.image = image;
    }
    
    //下落
    public void drop(){

这个是Tetromino类:

package com.tarena.tetris;

import java.util.Random;

/**
 * 四格方块类
 * 
 * @author 
 * 
 */
class Tetromino {
    Cell[] cells = new Cell[4];
    int type;// 方块可变换的种类
    int[][] state;// 用于变形的数据

    /**
     * 工厂方法,随机生成方块 生成[0,7)的随机数,用t接受,用来决定生成四格方块的类型
     */
    public Tetromino randomOne() {

        Random r = new Random();
        int t = r.nextInt(7);
        switch (t) {
        case 0:
            return new I();
        case 1:
            return new J();
        case 2:
            return new L();
        case 3:
            return new O();
        case 4:
            return new S();
        case 5:
            return new Z();
        case 6:
            return new T();
        }

        return null;
    }

    int ss = 10;// 用语变形取余数循环

    /*
     * 变形
     */
    public void rotateRight() {
        ss++;
        Cell o = cells[0];
        int row = o.getRow();
        int col = o.getCol();
        cells[1].setRow(row + state[ss % type][2]);
        cells[1].setCol(col + state[ss % type][3]);
        cells[2].setRow(row + state[ss % type][4]);
        cells[2].setCol(col + state[ss % type][5]);
        cells[3].setRow(row + state[ss % type][6]);
        cells[3].setCol(col + state[ss % type][7]);
    }

    public void rotatLeft() {
        ss--;
        Cell o = cells[0];
        int row = o.getRow();
        int col = o.getCol();
        cells[1].setRow(row + state[ss % type][2]);
        cells[1].setCol(col + state[ss % type][3]);
        cells[2].setRow(row + state[ss % type][4]);
        cells[2].setCol(col + state[ss % type][5]);
        cells[3].setRow(row + state[ss % type][6]);
        cells[3].setCol(col + state[ss % type][7]);

    }

    /*
     * 下落 右移 左移
     */
    public void softDrop() {
        for (Cell cell : cells) {
            cell.drop();
        }
    }

    public void moveRight() {
        for (Cell cell : cells) {
            cell.moveRight();
        }
    }

    public void moveLeft() {
        for (Cell cell : cells) {
            cell.moveLeft();
        }
    }
}

class I extends Tetromino {
    public I() {
        cells[0] = new Cell(0, 4, Tetris.I);
        cells[1] = new Cell(0, 3, Tetris.I);
        cells[2] = new Cell(0, 5, Tetris.I);
        cells[3] = new Cell(0, 6, Tetris.I);
        type = 2;
        state = new int[][] { { 0, 0, 0, -1, 0, 1, 0, 2 },
                { 0, 0, -1, 0, 1, 0, 2, 0 } };

    }
}

class T extends Tetromino {
    public T() {
        cells[0] = new Cell(0, 4, Tetris.T);
        cells[1] = new Cell(0, 3, Tetris.T);
        cells[2] = new Cell(0, 5, Tetris.T);
        cells[3] = new Cell(1, 4, Tetris.T);
        type = 4;
        state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, 0 },
                { 0, 0, 1, 0, -1, 0, 0, 1 }, { 0, 0, 0, -1, 0, 1, 1, 0 },
                { 0, 0, -1, 0, 1, 0, 0, -1 } };

    }
}

class J extends Tetromino {
    public J() {
        cells[0] = new Cell(0, 4, Tetris.J);
        cells[1] = new Cell(0, 3, Tetris.J);
        cells[2] = new Cell(0, 5, Tetris.J);
        cells[3] = new Cell(1, 5, Tetris.J);
        type = 4;
        state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, -1 },
                { 0, 0, 1, 0, -1, 0, -1, 1 }, { 0, 0, 0, -1, 0, 1, 1, 1 },
                { 0, 0, -1, 0, 1, 0, 1, -1 } };

    }
}

class L extends Tetromino {
    public L() {
        cells[0] = new Cell(0, 4, Tetris.L);
        cells[1] = new Cell(0, 3, Tetris.L);
        cells[2] = new Cell(0, 5, Tetris.L);
        cells[3] = new Cell(1, 3, Tetris.L);

        type = 4;
        state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, 1 },
                { 0, 0, 1, 0, -1, 0, 1, 1 }, { 0, 0, 0, -1, 0, 1, 1, -1 },
                { 0, 0, -1, 0, 1, 0, -1, -1 } };

    }
}

class O extends Tetromino {
    public O() {
        cells[0] = new Cell(0, 4, Tetris.O);
        cells[1] = new Cell(0, 5, Tetris.O);
        cells[2] = new Cell(1, 4, Tetris.O);
        cells[3] = new Cell(1, 5, Tetris.O);
        type = 2;
        state = new int[][] { { 0, 0, 0, 1, 1, 0, 1, 1 },
                { 0, 0, 0, 1, 1, 0, 1, 1 } };
    }
}

class S extends Tetromino {
    public S() {
        cells[0] = new Cell(0, 4, Tetris.S);
        cells[1] = new Cell(0, 5, Tetris.S);
        cells[2] = new Cell(1, 3, Tetris.S);
        cells[3] = new Cell(1, 4, Tetris.S);
        type = 2;
        state = new int[][] { { 0, 0, 0, 1, 1, -1, 1, 0 },
                { 0, 0, -1, 0, 1, 1, 0, 1 } };

    }
}

class Z extends Tetromino {
    public Z() {
        cells[0] = new Cell(1, 4, Tetris.Z);
        cells[1] = new Cell(0, 3, Tetris.Z);
        cells[2] = new Cell(0, 4, Tetris.Z);
        cells[3] = new Cell(1, 5, Tetris.Z);
        type = 2;
        state = new int[][] { { 0, 0, -1, -1, -1, 0, 0, 1 },
                { 0, 0, -1, 1, 0, 1, 1, 0 } };

    }
}

最后是游戏类也即Tetris类:

package com.tarena.tetris;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.Arrays;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * 俄罗斯方块的主体程序
 * 
 * @author 锟铰斤拷
 * 
 */
@SuppressWarnings("serial")
public class Tetris extends JPanel {
    /**
     * 各种图片数据
     */
    protected static BufferedImage I;
    protected static BufferedImage J;
    protected static BufferedImage L;
    protected static BufferedImage O;
    protected static BufferedImage S;
    protected static BufferedImage T;
    protected static BufferedImage Z;
    protected static BufferedImage background;
    protected static BufferedImage gameOver;
    private static final int COLS = 10;
    private static final int ROWS = 20;
    /**
     * 消行得分数据
     */
    private static int lines = 0;
    private static int score = 0;

    protected Tetromino tetromino = new Tetromino().randomOne();// 下落的方块
    protected Tetromino nextOne = new Tetromino().randomOne();//下一个方块
    private Cell wall[][] = new Cell[ROWS][COLS];//
    /**
     * 游戏状态数据
     */
    private static int state;
    private static final int PLAYING = 0;
    private static final int GAMEOVER = 1;
    private static final int PAUSE = 2;
    private static final String[] State = { "[P]Pause", "[S]Restart",
            "[C]Continue" };

    /**
     * 方块下落速度控制数据
     */
    private static int speed = 10;
    private int index = -5;
    private int ss = 0;

    static {

        try {
            I = ImageIO.read(Tetris.class.getResource("I.png"));
            J = ImageIO.read(Tetris.class.getResource("J.png"));
            L = ImageIO.read(Tetris.class.getResource("L.png"));
            O = ImageIO.read(Tetris.class.getResource("O.png"));
            S = ImageIO.read(Tetris.class.getResource("S.png"));
            T = ImageIO.read(Tetris.class.getResource("T.png"));
            Z = ImageIO.read(Tetris.class.getResource("Z.png"));
            background = ImageIO.read(Tetris.class.getResource("tetris.png"));
            gameOver = ImageIO.read(Tetris.class.getResource("game-over.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 定义action方法控制游戏运行
     */
    public void action() {
        /**
         * 按键控制游戏
         */
        KeyListener l = new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                int key = e.getKeyCode();
                /*
                 * 各种按键功能
                 */
                if (key == KeyEvent.VK_Q) {
                    System.exit(0);
                }
//                if (key == KeyEvent.VK_P) {
//                    state=PAUSE;
//                    return;
//                }
//                if (state == GAMEOVER) {
//                    if (key == KeyEvent.VK_S) {
//                        restart();   
//                    
//                    }
//                    
//                }
                if (state == PAUSE) {
                    if (key == KeyEvent.VK_C) {
                        state = PLAYING;
                        
                    }else{
                        return;
                    }
                
                }

                switch (key) {
                case KeyEvent.VK_DOWN:
                    softDropAction();
                    break;
                case KeyEvent.VK_LEFT:
                    moveLeftAction();
                    break;
                case KeyEvent.VK_RIGHT:
                    moveRightAction();
                    break;
                case KeyEvent.VK_UP:
                    rotateAction();
                    break;
                case KeyEvent.VK_SPACE:
                    hardDropAcation();
                    break;
                case KeyEvent.VK_P:
                    state=PAUSE;
                    break;
                case KeyEvent.VK_S:
                    if(state==GAMEOVER){
                        restart();
                    }
                    break;
                
                }

                repaint();

            
            }

        };
        

        this.addKeyListener(l);
        this.requestFocus();
        while (true) {
            if (speed > 1 && score - ss >= 10) {
                ss += 10;
                speed--;
            }
            if (index++ % speed == 0&&state==PLAYING&&!isGameOver()) {
                softDropAction();
            }
            try {
                Thread.sleep(100);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            
            repaint();
        }
        

    }

    protected void restart() {
        wall = new Cell[ROWS][COLS];
        tetromino = new Tetromino().randomOne();
        nextOne = new Tetromino().randomOne();
        lines = 0;
        score = 0;
        speed = 10;
        ss = 0;
        index = -5;
        state=PLAYING;
    }

    /*
     * 快速下落
     */
    protected void hardDropAcation() {
        while (canDrop()) {
            tetromino.softDrop();

        }
        inWall();
        destroyLines();
        if (!isGameOver()) {

            tetromino = nextOne;
            nextOne = new Tetromino().randomOne();

        }
    }

    /*
     * 实现方块的旋转
     */
    protected void rotateAction() {
        tetromino.rotateRight();
        if (outOfWall() || coincide()) {
            tetromino.rotatLeft();

        }

    }

    /*
     * 右移
     */
    protected void moveRightAction() {
        tetromino.moveRight();
        if (outOfWall() || coincide()) {
            tetromino.moveLeft();

        }

    }

    /*
     * 判断方块有没有和墙上的格子重合
     */
    private boolean coincide() {
        Cell[] cells = tetromino.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (wall[row][col] != null) {
                return true;
            }
        }
        return false;
    }

    /*
     * 左移
     */
    protected void moveLeftAction() {
        tetromino.moveLeft();
        if (outOfWall() || coincide()) {

            tetromino.moveRight();
        }

    }

    /*
     *判断出没出界
     */
    private boolean outOfWall() {
        Cell[] cells = tetromino.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (row > ROWS - 1 || row < 0 || col > COLS - 1 || col < 0) {
                return true;
            }
        }
        return false;
    }

    /*
     * 缓慢下落
     */
    protected void softDropAction() {
        if (canDrop()) {
            tetromino.softDrop();
        } else {
            inWall();
            destroyLines();
            if (!isGameOver()) {

                tetromino = nextOne;
                nextOne = new Tetromino().randomOne();

            } else {
                state = GAMEOVER;
            }
        }

    }

    /*
     * 将方块放到墙上
     */
    private void inWall() {
        Cell[] cells = tetromino.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            // BufferedImage image=cell.getImage();
            wall[row][col] = cell;
        }
    }

    /*
     * 判断游戏是否结束
     */
    private boolean isGameOver() {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (wall[row][col] != null) {
                state=GAMEOVER;
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否还能下落
     * 
     * @return
     */
    private boolean canDrop() {
        Cell[] cells = tetromino.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (row >= ROWS - 1 || wall[row + 1][col] != null) {
                return false;
            }
        }
        return true;
    }

    /*
     * 消行 得分
     */
    int[] scores = { 0, 1, 10, 50, 100 };

    private void destroyLines() {
        int line = 0;
        for (int row = 0; row < ROWS; row++) {
            if (fullLine(row)) {
                deleteLine(row);
                line++;
            }
        }
        lines += line;
        score += scores[line];

    }

    /*
     * 判断一行是否满了
     */
    private boolean fullLine(int row) {
        for (int i = 0; i < COLS; i++) {
            if (wall[row][i] == null) {
                return false;
            }
        }

        return true;
    }

    /*
     * 删除一行
     */
    private void deleteLine(int row) {
        for (int i = row; i > 0; i--) {
            wall[i] = Arrays.copyOf(wall[i - 1], COLS);
        }
        Arrays.fill(wall[0], null);
    }

    /**
     * 重写绘图方法
     */
    public void paint(Graphics g) {
        g.drawImage(background, 0, 0, null);//背景图
        g.translate(15, 15);// 换坐标
        paintWall(g);
        paintTetris(g);
        paintNextOne(g);
        paintScores(g);
        paintState(g);

    }

    /*
     * 绘出游戏所处的状态
     */
    private void paintState(Graphics g) {

        String str = State[state];
        g.setColor(new Color(0x667799));
        Font font = g.getFont();
        Font f = new Font(font.getName(), font.getStyle(), 30);
        g.setFont(f);
        
        g.drawString(str, 290, 160 + 56 + 56);
        if (state == GAMEOVER) {
            g.translate(-15, -15);
            g.drawImage(gameOver, 0, 0, null);
        }
        
    }

    /*
     * 画出得分
     */
    private void paintScores(Graphics g) {
        String str = "SCORE:" + score;
        g.setColor(new Color(0x667799));
        Font font = g.getFont();
        Font f = new Font(font.getName(), font.getStyle(), 30);
        g.setFont(f);
        g.drawString(str, 290, 160);

        str = "LINES:" + lines;
        g.drawString(str, 290, 160 + 56);
    }

    /**
     * 画出下一个出场的方块
     * 
     * @param g
     */
    private void paintNextOne(Graphics g) {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int x = (cell.getCol() + 10) * SIZE;
            int y = (cell.getRow() + 1) * SIZE;
            BufferedImage image = cell.getImage();
            g.drawImage(image, x, y, null);
        }
    }

    static final int SIZE = 26;

    /**
     * 画出正在下落的方块
     * 
     * @param g
     */
    private void paintTetris(Graphics g) {
        Cell[] cells = tetromino.cells;
        for (Cell cell : cells) {
            int x = cell.getCol() * SIZE;
            int y = cell.getRow() * SIZE;
            BufferedImage image = cell.getImage();
            g.drawImage(image, x, y, null);
        }

    }

    /**
     * 画出墙
     * 
     * @param g
     */

    private void paintWall(Graphics g) {
        for (int row = 0; row < ROWS; row++) {
            for (int col = 0; col < COLS; col++) {

                int y = row * SIZE;
                int x = col * SIZE;
                g.drawRect(x, y, SIZE, SIZE);
                if (wall[row][col] != null) {
                    Cell cell = wall[row][col];
                    BufferedImage image = cell.getImage();
                    g.drawImage(image, x, y, null);
                }
            }
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(525, 550);
        Tetris panel = new Tetris();
        frame.add(panel);
        frame.setLocationRelativeTo(null);
        frame.setUndecorated(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        
        panel.action();
        
        
    }

}

这一次,知道加注释了,很多地方注释写的不详细,但是因为格式、命名规范很多,就算很久没看了,依然可以很轻松看懂每个方法、每个变量的具体功能,也不在像第一次写飞扬小鸟那样很吃力很生涩,借助于功能分析文档,基本上可以独立完成全部代码,而不用参照老师的代码。

原文地址:https://www.cnblogs.com/djavachen/p/3633072.html