jQuery编写网页版2048小游戏


Posted in Javascript onJanuary 06, 2017

大致介绍

看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了,但是自己实现起来会遇到各种问题。比如,在最后判断游戏是否结束的时候,我写的语句语法是对的,但就是不执行。最后通过对视频源码的分析对比,发现原作者写的一个setTimeout定时器有额外的意思,本来我以为它就是简单的一个延时动画,其实他是在等待另外一个函数执行完毕。-_-||。最后还是很高兴能写出来,也改进了一些源代码的不足。

这篇博客并不是详细的讲解,只是大致介绍函数的作用,其中实现的细节注释中有解释,网上的这个源码有点乱,如果想看比较整齐的源码或者视频的可以QQ联系我(免费)(找共同学习的伙伴)

思路

这个小游戏可以抽象化分为3层(我觉得这样能更好理解)

◆最底下的一层是基本的样式(可见的)

◆中间的层是最主要的,是一个4x4的二维数组,游戏中我们都是对这个二维数组进行操作(不可见的)

◆最上面的一层也是一个4x4的二维数组,它只是根据第二层数组的每个数显示样式(可见的)

我们通过最底下的一层显示最基本的16个小方格,通过键盘的按键或者手指在屏幕的滑动来操作中间层的数组,最后在通过最上面的一层显示出数字

基本结构与样式

基本的结构和样式都挺简单,直接看代码

结构:

<div id="test2048">
 <div id="header">
  <h1>2048</h1>
  <a href="javascript:newgame()" >开始新的游戏</a>
  <p>分数:<span id="score">0</span></p>
 </div>
 <div id="container">
  <div class="cell" id="cell-0-0"></div>
  <div class="cell" id="cell-0-1"></div>
  <div class="cell" id="cell-0-2"></div>
  <div class="cell" id="cell-0-3"></div>
  <div class="cell" id="cell-1-0"></div>
  <div class="cell" id="cell-1-1"></div>
  <div class="cell" id="cell-1-2"></div>
  <div class="cell" id="cell-1-3"></div>
  <div class="cell" id="cell-2-0"></div>
  <div class="cell" id="cell-2-1"></div>
  <div class="cell" id="cell-2-2"></div>
  <div class="cell" id="cell-2-3"></div>
  <div class="cell" id="cell-3-0"></div>
  <div class="cell" id="cell-3-1"></div>
  <div class="cell" id="cell-3-2"></div>
  <div class="cell" id="cell-3-3"></div>
 </div>
 </div>

样式:

*{
 margin: 0;
 padding: 0;
}
#test2048{
 font-family: Arial;
 margin: 0 auto;
 text-align: center;
}
#header{
 margin: 20px;
}
#header a{
 font-family: Arial;
 text-decoration: none;
 display: block;
 color: white;
 margin: 20px auto;
 width: 125px;
 height: 35px;
 text-align: center;
 line-height: 40px;
 background-color: #8f7a66;
 border-radius: 10px;
 font-size: 15px;
}
#header p{
 font-family: Arial;
 font-size: 20px;
}
#container{
 width: 460px;
 height: 460px;
 background-color: #bbada0;
 margin: 0 auto;
 border-radius: 10px;
 position: relative;
 padding: 20px;
}
.cell{
 width: 100px;
 height: 100px;
 border-radius: 6px;
 background-color: #ccc0b3;
 position: absolute;
}

从CSS样式可以看出,我们并没有对每个格子的位置进行设置,因为如果用CSS给每个格子设置样式代码量太大,而且他们的位置有一定的规律,所以我们可以用js循环来完成每个格子样式的设置

代码:

// 初始化棋盘格
function initialize(){
 for(var i=0;i<4;i++){
  for(var j=0;j<4;j++){
   // 设置棋盘格的位置
   var everyCell = $('#cell-'+ i +'-'+ j);
   everyCell.css({top:getPos(i),left:getPos(j)});
  }
 }
}
// 获取位置
function getPos(num){
 return 20 + num*120;
}

这样我们的第一层就好了

效果:

 jQuery编写网页版2048小游戏

