摘要:基本的规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。
经过《俄罗斯方块小游戏的实现(一)》的分析后,对于实现一个《俄罗斯方块》游戏应该有初步的思路了吧。下面会详细的分析这个游戏的功能、流程、关键的技术点。

1 游戏的功能
游戏随机产生各种形状的不同颜色的方块;
通过方向键控制方块的移动和方块形状的变化;
显示下一个方块的形状和颜色;
显示游戏当前的分数和当前的等级;
游戏的开始、暂停、重新开始等功能;

2 实现流程
运行程序时,显示一个窗口,窗口左边有一个方块面板(只有背景);右边显示NEXT面板(用于显示下一个方块);右下边分别是分数、等级、开始按钮、停止按钮;
点击开始后,方块面板显示当前的方块,方块从上向下落下,NEXT面板显示下一个新的方块形状。

3 元素映射
图1 元素映射
我们需要游戏中的元素用符号描述出来,并且用一些变量把元素的状态存储起来。
我设定这个游戏的地图是10*20个方格组成的区域块,用一个二维数组表示:
private final int[][] map = new int[13][23]; //记录地图所有方格的类型信息
private final int[][] cmap = new int[13][23];//记录地图所有方格的颜色信息
渲染的地图大小是12*22的,活动区域大小是10*20的,为什么需要用13*23的数组来存储呢?
其实这跟方块取的坐标基点有关,在代码中定义方块的坐标基点是(1,1),即方块的坐标(0,0)映射为地图数组为(1,1),方块的坐标取值是(0,0)-(9,19)。如果地图数组不是13*23,那么在下面的代码中就会出现数组越界的异常(黑体部分):
/**
* 判断方块区是否可以移动,
* 若覆盖堆积区的方块,则不可以移动,
* 若覆盖边框区的方块,则不可以移动,
*
* @param x 移动后X坐标
* @param y 移动后Y坐标
* @return
*/
private boolean isCan(int x, int y) {
    for (int a = 0; a < 4; a++) {
        for (int b = 0; b < 4; b++) {
            if ((block[bType][bState][a * 4 + b] == 1) 
                    && (map[x + b + 1][y + a] == 1
                    || map[x + b + 1][y + a] == 2)) {
                return false;
            }
        }
    }
    return true;
}
每一个下落方块是由四个方格组成的,用一个三维的数组表示所有形态的所有方块:
/**
* 所有形态的所有方块都可以看做是由4*4=16个方格的区域块组成,称为方块区。
* 按顺序给这16个方格做标识,0~15,用长度0为16的一维数组来存储方块区,
* 数组中为1的元素表示方块区的这个方格有颜色覆盖,为0则表示空白方格
*/
private final int block[][][] = new int[][][]{
        // I
        {{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},
                {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}},
        // S
        {{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},
                {1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}},
        // Z
        {{1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
                {1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}},
        // J
        {{0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
                {1, 0, 0, 0, 1, 1, 1, 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, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
        // O
        {{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, 0, 0, 0, 0, 0}},
        // L
        {{1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
                {1, 1, 1, 0, 1, 0, 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},
                {0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
        // T
        {{0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
                {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}}};
一个方块的属性有类型、形态、颜色, 因为要绘制提示面板,所以这些属性都是预先生成的,即在一个方块还未结束就会生成下一个方块的属性 了。产生新的方块时,可以用提示面板的方块属性来初始化新的方块:
private int bType; //标识方块的形状,共有7种
private int bState;//标识方块的形态,每种形状的方块有4种形态
private int bColor;//标识方块的颜色,可选的颜色有3种

bType = nbType;
bState = nbState;
bColor = nbColor;

4 游戏引擎
游戏的引擎是一个定时器,在初始化窗口时初始化,代码如下所示:
timer = new Timer(SPEED, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (isCan(curX, curY + 1)) {
            //下移一格
            curY++;
        } else {
            saveBlockToStore(curX, curY);
            delline();
            createBlock();
        }
        //重绘地图面板
        board.repaint();
    }
});
当定时事件触发时,调用isCan(curX, curY + 1)方法判断是否可以下移,如果可以更新方块Y坐标,如果不可以,则把方块保存到地图的堆积区;然后调用delline()方法进行消行,消行完成后,需要产生新的方块,调用createBlock()产生新的方法;最后重绘地图面板。

5 游戏控制
主要指用户控制方块的移动,代码如下所示:
switch (e.getKeyCode()) {
    case KeyEvent.VK_DOWN: {
        if (isCan(curX, curY + 1)) {
            curY++;
        } else {
            saveBlockToStore(curX, curY);
            delline();
            createBlock();
        }
        break;
    }
    case KeyEvent.VK_RIGHT: {
        if (isCan(curX + 1, curY)) {
            curX++;
        }
        break;
    }
    case KeyEvent.VK_LEFT: {
        if (isCan(curX - 1, curY)) {
            curX--;
        }
        break;
    }
    //顺时针变换方块的形态
    case KeyEvent.VK_UP: {
        int state = bState;
        bState = (bState + 1) % 4;
        if (!isCan(curX, curY)) {
            bState = state;
        }
        break;
    }
}
实现时让窗口监听键盘事件,捕获方向键的事件,向下事件的做法跟定时器一样,向上事件则顺时针变换方块的形态,向左事件则把方块向左移动一格,向右事件则把方块向右移动一格。

6 游戏结束
什么时候判断游戏是否结束呢?怎么判断游戏结束呢?
按照游戏的规则,只有当新产生的方块区不能移动时,游戏就应该结束了,所以可以在产生新的方块区前进行判断,判断的方法是判断新的方块区是否可以移动,不能移动证明游戏结束,若能移动继续游戏,代码如下所示:
private boolean isOver(int x, int y) {
    if (isCan(x, y)) {
        return false;
    }
    return true;
}
新产生的方块区坐标设置为(4,0),所以可以调用isOver(4,0)判断游戏是否结束。

有关这个游戏的问题可以加QQ群399643539讨论,游戏的源码也放在群里保存,欢迎下载!!!

版权说明:如无特殊说明,文章均为本站原创,如需转载请注明出处

本文标题:俄罗斯方块小游戏的Java实现(二)

本文地址:http://www.wolfbe.com/detail/201609/357.html

本文标签: 游戏 俄罗斯方块 java

相关文章

感谢您的支持,朗度云将继续前行

扫码打赏,金额随意

温馨提醒:打赏一旦完成,金额无法退还,请谨慎操作!

扫二维码 我要反馈 回到顶部