Java实现俄罗斯方块

规则

1.方块会从上方缓慢下落,玩家可以通过键盘上的上下左右键来控制方块。
2.方块移到区域最下方或是着地到其他方块上无法移动时,就会固定在该处,而新的方块出现在区域上方开始落下。
3.当区域中某一列横向格子全部由方块填满,则该列会消失并成为玩家的得分。同时删除的列数越多,得分指数上升。
4.当固定的方块堆到区域最上方而无法消除层数时或者大于游戏区域时,则游戏结束。

代码目录 

实现代码

 

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


//俄罗斯方块主类
public class Tetris extends JPanel {
    static int ROW = 18;
    static int COL = 9;
    //声明正在下落的方块
    private Tetromino currentOne = Tetromino.randomOne();
    //声明将要下落的方块
    private Tetromino nextOne = Tetromino.randomOne();
    //声明游戏主区域
    private Cell[][] wall = new Cell[ROW][COL];
    //声明单元格像素为48像素
    private static final int CELL_SIZE = 48;
    //游戏分数池
    int[] scores_pool = {0, 1, 2, 5, 10};
    //游戏总分
    private int totalScore = 0;
    //游戏消除总行数
    private int totalLine = 0;
    //游戏的状态:游戏中,游戏暂停,游戏结束
    private static final int PLAYING = 0;
    private static final int PAUSE = 1;
    private static final int GAMEOVER = 2;
    //声明变量来存放当前游戏状态
    private int game_state;
    //数组显示当前游戏状态
    String[] show_state = {"P[暂停]","C[继续]","R[重开]"};


    //静态载入图片
    public static BufferedImage I;
    public static BufferedImage J;
    public static BufferedImage L;
    public static BufferedImage O;
    public static BufferedImage S;
    public static BufferedImage T;
    public static BufferedImage Z;
    public static BufferedImage backImage;