现在构造第二层,即构建一个4x4的值全部为0的数组,由于在构造第二层时,有两层循环,所以我们可以在构造第一层时也能构造第二层

第三层是用js生成16个格子,它和第一层的16个格子一一对应

代码:

// 数字格
function numFormat(){
 for(var i=0;i<4;i++){
  for(var j=0;j<4;j++){
   $('#container').append('<div class="number" id="number-'+ i +'-'+ j +'"></div>')
   // 设置数字格的位置,样式
   var everyNumber = $('#number-'+ i +'-'+ j);
   if(checkerboard[i][j] == 0){
    everyNumber.css({
     width:'0px',
     height:'opx',
     top:getPos(i) + 50,
     left:getPos(j) + 50
    })
   }else{
    everyNumber.css({
     width:'100px',
     height:'100px',
     top:getPos(i),
     left:getPos(j),
backgroundColor:getBackgroundColor(checkerboard[i][j]),
     color:getColor(checkerboard[i][j])
    });
    everyNumber.text(checkerboard[i][j]);
   }
  }
 }
}
// 获取相应数字的背景颜色
function getBackgroundColor(number){
 switch (number) {
  case 2:return "#eee4da";break;
  case 4:return "#ede0c8";break;
  case 8:return "#f2b179";break;
  case 16:return "#f59563";break;
  case 32:return "#f67c5f";break;
  case 64:return "#f65e3b";break;
  case 128:return "#edcf72";break;
  case 256:return "#edcc61";break;
  case 512:return "#9c0";break;
  case 1024:return "#33b5e5";break;
  case 2048:return "#09c";break;
  case 4096:return "#a6c";break;
  case 8192:return "#93c";break;
 }
}
// 设置相应数字的文字颜色
function getColor(number){
 if (number <= 4) {
  return "#776e65"
 }
 return "white";
}

初始化

在每次游戏重新开始时,都会在随机的位置出现两个随机的数字,我们写一个在随机位置出现一个随机数的函数,只要调用两次就可以实现了

代码:

// 随机的在一个位置上产生一个数字
function randomNum(){
 // 随机产生一个坐标值
 var randomX = Math.floor(Math.random() * 4);
 var randomY = Math.floor(Math.random() * 4);
 // 随机产生一个数字(2或4)
 var randomValue = Math.random() > 0.5 ? 2 : 4;
 // 在数字格不为0的地方生成一个随机数字
 while(true){
  if(checkerboard[randomX][randomY] == 0){
   break;
  }else{
   var randomX = Math.floor(Math.random() * 4);
   var randomY = Math.floor(Math.random() * 4);
  }
 }
 // 将随机产生的数字显示在随机的位置上
 checkerboard[randomX][randomY] = randomValue;
 // 动画
 randomNumAnimate(randomX,randomY,randomValue);
}
// 随机产生数字的动画
function randomNumAnimate(randomX,randomY,randomValue){
 var randomnum = $('#number-'+ randomX +'-'+ randomY);
 randomnum.css({
  backgroundColor:getBackgroundColor(randomValue),
  color:getColor(randomValue),
 })
    .text(randomValue)
    .animate({
     width:'100px',
     height:'100px',
     top:getPos(randomX),
     left:getPos(randomY)
    },50);
}

基本操作

我们通过switch循环,来根据用户不同的输入进行不同的操作

代码:

// 获取键盘事件,检测不同的按键进行不同的操作
$(document).keydown(function(event){
 switch(event.keyCode){
  case 37://左
   if(canMoveLeft(checkerboard)){
    // 如果可以向左移动
    MoveLeft();
    // 向左移动
    setTimeout(function(){
     randomNum();
    },200);
    // 随机产生一个数字
   }
   break;
  case 38://上
   if(canMoveUp(checkerboard)){
    // 如果可以向上移动
    MoveUp();
    // 向上移动
    setTimeout(function(){
     randomNum();
    },200);
    // 随机产生一个数字
   }
   break;
  case 39://右
   if(canMoveRight(checkerboard)){
    // 如果可以向右移动
    MoveRight();
    // 向右移动
    setTimeout(function(){
     randomNum();
    },200);
    // 随机产生一个数字
   }
   break;
  case 40://下
   if(canMoveDown(checkerboard)){
    // 如果可以向下移动
    MoveDown();
    // 向下移动
    setTimeout(function(){
     randomNum();
    },200);
    // 随机产生一个数字
   }
   break;
  default:
   break;
 }
});

