前端小项目——2048私人订制

游戏架构大致如下图所示,是典型的M(model游戏数据)V(view视图)C(control)结构。
游戏架构
我们的2048也是如上图结构所示。其中由于2048不需要什么游戏数据,所以无Model;游戏主逻辑是游戏的主函数,包括在main.js中;视图UI是html+css;支撑逻辑包含在support2048.js中;动画效果逻辑包含在showanimation2048.js。

UI部分(html+css)

  • HTML设置大致的布局——包含<header>grid-container结构。
<header>
            <h1>2048</h1>
            <a href="javascript:newgame();" id="newgamebutton">New Game</a>
            <p>score:<span id="score">0</span></p>
        </header>

        <div id="grid-container">
            <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>
        </div>
  • css主要就是对这两部分进行布局。用margin:0 auto;将块居中;用text-align:center;将块中的文字居中。<header>部分的宽度因为本身不大,所以可以设置死。grid-container部分的宽度可以先写一个固定宽度,看着舒服就行,之后再用js在不同的屏幕上显示不同的宽度。

主逻辑部分

  • 首先规定页面加载完完成的函数
    -$(document).ready(function(){ prepareForMobile(); newgame(); });
  • 其中的prepareForMobile()是根据屏幕的宽度动态调节grid-container与grid-cell的间距与大小。
function prepareForMobile(){

    if(documentWidth>500){
        gridContainerWidth=500;
        cellSpace=20;
        cellSideLength=100;
    }
    $('#grid-container').css('width',gridContainerWidth-2*cellSpace);
    $('#grid-container').css('height',gridContainerWidth-2*cellSpace);
    $('#grid-container').css('padding',cellSpace);
    $('#grid-container').css('border-radius',0.02*gridContainerWidth);

    $('.grid-cell').css('width',cellSideLength);
    $('.grid-cell').css('height',cellSideLength);
    $('.grid-cell').css('border-radius',0.02*cellSideLength);
}

其中的变量gridContainerWidth,cellSpace,cellSideLength等规定在support2048.js中。

documentWidth=window.screen.availWidth; //屏幕可用宽度。相当于500px
gridContainerWidth=0.92*documentWidth; //grid-container的设定宽度值
cellSideLength=0.18*documentWidth; //grid-cell的长宽?计算着有点不对,应该是0.2
cellSpace=0.04*documentWidth;
  • newgame()函数是新游戏入口程序。在每次页面加载或点击新游戏按钮之后都会运行。
function newgame(){
    init();
    generateOneNumber();
    generateOneNumber();
}
  • 其中的init()函数是初始化程序。完成了几件事:1.用jquery对每一个数字的背景归位;2.对每一个数字初始化为0,对每一个hasConflicted初始化为false;3.重新渲染页面:updateBoardView();4.对score初始化为0,并用jquery反馈到前台。
    updateBoardView()用jquery对每一个格子生成了number-cell,如果board[i][j]不为0,则number-cell将覆盖grid-cell显示出来。
  • generateOneNumber()函数是在随机一个空白格生成一个随机数。细节是如果只剩一个空白格,机器可能要试错很多次,使运行速度太慢,所以可以限制机器猜测的次数,如果超过50次还没有猜出来,就人为设定。

游戏循环部分

  • 键盘事件监听:主要针对键盘上面的上下左右四个按键进行监听,当按键按下时,执行响应的函数,并且生成新的随机数,随后判断游戏界面中是否还存在空格,如果满了,是否可以进行移动,确定游戏是继续循环还是结束。下面以左按钮事件为例:
$(document).keydown(function(event){
    event.preventDefault();
    switch(event.keyCode){
        case 37:
            if(moveLeft()){
                setTimeout("generateOneNumber()",210);
                setTimeout("isgameover()",300);
            }
            break;
        case 38: //up
            if(moveUp()){
                setTimeout("generateOneNumber()",210);
                setTimeout("isgameover()",300);}
            break;
        case 39: //right
            if(moveRight()){
                setTimeout("generateOneNumber()",210);
                setTimeout("isgameover()",300);}
            break;
        case 40: //down
            if(moveDown()){
                setTimeout("generateOneNumber()",210);
                setTimeout("isgameover()",300);}
            break;
        default:
            break;
    }
});

function isgameover(){
    if(nospace(board)&&nomove(board))
        gameover();
}

function gameover(){
    alert("game over!");
}

function moveLeft(){
    if(!canMoveLeft(board)){
        return false;
    }
    for(let i=0;i<4;i++){
        for(let j=1;j<4;j++){
            if(board[i][j]!==0){
                 for(let k=0;k<j;k++){
                     if(board[i][k]===0&&noBlockHorizontal(i,k,j,board)){
                         showMoveAnimation(i,j,i,k);
                         board[i][k]=board[i][j];
                         board[i][j]=0;
                         continue;
                     }
                     else if(board[i][k]===board[i][j]&&noBlockHorizontal(i,k,j,board)&&!hasConflicted[i][k]){
                         showMoveAnimation(i,j,i,k);
                         board[i][k]+=board[i][j];
                         board[i][j]=0;
                         score+=board[i][k];
                         updateScore(score);
                         hasConflicted[i][k]=true;
                         continue;
                     }
                 }
            }
        }
    }
    setTimeout("updateBoardView()",200);
    return true;
}

按下左键时,首先判断是否能够左移:格子里是否存在空白格或者左边的格子和右边的格子相等;如果能够左移再进行左移操作。从第二列开始,(1). 当前格子是否为空且往左边移动的过程中没有别的数字挡着道路,成立的话,执行左移;(2). 当前格子与其左边格子的数字是否相同,且中间无别的内容,使得话,左移并且执行加法操作,合并数字。
左移结束后重新渲染页面。

触摸事件的监听

主要是针对移动端设计的。主要是通过touch事件完成,在touchstart中记录触摸的起点坐标event.touches[0].pageX和event.touches[0].pageY;在touchend中记录触摸结束点的坐标:event.changedTouches[0].pageX和event.changedTouches[0].pageY,然后进行运算,比较差值,得到deltaX和deltaY,可以分为以下四种情况:

//x
    if((Math.abs(deltax))>=(Math.abs(deltay))){
        if (deltax>0){
            //move right
            setTimeout("generateOneNumber()",210);
            setTimeout("isgameover()",300);
        }
        else {
            //move left;
            setTimeout("generateOneNumber()",210);
            setTimeout("isgameover()",300);
        }
    }

    //y
    else{
        if (deltay>0){
            //move down
            setTimeout("generateOneNumber()",210);
            setTimeout("isgameover()",300);
        }
        else {
            //move up
            setTimeout("generateOneNumber()",210);
            setTimeout("isgameover()",300);
        }
    }

细节

1.游戏结束的判断,当四个方向都不能移动,且没有空格子时游戏结束
2.得分的处理,设置一个全局的变量score,在执行移动时,一旦发生相加操作,将相加的数值作为分数加入score中
3.动画的处理,通过jQuery的animate函数完成

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值