【2048小游戏】——原生js爬坑之封装行的移动算法&事件
2017-11-06 20:38
447 查看
引言:2048小游戏的核心玩法是移动行,包括横行和纵行,玩家可以选择4个方向,然后所有行内的数字就会随着行的移动而向特定的方向移动。这个行的移动是一个需要重复调用的算法,所以这里就要将一行的移动算法封装,循环调用给所用行,这样便实现了所有行的调用。
一、一行的左移 |
- 关键逻辑算法 伪代码 ↓
- c从0开始,遍历当前行每个元素
- 找c右侧下一个不为0的位置nextc
- 如果找到 → 如果c位置的值是0,将nextc位置的值赋值给c位置,将nextc位置的值置为0,c留在原地;否则如果c位置的值等于nextc位置的值,将c位置的值*2,将nextc位置的值置为0
- 否则(如果没找到) → 退出循环
- 坑:2048游戏的规则,如果一个数字在发生一次替换(比如左移)之后没有合并即*2,那么,它还有一次机会和相反方向的数字进行合并(还可以向右移动),这就要求如果c的位置为0,那么除了数值的替换程序之外,还应该把c位置留在原地,方便之前的数字再回来
- 解决:因为每次循环都会使c++,+1,要让c留在原地,保持不变 → 用c--,和c++抵消,保持不变
二、所有行的左移 |
- 思路:将一行的左移封装成一个算法,循环调用给所有行;同时为了函数的功能集中,把找c右侧下一个不为0的位置nextc的循环也封装为一个小函数
- 创建关键函数 ↓
- moveLeft() 左移所有行
- moveLeftInRow(r) 左移第r行 → 一行左移的算法封装
- 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(){ }
- 识别按键 ↓
- 原理:每一个键盘的按键下面都藏着一个唯一的编号(虚拟的)
- 通过判断每一个按键的键盘号的不同,做不同的事情(执行不同的移动控制函数)
- 上下左右的键盘号 keyCode
- 左 37
- 上 38
- 右 39
- 下 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
- data[r][c]*=2; → 将要合并的当前格数字*2
- score+=data[r][c]; → 将data中的数值赋给score
- 在updateView(){}元素填写页面的函数方法中,设置id为score的span的内容为score
- var span = document.getElementById("score");
- 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();
注:转载请注明出处
相关文章推荐
- 【2048小游戏】——原生js爬坑之遍历算法显示二维数组内容
- 【2048小游戏】——CSS/原生js爬坑之纯CSS模态对话框&游戏结束
- 原生js事件的添加和删除的封装
- 对原生js事件对象处理的封装
- 原生js事件的添加和删除的封装
- 用原生js写2048小游戏
- 使用原生JS封装Tap事件,解决移动端300ms延迟
- 使用原生JS封装Tap事件,解决移动端300ms延迟
- 用js原生方法封装跨浏览器注册事件的方法系列一(事件入门)
- 原生js绑定事件方法简单封装
- 原生js封装的滑动式轮播插件
- 原生js实现下拉到底事件
- 原生js封装的渐入式轮播插件
- (二)原生JS实现 - 事件类方法
- 兼容IE9以下和非IE浏览器的原生js事件绑定函数
- 原生JS封装Ajax插件(同域&&jsonp跨域)
- [js点击]JavaScript之原生触摸事件详解01
- 一个用原生js实现的小游戏---FlappyBird
- js原生代码封装$
- 原生JS实现图片无缝滚动方法(附带封装的运动框架)