Java简易俄罗斯方块

目前学到Swing第二章,这两天没有学新知识,写了个俄罗斯方块。 
写之前想不好怎么实现,找来别人的程序看了一下,再加上自己的想法,做了下面这个半成品,接下来可以加上各种菜单、按钮贴图等美化,都是些锦上添花的动作,繁琐但不难。 
我觉得写俄罗斯方块,难点在于如何将方块的形状(七种)、状态(四种翻转)、动作(左、右、下落)等提炼成数组。 这里采用四维数组来表示每一种方块:【种类】【翻转】【X坐标】【Y坐标】,每种方块都画在一个4*4的大方块中,把这个大方块作为一个整体进行移动。再写了一个方法遍历大方块,判断该方块的位置是否合法。 
有了合适的模型,再把每个步骤转化成代码,就不难了。



import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.Timer;
import javax.swing.JOptionPane;


public class KingTetris{
    // 20行,10列
    private final int ROW = 20;
    private final int COL = 10;

    // 每个方块边长,单位为像素
    private final int LEN = 35;

    // 游戏区域的左、上边距
    private final int LEFT_MARGIN = LEN*2;
    private final int UP_MARGIN = LEN;    
    
    // 面布大小,单位为像素
    private final int AREA_WIDTH = LEN*22;
    private final int AREA_HEIGHT = LEN*22;

    // 是否需要网格
    private boolean showGrid = true;

    // 是否彩色,将来可以作为贴图控制
    private boolean isColor = true;

    // 得分
    private int score = 0;

    // 画布
    private MyCanvas drawArea = new MyCanvas();
    // 窗口
    private JFrame f = new JFrame("俄罗斯方块");
    
