【JavaScript学习笔记】用js实现2048小游戏

360前端星技术笔试中的一道题目,现把解决过程记录如下:

1、首先是界面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>2048</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="grid-container">
    <div class="grid-row">
      <div class="grid-cell">
        <div id="num00"></div>
      </div>
      <div class="grid-cell">
        <div id="num01"></div>
      </div>
      <div class="grid-cell">
        <div id="num02"></div>
      </div>
      <div class="grid-cell">
        <div id="num03"></div>
      </div>
    </div>
    <div class="grid-row">
      <div class="grid-cell">
        <div id="num10"></div>
      </div>
      <div class="grid-cell">
        <div id="num11"></div>
      </div>
      <div class="grid-cell">
        <div id="num12"></div>
      </div>
      <div class="grid-cell">
        <div id="num13"></div>
      </div>
    </div>
    <div class="grid-row">
      <div class="grid-cell">
        <div id="num20"></div>
      </div>
      <div class="grid-cell">
        <div id="num21"></div>
      </div>
      <div class="grid-cell">
        <div id="num22"></div>
      </div>
      <div class="grid-cell">
        <div id="num23"></div>
      </div>
    </div>
    <div class="grid-row">
      <div class="grid-cell">
        <div id="num30"></div>
      </div>
      <div class="grid-cell">
        <div id="num31"></div>
      </div>
      <div class="grid-cell">
        <div id="num32"></div>
      </div>
      <div class="grid-cell">
        <div id="num33"></div>
      </div>
    </div>
  </div>
</body>
</html>

CSS中设置了4个类,当方格内的数字分别为个位、十位、百位、千位(最大值2048)时,通过添加不同的类,让数字能完整的放置在方格内。

.grid-container {
  margin: auto;
  width: 400px;
  height: 400px;
  background-color: rgb(220, 160, 20);
}

.grid-row {
  display: block;
  padding-left: 8px;
}

.grid-row:first-child {
  padding-top: 8px;
}

.grid-cell {
  /* display: inline-block; */
  position: relative;
  float: left;
  margin: 7px;
  width: 82px;
  height: 82px;
  background-color: rgb(230, 210, 170);
  text-align: center;
}

.numOne {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 35%;
  font-size: 48px;
}

.numTwo {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 16%;
  font-size: 48px;
}

.numThree {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 9%;
  font-size: 38px;
}

.numFour {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 9%;
  font-size: 28px;
}


2、存放游戏状态

let gameState = [
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0]
];

通过数组存放4乘4方格内的数字,0代表方格内数字为空。

3、初始化

(function () {
  const initBlockCount = 3;
  let initCount = 0;
  while (initCount < 2) {
    let randIndex = Math.floor(Math.random()*9);
    if (gameState[parseInt(randIndex/3)][randIndex%3] === 0) {
      let randNum = Math.random() > 0.5 ? 2 : 4;
      gameState[parseInt(randIndex/3)][randIndex%3] = randNum;
      initCount++;
    }
  }
  updateUI();
})();

随机将4乘4方格中的某2个方格的值设为2或4。

4、监听按键

$('body').on('keydown', function (e) {
  switch (e.which) {
    case 37: //左
    case 65:
      left();
      break;
    case 38: //上
    case 87:
      up();
      break;
    case 39: //右
    case 68:
      right();
      break;
    case 40: //下
    case 83:
      down();
      break;
  }
});

监听上下左右及WSAD键按下。

5、改变游戏状态

以按下向左或A键为例:

function left () {
  for (let i = 0; i < 4; i++) {
    let zeroIndex = -1;
    for (let j = 0; j < 4; j++) {
      if (zeroIndex === -1) {
        if (gameState[i][j] === 0) {
          zeroIndex = j;
        }
      }else{
        if (gameState[i][j] !== 0) {
          gameState[i][zeroIndex] = gameState[i][j];
          gameState[i][j] = 0;
          zeroIndex++;
        }
      }
    }
  }
  mergeLeft();
  initRight();
  updateUI();
}

首先每一行从左向右遍历,找到第一个为0的位置(i, zeroIndex),然后找到zeroIndex后第一个不为0的位置(i, j),将(i, zeroIndex)的值替换为(i, j)位置的值,并将(i, j)位置的值归0,之后继续将后面非0的元素插入到zeroIndex+n位置(n是已插入元素数)。这一部分相当于将每一行的元素“左对齐”。

之后需要检查每一行“对齐”后的数列中最后两元素是否相同,相同则合并,这部分放在mergeLeft()中。

