【实战】俄罗斯方块

一、初始化游戏窗口以及游戏界面

初始化窗口需要继承JFrame类,添加监听事件需要实现KeyListener接口

Tertris.java

package com.sxt;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Tertris extends JFrame implements KeyListener {

    //游戏的行数26,列数12
    private static final int game_x = 26;
    private static final int game_y = 12;
    //文本域数组
    JTextArea[][] text;//每个格子是一个文本域
    //二维数组
    int[][] data; //定义每一个格子的值,1代表有方块,0代表空白区域

    public void initWindow(){//初始化窗口
        //设置窗口大小
        this.setSize(500,700);
        //设置窗口是否可见
        this.setVisible(true);//默认false不可见
        //设置窗口居中
        this.setLocationRelativeTo(null);
        //设置释放窗体
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口大小不可变
        this.setResizable(false);
        //设置标题
        this.setTitle("俄罗斯方块游戏");
    }

    //初始化游戏界面
    public void initGamePanel(){
        JPanel game_main = new JPanel();//定义JPanel对象
        //调用setLayout方法设置屏幕组件的格式布局
        game_main.setLayout(new GridLayout(game_x,game_y,1,1));//行数、列数、水平间距、竖直间距
        //初始化面板
        for(int i = 0 ; i < text.length ; i++){
            for(int j = 0 ; j < text[i].length ; j++){
                //设置文本域的行列数
                text[i][j] = new JTextArea(game_x,game_y);
                //设置文本域的背景颜色
                text[i][j].setBackground(Color.WHITE);
                //添加键盘监听事件
                text[i][j].addKeyListener(this);
                //初始化游戏边界
                if(j == 0 || j == text[i].length-1 || i == text.length-1){//左边界、右边界、下边界
                    text[i][j].setBackground(Color.MAGENTA);
                    data[i][j] = 1;//表示这里有方块
                }
                //设置文本区域不可编辑
                text[i][j].setEditable(false);
                //文本区域添加到主面板上
                game_main.add(text[i][j]);
            }
        }
        //添加到窗口中
        this.setLayout(new BorderLayout());
        //添加到中间位置
        this.add(game_main,BorderLayout.CENTER);
    }

    public Tertris(){//空参构造
        text = new JTextArea[game_x][game_y];
        data = new int[game_x][game_y];
        initGamePanel();
        initWindow();//调用初始化的方法
    }

    public static void main(String[] args){
        Tertris tertris = new Tertris();//创建该类的对象
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

二、初始化游戏的说明面板

    //显示游戏状态的标签
    JLabel label1;
    //显示游戏分数的标签
    JLabel label;

    //初始化游戏的说明面板
    public void initExplainPanel(){
        //创建游戏的左说明面板
        JPanel explain_left  = new JPanel();
        //创建游戏的右说明面板
        JPanel explain_right = new JPanel();
        //调用setLayout方法初始化格式布局
        explain_left.setLayout(new GridLayout(4,1));
        explain_right.setLayout(new GridLayout(2,1));
        //初始化左说明面板

        //在左说明面板,添加说明文字
        explain_left.add(new JLabel("按空格键,方块变形"));
        explain_left.add(new JLabel("按左箭头,方块左移"));
        explain_left.add(new JLabel("按右箭头,方块右移"));
        explain_left.add(new JLabel("按下箭头,方块下落"));
        //设置标签的内容为红色字体
        label1.setForeground(Color.RED);
        //把游戏状态标签、游戏分数标签、添加到右说明面板
        explain_right.add(label);
        explain_right.add(label1);
        //将左说明面板添加到窗口的左侧
        this.add(explain_left,BorderLayout.WEST);
        //将右说明面板添加到窗口的右侧
        this.add(explain_right,BorderLayout.EAST);
    }
    public Tertris(){//空参构造
        //初始化表示游戏状态的标签
        label1 = new JLabel("游戏状态:正在游戏中!");
        //初始化表示游戏分数的标签
        label = new JLabel("游戏得分为:0");
        initExplainPanel();
    }

三、开始游戏的方法

    //用于判断游戏是否结束
    boolean isrunning;

    public Tertris(){//空参构造
        //初始化游戏开始的标志
        isrunning = true;
    }    

    //开始游戏的方法
    public void game_begin(){
        while (true){
            //判断游戏是否结束
            if(!isrunning){
                break;;
            }
            
            //进行游戏
            game_run();//在之后完成
        }
        //在标签位置显示“游戏结束”
        label1.setText("游戏状态:游戏结束!");
    }

四、随机生成下落方块形状的方法

设计一个蓝色的2*2的方块,将它放在4*4的白色方块中,如下:

 空白处用0表示,有颜色的地方用1表示,转化为16进制的数:0x00cc,“0x”代表这是一个16进制的数,“1100”=1*2^3+1*2^2=12,用字母c表示。

 以下是所有方块的索引,方块一共22个,数字0~21

        0               1                 2                 3                 4                5                 6                 7

              

    0x00cc      0x8888      0x000f         0x888f         0xf888       0xf111          0x111f         0x0eee

      8               9                10                11               12              13               14               15

              

   0xffff         0x0008        0x0888       0x000e       0x0088        0x000c      0x08c8        0x00e4

       16               17              18              19               20                21            

          

0x04c4         0x004e       0x08c4        0x006c           0x04c8        0x00c6

    //用于存储所有的方块的数组
    int[] allRect;
    //用于存储当前方块的数量
    int rect;

    public Tertris(){//空参构造
        //初始化存放方块的数组
        allRect = new int[]{0x00cc,0x8888,0x000f,0x888f,0xf888,0xf111, 0x111f,0x0eee,
                0xffff,0x0008,0x0888,0x000e,0x0088, 0x000c, 0x08c8, 0x00e4,
                0x04c4,0x004e,0x08c4,0x006c,0x04c8,0x00c6};
    }

    //随机生成下落方块形状的方法
    public void ranRect(){
        Random random = new Random();
        rect = allRect[random.nextInt(22)];
    }

五、游戏运行的方法

game_run(); 游戏运行的方法

canFall(); 方块是否可以继续下落的方法

changData(); 改变不可下降的方块对应的区域的值的方法

removeRow(); 移除某一行的所有方块,令以上方块掉落的方法

reflesh(); 刷新移除某一行后的游戏界面的方法

fall(); 方块向下掉落一层的方法

clear(); 清除方块掉落后,上一层有颜色的地方的方法

draw(); 重新绘制掉落后方块的方法

    //该变量用于计算得分
    int score = 0; 
   
    //游戏运行的方法
    public void game_run() {
        ranRect();
        //方块下落位置
        x = 0;
        y = 5;

        for (int i = 0;i < game_x; i++ ){
            try {
                Thread.sleep(time);

                //判断方块是否可以下落
                if(!canFall(x,y)){
                    //将data置为1,表示有方块占用
                    changData(x,y);
                    //循环遍历4层,看是否有行可以消除
                    for (int j = x;j < x + 4;j++){
                        int sum = 0;

                        for(int k = 1;k <= (game_y-2);k++){
                            if(data[j][k] == 1){
                                sum++;
                            }
                        }

                        //判断是否有一行可以被消除
                        if(sum == (game_y-2)){
                            //消除j这一行
                            removeRow(j);
                        }
                    }
                    //判断游戏是否失败
                    for(int j = 1;j <= (game_y-2);j++){//判断第四行是否有方块
                        if(data[3][j] == 1){
                            isrunning = false;
                            break;
                        }
                    }
                    break;
                }else {
                    //层数+1
                    x++;
                    //方块下落一行
                    fall(x,y);//让方块下落一层
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //判断方块是否可以继续下落的方法
    public boolean canFall(int m, int n) {
        //定义一个变量
        int temp = 0x8000;
        //遍历4*4方格
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp &rect) != 0){
                    //判断该位置的下一行是否有方块
                    if (data[m+1][n] == 1){
                        return false;
                    }
                }
                n++;
                temp >>= 1;//temp值右移一位
            }
            m++;//行数+1
            n = n - 4;//列数回归到首列
        }
        //可以下落
        return true;
    }

    //改变不可下降的方块对应的区域的值的方法
    public void changData(int m, int n) {
        //定义一个变量
        int temp = 0x8000;
        //遍历整个4*4的方块
        for (int i = 0;i < 4;i++){
            for (int j = 0;j < 4;j++){
                if ((temp & rect) != 0){
                    data[m][n] = 1;
                }
                n++;
                temp >>= 1;//temp值右移一位
            }
            m++;//行数+1
            n = n - 4;//列数回归到首列
        }
    }

    //移除某一行的所有方块,令以上方块掉落的方法
    public void removeRow(int row) {
        int temp = 100;
        for (int i = row;i >= 1;i--) {
            for (int j = 1;j <= (game_y-2);j++) {
                //进行覆盖
                data[i][j] = data[i-1][j];
            }
        }
        //刷新游戏区域
        reflesh(row);

        //方块加速
        if (time > temp) {
            time -= temp;
        }

        score += temp;

        //显示变化后的分数
        label.setText("游戏得分:" + score);
    }

    //刷新移除某一行后的游戏界面的方法
    public void reflesh(int row) {
        //遍历row行以上的游戏区域
        for (int i = row;i >= 1;i--){
            for (int j = 1;j <= (game_y-2);j++){
                if (data[i][j] == 1){
                    text[i][j].setBackground(Color.BLUE);
                }else {
                    text[i][j].setBackground(Color.WHITE);
                }
            }
        }
    }

    //方块向下掉落一层的方法
    public void fall(int m, int n) {
        if (m > 0) {
            //清除上一层方块
            clear(m-1,n);
        }
        //重新绘制
        draw(m,n);
    }

    //清除方块掉落后,上一层有颜色的地方的方法
    public void clear(int m,int n) {
        //定义变量、
        int temp = 0x8000;
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp & rect) !=0) {
                    text[m][n].setBackground(Color.WHITE);
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
    }
    
    //重新绘制掉落后方块的方法
    public void draw(int m,int n) {
        //定义变量
        int temp = 0x8000;
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp & rect) !=0) {
                    text[m][n].setBackground(Color.BLUE);
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
    }