由于数字格的移动只有左、上、右、下四种方式,并且他们都是大同小异的,所以就拿向左移动为例,

向左移动,我们首先需要判断它是否能向左移动,能向左移动有两种情况

第一种:当前格子的左边的格子是空的即值为0

第二种:当前格子的值和左边格子的值相同

由于向左移动,所以第一列的格子不可能向左移动,所以不需要判断

代码:

// 判断是否可以向左移动
function canMoveLeft(checkerboard){
 for(var i=0;i<4;i++){
  for(var j=1;j<4;j++){
   if(checkerboard[i][j] != 0){
    // 如果这个数字格它左边的数字格为空或者左边的数字格和它相等,则可以向左移动
    if(checkerboard[i][j-1] == 0 || checkerboard[i][j] == checkerboard[i][j-1]){
     return true;
    }
   }
  }
 }
 return false;
}

判断能否向左移动后,我们就要对可以移动的格子进行移动,这里需要特别注意,向哪个方向移动就要先从哪个方向开始判断

代码:

// 向左移动
function MoveLeft(){
 for(var i=0;i<4;i++){
  for(var j=1;j<4;j++){
   if(checkerboard[i][j] != 0){
    for(var k=0;k<j;k++){
     if(checkerboard[i][k] == 0 && noMiddleNumRow(i,k,j,checkerboard)){
      moveAnimation(i,j,i,k);
      checkerboard[i][k] = checkerboard[i][j];
      checkerboard[i][j] = 0;
     }else if(checkerboard[i][k] == checkerboard[i][j] && noMiddleNumRow(i,k,j,checkerboard) && !hasConflicted[i][k]){
      moveAnimation(i,j,i,k);
      checkerboard[i][k] += checkerboard[i][j];
      checkerboard[i][j] = 0;
     }
    }
   }
  }
 }
 // 设置刷新的时间是为了让运动的动画走完在进行更新数字格,否则数字格运动的动画将会被打断
 setTimeout(function(){
  numFormat();
 },200);
}
// 判断中间的数字格是否为0(行)
function noMiddleNumRow(row,col1,col2,checkerboard){
 for(var i=col1+1;i<col2;i++){
  if(checkerboard[row][i] != 0){
   return false;
  }
 }
 return true;
}

将上、右、下四个方向写完以后,游戏基本的操作就已经完成了。

游戏分数和判断游戏结束

游戏的分数是每个相加的数的和,所以我们在每个数相加的时候更新分数就可以了

代码:

// 更新分数
score += checkerboard[k][j];
updateScore(score);
// 设置分数
function updateScore(num){
 $('#score').text(num);
}

判断游戏是否结束很简单,用我们之前定义的方法就可以实现

代码:

// 判断游戏是否结束
function wheGameOver(checkerboard){
 if(!canMoveLeft(checkerboard) && !canMoveUp(checkerboard) && !canMoveRight(checkerboard) && !canMoveDown(checkerboard) ){
  showGameOver();
 }
}
// 显示游戏结束
function showGameOver(){
 $('#container').append("<div id='gameover'><p>最终得分</p><span>"+ score +"</span><a href='javascript:resert();'>重新开始游戏</a></div> ")
}
// 重新开始游戏
function resert(){
 $('#gameover').remove();
 newgame();
}

最后优化

1、游戏中会出现一次移动,一个数会被累加很多次

在原游戏中,每个数在每次操作中只能累加一次,所以我们在定义一个4x4的值为false的数组,与中间层的数组一一对应,专门用来防止一个数的多次累加,如果是false则可以累加,并将值改为false,否则不可以累加

2、结束死循环

由于在设置随机数的时候用到了一个死循环,但是在游戏结束后,该循环还在,所以我们在死循环中在添加一个条件,如果游戏结束就跳出循环