    static {
        try {
            I = ImageIO.read(new File("images/I.png"));
            J = ImageIO.read(new File("images/J.png"));
            L = ImageIO.read(new File("images/L.png"));
            O = ImageIO.read(new File("images/O.png"));
            S = ImageIO.read(new File("images/S.png"));
            T = ImageIO.read(new File("images/T.png"));
            Z = ImageIO.read(new File("images/Z.png"));
            backImage = ImageIO.read(new File("images/background.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(backImage,0,0,null);

        //平移坐标轴
        g.translate(22,15);
        //绘制游戏主区域
        paintWall(g);
        //绘制正在下落的四方格
        paintCurrentOne(g);
        //绘制下一个下落的四方格
        paintNextOne(g);
        //绘制游戏得分
        paintScore(g);
        //绘制游戏当前状态
        paintState(g);
    }
    //start 方法,用于调用游戏操作逻辑并监听键盘和描述游戏主要逻辑
    public void start() {
        game_state = PLAYING;
        KeyListener listener = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int code = e.getKeyCode();
                switch (code) {
                    case KeyEvent.VK_DOWN: //↓
                        sortDropAction(); //下落一格
                        break;
                    case KeyEvent.VK_LEFT://←
                        moveLeftAction(); //左移一格
                        break;
                    case KeyEvent.VK_RIGHT: //→
                        moveRightAction(); //右移一格
                        break;
                    case KeyEvent.VK_UP://↑
                        rotateRightAction();//顺时针旋转
                        break;
                    case KeyEvent.VK_SPACE://空格
                        handDropAction();//瞬间下落
                        break;
                    case KeyEvent.VK_P: //p
                        //判断游戏是否在运行,没有才能暂停
                        if (game_state == PLAYING) {
                            game_state = PAUSE;
                        }
                        break;
                    case KeyEvent.VK_C:
                        //游戏暂停后,才能继续
                        if (game_state == PAUSE) {
                            game_state = PLAYING;
                        }
                        break;
                    case KeyEvent.VK_R:
                        //重新开始游戏,把游戏状态变为正在游戏
                        game_state = PLAYING;
                        //界面清空
                        wall = new Cell[ROW][COL];
                        currentOne = Tetromino.randomOne();
                        nextOne = Tetromino.randomOne();
                        //数据清空
                        totalLine = 0;
                        totalScore = 0;
                        break;

                }
            }
        };
        //把俄罗斯方块窗口设置为焦点
        this.addKeyListener(listener);
        this.requestFocus();

        while(true){
            //判断,当前游戏状态在游戏中时,每隔0.5秒下落
            if(game_state == PLAYING){
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //判断能否下落
                if(canDrop()){
                    currentOne.softDrop();
                }else{
                    //嵌入到墙中
                    landToWall();
                    //判断能否消行
                    destroyLine();
                    //判断游戏是否结束
                    if(isGameOver()){
                        game_state = GAMEOVER;
                    }else{
                        currentOne = nextOne;
                        nextOne = Tetromino.randomOne();
                    }
                }
            }
            //重新绘制
            repaint();
        }
    }
    //顺时针旋转
    public void rotateRightAction() {
        currentOne.rotateRight();
        //判断是否越界或者重合,否则恢复原来的状态
        if (outOfBounds() || coincide()) {
            currentOne.rotateLeft();
        }
    }
    //瞬间下落
    public void handDropAction() {
        while (canDrop()) {
            currentOne.softDrop();
        }
        //把四方格嵌入墙中
        landToWall();
        //判断能否销行
        destroyLine();
        //判断游戏是否结束
        if (isGameOver()) {
            game_state = GAMEOVER;
        } else {
            //继续生成四方格
            currentOne = nextOne;
            nextOne = Tetromino.randomOne();
        }
    }
    //按键一次四方格下落一个
    public void sortDropAction() {
        //判断能否下落
        if (canDrop()) {
            //当前四方格下落一格
            currentOne.softDrop();
        } else {
            //把四方格嵌入墙中
            landToWall();
            //判断能否销行
            destroyLine();
            //判断游戏是否结束
            if (isGameOver()) {
                game_state = GAMEOVER;
            } else {
                //继续生成四方格
                currentOne = nextOne;
                nextOne = Tetromino.randomOne();
            }
        }
    }
    //把四方格嵌入墙中
    private void landToWall() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            wall[row][col] = cell;
        }
    }
    //判断四方格能否下落
    public boolean canDrop() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            //判断能否全部到达底部
            if (row == wall.length - 1) {
                return false;
            } else if(wall[row + 1][col] != null) { //判断该位置是否有方块
                return false;
            }
        }
        return true;
    }
    //创建销行方法
    public void  destroyLine() {
        //统计当前形参行数
        int line = 0;
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            //判断当前行是否满
            if (isFullLine(row)) {
                line++;
                //将消除行以上的方块下落到对应行数
                for (int i = row; i > 0; i--) {
                    System.arraycopy(wall[i - 1],0,wall[i],0,wall[0].length);
                }
                //重新创造第一行
                wall[0] = new Cell[9];
            }
        }
        //更新分数
        totalScore += scores_pool[line];
        //更新消除行数
        totalLine += line;
    }
    //判断当前行是否满,满返回true,没有满返回false
    public boolean isFullLine(int row) {
        Cell[] cells = wall[row];
        for (Cell cell : cells) {
            if (cell == null) {
                return false;
            }
        }
        return true;
    }
    //判断游戏是否结束,结束返回true,继续返回false
    public boolean isGameOver() {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (wall[row][col] != null) {
                return true;
            }
        }
        return false;
    }
    //按键←触发左移
    public void moveLeftAction() {
        currentOne.moveLeft();
        //判断是否越界或者重合,如果是则右移恢复原来的状态,不是则左移
        if (outOfBounds() || coincide()) {
            currentOne.moveRight();
        }
    }
    //按键→触发右移
    public void moveRightAction() {
        currentOne.moveRight();
        //判断是否越界或者重合,如果是则左移恢复原来的状态,不是则右移
        if (outOfBounds() || coincide()) {
            currentOne.moveLeft();
        }
    }
    //判断方块是否重合,重合返回true,没有返回false
    public boolean coincide() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int col = cell.getCol();
            int row = cell.getRow();
            if (wall[row][col] != null) {
                return true;
            }
        }
        return false;
    }
    //判断方块是否出界,出界返回true,没有则返回false
    public boolean outOfBounds() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int col = cell.getCol();
            int row = cell.getRow();
            if (col < 0 || col > COL - 1 || row < 0 || row > ROW - 1) {
                return true;
            }
        }
        return false;
    }
    //绘制游戏当前状态
    private void paintState(Graphics g) {
        if (game_state == PLAYING) {
            g.drawString(show_state[0],500,660);
        } else if (game_state == PAUSE) {
            g.drawString(show_state[1],500,660);
        } else if (game_state == GAMEOVER) {
            g.drawString(show_state[2],500,660);
            g.setColor(Color.red);
            g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,60));
            g.drawString("GAMEOVER!",30,400);
        }
    }
    //绘制游戏得分
    private void paintScore(Graphics g) {
        g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,40));
        g.drawString("得分:" + totalScore,500,248);
        g.drawString("行数:"+ totalLine,500,440);
    }
    //绘制下一个下落的四方格
    private void paintNextOne(Graphics g) {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int x = cell.getCol() * CELL_SIZE + 370;
            int y = cell.getRow() * CELL_SIZE + 25;
            g.drawImage(cell.getImage(),x,y,null);
        }
    }
    //绘制正在下落的四方格
    private void paintCurrentOne(Graphics g) {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int x = cell.getCol() * CELL_SIZE;
            int y = cell.getRow() * CELL_SIZE;
            g.drawImage(cell.getImage(),x,y,null);
        }
    }
    //绘制游戏主区域
    private void paintWall(Graphics g) {
        for (int i = 0; i < wall.length; i++) {
            for (int j = 0; j < wall[i].length; j++) {
                int x = j * CELL_SIZE;
                int y = i * CELL_SIZE;
                Cell cell = wall[i][j];
                //判断当前单元格是否有小方格,如果无则绘制矩形,否则将小方块嵌到墙中
                if (cell == null) {
                    g.drawRect(x,y,CELL_SIZE,CELL_SIZE);
                } else {
                    g.drawImage(cell.getImage(),x,y,null);
                }
            }
        }
    }

    public static void main(String[] args) {
        //创建窗口对象
        JFrame frame = new JFrame("俄罗斯方块");
        //创建游戏窗口(即面板)
        Tetris panel = new Tetris();
        //把面板嵌入到窗口中
        frame.add(panel);
        //设置可见
        frame.setVisible(true);
        //设置窗口尺寸
        frame.setSize(810,940);
        //设置窗口居中
        frame.setLocationRelativeTo(null);
        //设置窗口关闭时程序中止
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //游戏逻辑封装在方法中
        panel.start();
    }
}

