jQuery实现2048游戏

整体思路:

1. 基础设置:游戏的背景是一个4*4的格子背景, 游戏中分别有2、4、8、16等不同的色块,每种色块的颜色由js脚本来控制。

jQuery实现2048游戏
游戏界面

2. 游戏开始: 游戏开始的时候,进行初始化操作。在4*4的格子上面,随机生成2个色块,色块的里面的数字是2或4。这里应当注意的是格子和色块不是同一个元素,即整个页面中有16个格子元素和若干个色块元素。这里要注意一下格子元素和色块元素的CSS样式布局。

3. 游戏控制:通过键盘事件来对色块进行控制。每按下一次键盘上面的"上下左右"键,页面中的色块向对应的方向移动,这里通过调用。如果色块发生移动,则在页面内再随机生成一个色块,色块的内容是2或4。

4.检测游戏是否结束:每当色块移动之后,就进行一次检测,检测游戏是否已经结束。游戏结束判断的依据是:当前没有空白格子,即每一个格子上面都有一个色块;任何一个色块,其紧邻的色块没有与之相同的。

5.游戏结束:游戏结束,进行一些细节方面的处理。比如我这里会显示游戏结束四个字,会让游戏的时间停下来等,当然,这是游戏的收尾工作,该怎么来写,根据个人的想法来就行了。

jQuery实现2048游戏
游戏结束画面

6.源码获取方式在文末


具体代码:

  • HTML代码:

<!-- 游戏的得分和持续时间 -->
<div id="game_show">
    <div id="score">
        <b>得分:</b>
        <span id="score_val">0</span>
    </div>
    <div id="time">
        <b>时间:</b>
        <span id="time_val">0</span>
    </div>
</div>

<div id="box">
    <!-- 4*4格子 -->
    <div class="grid-cell" id="grid-cell-0-0"></div>
    <div class="grid-cell" id="grid-cell-0-1"></div>
    <div class="grid-cell" id="grid-cell-0-2"></div>
    <div class="grid-cell" id="grid-cell-0-3"></div>

    <div class="grid-cell" id="grid-cell-1-0"></div>
    <div class="grid-cell" id="grid-cell-1-1"></div>
    <div class="grid-cell" id="grid-cell-1-2"></div>
    <div class="grid-cell" id="grid-cell-1-3"></div>

    <div class="grid-cell" id="grid-cell-2-0"></div>
    <div class="grid-cell" id="grid-cell-2-1"></div>
    <div class="grid-cell" id="grid-cell-2-2"></div>
    <div class="grid-cell" id="grid-cell-2-3"></div>

    <div class="grid-cell" id="grid-cell-3-0"></div>
    <div class="grid-cell" id="grid-cell-3-1"></div>
    <div class="grid-cell" id="grid-cell-3-2"></div>
    <div class="grid-cell" id="grid-cell-3-3"></div>

    <!-- js中要向页面中插入的色块元素 -->
    <!--<div class="number-cell" id="number-cell-0-0">2</div>-->

    <!-- 游戏结束的时候要显示的字体和后面的半透明遮罩 -->
    <div id="cover">
        <div class="text_box">游</div>
        <div class="text_box">戏</div>
        <div class="text_box">结</div>
        <div class="text_box">束</div>
    </div>
    <div id="cover_bg"></div>
</div>
  • CSS代码:

*{
    margin: 0;
    padding: 0;
}
#game_show{
    width: 500px;
    height: 100px;
    background-color: #b8aea4;
    margin: 10px auto;
    text-align: center;
    padding: 20px 0px;
    box-sizing: border-box;
}
#game_show div{
    width: 200px;
    height: 56px;
    background-color: rgb(121, 111, 101);
    border-radius: 5px;
    display: inline-block;
    margin: 0px 20px;
    line-height: 56px;
    box-sizing: border-box;
}
#game_show div span{
    color: white;
    margin: 0px 10px;
}
#box{
    width: 500px;
    height: 500px;
    background-color: #b8aea4;
    margin: 5px auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    position: relative;
}
#box .grid-cell{
    width: 100px;
    height: 100px;
    background-color: #ccc0b2;
    margin: 10px;
    border-radius: 5px;

    text-align: center;
    line-height: 100px;
}
.number-cell{
    width: 100px;
    height: 100px;
    /*background-color: #eee4da;*/
    border-radius: 5px;
    font-size: 36px;
    font-weight: bolder;
    color: #796f65;
    text-align: center;
    line-height: 100px;
    position: absolute;
    left: 20px;     /* 20+(120)n */
    top: 13px;      /* 13+(125)n */
}

