摘要:用一个4*4的二维数组来存储所有瓦片,这样游戏的整个过程就可以抽象为对这个二维数组数据的控制,游戏界面就可以根据数组的数据绘制出来。
《2048》这个游戏是一款比较经典的游戏,相信大家都曾经玩过。只要左右滑动方块,只要相信的方块数字相同就可以在滑动方向上被合并。每次滑动随机出现2、4数字的方块,只要合并的方块最大数字出现2048,那么就认为游戏通关成功!
作者曾经也深深地迷恋这款游戏,主要是在坐车和等车时拿出来刷几下,时间一下子就过去了。作为一个程序员,总希望自己也能写一个这样的游戏出来吧。利用一点时间,用Java的Swing重新实现了一个自己的《2048》。下面将详细讲解《2048》的实现过程,并且提供源码下载。


图1 《2048》效果图

1、游戏界面分析

由图1可以知道,游戏的界面由LOGO组件、分数组件、提示组件、游戏面板组成,其中:
  • LOGO组件:一个JLabel文本组件;
  • 分数组件:两个Jabel文本组件,其中“SCORE”文字固定,分数值文本可以变化;
  • 提示组件:一个JL文本组件;
  • 游戏面板:自定义实现的JPanel面板;
上面的这些组件都是包含在一个JFrame窗体中的,窗体的背景色是一种浅灰色 ,窗体也设置了标题和版本信息(版权归朗度云所有)。在设置窗体背景色时,直接使用JFrame.setBackground()方法是不会生效,正确的设置方法是:
JFramegetContentPane().setBackground(Color);
在设置窗体的大小时,我们需要注意去掉窗体标题栏的大小,正确是设置方法是:
JFramegetContentPane().setPreferredSize(new Dimension(400, 500));


2、游戏设计分析

这个游戏面板很简单,总共只有16个瓦片,我们可以用一个4*4的二维数组来存储所有瓦片,这样游戏的整个过程就可以抽象为对这个二维数组数据的控制,游戏界面就可以根据数组的数据绘制出来。
我们来看下瓦片的属性,瓦片在地图中的某一个位置,可以设置背景色,可以设置数字,不同的数字设置不同的颜色,可以左右上下移动,还可以合并。在程序实现时我们可以用一个类Tile表示瓦片,Tile的实现如下:
class Tile {
    public int value;//显示的数字
    public boolean ismerge;//是否是合并的
    public void swap(Tile tile) {
        this.value = tile.value;
        this.ismerge = tile.ismerge;
    }
    public Color getForeground() {
        ...
    }
    public Color getBackground() {
        ...
    }
     ...
}

在Tile类中并没有保存背景色的属性,主要是因为背景色是根据value数字属性关联的,可以根据value找出它的背景色属性,这样可以减少存储内存大小,提高性能;数字的背景色属性不存储的原因也一样。Tile类也没有表示位置和移动的属性,只有一个ismerge表示瓦片是合并得到的。位置可以通过二维数组的下标定位,在移动的过程中,实际中并不需要真正地在数组中移动瓦片对象,我们可以在二维数组中保持原来的瓦片对象位置不动,移动时把相邻的两个瓦片的属性做一下交换就可以达到移动的效果了,方法swap()实现了交换的功能。

可能你有一个疑问,从图1中我们可以看到有几个灰色的空白瓦片,没有数字的,这种没有数字的瓦片怎么实现呢?其实,在我实现的过程使用了一个小技巧,我把这种空白瓦片的value设置为0,并且把数字的背景色设置成瓦片的背景色,这样在绘制时0就不会显示出来了。让空白瓦片的value=0其实还有很多的好处,在读源码时你发现在判断是否可以生成新瓦片、判断是否可以移动时带来了很多的方便。

游戏瓦片的移动是通过方向键来控制的,在面板中需要一个监听器,用于监听方向键的点击事件。


3、游戏功能的实现