/**
 * 描述:四方格父类
 * 属性:Cell[]数组用于创建4个小方块
 * 方法:左移一格,右移一格,下移一格,变形(等会写)
 */

public class Tetromino {
    protected Cell[] cells = new Cell[4];

    //编写旋转状态
    protected State[] states;
    //声明旋转次数
    protected int count = 10000;

    //编写顺时针旋转四方格方法
    public void rotateRight() {
        if (states.length == 0) {
            return;
        }
        //旋转次数加1
        count++;
        //获取当前状态
        State s = states[count % states.length];
        Cell cell = cells[0];
        int row = cell.getRow();
        int col = cell.getCol();
        //变形
        cells[1].setRow(row + s.row1);
        cells[1].setCol(col + s.col1);
        cells[2].setRow(row + s.row2);
        cells[2].setCol(col + s.col2);
        cells[3].setRow(row + s.row3);
        cells[3].setCol(col + s.col3);
    }
    //逆时针旋转四方格方法
    public void rotateLeft() {
        //旋转次数加1
        count--;
        //获取当前状态
        State s = states[count % states.length];
        Cell cell = cells[0];
        int row = cell.getRow();
        int col = cell.getCol();
        //变形
        cells[1].setRow(row + s.row1);
        cells[1].setCol(col + s.col1);
        cells[2].setRow(row + s.row2);
        cells[2].setCol(col + s.col2);
        cells[3].setRow(row + s.row3);
        cells[3].setCol(col + s.col3);
    }

    /**描述:四方格旋转状态的内部类
     * 属性:记录四方格的相对位置
     */
    class State {
        int row0,col0,row1,col1,row2,col2,row3,col3;

        public State(){

        }

        public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {
            this.row0 = row0;
            this.col0 = col0;
            this.row1 = row1;
            this.col1 = col1;
            this.row2 = row2;
            this.col2 = col2;
            this.row3 = row3;
            this.col3 = col3;
        }

        public int getRow0() {
            return row0;
        }

        public void setRow0(int row0) {
            this.row0 = row0;
        }

        public int getRow1() {
            return row1;
        }

        public void setRow1(int row1) {
            this.row1 = row1;
        }

        public int getRow2() {
            return row2;
        }

        public void setRow2(int row2) {
            this.row2 = row2;
        }

        public int getRow3() {
            return row3;
        }

        public void setRow3(int row3) {
            this.row3 = row3;
        }

        public int getCol0() {
            return col0;
        }

        public void setCol0(int col0) {
            this.col0 = col0;
        }

        public int getCol1() {
            return col1;
        }

        public void setCol1(int col1) {
            this.col1 = col1;
        }

        public int getCol2() {
            return col2;
        }

        public void setCol2(int col2) {
            this.col2 = col2;
        }

        public int getCol3() {
            return col3;
        }

        public void setCol3(int col3) {
            this.col3 = col3;
        }
    }