#cover_bg{
    width: 100%;
    height: 100%;
    background-color: #333333;
    position: absolute;
    z-index: 998;
    opacity: 0.5;
    display: none;
}
#cover{
    z-index: 999;
    opacity: 1.0;
    position: absolute;
    text-align: center;
    display: none;
}
#cover .text_box{
    width: 80px;
    height: 80px;
    background-color: #f57c5f;
    border-radius: 5px;
    display: inline-block;
    margin: 0px 10px;
    line-height: 80px;
    font-size: 26px;
    font-weight: bolder;
    color: white;
    opacity: 1.0;
}
#cover .text_box:nth-child(1){
    background-color: #f2b179;
}
#cover .text_box:nth-child(2){
    background-color: #f59562;
}
#cover .text_box:nth-child(4){
    background-color: #f75e3c;
}
  • JS代码(使用了jQuery插件):

0.代码中设置的一些全局变量

var lock = false;                       // 按键锁,防止连续按键盘造成色块不停运动
var piece = new Array();                // piece[3][3]表示某个位置上是否有色块
var move_mark = false;                  // 表示当前是否有色块移动
var timer = 200;                        // 随机出现色块的时间间隔
var score=0;                            //游戏得分

1.游戏开始,随机显示两个色块

// 游戏初始化,首先将每个位置设置为没有色块
for (var i = 0; i < 4; i++) {
    piece[i] = new Array();
    for (var j = 0; j < 4; j++) {
        piece[i][j] = 0;
    }
}
// 随机显示色块
radom_piece();
radom_piece();

/**
 * 在空白的格子上面随机产生一个"2"或"4"的色块
 */
function radom_piece() {
    var test_num = 0;
    var randomX = Math.floor(Math.random() * 4);
    var randomY = Math.floor(Math.random() * 4);
    var randomText = Math.random() < 0.5 ? 2 : 4;
    while (true) {
        if (!piece[randomX][randomY]) {                 // 当前格子上面没有色块
            // 给当前格子上面添加色块,以及设置色块里面的数字
            piece[randomX][randomY] = randomText;
            var newPiece = '<div class="number-cell" merged="false" x="' + randomX + '" y="' + randomY + '" id="number-cell-' + randomX + '-' + randomY + '">' + randomText + '</div>';
            $('#box').append(newPiece);
            setColor();         //添加色块成功,设置色块的颜色
            // 根据随机数字,安排色块的位置
            $("#number-cell-" + randomX + "-" + randomY).css('left', 20 + 120 * randomY);
            $("#number-cell-" + randomX + "-" + randomY).css('top', 13 + 125 * randomX);
            break;      //成功产生随机色块,跳出循环
        }
        test_num++;
        var randomX = Math.floor(Math.random() * 4);
        var randomY = Math.floor(Math.random() * 4);
    }
}

2.检测键盘事件,执行一系列函数
备注:

  1. 这是整个游戏中最关键的部分,特别是四个方向的运动函数,不仅要使色块运动起来,还要判断相同色块进行合并的问题。色块运动执行完毕之后,要执行随机产生新的色块、判断游戏是否结束等函数。
  2. 色块运动函数一共有四个,四个函数大体相同,这里只给出一个函数,其他的函数可以在源码中获得。
