0
点赞
收藏
分享

微信扫一扫

俄罗斯方块游戏开发 ——图形化编程java swing 项目

书坊尚 2022-04-14 阅读 49

通过java swing技术开发的俄罗斯方块游戏,实现了基础的游戏绘制、移动逻辑、消除逻辑、得分统计、死亡判定、预测方块等功能。

成果展示截图:

        

项目源码及 可执行文件jar 下载 :

https://github.com/echowz/Teris

游戏介绍:

俄罗斯方块的规则:

玩家不断操作下落的不同板块,直达底部,并刷新新的板块。在操作时通过调整位置和旋转方向,使得落下的方块可以填满一整层,每当一层填满,则该层会消失,上方所积累的方块会落下,同时积累分数,当累积到上方空间不足以刷新新的方块,且整个空间无法消除时,则游戏结束

游戏场地10×20的平面网格地图。每个单位为相同宽度的小正方形。

操作图形:一组固定由4个小正方形组成的不同规则的图形,称为Tetromino。共有6种形态,通过字母象形分别记作: I, J, L, S, Z, O, T

I:

 连续的4个方块组成一长条图形,形如字母I。一次最多可以消除4层。共有2种旋转状态。

J:

形如字母J,一次最多可以消除3层,共有4种旋转状态。

L:

形如字母L,J的对称形状,一次最多可以消除3层,共有4种旋转状态

O:

形如字母O,4个小正方形堆叠形成的大正方形,一次最多可以消除2层,仅有一种旋转状态。

S:

形如字母S,一次最多可以消除2层,共有2种旋转状态

 T:

形如字母T,一次最多可以消除2层,有4种旋转状态。

Z:

形如字母Z,一次最多可以消除2层,共有2种旋转状态

分数累计:  一次消除层数的平方*10,1层为10分,2层为40分,3层为90分,4层为160分。

项目结构:

主框体程序结构:

GamePanel 即框体左侧的游戏本体部分,继承自模块Jpanel,负责绘制游戏平面显示。

Menu即框体右侧的菜单信息部分,继承自模块Jpanel负责展示信息。

 

MainFrame 为整个框体,继承自Jframe。

Main 主类中负责创建和编写游戏流程等逻辑。 

 方块样式结构:

抽象类Tetromino描述了一个类型方块的基本特征,坐标,移动方法,旋转方法等。

cell类描述一个单独的小方块,记录坐标。

7个实现类都以 Cell 组成,记录其各个子小方块所处的位置,从而组成了整个方块的形态。

每个Cell方块都拥有其坐标属性

I 方块的组成可以表示为: 以其中一个靠中的方块(x,y)作为整体 I方块 的坐标表示,

 生成 I 方块 对象时,将生成四个Cell对象来组成整体。此时,I 方块 的其中一个旋转状态便表示完毕,但每个类型的方块都可能有多个旋转状态,因此

项目介绍 

框体部分:

 GamePanel

该区域绘制整个版面,用成员变量二维数组graph记录10*20的网格中的信息,当网格的值为0时表示该网格为空,其他正值分别表示不同的颜色,若为-1则该网格为界外网格,也是不显示的网格。由于gamepanel只创建一次,其成员变量采用静态方法,方便全局调用。

 成员变量部分:

    public static int[][] graph;        //地图的二维数组映射
    public static Color[] colors;       //7种方块7种颜色,便于区分和识别。

构造部分:

    public GamePanel()
    {
        init();
        setBackground(Color.CYAN);          //背景色设为淡蓝 方格填充后作为淡蓝色网格线
        setBounds(new Rectangle(0,0,MainFrame.width*2/3,MainFrame.height));//设置panel区域的长宽和位置,为整个框体的左三分之二处
        setVisible(true);
    }
    public void init() //初始化辅助参数,颜色集,地图等。
    {
        colors=new Color[]{ //初始化颜色数组
                Color.white,    //默认静态final变量,返回的是白色的Color对象
                new Color(0,240,240),
                new Color(0,0,240),
                new Color(240,160,0),
                new Color(240,240,0),
                new Color(0,240,0),
                new Color(160,0,240),
                new Color(240,0,0)};
        graph = new int[25][15];    //初始化地图大小

        for(int i=0;i<=22;i++)
        Arrays.fill(graph[i], -1);  //先全部初始化-1,再将合法区域初始化为0

        for(int i=1;i<=20;i++)      //地图的合法区域为x:1-20  y:1-10;
            for(int j=1;j<=10;j++)
                graph[i][j]=0;          //经初始化,-1为出界。
    }

 重写paint方法,整个程序的核心绘制,整个版面的显示:

    @Override
    public void paint(Graphics g)
    {
        super.paint(g);

        for(int i=1;i<=20;i++)
            for(int j=1;j<=10;j++)
            {
                g.setColor(colors[graph[i][j]]);
                int x=MainFrame.width/40+(j-1)*MainFrame.width/16;
                int y=MainFrame.width/20+(i-1)*MainFrame.width/16;
                int width=MainFrame.width/16-2,height=MainFrame.width/16-2;
                g.fillRect(x,y,width,height);
            }
    }

 Menu