六、键盘控制方块左移

    @Override
    public void keyPressed(KeyEvent e) {
        //方块进行左移
        if (e.getKeyCode() == 37) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //方块是否碰到左墙壁
            if (y <= 1) {
                return;
            }

            //定义一个变量
            int temp = 0x8000;

            for (int i = x;i < x + 4;i++) {
                for (int j = y;j < y + 4;j++) {
                    if ((temp & rect) !=0) {
                        if (data[i][j-1] == 1) {
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            //首先清除目前方块
            clear(x,y);

            y--;

            draw(x,y);
        }
    }

七、键盘控制方块右移

    @Override
    public void keyPressed(KeyEvent e) {
        
        //方块进行右移
        if (e.getKeyCode() == 39) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //定义一个变量
            int temp = 0x8000;
            int m = x;
            int n = y;

            //存储最右边的坐标值
            int num = 1;

            for (int i = 0;i < 4;i++) {
                for (int j = 0;j < 4;j++) {
                    if ((temp & rect) !=0) {
                        if (n > num) {
                            num = n;
                        }
                    }
                    n++;
                    temp >>= 1;
                }
                m++;
                n = n - 4;
            }

            //判断是否碰到右墙壁
            if (num >= (game_y-2)) {
                return;
            }

            //方块右移途中是否碰到别的方块
            temp = 0x8000;
            for (int i = x;i < x + 4;i++) {
                for (int j = y;j < y + 4;j++) {
                    if ((temp & rect) !=0) {
                        if (data[i][j+1] == 1) {
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            //清除当前方块
            clear(x,y);

            y++;

            draw(x,y);
        }
    }

八、键盘控制方块下落

    @Override
    public void keyPressed(KeyEvent e) {

        //方块进行下落
        if (e.getKeyCode() == 40) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }
            
            //判断方块是否可以下落
            if (!canFall(x,y)) {
                return;
            }
            
            clear(x,y);

            //改变方块的坐标
            x++;

            draw(x,y);
        }
    }

九、键盘控制方块变形

canTurn(); 判断方块此时是否可以变形的方法

    @Override
    public void keyTyped(KeyEvent e) {
        //控制方块进行变形
        if (e.getKeyChar() == KeyEvent.VK_SPACE) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //定义变量,存储目前方块的索引
            int old;
            for (old = 0;old < allRect.length;old++) {
                //判断是否是当前方块
                if (rect == allRect[old]) {
                    break;
                }
            }

            //定义变量,存储变形后方块
            int next;

            //判断是方块
            if (old == 0 || old == 7 || old == 8 || old == 9) {
                return;
            }

            //清除当前方块
            clear(x,y);

            if (old == 1 || old == 2) {
                next = allRect[old == 1 ? 2 : 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old >= 3 && old <= 6) {
                next = allRect[old + 1 > 6 ? 3 : old + 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 10 || old == 11) {
                next = allRect[old == 10 ? 11 : 10];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 12 || old == 13) {
                next = allRect[old == 12 ? 13 : 12];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old >= 14 && old <= 17) {
                next = allRect[old + 1 > 17 ? 14 : old + 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 18 || old == 19) {
                next = allRect[old == 18 ? 19 : 18];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 20 || old == 21) {
                next = allRect[old == 20 ? 21 : 20];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            //重新绘制变形后方块
            draw(x,y);
        }
    }

    //判断方块此时是否可以变形的方法
    public boolean canTurn(int a,int m,int n) {
        //创建变量
        int temp = 0x8000;
        //遍历整个方块
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((a & temp) !=0) {
                    if (data[m][n] ==1) {
                        return false;
                    }
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
        //可以变形
        return true;
    }

十、添加游戏暂停功能后的全部代码

Tertris.java

package com.sxt;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class Tertris extends JFrame implements KeyListener {

    //游戏的行数26,列数12
    private static final int game_x = 26;
    private static final int game_y = 12;
    //文本域数组
    JTextArea[][] text;//每个格子是一个文本域
    //二维数组
    int[][] data; //定义每一个格子的值,1代表有方块,0代表空白区域
    //显示游戏状态的标签
    JLabel label1;
    //显示游戏分数的标签
    JLabel label;
    //用于判断游戏是否结束
    boolean isrunning;
    //用于存储所有的方块的数组
    int[] allRect;
    //用于存储当前方块的数量
    int rect;
    //线程的休眠时间
    int time = 1000;
    //表示方块坐标
    int x, y;
    //该变量用于计算得分
    int score = 0;
    //定义一个标志变量,用于判断游戏是否暂停
    boolean game_pause = false;
    //定义一个变量用于记录按下暂停键的次数
    int pause_times = 0;

    public void initWindow() {//初始化窗口
        //设置窗口大小
        this.setSize(500,700);
        //设置窗口是否可见
        this.setVisible(true);//默认false不可见
        //设置窗口居中
        this.setLocationRelativeTo(null);
        //设置释放窗体
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口大小不可变
        this.setResizable(false);
        //设置标题
        this.setTitle("俄罗斯方块游戏");
    }

    //初始化游戏界面
    public void initGamePanel() {
        JPanel game_main = new JPanel();//定义JPanel对象
        //调用setLayout方法设置屏幕组件的格式布局
        //行数、列数、水平间距、竖直间距
        game_main.setLayout(new GridLayout(game_x,game_y,1,1));
        //初始化面板
        for(int i = 0 ; i < text.length ; i++){
            for(int j = 0 ; j < text[i].length ; j++){
                //设置文本域的行列数
                text[i][j] = new JTextArea(game_x,game_y);
                //设置文本域的背景颜色
                text[i][j].setBackground(Color.WHITE);
                //添加键盘监听事件
                text[i][j].addKeyListener(this);
                //初始化游戏边界
                if(j == 0 || j == text[i].length-1 || i == text.length-1){
                    //左边界、右边界、下边界
                    text[i][j].setBackground(Color.MAGENTA);
                    data[i][j] = 1;//表示这里有方块
                }
                //设置文本区域不可编辑
                text[i][j].setEditable(false);
                //文本区域添加到主面板上
                game_main.add(text[i][j]);
            }
        }
        //添加到窗口中
        this.setLayout(new BorderLayout());
        //添加到中间位置
        this.add(game_main,BorderLayout.CENTER);
    }

    //初始化游戏的说明面板
    public void initExplainPanel() {
        //创建游戏的左说明面板
        JPanel explain_left  = new JPanel();
        //创建游戏的右说明面板
        JPanel explain_right = new JPanel();
        //调用setLayout方法初始化格式布局
        explain_left.setLayout(new GridLayout(4,1));
        explain_right.setLayout(new GridLayout(2,1));
        //初始化左说明面板

        //在左说明面板,添加说明文字
        explain_left.add(new JLabel("按空格键,方块变形"));
        explain_left.add(new JLabel("按左箭头,方块左移"));
        explain_left.add(new JLabel("按右箭头,方块右移"));
        explain_left.add(new JLabel("按下箭头,方块下落"));
        //设置标签的内容为红色字体
        label1.setForeground(Color.RED);
        //把游戏状态标签、游戏分数标签、添加到右说明面板
        explain_right.add(label);
        explain_right.add(label1);
        //将左说明面板添加到窗口的左侧
        this.add(explain_left,BorderLayout.WEST);
        //将右说明面板添加到窗口的右侧
        this.add(explain_right,BorderLayout.EAST);
    }

    public Tertris() {//空参构造
        text = new JTextArea[game_x][game_y];
        data = new int[game_x][game_y];
        //初始化表示游戏状态的标签
        label1 = new JLabel("游戏状态:正在游戏中!");
        //初始化表示游戏分数的标签
        label = new JLabel("游戏得分为:0");
        initGamePanel();
        initExplainPanel();
        initWindow();//调用初始化的方法
        //初始化游戏开始的标志
        isrunning = true;
        //初始化存放方块的数组
        allRect = new int[]{0x00cc,0x8888,0x000f,0x888f,0xf888,0xf111, 0x111f,0x0eee,
                0xffff,0x0008,0x0888,0x000e,0x0088, 0x000c, 0x08c8, 0x00e4,
                0x04c4,0x004e,0x08c4,0x006c,0x04c8,0x00c6};
    }

    public static void main(String[] args) {
        Tertris tertris = new Tertris();//创建该类的对象
        tertris.game_begin();
    }

    //开始游戏的方法
    public void game_begin() {
        while (true){
            //判断游戏是否结束
            if(!isrunning){
                break;
            }

            //进行游戏
            game_run();//在之后完成
        }
        //在标签位置显示“游戏结束”
        label1.setText("游戏状态:游戏结束!");
    }

    //随机生成下落方块形状的方法
    public void ranRect() {
        Random random = new Random();
        rect = allRect[random.nextInt(22)];
    }

    //游戏运行的方法
    public void game_run() {
        ranRect();
        //方块下落位置
        x = 0;
        y = 5;

        for (int i = 0;i < game_x; i++ ){
            try {
                Thread.sleep(time);

                if (game_pause) {
                    i--;
                }else {
                    //判断方块是否可以下落
                    if (!canFall(x, y)) {
                        //将data置为1,表示有方块占用
                        changData(x, y);
                        //循环遍历4层,看是否有行可以消除
                        for (int j = x; j < x + 4; j++) {
                            int sum = 0;

                            for (int k = 1; k <= (game_y - 2); k++) {
                                if (data[j][k] == 1) {
                                    sum++;
                                }
                            }

                            //判断是否有一行可以被消除
                            if (sum == (game_y - 2)) {
                                //消除j这一行
                                removeRow(j);
                            }
                        }
                        //判断游戏是否失败
                        for (int j = 1; j <= (game_y - 2); j++) {//判断第四行是否有方块
                            if (data[3][j] == 1) {
                                isrunning = false;
                                break;
                            }
                        }
                        break;
                    } else {
                        //层数+1
                        x++;
                        //方块下落一行
                        fall(x, y);//让方块下落一层
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //判断方块是否可以继续下落的方法
    public boolean canFall(int m, int n) {
        //定义一个变量
        int temp = 0x8000;
        //遍历4*4方格
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp &rect) != 0){
                    //判断该位置的下一行是否有方块
                    if (data[m+1][n] == 1){
                        return false;
                    }
                }
                n++;
                temp >>= 1;//temp值右移一位
            }
            m++;//行数+1
            n = n - 4;//列数回归到首列
        }
        //可以下落
        return true;
    }

    //改变不可下降的方块对应的区域的值的方法
    public void changData(int m, int n) {
        //定义一个变量
        int temp = 0x8000;
        //遍历整个4*4的方块
        for (int i = 0;i < 4;i++){
            for (int j = 0;j < 4;j++){
                if ((temp & rect) != 0){
                    data[m][n] = 1;
                }
                n++;
                temp >>= 1;//temp值右移一位
            }
            m++;//行数+1
            n = n - 4;//列数回归到首列
        }
    }

    //移除某一行的所有方块,令以上方块掉落的方法
    public void removeRow(int row) {
        int temp = 100;
        for (int i = row;i >= 1;i--) {
            for (int j = 1;j <= (game_y-2);j++) {
                //进行覆盖
                data[i][j] = data[i-1][j];
            }
        }
        //刷新游戏区域
        reflesh(row);

        //方块加速
        if (time > temp) {
            time -= temp;
        }

        score += temp;

        //显示变化后的分数
        label.setText("游戏得分:" + score);
    }

    //刷新移除某一行后的游戏界面的方法
    public void reflesh(int row) {
        //遍历row行以上的游戏区域
        for (int i = row;i >= 1;i--){
            for (int j = 1;j <= (game_y-2);j++){
                if (data[i][j] == 1){
                    text[i][j].setBackground(Color.BLUE);
                }else {
                    text[i][j].setBackground(Color.WHITE);
                }
            }
        }
    }

    //方块向下掉落一层的方法
    public void fall(int m, int n) {
        if (m > 0) {
            //清除上一层方块
            clear(m-1,n);
        }
        //重新绘制
        draw(m,n);
    }

    //清除方块掉落后,上一层有颜色的地方的方法
    public void clear(int m,int n) {
        //定义变量、
        int temp = 0x8000;
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp & rect) !=0) {
                    text[m][n].setBackground(Color.WHITE);
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
    }

    //重新绘制掉落后方块的方法
    public void draw(int m,int n) {
        //定义变量
        int temp = 0x8000;
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((temp & rect) !=0) {
                    text[m][n].setBackground(Color.BLUE);
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
        //控制游戏暂停
        if (e.getKeyChar() == 'p') {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            pause_times++;

            //判断按下一次,暂停游戏
            if (pause_times == 1) {
                game_pause = true;
                label1.setText("游戏状态:暂停中!");
            }

            //判断按下两次,继续游戏
            if (pause_times == 2) {
                game_pause = false;
                pause_times = 0;
                label1.setText("游戏状态:正在进行中!");
            }
        }
        //控制方块进行变形
        if (e.getKeyChar() == KeyEvent.VK_SPACE) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //判断游戏是否暂停
            if (game_pause) {
                return;
            }

            //定义变量,存储目前方块的索引
            int old;
            for (old = 0;old < allRect.length;old++) {
                //判断是否是当前方块
                if (rect == allRect[old]) {
                    break;
                }
            }

            //定义变量,存储变形后方块
            int next;

            //判断是方块
            if (old == 0 || old == 7 || old == 8 || old == 9) {
                return;
            }

            //清除当前方块
            clear(x,y);

            if (old == 1 || old == 2) {
                next = allRect[old == 1 ? 2 : 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old >= 3 && old <= 6) {
                next = allRect[old + 1 > 6 ? 3 : old + 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 10 || old == 11) {
                next = allRect[old == 10 ? 11 : 10];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 12 || old == 13) {
                next = allRect[old == 12 ? 13 : 12];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old >= 14 && old <= 17) {
                next = allRect[old + 1 > 17 ? 14 : old + 1];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 18 || old == 19) {
                next = allRect[old == 18 ? 19 : 18];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            if (old == 20 || old == 21) {
                next = allRect[old == 20 ? 21 : 20];

                if (canTurn(next,x,y)) {
                    rect = next;
                }
            }

            //重新绘制变形后方块
            draw(x,y);
        }
    }

    //判断方块此时是否可以变形的方法
    public boolean canTurn(int a,int m,int n) {
        //创建变量
        int temp = 0x8000;
        //遍历整个方块
        for (int i = 0;i < 4;i++) {
            for (int j = 0;j < 4;j++) {
                if ((a & temp) !=0) {
                    if (data[m][n] ==1) {
                        return false;
                    }
                }
                n++;
                temp >>= 1;
            }
            m++;
            n = n - 4;
        }
        //可以变形
        return true;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        //方块进行左移
        if (e.getKeyCode() == 37) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //判断游戏是否暂停
            if (game_pause) {
                return;
            }

            //方块是否碰到左墙壁
            if (y <= 1) {
                return;
            }

            //定义一个变量
            int temp = 0x8000;

            for (int i = x;i < x + 4;i++) {
                for (int j = y;j < y + 4;j++) {
                    if ((temp & rect) !=0) {
                        if (data[i][j-1] == 1) {
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            //首先清除目前方块
            clear(x,y);

            y--;

            draw(x,y);
        }

        //方块进行右移
        if (e.getKeyCode() == 39) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //判断游戏是否暂停
            if (game_pause) {
                return;
            }

            //定义一个变量
            int temp = 0x8000;
            int m = x;
            int n = y;

            //存储最右边的坐标值
            int num = 1;

            for (int i = 0;i < 4;i++) {
                for (int j = 0;j < 4;j++) {
                    if ((temp & rect) !=0) {
                        if (n > num) {
                            num = n;
                        }
                    }
                    n++;
                    temp >>= 1;
                }
                m++;
                n = n - 4;
            }

            //判断是否碰到右墙壁
            if (num >= (game_y-2)) {
                return;
            }

            //方块右移途中是否碰到别的方块
            temp = 0x8000;
            for (int i = x;i < x + 4;i++) {
                for (int j = y;j < y + 4;j++) {
                    if ((temp & rect) !=0) {
                        if (data[i][j+1] == 1) {
                            return;
                        }
                    }
                    temp >>= 1;
                }
            }

            //清除当前方块
            clear(x,y);

            y++;

            draw(x,y);
        }

        //方块进行下落
        if (e.getKeyCode() == 40) {
            //判断游戏是否结束
            if (!isrunning) {
                return;
            }

            //判断游戏是否暂停
            if (game_pause) {
                return;
            }

            //判断方块是否可以下落
            if (!canFall(x,y)) {
                return;
            }

            clear(x,y);

            //改变方块的坐标
            x++;

            draw(x,y);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

十一、运行结果

俄罗斯方块游戏 2023-07-12 15-09-50

<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>俄罗斯方块</title> [removed] var TETRIS_ROWS = 20; var TETRIS_COLS = 14; var CELL_SIZE = 24; // 没方块是0 var NO_BLOCK = 0; var tetris_canvas; var tetris_ctx; // 记录当前积分 var curScore = 0; // 记录当前速度 var curSpeed = 1; // 记录曾经的最高积分 var maxScore = 0; var curScoreEle , curSpeedEle , maxScoreEle; var curTimer; // 记录当前是否游戏中的旗标 var isPlaying = true; // 记录正在下掉的四个方块 var currentFall; // 该数组用于记录底下已经固定下来的方块。 var tetris_status = []; for (var i = 0; i < TETRIS_ROWS ; i++ ) { tetris_status[i] = []; for (var j = 0; j < TETRIS_COLS ; j++ ) { tetris_status[i][j] = NO_BLOCK; } } // 定义方块的颜色 colors = ["#fff", "#f00" , "#0f0" , "#00f" , "#c60" , "#f0f" , "#0ff" , "#609"]; // 定义几种可能出现的方块组合 var blockArr = [ // 代表第一种可能出现的方块组合:Z [ {x: TETRIS_COLS / 2 - 1 , y:0 , color:1}, {x: TETRIS_COLS / 2 , y:0 ,color:1}, {x: TETRIS_COLS / 2 , y:1 ,color:1}, {x: TETRIS_COLS / 2 + 1 , y:1 , color:1} ], // 代表第二种可能出现的方块组合:反Z [ {x: TETRIS_COLS / 2 + 1 , y:0 , color:2}, {x: TETRIS_COLS / 2 , y:0 , color:2}, {x: TETRIS_COLS / 2 , y:1 , color:2}, {x: TETRIS_COLS / 2 - 1 , y:1 , color:2} ], // 代表第三种可能出现的方块组合: 田 [ {x: TETRIS_COLS / 2 - 1 , y:0 , color:3}, {x: TETRIS_COLS / 2 , y:0 , color:3}, {x: TETRIS_COLS / 2 - 1 , y:1 , color:3}, {x: TETRIS_COLS / 2 , y:1 , color:3} ], // 代表第四种可能出现的方块组合:L [ {x: TETRIS_COLS / 2 - 1 , y:0 , color:4}, {x: TETRIS_COLS / 2 - 1, y:1 , color:4}, {x: TETRIS_COLS / 2 - 1 , y:2 , color:4}, {x: TETRIS_COLS / 2 , y:2 , color:4} ], // 代表第五种可能出现的方块组合:J [ {x: TETRIS_COLS / 2 , y:0 , color:5}, {x: TETRIS_COLS / 2 , y:1, color:5}, {x: TETRIS_COLS / 2 , y:2, color:5}, {x: TETRIS_COLS / 2 - 1, y:2, color:5} ], // 代表第六种可能出现的方块组合 : 条 [ {x: TETRIS_COLS / 2 , y:0 , color:6}, {x: TETRIS_COLS / 2 , y:1 , color:6}, {x: TETRIS_COLS / 2 , y:2 , color:6}, {x: TETRIS_COLS / 2 , y:3 , color:6} ], // 代表第七种可能出现的方块组合 : ┵ [ {x: TETRIS_COLS / 2 , y:0 , color:7}, {x: TETRIS_COLS / 2 - 1 , y:1 , color:7}, {x: TETRIS_COLS / 2 , y:1 , color:7}, {x: TETRIS_COLS / 2 + 1, y:1 , color:7} ] ]; // 定义初始化正在下掉的方块 var initBlock = function() { var rand = Math.floor(Math.random() * blockArr.length); // 随机生成正在下掉的方块 currentFall = [ {x: blockArr[rand][0].x , y: blockArr[rand][0].y , color: blockArr[rand][0].color}, {x: blockArr[rand][1].x , y: blockArr[rand][1].y , color: blockArr[rand][1].color}, {x: blockArr[rand][2].x , y: blockArr[rand][2].y , color: blockArr[rand][2].color}, {x: blockArr[rand][3].x , y: blockArr[rand][3].y , color: blockArr[rand][3].color} ]; }; // 定义一个创建canvas组件的函数 var createCanvas = function(rows , cols , cellWidth, cellHeight) { tetris_canvas = document.createElement("canvas"); // 设置canvas组件的高度、宽度 tetris_canvas.width = cols * cellWidth; tetris_canvas.height = rows * cellHeight; // 设置canvas组件的边框 tetris_canvas.style.border = "1px solid black"; // 获取canvas上的绘图API tetris_ctx = tetris_canvas.getContext('2d'); // 开始创建路径 tetris_ctx.beginPath(); // 绘制横向网络对应的路径 for (var i = 1 ; i < TETRIS_ROWS ; i++) { tetris_ctx.moveTo(0 , i * CELL_SIZE); tetris_ctx.lineTo(TETRIS_COLS * CELL_SIZE , i * CELL_SIZE); } // 绘制竖向网络对应的路径 for (var i = 1 ; i < TETRIS_COLS ; i++) { tetris_ctx.moveTo(i * CELL_SIZE , 0); tetris_ctx.lineTo(i * CELL_SIZE , TETRIS_ROWS * CELL_SIZE); } tetris_ctx.closePath(); // 设置笔触颜色 tetris_ctx.strokeStyle = "#aaa"; // 设置线条粗细 tetris_ctx.lineWidth = 0.3; // 绘制线条 tetris_ctx.stroke(); } // 绘制俄罗斯方块的状态 var drawBlock = function() { for (var i = 0; i < TETRIS_ROWS ; i++ ) { for (var j = 0; j < TETRIS_COLS ; j++ ) { // 有方块的地方绘制颜色 if(tetris_status[i][j] != NO_BLOCK) { // 设置填充颜色 tetris_ctx.fillStyle = colors[tetris_status[i][j]]; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } // 没有方块的地方绘制白色 else { // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } } } // 当页面加载完成时,执行该函数里的代码。 window. { // 创建canvas组件 createCanvas(TETRIS_ROWS , TETRIS_COLS , CELL_SIZE , CELL_SIZE); document.body.appendChild(tetris_canvas); curScoreEle = document.getElementById("curScoreEle"); curSpeedEle = document.getElementById("curSpeedEle"); maxScoreEle = document.getElementById("maxScoreEle"); // 读取Local Storage里的tetris_status记录 var tmpStatus = localStorage.getItem("tetris_status"); tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus); // 把方块状态绘制出来 drawBlock(); // 读取Local Storage里的curScore记录 curScore = localStorage.getItem("curScore"); curScore = curScore == null ? 0 : parseInt(curScore); curScoreEle[removed] = curScore; // 读取Local Storage里的maxScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : parseInt(maxScore); maxScoreEle[removed] = maxScore; // 读取Local Storage里的curSpeed记录 curSpeed = localStorage.getItem("curSpeed"); curSpeed = curSpeed == null ? 1 : parseInt(curSpeed); curSpeedEle[removed] = curSpeed; // 初始化正在下掉的方块 initBlock(); // 控制每隔固定时间执行一次向下”掉“ curTimer = setInterval("moveDown();" , 500 / curSpeed); } // 判断是否有一行已满 var lineFull = function() { // 依次遍历每一行 for (var i = 0; i < TETRIS_ROWS ; i++ ) { var flag = true; // 遍历当前行的每个单元格 for (var j = 0 ; j < TETRIS_COLS ; j++ ) { if(tetris_status[i][j] == NO_BLOCK) { flag = false; break; } } // 如果当前行已全部有方块了 if(flag) { // 将当前积分增加100 curScoreEle[removed] = curScore+= 100; // 记录当前积分 localStorage.setItem("curScore" , curScore); // 如果当前积分达到升级极限。 if( curScore >= curSpeed * curSpeed * 500) { curSpeedEle[removed] = curSpeed += 1; // 使用Local Storage记录curSpeed。 localStorage.setItem("curSpeed" , curSpeed); clearInterval(curTimer); curTimer = setInterval("moveDown();" , 500 / curSpeed); } // 把当前行的所有方块下移一行。 for (var k = i ; k > 0 ; k--) { for (var l = 0; l < TETRIS_COLS ; l++ ) { tetris_status[k][l] =tetris_status[k-1][l]; } } // 消除方块后,重新绘制一遍方块 drawBlock(); //② } } } // 控制方块向下掉。 var moveDown = function() { // 定义能否下掉的旗标 var canDown = true; //① // 遍历每个方块,判断是否能向下掉 for (var i = 0 ; i < currentFall.length ; i++) { // 判断是否已经到“最底下” if(currentFall[i].y >= TETRIS_ROWS - 1) { canDown = false; break; } // 判断下一格是否“有方块”, 如果下一格有方块,不能向下掉 if(tetris_status[currentFall[i].y + 1][currentFall[i].x] != NO_BLOCK) { canDown = false; break; } } // 如果能向下“掉” if(canDown) { // 将下移前的每个方块的背景色涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 遍历每个方块, 控制每个方块的y坐标加1。 // 也就是控制方块都下掉一格 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.y ++; } // 将下移后的每个方块的背景色涂成该方块的颜色值 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } // 不能向下掉 else { // 遍历每个方块, 把每个方块的值记录到tetris_status数组中 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 如果有方块已经到最上面了,表明输了 if(cur.y < 2) { // 清空Local Storage中的当前积分值、游戏状态、当前速度 localStorage.removeItem("curScore"); localStorage.removeItem("tetris_status"); localStorage.removeItem("curSpeed"); if(confirm("您已经输了!是否参数排名?")) { // 读取Local Storage里的maxScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : maxScore ; // 如果当前积分大于localStorage中记录的最高积分 if(curScore >= maxScore) { // 记录最高积分 localStorage.setItem("maxScore" , curScore); } } // 游戏结束 isPlaying = false; // 清除计时器 clearInterval(curTimer); return; } // 把每个方块当前所在位置赋为当前方块的颜色值 tetris_status[cur.y][cur.x] = cur.color; } // 判断是否有“可消除”的行 lineFull(); // 使用Local Storage记录俄罗斯方块的游戏状态 localStorage.setItem("tetris_status" , JSON.stringify(tetris_status)); // 开始一组新的方块。 initBlock(); } } // 定义左移方块的函数 var moveLeft = function() { // 定义能否左移的旗标 var canLeft = true; for (var i = 0 ; i < currentFall.length ; i++) { // 如果已经到了最左边,不能左移 if(currentFall[i].x <= 0) { canLeft = false; break; } // 或左边的位置已有方块,不能左移 if (tetris_status[currentFall[i].y][currentFall[i].x - 1] != NO_BLOCK) { canLeft = false; break; } } // 如果能左移 if(canLeft) { // 将左移前的每个方块的背景色涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE +1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } // 左移所有正在下掉的方块 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x --; } // 将左移后的每个方块的背景色涂成方块对应的颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } } } // 定义右移方块的函数 var moveRight = function() { // 定义能否右移的旗标 var canRight = true; for (var i = 0 ; i < currentFall.length ; i++) { // 如果已到了最右边,不能右移 if(currentFall[i].x >= TETRIS_COLS - 1) { canRight = false; break; } // 如果右边的位置已有方块,不能右移 if (tetris_status[currentFall[i].y][currentFall[i].x + 1] != NO_BLOCK) { canRight = false; break; } } // 如果能右移 if(canRight) { // 将右移前的每个方块的背景色涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 右移所有正在下掉的方块 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x ++; } // 将右移后的每个方块的背景色涂成各方块对应的颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE -2); } } } // 定义旋转方块的函数 var rotate = function() { // 定义记录能否旋转的旗标 var canRotate = true; for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转的中心, // i == 2时,说明是旋转的中心 if(i != 2) { // 计算方块旋转后的x、y坐标 var afterRotateX = currentFall[2].x + preY - currentFall[2].y; var afterRotateY = currentFall[2].y + currentFall[2].x - preX; // 如果旋转后所在位置已有方块,表明不能旋转 if(tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { canRotate = false; break; } // 如果旋转后的坐标已经超出了最左边边界 if(afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) { moveRight(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX < 0 || tetris_status[afterRotateY-1][afterRotateX] != NO_BLOCK) { moveRight(); break; } // 如果旋转后的坐标已经超出了最右边边界 if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); break; } } } // 如果能旋转 if(canRotate) { // 将旋转移前的每个方块的背景色涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转的中心, // i == 2时,说明是旋转的中心 if(i != 2) { currentFall[i].x = currentFall[2].x + preY - currentFall[2].y; currentFall[i].y = currentFall[2].y + currentFall[2].x - preX; } } // 将旋转后的每个方块的背景色涂成各方块对应的颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } } } window.focus(); // 为窗口的按键事件绑定事件监听器 window. { switch(evt.keyCode) { // 按下了“向下”箭头 case 40: if(!isPlaying) return; moveDown(); break; // 按下了“向左”箭头 case 37: if(!isPlaying) return; moveLeft(); break; // 按下了“向右”箭头 case 39: if(!isPlaying) return; moveRight(); break; // 按下了“向上”箭头 case 38: if(!isPlaying) return; rotate(); break; } } [removed] <style type="text/css"> body>div { font-size: 13pt; padding-bottom: 8px; } span { font-size: 20pt; color: red; } </style> </head> <body> <h2>俄罗斯方块</h2> <div <div id="curSpeedEle"></span> 当前积分:<span id="curScoreEle"></span></div> <div id="maxScoreEle"></span></div> </div> </body> </html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值