// 键盘事件
$(document).keydown(function (e) {
    move_mark = false;        // 每一次按下键盘之前,将是否移动标志置为false,表示当前没有移动
    $('.number-cell').attr('merged','false');       // 每一次按下键盘,将是否合并的标志位初始化,没有合并
    if (lock) {
        return false;
    }
    lock = true;
    switch (e.keyCode) {
        case 37:            // 左边
        {
            /*------ 向左移动,左边第二列先移动,然后第三列,然后第四列 ------*/
            for (var a = 0; a <= 3; a++) {
                for (var b = 0; b <= 3; b++) {
                    var tar_piece = $('#number-cell-' + b + '-' + a);
                    if (tar_piece.hasClass('number-cell')) {
                        // console.log(tar_piece);
                        moveToLeft(tar_piece);
                    }
                }
            }
            // 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
            setTimeout(function () {
                if (move_mark) {
                    radom_piece();
                    game_score();       //每按下键盘一次,刷新当前的分数
                    if(is_game_over()){
                        game_over();                    // 游戏停止,显示遮罩
                        clearInterval(game_timer);      // 停止时间
                    }
                }
                lock = false;
            }, timer);
            break;
        }
        case 38:            // 上边
        {
            for (var a = 0; a <= 3; a++) {
                for (var b = 0; b <= 3; b++) {
                    var tar_piece = $('#number-cell-' + a + '-' + b);
                    if (tar_piece.hasClass('number-cell')) {
                        // console.log(tar_piece);
                        moveToUp(tar_piece);
                    }
                }
            }
            // 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
            setTimeout(function () {
                if (move_mark) {
                    radom_piece();
                    game_score();       //每按下键盘一次,刷新当前的分数
                    if(is_game_over()){
                        game_over();                    // 游戏停止,显示遮罩
                        clearInterval(game_timer);      // 停止时间
                    }
                }
                lock = false;
            }, timer);
            break;
        }
        case 39:            // 右边
        {
            for (var a = 3; a >= 0; a--) {
                for (var b = 3; b >= 0; b--) {
                    var tar_piece = $('#number-cell-' + b + '-' + a);
                    if (tar_piece.hasClass('number-cell')) {
                        // console.log(tar_piece);
                        moveToRight(tar_piece);
                    }
                }
            }
            // 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
            setTimeout(function () {
                if (move_mark) {
                    radom_piece();
                    game_score();       //每按下键盘一次,刷新当前的分数
                    if(is_game_over()){
                        game_over();                    // 游戏停止,显示遮罩
                        clearInterval(game_timer);      // 停止时间
                    }
                }
                lock = false;
            }, timer);
            break;
        }
        case 40:            // 下边
        {
            for (var a = 3; a >= 0; a--) {
                for (var b = 3; b >= 0; b--) {
                    var tar_piece = $('#number-cell-' + a + '-' + b);
                    if (tar_piece.hasClass('number-cell')) {
                        // console.log(tar_piece);
                        moveToDown(tar_piece);
                    }
                }
            }
            // 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
            setTimeout(function () {
                if (move_mark) {
                    radom_piece();
                    game_score();       //每按下键盘一次,刷新当前的分数
                    if(is_game_over()){
                        game_over();                    // 游戏停止,显示遮罩
                        clearInterval(game_timer);      // 停止时间
                    }
                }
                lock = false;
            }, timer);
            break;
        }
    }
});

/**
 * 色块向左移动
 * @param piece_selectd 选中的色块
 * @returns {boolean}
 */
function moveToLeft(piece_selectd) {
    var grid_num = 0;                     // 当前色块左边的空白格子个数
    var x = parseInt(piece_selectd.attr('x'));
    var y = parseInt(piece_selectd.attr('y'));
    // 最左边一列不移动
    if (y == 0) {
        return true;
    }
    // 得到当前色块,左边空白格子的数量
    for (var i = y - 1; i >= 0; i--) {
        if (piece[x][i] == 0) {      // 当前色块的左边格子上面没有色块
            grid_num++;
        }
    }
    var merge=$('#number-cell-'+x+'-'+(y - grid_num - 1)).attr('merged');
    // 当前色块与相邻(不一定紧贴在一起)的色块一样,且相邻的色块之前没有合并过
    if (piece[x][y] == piece[x][y - grid_num - 1] && merge == 'false') {
        // 计算出色块移动到的位置,并移动色块
        var moveTo = parseInt(piece_selectd.css('left')) - 120 * (grid_num + 1) + 'px';
        piece_selectd.animate({left: moveTo}, 'fast', 'linear');
        // 将与当前色块相邻且一样的色块删除
        $('#number-cell-'+ x +'-'+(y - grid_num - 1)).remove();
        // 更新当前色块以及色块数组的信息
        piece_selectd.attr('y', y - grid_num-1);
        piece_selectd.attr('id', 'number-cell-' + x + '-' + (y - grid_num-1));
        piece_selectd.text(parseInt(piece_selectd.text()) * 2);
        piece[x][y - grid_num-1] = parseInt(piece_selectd.text());
        // 表示该色块已经合并过了,后面的色块不能合并这个
        piece_selectd.attr('merged','true');
        piece[x][y] = 0;
        move_mark = true;         // 表示当前有色块移动
        setColor();         //添加色块成功,设置色块的颜色
    }
    else{
        // 计算出色块移动到的位置,并移动色块
        var moveTo = parseInt(piece_selectd.css('left')) - 120 * grid_num + 'px';
        // console.log(moveTo);
        piece_selectd.animate({left: moveTo}, 'fast', 'linear');
        // 更新当前色块以及色块数组的信息
        piece_selectd.attr('y', y - grid_num);
        piece_selectd.attr('id', 'number-cell-' + x + '-' + (y - grid_num));
        piece[x][y - grid_num] = parseInt(piece_selectd.text());
        if (grid_num) {
            piece[x][y] = 0;
            move_mark = true;         // 表示当前有色块移动
        }
    }
    // console.log(grid_num);
    return true;
}