    //左移
    public void moveLeft() {
        for (Cell cell : cells) {
            cell.left();
        }
    }
    //右移
    public void moveRight() {
        for (Cell cell : cells) {
            cell.right();
        }
    }
    //下移
    public void softDrop() {
        for (Cell cell : cells) {
            cell.drop();
        }
    }

    //随机生成四方格
    public static Tetromino randomOne() {
        int num = (int)(Math.random() * 7);
        Tetromino tetromino = null;
        switch (num) {
            case 0 :
                tetromino = new I();
                break;
            case 1 :
                tetromino = new J();
                break;
            case 2 :
                tetromino = new L();
                break;
            case 3 :
                tetromino = new O();
                break;
            case 4 :
                tetromino = new S();
                break;
            case 5 :
                tetromino = new T();
                break;
            case 6 :
                tetromino = new Z();
                break;
        }
        return tetromino;
    }
}

游戏界面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/150727.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云腾讯云大比拼!阿里云99,腾讯云88!

首先&#xff0c;我们来看一下阿里云和腾讯云在云服务器价格上的差异。根据官方公布的信息&#xff0c;阿里云在双11大促活动中推出了全年最低价的云服务器&#xff0c;最低价格为87元1年。而腾讯云的云服务器价格稍高&#xff0c;最低为88元1年。虽然价格上的差距很小&#xf…

javaweb---maventomcat使用教程

文章目录 今日内容0 复习昨日1 Maven1.0 引言1.1 介绍1.2 下载1.3 安装1.3.1 解压1.3.2 配置环境变量1.3.3 测试 1.4 仓库1.5 Maven配置1.5.1 修改仓库位置1.5.2 设置镜像 2 IDEA - MAVEN2.1 idea关联maven2.2 创建java项目2.3 java项目结构2.4 pom2.5 导入依赖2.5.1 查找依赖2…

CocosCreator3.8神秘面纱 CocosCreator 项目结构说明及编辑器的简单使用

我们通过Dashboard 创建一个2d项目&#xff0c;来演示CocosCreator 的项目结构。 等待创建完成后&#xff0c;会得到以下项目工程&#xff1a; 一、assets文件夹 assets文件夹&#xff1a;为资源目录&#xff0c;用来存储所有的本地资源&#xff0c;如各种图片&#xff0c;脚本…

synchronized jvm实现思考

底层实现时&#xff0c;为什么使用了cxq队列和entryList双向链表&#xff1f;这里为什么不跟AQS中使用一个队列就行了&#xff0c;加了一个entryList的目的是为了什么&#xff1f; 个人理解这里多一个entryList&#xff0c;可能是用于减少频繁的cas操作。假设存在很多锁竞争时&…

[Unity3D] C# 十进制、二进制、十六进制 之间进制的转换

//十进制 --> 二进制 int data 100; string bin Convert.ToString(data, 2); // “1100100”//十进制 --> 十六进制 int data 100; string hex “”;hex Convert.ToString(data, 16); // “64” hex “0X” Convert.ToString(data, 16); // “0X64” hex string.…

为开发GPT-5,OpenAI向微软寻求新融资

11月14日&#xff0c;金融时报消息&#xff0c;OpenAI正在向微软寻求新一轮融资&#xff0c;用于开发超级智能向AGI&#xff08;通用人工智能&#xff09;迈进&#xff0c;包括最新模型GPT-5。 最近&#xff0c;OpenAI召开了首届开发者大会&#xff0c;推出了GPT-4 Turbo、自定…

js添加dom到指定div之后,并给添加的dom类名,然后设置其样式,以及el-popover层级z-index过高问题解决。

遇到一个需求,Vue项目做一个表格,要求表头与表格内容分开,如下效果所示,表头与表格有个高度间隔边距(箭头所示),因为默认我们的el-table的表头与内容是一起的: 思路:通过querySelector获取el-table__header-wrapper元素,通过createElement创建一个div,通过 newElem…

Postman配置环境请求接口

一、准备配置dev、test、demo、eprod 二、使用切换环境变量调用接口 三、使用登录接口自动获取token

【吞噬星空】罗峰成功抵达虬龙星,宇宙超级富二代登场,不容错过

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料《吞噬星空》93集&#xff0c;在虬龙星港口&#xff0c;当罗峰的飞船一进入&#xff0c;牵引信号就立刻响起&#xff0c;像一道无形的指引线&#xff0c;将他飞船牵引至指定的停靠区域。罗峰踏出飞船…

如何检查 Docker 和 Kubernetes 是否可以访问外部网络,特别是用于拉取镜像的仓库?