menu区域用作信息显示,不作重点讲解,预测下一个方块的功能有一定代码逻辑。与Tetromino的构造结合应用。由于Menu只会创建一个,其成员变量采用静态方式。

成员变量:

    public static int score;    //当前分数
    public static ArrayList<Image> imageList;    //用于显示下一个方块的图片,打印在menu中
    public static int currentPicture;    //当前需要打印的图片为

构造方法中初始化信息,paint中绘制界面。

MainFrame

定义了整个程序需要参考的参数:宽、高

    public static final int height=800;
    public static final int width=height*3/4;

构造函数中设置框体的基本属性即可。

Main

主类中创建的全局变量为:

    public static Tetromino select; //当前操控的方块设置为select
    public static Menu menu;
    public static GamePanel gamePanel;
    public static MainFrame mainFrame;
    public static final Object object = new Object();   //用作synchronized 参数,用于线程阻塞,循环下降操作的等待过程。
    public static int pauseTime=500;        //自动下落的间隔时间,用于控制速度,间隔越短下落速度越快。

 在main方法中创建gamepanel、menu后添加到创建的mainframe中,并添加一个新线程用作不断重绘两个板块:gamepanel和menu中的内容。

public static void main(String[] args) {
        System.out.println("运行开始");
        gamePanel = new GamePanel();
        menu = new Menu();
        mainFrame = new MainFrame();//Mainframe需要在后,因为其中有需要调用到panel的成员的语句

        mainFrame.add(gamePanel);
        mainFrame.add(menu);

        new Thread(()-> {
            while (true) {
                gamePanel.repaint();
                menu.repaint();
            }
        }).start(); //持续刷新画布的线程。
        updateTet();

        Thread downs = new Thread(()->{    //不断循环下落的线程。
            try {
                while (true) {
                    synchronized (object) {
                        down();
                        object.wait(pauseTime/2);       //下落速度
                        object.wait(pauseTime/2);
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        });
        downs.start();
...
...

}

在main中添加键盘监听,完善游戏的操作逻辑。

       mainFrame.addKeyListener(new KeyAdapter() {       //监听玩家操作
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyChar()) {    //获取键盘活动的char值
                    case 'a':        //左右移动的逻辑,当前选定的Tetromino方块 有抽象方法移动和旋转,移动传递参数01分别为左右移动。
                    case 'A':
                        select.move(0);
                        break;
                    case 'd':
                    case 'D':
                        select.move(1);
                        break;
                    case 's':        //s用作单次点击加速下落
                    case 'S':
                        down();
                        break;
                    case ' ':        //调用当前选定的Tetromino方块的自我旋转方法,实现旋转
                        select.spin();
                        break;
                    case 'w':        //触发/取消 暂停下落
                    case 'W':
                        if (downs.isAlive())
                            synchronized (object) {
                                object.notify();       //下落速度
                            }
                        if (pauseTime == 500) pauseTime = 2000000;
                        else pauseTime = 500;    //通过改变下落间隔实现暂停和继续的功能
                        break;
                }
                }
            }
        );

方块部分:

小方块Cell类

成员变量

即记录当前小方块所处坐标的x和y

    public int x;
    public int y;

成员方法

setColor() 将当前小方块所处的位置的单元格上色。

isBorder()是否处于边界(根据将要移动的方向来判断,即可能是边界,也可能是另一个已填充的方块)

legal()判断这个小方块当前的位置是否是合法的。(在染色前判断,该位置应该是空的才会被染色,因为俄罗斯方块中不存在覆盖颜色的行为,所有只有不出界且为空才合法);

    public void setColor(int color) {       //给当前小方块所在位置上色
        GamePanel.graph[Math.max(0,x)][Math.max(0,y)]=color;
    }
    public boolean isBorder(int dir) {    //参数 0向左移动,1向右移动 2向下 通过该小方块的移动判断整体移动后是否会撞击边界或其他方块 作为碰撞判定的一部分
        if(dir==0) return GamePanel.graph[x][y-1] != 0;
        else if(dir==1) return GamePanel.graph[x][y+1]!=0;
        else if(dir==2) return GamePanel.graph[x+1][y]!=0;
        return true;
    }
    public boolean legal(){     //判断该方块当前位置是否合法。    //合法返回真
        if(x <1 || x > 20 || y <1 || y > 10)return false;
        return GamePanel.graph[x][y]==0;
    }

抽象类Tetromino

拥有的成员变量为:

    public int color;   //颜色
    public int x;       //当前方块的坐标
    public int y;
    public int status;  //当前方块处于的旋转状态为
    public int statusNum=4; //当前方块一定有几个状态
    public static int currentTetromino=new Random().nextInt(7);//当前选定的方块,通过随机数选定。
    public Cell[][] cells;  //当前方块的i个状态,每个状态4个小方块,所记录的小方块信息。
        //旋转时将在第一维,不同的状态之间切换; 移动时将在第二维,对当前状态下的4个方块进行遍历,查看是否可以移动,和修改其位置信息。

Tetromino的核心方法为move方法和spin方法,移动和旋转是俄罗斯方块的核心操作方式,是画面的主要改变方式。

move(int dir)接收参数,通过参数值的不同实现左右下三个方向的移动。遍历所含的4个小方块,假定朝既定方向移动,判断移动后的位置是否合法,如果都合法则实现移动,有不合法的则撤销。

spin()不接收参数,将当前操控的方块进行顺时针90°的旋转。

在实现层面上讲,是将当前选定方块切换至下一个旋转状态,因为每个方块都有成员cell【】【】,也就是每个旋转状态的每个小方块儿子属性,在旋转时遍历所含的小方块判定下个状态是否合法,合法则旋转。

    public void move(int dir) {  //0左 1右 2下
        setColor(0);        //将该方块移除
        for(int j=0;j<4;j++)    
            if(cells[status%statusNum][j].isBorder(dir)) {    //是否为底部,即下面一个单位是否有元素。
                setColor(color);
                return;
            }
        if (dir == 0) y--;
        else if(dir==1) y++;
        else if(dir==2) x++;

        setCells(x,y);
        setColor(color);
    }
    public void spin() {        //旋转
        setColor(0);                    //实现逻辑为先假设旋转,先移除旧的,再切换状态,然后判断旋转后的方块所处位置是否合法(全为0)。如果合法则应用填色,不合法则恢复
        status++;    //下一个状态
        for (int j = 0; j < 4; j++) {   //所有小方块合法才改变。
            if (!cells[status%statusNum][j].legal()) {
                status--;

                setColor(color);
                return;
            }
        }
        setColor(color);
    }

7个实现类

都实现了父类的setcells方法,初始化各个状态的信息。以J方块举例,其完整代码如下:

public class J extends Tetromino{
    public J(int x, int y) {
        super(x,y);
        statusNum=4;
        color=2;
    }

    @Override
    public void setCells(int x, int y) {        //各个形状如下:

        cells[0][0].setLocation(x-1, y-1);                // *
        cells[0][1].setLocation(x, y);                    // * * *
        cells[0][2].setLocation(x, y -1);
        cells[0][3].setLocation(x, y +1);

        cells[1][0].setLocation(x-1, y);               // * *
        cells[1][1].setLocation(x, y);                 // *
        cells[1][2].setLocation(x + 1, y);             // *
        cells[1][3].setLocation(x -1, y+1);

        cells[2][0].setLocation(x+1, y+1);              // * * *
        cells[2][1].setLocation(x, y);                  //     *
        cells[2][2].setLocation(x , y-1);
        cells[2][3].setLocation(x , y+1);

        cells[3][0].setLocation(x-1, y);                //   *
        cells[3][1].setLocation(x, y);                  //   *
        cells[3][2].setLocation(x +1, y);               // * *
        cells[3][3].setLocation(x +1, y-1);
    }
}

举报

相关推荐

0 条评论