3、最后的结束游戏提示不执行

case 37://左
   if(canMoveLeft(checkerboard)){
    // 如果可以向左移动
    MoveLeft();
    // 向左移动
    setTimeout(function(){
     wheGameOver(checkerboard)
    },300);
    // 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加
    // 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了
    setTimeout(function(){
     randomNum();
    },200);
    // 随机产生一个数字
   }
   break;

从代码中可以看出,判断游戏是否结束是在随机产生一个数字前执行的,所以在判断游戏结束时,总是有一个空的格子,所以代码执行后认为游戏没有结束,但是当这个随机数字产生后,所有的格子不能移动,当我们按键时,if条件不通过,判断游戏是否结束的函数不能执行。所以我们要给判断游戏结束的函数设置定时器,让他在随机产生一个数字后再进行判断

4、在移动端可以执行

由于原作者没有写有关移动端的操作,所以我在网上找的判断移动端触屏手机滑动位置的代码,加入了游戏的事件就可以执行了

//返回角度 
   function GetSlideAngle(dx, dy) { 
    return Math.atan2(dy, dx) * 180 / Math.PI; 
   } 
   //根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动 
   function GetSlideDirection(startX, startY, endX, endY) { 
    var dy = startY - endY; 
    var dx = endX - startX; 
    varresult = 0; 
    //如果滑动距离太短 
    if(Math.abs(dx) < 2 && Math.abs(dy) < 2) { 
     returnresult; 
    } 
    var angle = GetSlideAngle(dx, dy); 
    if(angle >= -45 && angle < 45) { 
     result = 4; 
    }else if (angle >= 45 && angle < 135) { 
     result = 1; 
    }else if (angle >= -135 && angle < -45) { 
     result = 2; 
    } 
    else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) { 
     result = 3; 
    } 
    return result; 
   } 
   //滑动处理 
   var startX, startY; 
   document.addEventListener('touchstart',function (ev) { 
    startX = ev.touches[0].pageX; 
    startY = ev.touches[0].pageY; 
   }, false); 
   document.addEventListener('touchend',function (ev) { 
    var endX, endY; 
    endX = ev.changedTouches[0].pageX; 
    endY = ev.changedTouches[0].pageY; 
    var direction = GetSlideDirection(startX, startY, endX, endY); 
    switch(direction) { 
     case 0: 
      //没滑动 
      break; 
     case 1: 
      if(canMoveUp(checkerboard)){
      // 如果可以向上移动
      MoveUp();
      // 向上移动
      setTimeout(function(){
       wheGameOver(checkerboard)
      },300);
      // 判断游戏是否结束
      setTimeout(function(){
       randomNum();
      },200);
      // 随机产生一个数字
      } 
      break; 
     case 2: 
      if(canMoveDown(checkerboard)){
      // 如果可以向下移动
      MoveDown();
      // 向下移动
      setTimeout(function(){
       wheGameOver(checkerboard)
      },300);
      // 判断游戏是否结束
      setTimeout(function(){
       randomNum();
      },200);
      // 随机产生一个数字
      } 
      break; 
     case 3: 
      if(canMoveLeft(checkerboard)){
      // 如果可以向左移动
      MoveLeft();
      // 向左移动
      setTimeout(function(){
       wheGameOver(checkerboard)
      },300);
      // 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加
      // 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了
      setTimeout(function(){
       randomNum();
      },200);
      // 随机产生一个数字
      } 
      break; 
     case 4: 
      if(canMoveRight(checkerboard)){
      // 如果可以向右移动
      MoveRight();
      // 向右移动
      setTimeout(function(){
       wheGameOver(checkerboard)
      },300);
      // 判断游戏是否结束
      setTimeout(function(){
       randomNum();
      },200);
      // 随机产生一个数字
      } 
      break; 
     default:    
    } 
   }, false);

总结

