> 文章列表 > 2023-04-14 使用纯JS实现一个2048小游戏

2023-04-14 使用纯JS实现一个2048小游戏

2023-04-14 使用纯JS实现一个2048小游戏

文章目录

      • 一.实现思路
        • 1.2048的逻辑
        • 2.移动操作的过程中会有三种情况
      • 二.代码部分:分为初始化部分和移动部分
        • 1.初始化部分
          • 1.1.生成第一个方块:
          • 1.2.生成第二个方块:
        • 2.移动过程部分:
      • 三.实现代码
        • 1.HTML结构
        • 2.CSS部分
        • 3.JS部分
      • 四.效果

一.实现思路

1.2048的逻辑

一共有16个格子,开局5个随机格子上生成带数字的方块,2或者4
通过控制方向来使原有的方块移动并产生新的方块

2.移动操作的过程中会有三种情况

  • 方块移动的方向没有其他方块
  • 方块移动的方向有其他方块,但数字不同
  • 方块移动的方向有其他方块,且数字相同

二.代码部分:分为初始化部分和移动部分

1.初始化部分

新建一个4X4二维数组,存储16个方块的值,
分两个步骤生成第一个和第二个方块

1.1.生成第一个方块:

用0-15的随机数代表第几个数组元素上面生成第一个方块块的值(值为2或者4),其余的值为0
具体生成2或者4,设置为随机,其中2的概率高一些,4的概率低一些,比如:
0-10之间,若随机值为0,生成2,否则生成4,比例:10:1

1.2.生成第二个方块:
  • 为什么第二个方块(以及其他所有后续的方块)不能使用上述方法?
    因为如果两次生成的随机数一样,那么就会出现bug:
    同一位置产生两个值

  • 第二个方块如何生成?
    先获取16个格子中,空格的个数,也就是二维数组中0的个数
    以空格的个数作为循环的次数

2.移动过程部分:

移动过程是根据输入的方向来控制方块的移动的

  • 首先获取输入的状态,即玩家操作的上下左右键
  • 其次,移动的控制逻辑是:
