【2048小游戏】——原生js爬坑之封装行的移动算法&事件

引言:2048小游戏的核心玩法是移动行,包括横行和纵行,玩家可以选择4个方向,然后所有行内的数字就会随着行的移动而向特定的方向移动。这个行的移动是一个需要重复调用的算法,所以这里就要将一行的移动算法封装,循环调用给所用行,这样便实现了所有行的调用。


 

一、一行的左移

 

 

  • 关键逻辑算法   伪代码 ↓
  1. c从0开始,遍历当前行每个元素
  2. 找c右侧下一个不为0的位置nextc
  3. 如果找到 →  如果c位置的值是0,将nextc位置的值赋值给c位置,将nextc位置的值置为0,c留在原地;否则如果c位置的值等于nextc位置的值,将c位置的值*2,将nextc位置的值置为0
  4. 否则(如果没找到) → 退出循环
  • 坑:2048游戏的规则,如果一个数字在发生一次替换(比如左移)之后没有合并即*2,那么,它还有一次机会和相反方向的数字进行合并(还可以向右移动),这就要求如果c的位置为0,那么除了数值的替换程序之外,还应该把c位置留在原地,方便之前的数字再回来
  • 解决:因为每次循环都会使c++,+1,要让c留在原地,保持不变  →  用c--,和c++抵消,保持不变

 

二、所有行的左移

 

 

  • 思路:将一行的左移封装成一个算法,循环调用给所有行;同时为了函数的功能集中,把找c右侧下一个不为0的位置nextc的循环也封装为一个小函数
  • 创建关键函数  ↓
  1. moveLeft()    左移所有行
  2. moveLeftInRow(r)     左移第r行  →  一行左移的算法封装
  3. getNextcInRow(r,c)    找r行c列右侧下一个不为0的位置nextc   →   循环,找到返回位置nextc,没找到返回-1
  • 2048游戏规则:每一次控制移动都要随机生成一个2或4,但反过来,一定要发生了移动才能生成2或4,只是按键控制了移动,但没有发生移动,那就不生成。
  • 坑:要判定控制移动是否发生了真实的移动
  • 解决:在控制移动前将data转为字符串保存在before中,在控制移动后将data转为字符串保存在after中,然后再判断before是否等于after,如果不等于,重绘页面,生成2或4,如果等于,不重绘,也不生成。
  • 坑:如果先刷新了页面,再生成一个2或4,这样页面就看不到这个2或4了
  • 解决:先生成一个2或4,然后再刷新页面


 

三、键盘按下事件

 

 

  • 事件:用户在页面上,用鼠标触发的行为的变化
  • 为页面添加  键盘按下事件 处理函数  →   document.onkeydown=function(){ }
  • 识别按键  ↓
  1. 原理:每一个键盘的按键下面都藏着一个唯一的编号(虚拟的) 
  2. 通过判断每一个按键的键盘号的不同,做不同的事情(执行不同的移动控制函数)
  • 上下左右的键盘号   keyCode
  1. 左   37
  2. 上   38
  3. 右   39
  4. 下   40
  • 关键代码   ↓
//为当前页面添加键盘按下事件处理函数
    document.onkeydown = function(e){
          //判断按键号:
          switch (e.keyCode){
              case 37://
                  moveLeft();
                  break;
              case 38://
                  moveUp();
                  break;
              case 39://
                  moveRight();
                  break;
              case 40://
                  moveDown();
                  break;
          }
    }

 

四、游戏得分Score

 

 

  • 游戏的得分,应该作为游戏的一个重要属性保存下来  →   var  score = 0;初始值为0
  • 在每一次游戏开始的时候,即start()起始,都应该将得分归零  →   score=0;
  • 2048游戏规则:每一次发生数字合并都会增加分值,分值数=合并后的显示数字=合并前的数字*2
  1. data[r][c]*=2;   →   将要合并的当前格数字*2
  2. score+=data[r][c];   →   将data中的数值赋给score
  • 在updateView(){}元素填写页面的函数方法中,设置id为score的span的内容为score
  1. var span = document.getElementById("score");
  2. span.innerHTML=score;

 

五、游戏移动完整代码

 

 