function mergeLeft() {
  for (let i = 0; i < 4; i++) {
    let zeroIndex = gameState[i].indexOf(0)===-1 ? 4 : gameState[i].indexOf(0);
    if (zeroIndex > 1 && gameState[i][zeroIndex-1] === gameState[i][zeroIndex-2]) {
      gameState[i][zeroIndex-1] = 0;
      gameState[i][zeroIndex-2] *= 2;
    }
  }
}

之后要从最右边一列随机生成1-2个新元素。当最右边一列被占满无法生成新元素时,则判定游戏结束。并且为避免每次都在第一位生成新元素,适当调低一些生成概率。

function initRight() {
  let nullBlock = 0;
  for (let i = 0; i < 4; i++) {
    if (gameState[i][3] === 0) {
      if (Math.random() > 0.3 && nullBlock < 2) { //调低一点,避免都产生在第一位
        gameState[i][3] = Math.random() > 0.5 ? 2 : 4;
        nullBlock++;
      }
    }
  }
  if (nullBlock === 0) {
    gameOver();
  }
}

向上,向右,向下的代码部分如下。

function  up() {
  for (let j = 0; j < 4; j++) {
    let zeroIndex = -1;
    for (let i = 0; i < 4; i++) {
      if (zeroIndex === -1) {
        if (gameState[i][j] === 0) {
          zeroIndex = i;
        }
      }else{
        if (gameState[i][j] !== 0) {
          gameState[zeroIndex][j] = gameState[i][j];
          gameState[i][j] = 0;
          zeroIndex++;
        }
      }
    }
  }
  mergeUp();
  initDown();
  updateUI();
}

function mergeUp() {
  for (let j = 0; j < 4; j++) {
    for (let i = 0; i < 4; i++) {
      if (gameState[i][j] === 0) {
        if (i > 1 && gameState[i-1][j] === gameState[i-2][j]) {
          gameState[i-1][j] = 0;
          gameState[i-2][j] *= 2;
        }
        break;
      }else if (i === 3) {
        if (gameState[3][j] === gameState[2][j]) {
          gameState[3][j] = 0;
          gameState[2][j] *= 2;
        }
      }
    }
  }
}

function initDown() {
  let nullBlock = 0;
  for (let i = 0; i < 4; i++) {
    if (gameState[3][i] === 0) {
      if (Math.random() > 0.3 && nullBlock < 2) {
        gameState[3][i] = Math.random() > 0.5 ? 2 : 4;
        nullBlock++;
      }
    }
  }
  if (nullBlock === 0) {
    gameOver();
  }
}

function right () {
  for (let i = 0; i < 4; i++) {
    let zeroIndex = -1;
    for (let j = 3; j >= 0; j--) {
      if (zeroIndex === -1) {
        if (gameState[i][j] === 0) {
          zeroIndex = j;
        }
      }else{
        if (gameState[i][j] !== 0) {
          gameState[i][zeroIndex] = gameState[i][j];
          gameState[i][j] = 0;
          zeroIndex--;
        }
      }
    }
  }
  mergeRight();
  initLeft();
  updateUI();
}

function mergeRight() {
  for (let i = 0; i < 4; i++) {
    let zeroIndex = -1;
    for (let j = 3; j >= 0; j--) {
      if (gameState[i][j] === 0) {
        zeroIndex = j;
        break;
      }
    }
    if (zeroIndex < 2 && gameState[i][zeroIndex+1] === gameState[i][zeroIndex+2]) {
      gameState[i][zeroIndex+1] = 0;
      gameState[i][zeroIndex+2] *= 2;
    }
  }
}

function initLeft() {
  let nullBlock = 0;
  for (let i = 0; i < 4; i++) {
    if (gameState[i][0] === 0) {
      if (Math.random() > 0.3 && nullBlock < 2) { //调低一点,避免都产生在第一位
        gameState[i][0] = Math.random() > 0.5 ? 2 : 4;
        nullBlock++;
      }
    }
  }
  if (nullBlock === 0) {
    gameOver();
  }
}

function  down() {
  for (let j = 0; j < 4; j++) {
    let zeroIndex = -1;
    for (let i = 3; i >= 0; i--) {
      if (zeroIndex === -1) {
        if (gameState[i][j] === 0) {
          zeroIndex = i;
        }
      }else{
        if (gameState[i][j] !== 0) {
          gameState[zeroIndex][j] = gameState[i][j];
          gameState[i][j] = 0;
          zeroIndex--;
        }
      }
    }
  }
  mergeDown();
  initUp();
  updateUI();
}

function mergeDown() {
  for (let j = 0; j < 4; j++) {
    for (let i = 3; i >= 0; i--) {
      if (gameState[i][j] === 0) {
        if (i < 2 && gameState[i+1][j] === gameState[i+2][j]) {
          gameState[i+1][j] = 0;
          gameState[i+2][j] *= 2;
        }
        break;
      }else if (i === 0) {
        if (gameState[1][j] === gameState[0][j]) {
          gameState[1][j] = 0;
          gameState[0][j] *= 2;
        }
      }
    }
  }
}