遍历二维数组,找到每一个方块(以左移动为例)
由于第一列无需移动,只从每一行的第二个方块开始判断
(若第一个方块不存在:交换值,并让交换的原位置的值清0
若当前方块的紧邻左方块和当前方块,两者的值相同,那么左边的值变为原来的两倍,当前方块的值清0
若当前方块的紧邻左方块看和当前方块,两者的值不同,不作处理,不移动

三.实现代码

1.HTML结构

快捷键:div#all>div.cell#n0$@0*4+div.cell#n1$@0*4+div.cell#n2$@0*4+div.cell#n3$@0*4
代码:2048.html

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>2048小游戏</title><link rel="stylesheet" href="2048.css" />
</head>
<body><!--分数--><p class="header">SCORE: <span id="score01"></span></p><!--游戏部分--><div id="all"><!--第一行--><div class="cell"  id="n00"></div><div class="cell"  id="n01"></div><div class="cell"  id="n02"></div><div class="cell"  id="n03"></div><!--第二行--><div class="cell"  id="n10"></div><div class="cell"  id="n11"></div><div class="cell"  id="n12"></div><div class="cell"  id="n13"></div><!--第三行--><div class="cell"  id="n20"></div><div class="cell"  id="n21"></div><div class="cell"  id="n22"></div><div class="cell"  id="n23"></div><!--第四行--><div class="cell"  id="n30"></div><div class="cell"  id="n31"></div><div class="cell"  id="n32"></div><div class="cell"  id="n33"></div></div><!--弹框--><div id="gameover" ><p>GAME OVER<br />SCORE:<span id="score02"></span><br /><a onclick="typeonce()">TYPE AGAIN</a></p></div><script type="text/javascript" src="2048.js" ></script>
</body>
</html>

2.CSS部分

2038.css

*{font-family: arial;/*	user-select: none;*/font-weight: bold;}.header{width: 480px;font-size:40px ;margin: 60px auto 5px auto;}#score01{color: #F00;}#all{width: 480px;height: 480px;background-color: #bbada0;margin: 0 auto;border-radius: 20px;}.cell{width: 100px;height: 100px;background-color: #ccc0b3;;border-radius: 10px;text-align: center;line-height: 100px;font-size: 40px;color: #fff;float: left;margin: 16px 0px 0px 16px;}.n2{background-color:#eee3da;color:#776e65;}.n4{background-color:#ede0c8;color:#776e65;}.n8{background-color:#f2b179;}.n16{background-color:#f59563;}.n32{background-color:#f67c5f;}.n64{background-color:#f65e3b;}.n128{background-color:#edcf72;}.n256{background-color:#edcc61;}.n512{background-color:#9c0;}.n1024{background-color:#33b5e5;font-size:40px;}.n2048{background-color:#09c;font-size:40px;}.n4096{background-color:#a6c;font-size:40px;}.n8192{background-color:#93c;font-size:40px;}#gameover{position: absolute;background-color: rgba(0,0,0,0.2);left: 0;top: 0;right: 0;bottom: 0;font-size: 40px;display: none;}#gameover p{background-color: #fff;width: 300px;height: 200px;border-radius: 10px;line-height: 66.66px;text-align: center;position: absolute;left: 50%;top: 50%;margin-left: -150px;margin-top: -150px;}#gameover p a{padding: 10px;text-decoration: none;background-color: #9f8d77;color: #fff;border-radius: 10px;cursor: pointer;}

3.JS部分

/******思路*******//*一.游戏环节:创建game对象*/
//属性:data,score,gamerunning,gameover,status
//普通方法:start,randomNum,dataView,isGameOver
//移动的方法:moveLeft,moveRight,moveUp,moveDown
//移动方法中处理每一行数据的方法:
//moveLeftinRow,moveLeftNum,moveRightinRow,moveRightNum
//moveUpinRow,moveUpNum,moveDowninRow,moveDownNum// var game={};/*二.监听鼠标按下事件,并调用game对象中的移动方法*/
//如果监听到上下左右对应的ASCII码,就触发相应的上下左右game移动方法// document.οnkeydοwn=function(){}/*三.游戏开始时,调用game对象中的start方法*/// game.start();/*四.点击再来一次*/
//隐藏#gameover元素,并跳转回2048.html// function typeonce(){}/*一.游戏环节:创建game对象*/
//属性:data,score,gamerunning,gameover,status
//普通方法:start,randomNum,dataView,isGameOver
//移动的方法:moveLeft,moveRight,moveUp,moveDown
//移动方法中处理每一行数据的方法:
//moveLeftinRow,moveLeftNum,moveRightinRow,moveRightNum
//moveUpinRow,moveUpNum,moveDowninRow,moveDownNum
var game={/*****对象属性*****/data:[],//二维数组:用于存储2048的值score:0,//游戏分数:开始时分数为0gamerunning:1,//游戏开始时:游戏状态为1gameover:0,//游戏结束时:游戏状态为0status:1,//个人状态与游戏状态相对应,默认为1/*****普通对象方法*****///1.start方法:游戏开始时还原所有(把game属性还原)start:function(){this.data=[//二维数组的值都清零[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];this.score=0;//分数也清零this.status=this.gamerunning;//状态码=游戏开始的状态码(也就是1)// 调用对象方法randomNum,用于获取随机数,调用五次for(var i=0;i<5;i++){this.randomNum();}// 调用对象方法dataView,用于更新视图this.dataView();},//2.randomNum方法:随机赋值,让随机的数组元素获得2或4作为其值randomNum:function(){while(1){//是否方块存在空白位置var row=Math.floor(Math.random()*4);//获取一个0~3的随机整数,代表第几行var col=Math.floor(Math.random()*4);//获取一个0~3的随机整数,代表第几列//判断:如果二维数组data的某个元素的值为0,就让其其值变为2或4if(this.data[row][col]==0){var num=Math.random()>.2?2:4;//num的值只能是2或者4,概率分别是80%和20%this.data[row][col]=num;break;}}},//3.dataView方法:更新视图dataView:function(){//思路:数组元素的值不为0时,把其值赋给html结构中对应的div中for(var row=0;row<4;row++){for(var col=0;col<4;col++){//拼接成div#n23这样的字符串var div=document.getElementById("n"+row+col);if(this.data[row][col]!=0){//数组元素的值不为0div.innerHTML=this.data[row][col];//<div id="n23">2</div>div.className="cell n"+this.data[row][col];//赋予新类名:n23,即<div class="cell n23"></div>}else{//数组元素的值为0div.innerHTML="";//不显示任何内容div.className="cell";}}}//更新分数document.getElementById("score01")=this.score;//判断游戏是否结束//游戏结束时弹出gameover框,并显示最终分数if(this.status==this.gameover){document.getElementById("gameover").style.display="block";document.getElementById("score02")=this.score;}else{document.getElementById("gameover").style.display="none";}},//4.isGameOver方法:判断游戏是否结束isGameOver:function(){//思路:游戏结束就返回一个true,未结束就返回一个return false;//未结束的几种情况(返回false):1.有空白;2.左右有相同;3.上下有相同for(var row=0;row<4;row++){for(var col=0;col<4;col++){if (this.data[row][col] == 0) {  //有空白的时候return false;}if (col < 3) {  //判断左右是否有相同,只需要判断到第三个格子即可if (this.data[row][col] == this.data[row][col + 1]) {return false;}}if (row < 3) {  //判断上下是否有相同,只需要判断到第三个格子即可if (this.data[row][col] == this.data[row + 1][col]) {return false;}}}}return true;}},/*****移动的对象方法*****//*向左移动的思路:- 遍历二维数组data,第一列无需移动,只从第二个方块开始- 若第一个方块不存在:赋值(交换),并让原位置的值清零- 若当前方块的紧邻左方块和当前方块两者值相同,左方块倍增,右方块清零- 若当前方块和紧邻左方块和当前方块两者值不同,不作处理*//**1.左移**///1.3.moveLeft方法:向左移动moveLeft:function(){var before=String(this.data);for(var row=0;row<4;row++){this.moveLeftinRow(row);}var after=String(this.data);if(before!=after){this.randomNum();if(this.isGameOver()) this.status=this.gameover;this.dataView();}},//1.2.moveLeftinRow方法:处理向左移动时每一行的数据moveLeftinRow:function(row){for(var col=0;col<3;col++){var nextCol=this.moveLeftNum(row,col);if(nextCol!=-1){if(this.data[row][col]==0){this.data[row][col]=this.data[row][nextCol];this.data[row][nextCol]=0;col--;}else if(this.data[row][col]==this.data[row][nextCol]){this.data[row][col]*=2;this.data[row][nextCol]=0;this.score+=this.data[row][col];}}else{break;}}},//1.1.moveLeftNum方法:获取符合条件的列数moveLeftNum:function(row,col){for(var i=col+1;i<4;i++){if(this.data[row][i]!=0) return i;else return -1;}},/**2.右移**///2.3.moveRight方法:向右移动moveRight:function(){// 移动前:把二维数组data转化成字符串var before=String(this.data);// 调用对象方法moveRightinRow:用于处理每一行的函数for(var row=0;row<4;row++){this.moveRightinRow(row);}// 移动后:把二维数组转化成字符串var after=String(this.data);// 判断:移动前后的data是否不一样if(before!=after){this.randomNum();//调用对象方法randomNum:生成2或者4的随机数if(this.isGameOver) this.status=this.gameover;//若游戏结束,状态值为结束状态this.dataView();//调用对象方法dataView:更新视图}},//2.2.moveRightinRow方法:处理向右移动时每一行的数据moveRightinRow:function(row){for(var col=3;col>=0;col--){// 调用对象方法moveRightNum:获取符合条件的列数ivar nextCol=this.moveRightNum(row,col);//赋值:把符合条件的col赋给nextColif(nextCol!=-1){//若有值,说明需要进行左右值交换if(this.data[row][col]==0){//若当前数组元素的值为0this.data[row][col]=this.data[row][nextCol];//给当前元素赋值this.data[row][nextCol]=0;//让下一元素赋值后清零col++;//再次从最后边的数进行循环}else if(this.data[row][col]==this.data[row][nextCol]){//若左右元素有相同的值this.data[row][col]*=2;//当前元素的值倍增this.data[row][nextCol]=0;//下一元素的值清零this.score+=this.data[row][col];//分数叠加}}else{//若没有值,跳出循环break;}}},//2.1.moveRightNum方法:获取符合条件的列数//形参:row-行数,col-列数moveRightNum:function(row,col){//循环并获取前面的数据for(var i=col-1;i>=0;i--){//最右边一列不移动,从第col-i列开始循环//判断:前面是否找到数字if(this.data[row][i]!=0) return i;//返回下标else return-1;//返回-1}},/**3.上移**///3.3.moveUp方法:向上移动moveUp:function(){var before=String(this.data);for(var col=0;col<4;col++){this.moveUpinCol(col);}var after=String(this.data);if(before!=after){this.randomNum();if(this.isGameOver()) this.status=this.gameover;this.dataView();}},//3.2.moveUpinCol方法:处理所有行的每一列数据moveUpinCol:function(col){for(var row=0;row<3;row++){var nextRow=this.moveUpNum(row,col);if(nextRow!=-1){if(this.data[row][col]==0){this.data[row][col]=this.data[nextRow][col];this.data[nextRow][col]=0;row--;}else if(this.data[row][col]==this.data[nextRow][col]){this.data[row][col]*=2;this.data[nextRow][col]=0;this.score+=this.data[row][col];}}else{break;}}},//3.1.moveUpNum:获取符合条件的列数moveUpNum:function(row,col){for(var i=row+1;i<4;i++){if(this.data[i][col]!=0) return i;else return 0;}},/**4.下移**///4.3.moveDown方法:向下移动moveDown:function(){var before=String(this.data);for(var col=0;col<4;col++){this.moveDowninCol(col);}var after=String(this.data);if(before!=after){this.randomNum();if(this.isGameOver()) this.status=this.gameover;this.dataView();}},//4.2.moveDowninCol方法:处理每一列所有行数的数据moveDowninCol:function(col){for(var row=3;row>=0;row--){var nextRow=this.moveDownNum(row,col);if(nextRow!=-1){if(this.data[row][col]==0){this.data[row][col]=this.data[nextRow][col];this.data[nextRow][col]=0;row++;}else if(this.data[row][col]==this.data[nextRow][col]){this.data[row][col]*=2;this.data[nextRow][col]=0;this.score+=this.data[row][col];}}else{break;}}},//4.1.moveDownNum方法:获取符合条件的行数moveDownNum:function(row,col){// 如果当前元素不为空,就返回ifor(var i=row-1;i>=0;i--){if(this.data[i][col]!=0) return i;else return -1;}}
};
/*二.监听鼠标按下事件,并调用game对象中的移动方法*/
//如果监听到上下左右对应的ASCII码,就触发相应的上下左右game移动方法
document.onkeydown=function(event){if(event.keyCode==65) game.moveLeft();if(event.keyCode==87) game.moveUp();if(event.keyCode==68) game.moveRight();if(event.keyCode==83) game.moveDown();
}
/*三.游戏开始时,调用game对象中的start方法*/
game.start();
/*四.点击再来一次时*/
//隐藏#gameover元素,并跳转回2048.html
function typeonce(){document.getElementById("gameover").style.display="none";window.location.href="2048.html";
}

四.效果

在这里插入图片描述