总体来说这个游戏实现起来并不是太难,就是许多小的操作集合起来

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
更正确的asp冒泡排序
May 24 Javascript
js添加table的行和列 具体实现方法
Jul 22 Javascript
javascript实现修改微信分享的标题内容等
Dec 11 Javascript
JS和css实现检测移动设备方向的变化并判断横竖屏幕
May 25 Javascript
使用CDN和AJAX加速WordPress中jQuery的加载
Dec 05 Javascript
基于BootStrap Metronic开发框架经验小结【一】框架总览及菜单模块的处理
May 12 Javascript
socket.io学习教程之基本应用(二)
Apr 29 Javascript
vue2.0实现分页组件的实例代码
Jun 22 Javascript
vue的一个分页组件的示例代码
Dec 25 Javascript
npm 常用命令详解(小结)
Jan 17 Javascript
微信小程序wx.request拦截器使用详解
Jul 09 Javascript
js实现简单五子棋游戏
May 28 Javascript
利用JQuery实现datatables插件的增加和删除行功能
Jan 06 #Javascript
javascript正则表达式模糊匹配IP地址功能示例
Jan 06 #Javascript
bootstrap导航栏、下拉菜单、表单的简单应用实例解析
Jan 06 #Javascript
使用BootStrap进行轮播图的制作
Jan 06 #Javascript
BootStrap便签页的简单应用
Jan 06 #Javascript
bootstrap table实例详解
Jan 06 #Javascript
node.js发送邮件email的方法详解
Jan 06 #Javascript
You might like
简单了解WordPress开发中update_option()函数的用法
2016/01/11 PHP
解决php extension 加载顺序问题
2019/08/16 PHP
Aliyun Linux 编译安装 php7.3 tengine2.3.2 mysql8.0 redis5的过程详解
2020/10/20 PHP
仿中关村在线首页弹出式广告插件(jQuery版)
2012/05/03 Javascript
JavaScript 操作table,可以新增行和列并且隔一行换背景色代码分享
2013/07/05 Javascript
jquery实现的美女拼图游戏实例
2015/05/04 Javascript
跟我学习javascript的作用域与作用域链
2015/11/19 Javascript
微信小程序 地图定位简单实例
2016/10/14 Javascript
javascript实现根据函数名称字符串动态执行函数的方法示例
2016/12/28 Javascript
vue.js从安装到搭建过程详解
2017/03/17 Javascript
vue2.0构建单页应用最佳实战
2017/04/01 Javascript
Vuex 进阶之模块化组织详解
2018/01/12 Javascript
详解Vue单元测试Karma+Mocha学习笔记
2018/01/31 Javascript
在vue中实现点击选择框阻止弹出层消失的方法
2018/09/15 Javascript
vue vantUI tab切换时 list组件不触发load事件的问题及解决方法
2020/02/14 Javascript
[01:01:41]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Magma BO3 第二场 1月31日
2021/03/11 DOTA
python两种遍历字典(dict)的方法比较
2014/05/29 Python
Python实现简单的语音识别系统
2017/12/13 Python
python pandas 对时间序列文件处理的实例
2018/06/22 Python
网易2016研发工程师编程题 奖学金(python)
2019/06/19 Python
pyqt5 QProgressBar清空进度条的实例
2019/06/21 Python
python使用tomorrow实现多线程的例子
2019/07/20 Python
Python while循环使用else语句代码实例
2020/02/07 Python
Python+Kepler.gl轻松制作酷炫路径动画的实现示例
2020/06/02 Python
Python 绘制可视化折线图
2020/07/22 Python
python3 中使用urllib问题以及urllib详解
2020/08/03 Python
python中的插入排序的简单用法
2021/01/19 Python
英国复古服装和球衣购买网站:3Retro Football
2018/07/09 全球购物
大学生咖啡店创业计划书
2014/01/21 职场文书
领导干部培训感言
2014/01/23 职场文书
2014最新开业庆典策划方案(5篇)
2014/09/15 职场文书
医院见习报告范文
2014/11/03 职场文书
员工离职感谢信
2015/01/22 职场文书
2015年医务人员医德医风自我评价
2015/03/03 职场文书
2015年乡镇统计工作总结
2015/04/22 职场文书
2015年小学数学教研组工作总结
2015/05/21 职场文书