function initUp() {
  let nullBlock = 0;
  for (let i = 0; i < 4; i++) {
    if (gameState[0][i] === 0) {
      if (Math.random() > 0.3 && nullBlock < 2) {
        gameState[0][i] = Math.random() > 0.5 ? 2 : 4;
        nullBlock++;
      }
    }
  }
  if (nullBlock === 0) {
    gameOver();
  }
}

6、更新游戏界面

根据游戏状态数组更新界面。

function updateUI() {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j< 4; j++) {
      if (gameState[i][j] !== 0) {
        let id = '#num' + i + j;
        $(id).removeClass();
        if (gameState[i][j]/1000 > 1) {
          $(id).addClass('numFout');
        }else if (gameState[i][j]/100 > 1) {
          $(id).addClass('numThree');
        }else if (gameState[i][j]/10 > 1) {
          $(id).addClass('numTwo');
        }else{
          $(id).addClass('numOne');
        }
        $(id).text(gameState[i][j]);
      }else{
        let id = '#num' + i + j;
        $(id).text('');
      }
    }
  }
}

7、合并逻辑的BUG

之前只考虑到空元素前两个元素需要合并,未考虑到游戏进行过程中应该是任意与按键相同方向的两相同元素都可以合并。例如下图第二行第一个元素(1, 0)与第三行第一个元素(2, 0)均为16,若此时按下向上或向下键后,两位置元素应合并。

改进:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JavaScript2048小游戏实现是通过使用JavaScript编写代码来创建一个2048游戏的网页应用程序。该游戏的核心逻辑是通过将相同数字的方块合并来达到2048的目标。在实现过程中,需要使用HTML和CSS来创建游戏的界面,并使用JavaScript来处理游戏的逻辑和交互。具体实现过程需要涉及到数组操作、事件监听、动画效果等技术。 ### 回答2: 2048游戏是一款数字类益智小游戏,玩法非常简单,就是将相同数字的方块进行合并直到得到数字2048实现这个游戏可以使用几乎所有编程语言,因为其界面相对简单,逻辑也比较简单。其中,用JavaScript实现2048游戏也很简单。 JavaScript是一种弱类型的脚本语言,由于其在浏览器中实现,当前web应用程序和移动应用程序开发已经离不开JavaScript了。可以通过JavaScript DOM编程来容易地操纵HTML文档的内容,属性和样式,并且更加灵活自由地控制页面的交互和效果。 HTML5的canvas提供了非常方便的绘图功能,可以很容易地在web页面中绘制图形和游戏效果。我们可以使用canvas元素把游戏的界面绘制出来,并通过JavaScript代码来实现游戏的逻辑。我们可以通过监听键盘事件来控制方块的移动,并且配合进行相应的位置合并。 游戏中,我们需要有一个数据模型,来控制图形块的生成、移动和合并操作。因为游戏是一个可操作的实体,所以使用JavaScript中的面向对象编程,把游戏做成一个对象,可以更好地管理游戏的数据和状态。在游戏对象中,我们可以定义方法,包括渲染页面、绑定事件和状态更新等。 最后,我们可以把这个游戏发布在web服务器上,也可以打包成Hybrid App发布在各种移动应用商店中,玩家可以通过浏览器或者安装在手机中的游戏来体验这个有趣的小游戏。 ### 回答3: JavaScript2048是一款智力益智类小游戏,玩法简单,玩家需要将数字方块合并组成2048这个数字方块。下面将详细介绍JavaScript2048实现过程。 1.界面设计:通过HTML和CSS来完成游戏的界面设计。使用div标签来创建游戏面板,使用CSS进行样式设置,添加游戏背景图案和数字方块样式。 2.生成随机数字:使用JavaScript随机生成2或4的数字方块,将其按照一定规则添加到游戏面板上。 3.移动合并数字方块:通过监听键盘事件,根据不同的按键进行数字方块的移动和合并。当数字方块移动时,需要判断当前位置是否为空,且左侧是否有相同数字,如果为空或是相邻数字相同,就将其合并。 4.判定游戏结束:当游戏中没有空位置,也没有可以合并的相邻数字时即判定游戏结束。 5.添加游戏功能:增加游戏分数统计,最高分数记录,撤销上一次操作等游戏功能。 总之,JavaScript2048小游戏实现离不开HTML、CSS和JavaScript语言,需要对前端基础知识有一定的了解。要完成一个完整的游戏还需要思维逻辑和综合能力,需要不断尝试和优化,才能得到一个优秀的JavaScript2048小游戏

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值