实现游戏面板时,定义一个类Window,继承JPanel,并实现KeyListener接口,监听方向键点击事件来控制游戏。
初始化一个二维数组Tile[4][4] tiles,数组中所有的Tile对象value = 0、ismerge = false,表示空白的瓦片,代码如下:
private void initGame() {
    //初始化地图
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            tiles[i][j] = new Tile();
        }
    }
    //生成两个瓦片
    createTile();
    createTile();
    ...
}

生成瓦片的代码很简单,查找所有空白的瓦片对象加入一个列表,随机得到一个在列表长度范围内的数字,拿出这个数字作为下标的tile对象,随机获取一个2或4的数字v,设置tile的value=v,代码如下:
private void createTile() {
    List<Tile> list = getBlankTiles();
    if (!list.isEmpty()) {
        Random random = new Random();
        int index = random.nextInt(list.size());
        Tile tile = list.get(index);
        //初始化新瓦片的值为2或4
        tile.value = random.nextInt(100) > 50 ? 4 : 2;
    }
}

先看一段监听器的实现代码,如下所示:
public void keyPressed(KeyEvent e) {
    boolean moved = false;
    switch (e.getKeyCode()) {
        ...
        case KeyEvent.VK_LEFT:
            moved = moveLeft();
            inovkeCreateTile();
            checkGameOver(moved);
            break;
        ...
}

可以看到点击向左方向键后,先是调用moveLeft()方法执行向左的操作,如果瓦片可以移动返回true,然后执行inovkeCreateTile()方法生成新的瓦片,最后调用checkGameOver()判断游戏是否结束。moveLeft方法会对瓦片进行移动或合并操作,移动、合并的实现代码如下:
private void doMove(Tile src, Tile dst) {
    dst.swap(src);
    src.clear();
    isMove = true;
}

private void doMerge(Tile src, Tile dst) {
    dst.value = dst.value << 1;
    dst.ismerge = true;
    src.clear();
    score += dst.value;
    isMove = true;
}

可以看到,移动瓦片时是调用swap方法来交换两个瓦片之间的属性来实现的,合并的过程其实也包含了移动的过程,只是需要把合并后的瓦片value值加倍,并且ismerge设置为true。

那么怎么生成新的瓦片呢,inovkeCreateTile代码如下:
private void inovkeCreateTile(){
    if(isMove){
        createTile();
        isMove = false;
    }
}
先是对按键操作的结果做一下判断,如果前一操作有瓦片被移动过(合并也有移动过程),才会调用createTile()创建新的瓦片,createTile已经在上面给出。

每一个操作之后都需要判断游戏是否结束,游戏结束的依据是没有空白的瓦片且所有相邻的两个瓦片不能合并,checkGameOver代码如下:
private void checkGameOver(boolean moved) {
    lscore.setText(score + "");
    if (!getBlankTiles().isEmpty()) {
        return;
    }
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            //判断是否存在可合并的两个瓦片
            if (tiles[i][j].value == tiles[i][j + 1].value || tiles[i][j].value == tiles[i + 1][j].value) {
                isOver = false;
                return;
            }
        }
    }
    isOver = true;
}

4、总结与展望

项目已经基本实现《2048》游戏的所有功能,不同的瓦片有不同的颜色,不同的数字有不同的颜色,可以移动瓦片,也可以合并瓦片得分。但是,一个游戏除了界面还可以包含更多的元素,比如音效、特效等,后面打算把这个项目再完善,增加音效、移动特效、合并特效等功能。
具体讲解请看《《2048》游戏Java版本+源码(二)

游戏的源码已经放在github上托管,有兴趣的童鞋可以下载玩玩,地址是:
https://github.com/beyondfengyu/G2048

如果你希望跟作者讨论或者有交流技术,可以加群:399643539

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

本文标题:《2048》游戏Java版本+源码(一)

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

本文标签: 游戏 2048 java

相关文章

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

扫码打赏,金额随意

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

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