/**
 * 游戏的得分
 */
function game_score(){
    score=0;
    $.each($('.number-cell'),function(index,value){
        score=score+parseInt($(value).text());
    });
    $('#score_val').text(score);
}

/**
 * 判断游戏是否结束
 * @returns {number} 返回0表示游戏没有结束,返回1表示游戏结束
 */
function is_game_over(){
    console.log(666);
    // 一次判断16个格子上面是否有色块,只要有一个格子没有色块,则返回0,表示游戏没有结束
    for(var i=0;i<4;i++) {
        for(var j=0;j<4;j++){
            if(piece[i][j] == 0){
                return 0;
            }
        }
    }
    // 任何一个色块,如果其上下左右紧邻的色块有与自己想通的色块,表示游戏没有结束
    for(var i=0;i<4;i++){
        for(var j=0;j<4;j++){
            if(piece[i][j] == piece[i+1][j]){
                return 0;
            }
            if(piece[i][j] == piece[i-1][j]){
                return 0;
            }
            if(piece[i][j] == piece[i][j+1]){
                return 0;
            }
            if(piece[i][j] == piece[i][j-1]){
                return 0;
            }
        }
    }
    return 1;           //上面条件均不满足,则表示游戏已经结束
}

 /**
  * 游戏结束,显示出遮罩
  */
 function game_over(){
     $('#cover').show();
     $('#cover_bg').show();
 }

3.一旦有色块出现,或者是色块合并产生新的色块,进行设置或更新色块的颜色

// 根据色块里面的数字,设置色块的背景
setColor();
/**
 * 设置页面中所有色块的背景色和前景色
 */
function setColor() {
    $.each($('.number-cell'), function (index, value) {
        $(value).css('background-color', setBgColorFromNum($(value).text()));
        $(value).css('color', setTextColorFromNum($(value).text()));
    });
}

/**
 * 根据数字,返回色块的背景色
 * @param num 色块里面的数字
 * @returns {string} 返回色块的背景色
 */
function setBgColorFromNum(num) {
    switch (num) {
        case '2':
            return "#eee4da";
        case '4':
            return "#ece0ca";
        case '8':
            return "#f2b179";
        case '16':
            return "#f59562";
        case '32':
            return "#f57c5f";
        case '64':
            return "#f75e3c";
        case '128':
            return "#edce71";
        case '256':
            return "#f1c967";
        case '512':
            return "#ecc850";
        case '1024':
            return "#efc53f";
        default:
            return "red"
    }
}

/**
 * 根据数字,返回色块中数字的颜色
 * @param num 色块里面的数字
 * @returns {string} 返回色块中数字的颜色
 */
function setTextColorFromNum(num) {
    switch (num) {
        case '2':
            return "#796f65";
        case '4':
            return "#796f65";
        default:
            return "#ffffff";
    }
}

4.显示游戏进行的时间

// 游戏的已经持续的时间
var game_timer=setInterval(function(){
    $('#time_val').text(parseInt($('#time_val').text())+1);
},1000);

5.源码获取

  1. 这里首先给出一个CSDN下载地址:https://download.csdn.net/download/fengzhen8023/10545750
  2. 后期会给出GitHub地址
上一篇:使用js控制滚动条的位置


下一篇:基于关系的违规团伙发掘风控方案