【JavaSE】实现游戏俄罗斯方块

前言

   教学案例,快速巩固JavaSE内容,主要方便学生查询,复习使用

一、游戏图展示

游戏效果图

 

 

二、实现代码

package com.mg.Three;

import javax.swing.*;
import java.awt.*;
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;

/**
 * 类名:Main
 * 包名:com
 * 创建时间:2020/7/1 10:11
 * 创建人: 明哥
 * 描述: 俄罗斯方块
 * <p>
 * 难点在于方块的形状(七种)、状态(四种状态)、动作(左、右、下落)等提炼成数组
 * 这里采用四维数组来表示每一种方块,【种类】【翻转】【X坐标】【Y坐标】
 * 每种方块懂画一个4*4的大方块中,把这个大方块作为一个整体进行移动
 * 然后我们在写一个方法遍历大方块,判断该方块的位置是否合法
 **/
public class MainTest {
    // 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 < COL; i++) {
                        map[i][t] = map[i][t - 1];
                    }
                }
            }
        }
        score += addScore * addScore / COL;
    }

    /**
     * 计时器所用的事件监听器
     */
    private class timerListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (check(type, state, x, y + 1)) {
                y = y + 1;
            } else {
                add(type, state, x, y);
                delLine();
                newShape();
            }
            paintArea();
        }
    }

    /**
     * 把shape存到map的add方法
     */
    private void add(int type, int state, int x, int y) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if ((y + j < ROW) && (x + i < COL) && (x + 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 MainTest().init();
    }
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈老说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值