    // 画图用的image
    private BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);
    private Graphics g = image.createGraphics();

    // 数组,用于保存背景
    private int[][] map = new int[COL][ROW];
    // 数组,用于保存颜色
    private Color[] color = new Color[]{Color.green, Color.red, Color.orange, Color.blue, Color.cyan, Color.yellow, Color.magenta, Color.gray};
    //默认灰色
    private final int DEFAULT = 7;
    private Color[][] mapColor = new Color[COL][ROW];

    //组件的横坐标
    int wordX = LEN*14;// 组件的横坐标
    int wordY = LEN*9; // 字的初始纵坐标

    //shape的四个参数
    private int type, state, x, y, nextType, nextState;

    //如果刚开始游戏,由于无nextType,先给type等随机一个,下为首次开始游戏的标志
    private boolean newBegin = true;

    // 用数组来代表不同形状的下坠物,四维分别是类型Type、旋转状态State、横坐标X、纵坐标Y。画示意图即可得出坐标
    // 方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名
    private int[][][][] shape = new int[][][][]{
        // S的四种翻转状态:
        { { {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0} }, 
        { {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0} } },
        // Z:
        { { {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, 
        { {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} } },
        // L:
        { { {0,0,0,0}, {1,1,1,0}, {0,0,1,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {0,1,1,0}, {0,1,0,0}, {0,1,0,0} }, 
        { {0,0,0,0}, {0,1,0,0}, {0,1,1,1}, {0,0,0,0} }, 
        { {0,0,1,0}, {0,0,1,0}, {0,1,1,0}, {0,0,0,0} } },
        // J:
        { { {0,0,0,0}, {0,0,1,0}, {1,1,1,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {0,1,1,0}, {0,0,1,0}, {0,0,1,0} }, 
        { {0,0,0,0}, {0,1,1,1}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,1,0,0}, {0,1,0,0}, {0,1,1,0}, {0,0,0,0} } },
        // I:
        { { {0,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,1,0,0} }, 
        { {0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0} }, 
        { {0,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,1,0,0} }, 
        { {0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0} } },
        // O:
        { { {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}  }, 
        { {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}  }, 
        { {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}  } },
        // T:
        { { {0,1,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,0,0,0}, {1,1,1,0}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,1,0,0}, {0,1,1,0}, {0,1,0,0}, {0,0,0,0} }, 
        { {0,1,0,0}, {1,1,1,0}, {0,0,0,0}, {0,0,0,0} } },

    };

    /**
     * 初始化界面
     */
    private void init(){
        drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
        f.add(drawArea);
        JCheckBox gridCB = new JCheckBox("显示网格",true);
        JCheckBox colorCB = new JCheckBox("彩色方块", false);
        gridCB.setBounds(wordX, wordY-LEN,LEN,LEN);
        colorCB.setBounds(wordX, wordY-2*LEN,LEN,LEN);

        // paintArea();
        // 加键盘监听器
        f.addKeyListener(new KeyAdapter(){
            public void keyPressed(KeyEvent e){
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        turn();
                        break;
                    case KeyEvent.VK_LEFT:
                        left();
                        break;
                    case KeyEvent.VK_RIGHT:
                        right();
                        break;
                    case KeyEvent.VK_DOWN:
                        down();
                        break;
                }                
            }
        });   
        Timer timer = new Timer(1000, new timerListener());
        newShape();
        timer.start();
        // 窗口显示在屏幕正中
        // Toolkit是抽象类,只能用getDefaultToolkit()方法来获取实例。
        // getScreenSize()方法返回的是一个Dimension对象,还须用getWidth()获取宽度
        f.pack();
        int screenSizeX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
        int screenSizeY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
        int fSizeX = (int)f.getSize().getWidth();
        int fSizeY = (int)f.getSize().getHeight();
        f.setResizable(false);// 禁止改变Frame大小
        f.setBounds((screenSizeX-fSizeX)/2, (screenSizeY-fSizeY)/2, fSizeX,fSizeY );    
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.setVisible(true);
    }

    /**
     * 绘图
     */
    private void paintArea(){
        //  默认黑色,填充白色
        g.setColor(Color.white);
        g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
        //  方格线
        //  先画最外围
        g.setColor(Color.gray);
        for (int offset = 0; offset <= 2; offset++){
            g.drawRect(LEFT_MARGIN-offset, UP_MARGIN-offset, COL*LEN+offset*2, ROW*LEN+offset*2);
        }
        
        //  如果showGrid为true则显示网格
        if(showGrid){
            g.setColor(Color.gray);
            // 9条竖线
            for (int i = 1 ; i <= 9; i++){
                g.drawLine(LEFT_MARGIN+LEN*i, UP_MARGIN, LEFT_MARGIN+LEN*i, UP_MARGIN+ROW*LEN);
            }
            // 19条横线
            for(int i = 1; i <= 19; i++){
                g.drawLine(LEFT_MARGIN, UP_MARGIN+LEN*i, LEFT_MARGIN+COL*LEN, UP_MARGIN+LEN*i);
            }
        }
        // 右上角显示下一个shape        
        int offset2 = 3;// 边框粗细
        int col = 4;// 右上角方框的列数
        int row = 4;// 行数
        g.setColor(Color.gray);
        g.setFont(new Font("Microsoft YaHei Mono", Font.BOLD, 20));
        g.drawString("下一个:", wordX, LEN*2);
        int nextX = wordX;
        int nextY = LEN*2;
        //暂不画方框
        // for (int offset = 0; offset <= 2; offset++){
        //     g.drawRect(nextX-offset+10, nextY+10-offset, col*LEN+offset*2, row*LEN+offset*2);
        // }
        //画下一次出现的下坠方块
        g.setColor(isColor?color[nextType]:color[DEFAULT]);
        for(int i = 0; i < 4; i++){
            for(int j = 0; j < 4; j++){
                if (shape[nextType][nextState][i][j]==1) {
                    g.fill3DRect(nextX+10+i*LEN, nextY+10+j*LEN, LEN, LEN,true);               
                }
            }
        }
        g.setColor(Color.gray);
        g.setFont(new Font("Times", Font.BOLD, 15));      
        g.drawString("玩法:", wordX, wordY+LEN*2);
        g.drawString("上箭头:翻转", wordX, wordY+LEN*3);
        g.drawString("左箭头:左移", wordX, wordY+LEN*4);
        g.drawString("右箭头:右移", wordX, wordY+LEN*5);
        g.drawString("下箭头:下落", wordX, wordY+LEN*6);
        g.setFont(new Font("Times", Font.BOLD, 25));
        g.drawString("得分:" + score, wordX, wordY+LEN*8);
        //画下坠物shape
        g.setColor(isColor?color[type]:color[DEFAULT]);
        for(int i = 0; i < 4; i++){
            for(int j = 0; j < 4; j++){
                if (shape[type][state][i][j]==1) {
                    g.fill3DRect(LEFT_MARGIN+(x+i)*LEN, UP_MARGIN+(y+j)*LEN, LEN, LEN,true);               
                }
            }
        }
        //画背景map
        for(int i = 0; i < COL; i++){
            for(int j = 0; j < ROW; j++){
                if (map[i][j] == 1) {
                    g.setColor(mapColor[i][j]);
                    g.fill3DRect(LEFT_MARGIN+i*LEN, UP_MARGIN+j*LEN, LEN, LEN,true);
                }
            }
        }

        drawArea.repaint();
    }

    /**
     * 自定义画布,重写paint()方法
     */
    private class MyCanvas extends JPanel{
        public void paint(Graphics g){
            g.drawImage(image, 0, 0, null);
        }
    }

    /**
     * 判断位置是否合法
     */
    private boolean check(int type, int state, int x, int y){
        for(int i = 0; i < 4; i++){
            for(int j = 0; j < 4; j++){
                if ( (shape[type][state][i][j] == 1) && ( (x+i>=COL) || (x+i<0 ) || (y+j>=ROW) || (map[x+i][y+j]==1) ) ) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 判断游戏是否结束
     */
    private boolean isGameOver(int type, int state, int x, int y){
        return !check(type, state, x, y);
    }

    /**
     * 新建方块
     */
    private void newShape(){
        Random rand = new Random();
        if(newBegin){
            type = rand.nextInt(7); 
            state = rand.nextInt(4); 
            newBegin = false;
        }
        else{
            type = nextType;        
            state = nextState;
        }        
        nextType = rand.nextInt(7); 
        nextState = rand.nextInt(4);        
        x = 3;
        y = 0;
        // 如果游戏已结束,则重新开始
        if(isGameOver(type, state, x, y)){            
            JOptionPane.showMessageDialog(f, "GAME OVER!");
            newGame();
        }
        paintArea();
        
    }

    /**
     * 新建游戏
     */
    private void newGame(){
        newMap();
        score = 0;
        newBegin = true;
    }

    /**
     * 清空背景图
     */
    private void newMap(){
        for(int i = 0; i < COL; i++){
            Arrays.fill(map[i],0);
        }        
    }

    /**
     * 消行
     */
    private void delLine(){
        boolean flag = true;
        int addScore = 0;
        for(int j = 0; j < ROW; j++){
            flag = true;
            for( int i = 0; i < COL; i++){
                if (map[i][j]==0){
                    flag = false;
                    break;
                }
            }
            if(flag){
                addScore += 10;
                for(int t = j; t > 0; t--){
                    for(int i = 0; i =0)&&(map[x+i][y+j]==0)){
                    map[x+i][y+j]=shape[type][state][i][j];
                    mapColor[x+i][y+j]=color[isColor?type:DEFAULT];
                }
            }
        }
    }

    /**
     * 下面为四个方向键对应的方法
     */
    private void turn(){
        int tmpState = state;
        state = (state + 1)%4;
        if (!check(type,state, x, y )) {
            state = tmpState; //不能转就什么都不做           
        }
        paintArea();
    }

    private void left(){
        if(check(type,state, x-1, y)){
            --x;
        }
        paintArea();
    }

    private void right(){
        if (check(type,state, x+1, y)) {
            ++x;
        }
        paintArea();
    }

    private void down(){
        if (check(type,state, x, y+1)) {
            ++y;
        }
        //如果下不去则固定之
        else{
            add(type, state, x, y);
            delLine();
            newShape();
        }
        paintArea();
    }


    /**
     * 主函数
     */
    public static void main(String[] args){
        new KingTetris().init();
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/kinglearnjava/p/4883344.html

你可能感兴趣的:(Java简易俄罗斯方块)