引言: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(){ }
- 识别按键 ↓
- 原理:每一个键盘的按键下面都藏着一个唯一的编号(虚拟的)
- 通过判断每一个按键的键盘号的不同,做不同的事情(执行不同的移动控制函数)
- 左 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;
}
}
- 游戏的得分,应该作为游戏的一个重要属性保存下来 → 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();
注:转载请注明出处