要检查 Docker 和 Kubernetes 是否可以访问外部网络&#xff0c;尤其是用于拉取容器镜像的仓库&#xff0c;您可以按照以下步骤进行&#xff1a; 1. 检查节点的网络连接 首先&#xff0c;您需要确保 Kubernetes 节点能够访问外部网络。这可以通过在节点上执行 ping 命令来测试…

VN5620以太网测试——DoIP配置

文章目录 前言一、DoIP简介二、Vector Hardware Configuration三、Diagnostics/ISO TP Configuration四、Diagnostic Console五、添加Ethernet Packet Builder前言 CANoe(CAN open environment)VN5620 :是一个紧凑而强大的接口,用于以太网网络的分析、仿真、测试和验证。 V…

【6】Spring Boot 3 集成组件:knift4j+springdoc+swagger3

目录 【6】Spring Boot 3 集成组件&#xff1a;knift4jspringdocswagger3OpenApi规范SpringFox Swagger3SpringFox工具&#xff08;不推荐&#xff09; Springdoc&#xff08;推荐&#xff09;从SpringFox迁移引入依赖配置jAVA Config 配置扩展配置&#xff1a;spring securit…

istio安装文档

1、重装命令 istioctl manifest generate --set profiledemo | kubectl delete --ignore-not-foundtrue -f - 2、下载 参考&#xff1a;02、istio部署到k8s中 - 简书 (jianshu.com) 参考 Istio / 入门 curl -L https://istio.io/downloadIstio | ISTIO_VERSION1.20.0 TAR…

[工业自动化-22]:西门子S7-15xxx编程 - 软件编程 - 如何PLC建立用户界面: SIMATIC 面板式HMI 或工控机PC HMI

目录 前言&#xff1a; 一、PLC&#xff08;可编程逻辑控制器&#xff09;的用户界面支持方式 1.1 概述 1.2 西门子&#xff08;Siemens&#xff09;的人机界面&#xff08;HMI&#xff09;支持多种类型 1.3 PC HMI VS SIMATIC HMI 二、PC—HMI—PLC连接架构的实现 三、…

OpenAI与微软合作,构建 ChatGPT 5 模型;10天准确天气预报

&#x1f989; AI新闻 &#x1f680; OpenAI与微软合作&#xff0c;构建 ChatGPT 5 模型&#xff0c;下一代人工智能或拥有超级智能 摘要&#xff1a;OpenAI首席执行官 Sam Altman 在接受采访时表示&#xff0c;OpenAI正在与微软合作构建下一代人工智能模型 ChatGPT 5&#x…

Winodws核心编程 多线程

目录 一、基本概念 二、线程创建函数 三、Windows内核对象与句柄 四、简单的多线程案例 五、线程同步 - 互斥对象 六、多线程实现群聊的服务端和客户端 七、线程同步 - 事件对象 八、事件对象 与 互斥对象区别 九、线程同步 - 信号量 十、线程同步 - 关键代码段 十一…

SpringSecurity6从入门到上天系列第六篇:解决这个问题为什么在引入SpringSecurity之后所有的请求都需要先做登录认证才可以进行访问呢

文章目录 问题引入 1&#xff1a;问题阐述 2&#xff1a;问题分析 一&#xff1a;从SpringBoot的自动装配 1&#xff1a;SpringBootApplication介绍 2&#xff1a;自动装配的核心方法 3&#xff1a;核心方法的调用路径 4&#xff1a;SpringSecurity核心配置 5&#xf…

每日一题:编写程序,使程序分别输出两个整数的加减乘除运算结果

文章目录 每日一题一、编写程序&#xff0c;使程序分别输出两个整数的加减乘除运算结果以下是一个使用 Java 编写的程序&#xff0c;可以输出两个整数的加减乘除运算结果&#xff1a;以下是一个简单的 Python 程序&#xff0c;可以计算两个整数的加减乘除运算结果&#xff1a; …

Python---数据序列类型之间的相互转换

list()方法&#xff1a;把某个序列类型的数据转化为列表 # 1、定义元组类型的序列 tuple1 (10, 20, 30) print(list(tuple1))# 2、定义一个集合类型的序列 set1 {a, b, c, d} print(list(set1))# 3、定义一个字典 dict1 {name:刘备, age:18, address:蜀中} print(list(dict1…

JavaScript的函数的形参与实参是怎么回事

0 写在前面 此文给小白看的&#xff0c;如果不是可以直接关闭 1 讲解 例如JavaScript中定义函数 //定义函数 function 方法名(形参){方法体-->使用形参}//使用函数 方法名字(实参)具体干了什么呢&#xff1f;此处以伪代码举例 //定义函数 function eat(A,B){A 去 B 家吃…