• 【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();


    注:转载请注明出处

  • 相关阅读:
    dubbo源码阅读-服务订阅(八)之本地订阅(injvm)
    dubbo源码阅读-服务订阅(八)之主流程
    dubbo源码阅读-服务暴露(七)之远程暴露(dubbo)
    dubbo源码阅读-配置(二)之API配置
    dubbo源码阅读-容器启动(六)
    LIRe 源代码分析 6:检索(ImageSearcher)[以颜色布局为例]
    LIRe 源代码分析 5:提取特征向量[以颜色布局为例]
    LIRe 源代码分析 4:建立索引(DocumentBuilder)[以颜色布局为例]
    智能电视大战背后的秘密
    二线视频网站突围战
  • 原文地址:https://www.cnblogs.com/ljq66/p/7784982.html
Copyright © 2020-2023  润新知