var game={
    RN:4,
    CN:4,
    data:null,
    score:0,
    state:1,
    GAMEOVER:0,
    RUNNING:1,
    //启动游戏
    start:function(){
        this.state=this.RUNNING;
        this.score=0;
        this.data=[];
        for(var r=0;r<this.RN;r++){
            this.data[r]=[];
            for(var c=0;
                c<this.CN;
                this.data[r][c]=0,c++);
        }
        this.randomNum();
        this.randomNum();
        this.updateView();
        //为页面绑定键盘按下事件
        document.onkeydown=function(e){
            switch(e.keyCode){
                case 37: this.moveLeft();break;
                case 38: this.moveUp();break;
                case 39: this.moveRight();break;
                case 40: this.moveDown();break;
            }
        }.bind(this);/*this是start方法的this*/
    },
    move:function(callback){
        var before=String(this.data);
        callback();//this->window
        var after=String(this.data);
        if(before!=after){
            this.randomNum();
            if(this.isGameOver()){
                this.state=this.GAMEOVER;
            }
            this.updateView();
        }
    },
    isGameOver:function(){
        for(var r=0;r<this.RN;r++){
            for(var c=0;c<this.CN;c++){
                if(this.data[r][c]==0){return false;}
                else if(c<this.CN-1
                    &&this.data[r][c]==this.data[r][c+1]){
                    return false;
                }
                else if(r<this.RN-1
                    &&this.data[r][c]==this.data[r+1][c]){
                    return false;
                }
            }
        }
        return true;
    },
    moveLeft:function(){
        this.move(function(){
            for(var r=0;r<this.RN;r++){
                this.moveLeftInRow(r);
            }
        }.bind(this));/*this是moveLeft方法的this*/
    },
    moveLeftInRow:function(r){
        for(var c=0;c<this.CN-1;c++){
            var nextc=this.getNextInRow(r,c);
            if(nextc==-1){break;}
            else{
                if(this.data[r][c]==0){
                    this.data[r][c]=this.data[r][nextc];
                    this.data[r][nextc]=0;
                    c--;
                }else if(this.data[r][c]
                    ==this.data[r][nextc]){
                    this.data[r][c]*=2;
                    this.score+=this.data[r][c];
                    this.data[r][nextc]=0;
                }
            }
        }
    },
    getNextInRow:function(r,c){
        c++;
        for(;c<this.CN;c++){
            if(this.data[r][c]!=0){
                return c;
            }
        }
        return -1;
    },
    moveRight:function(){
        this.move(function(){
            for(var r=0;r<this.RN;r++){
                this.moveRightInRow(r);
            }
        }.bind(this));
    },
    moveRightInRow:function(r){
        for(var c=this.CN-1;c>0;c--){
            var prevc=this.getPrevInRow(r,c);
            if(prevc==-1){break;}
            else{
                if(this.data[r][c]==0){
                    this.data[r][c]=this.data[r][prevc];
                    this.data[r][prevc]=0;
                    c++;
                }else if(this.data[r][c] ==this.data[r][prevc]){
                    this.data[r][c]*=2;
                    this.score+=this.data[r][c];
                    this.data[r][prevc]=0;
                }
            }
        }
    },
    getPrevInRow:function(r,c){
        c--;
        for(;c>=0;c--){
            if(this.data[r][c]!=0){
                return c;
            }
        }
        return -1;
    },
    moveUp:function(){
        this.move(function(){
            for(var c=0;c<this.CN;c++){
                this.moveUpInCol(c);
            }
        }.bind(this));
    },
    moveUpInCol:function(c){
        for(var r=0;r<this.RN-1;r++){
            var nextr=this.getNextInCol(r,c);
            if(nextr==-1){break;}
            else{
                if(this.data[r][c]==0){
                    this.data[r][c]=this.data[nextr][c];
                    this.data[nextr][c]=0;
                    r--;
                }else if(this.data[r][c]
                    ==this.data[nextr][c]){
                    this.data[r][c]*=2;
                    this.score+=this.data[r][c];
                    this.data[nextr][c]=0;
                }
            }
        }
    },
    getNextInCol:function(r,c){
        r++;
        for(;r<this.RN;r++){
            if(this.data[r][c]!=0){
                return r;
            }
        }
        return -1;
    },
    moveDown:function(){
        this.move(function(){
            for(var c=0;c<this.CN;c++){
                this.moveDownInCol(c);
            }
        }.bind(this));
    },
    moveDownInCol:function(c){
        for(var r=this.RN-1;r>0;r--){
            var prevr=this.getPrevInCol(r,c);
            if(prevr==-1){break;}
            else{
                if(this.data[r][c]==0){
                    this.data[r][c]=this.data[prevr][c];
                    this.data[prevr][c]=0;
                    r++;
                }else if(this.data[r][c]
                    ==this.data[prevr][c]){
                    this.data[r][c]*=2;
                    this.score+=this.data[r][c];
                    this.data[prevr][c]=0;
                }
            }
        }
    },
    getPrevInCol:function(r,c){
        r--;
        for(;r>=0;r--){
            if(this.data[r][c]!=0)
                return r;
        }
        return -1;
    },
    //将数组中每个元素更新到页面的div中
    updateView:function(){
        for(var r=0;r<this.RN;r++){
            for(var c=0;c<this.CN;c++){
                var div= document.getElementById("c"+r+c);
                if(this.data[r][c]!=0){
                    div.innerHTML=this.data[r][c];
                    div.className="cell n"+this.data[r][c];
                }else{//否则
                    div.innerHTML="";
                    div.className="cell";
                }
            }
        }
        //找到id为score的元素,设置其内容为score属性
        document.getElementById("score")
            .innerHTML=this.score;
        //如果游戏状态为结束
        if(this.state==this.GAMEOVER){
            document.getElementById("gameover")
                .style.display="block";
            document.getElementById("final")
                .innerHTML=this.score;
        }else{
            document.getElementById("gameover").style.display="none";
        }
    },
    randomNum:function(){
        while(true){
            var r=Math.floor(Math.random()*(this.RN));
            var c=Math.floor(Math.random()*(this.CN));
            if(this.data[r][c]==0){
                this.data[r][c]=Math.random()<0.5?2:4;
                break;
            }
        }
    }
};
game.start();


注:转载请注明出处

posted @ 2017-11-06 20:38  柳洁琼Elena  阅读(1268)  评论(0编辑  收藏  举报