2048,设计思路,以及一点感想

在网上看了别人的代码,写得挺浅显易懂的,自己整合了下思路,重新写了一个,目前还是普通版,后续有时间再改造下游戏模式 😊
体验室传送门:2048
代码仓库传送门:源码


游戏逻辑

设置按键事件监听,根据keyCode判断移动方向。(截取部分代码分析,【】内标记问题在代码段后解释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
move: function (direct) { // direct为移动方向,1:左;2:右;3:上;4:下
this.direct = direct;
var oldData, newData; // oldData, newData分别保存移动前和移动后的数据信息
oldData = this.data.toString();
switch (this.direct) {
case 1: // 按下左方向键
for (var i = 0; i < this.rowNum; i++) { // 遍历各行【问题1】
for (var j = 0; j < this.colNum-1; j++) { // 遍历除最后一列的所有列【问题2】
var nextNotNull = this.getNextNum(i, j); // 获得当前被访问元素后不为0的数的列下标,该数不存在时返回-1【问题3】
if (nextNotNull == -1) { // 该数不存在,跳出循环【问题4】
break;
} else { // 该数存在
if (this.data[i][j] == 0) { // 如果当前被访问元素的值为0
this.data[i][j] = this.data[i][nextNotNull]; // 将当前被访问元素后不为0的数赋给当前被访问元素
this.data[i][nextNotNull] = 0; // 清除当前被访问元素后不为0的数
j--; // 【问题5】
} else if (this.data[i][nextNotNull] == this.data[i][j]) {
this.changeData(i,j,nextNotNull); // 【问题6】
}
}
}
}
break;
case 2:
// 代码省略,与1的区别在于在第二轮循环内,获得当前被访问元素后不为0的数的列下标从当前被访问元素开始向前搜索
break;
case 3:
// 代码省略,与1的区别在于在第一轮循环为列,第二轮为行
break;
case 4:
// 代码省略,与3的区别在于在第二轮循环内,获得当前被访问元素后不为0的数的行下标从当前被访问元素开始向前搜索
break;
}
newData = this.data.toString();
if (newData != oldData) { // 判断当前方向的所有方块是否可移动
this.randomNum(); // 随机产生一个新的方块
this.updateView(); // 更新视图【问题6】
}
}

【问题1】

  • A: 水平方向(按下左右键)先遍历行再遍历列,垂直方向(按下上下方向键)先遍历列再遍历行。对水平方向的移动而言,列与列之间的数据相互影响,而不同行的列数据不影响

【问题2】为什么不遍历所有列?

  • A: 可以选择遍历所有列,但不都进行遍历更好。在后面的步骤中有求当前遍历元素后不为0的数的下标的算法,该算法能访问到最后一个元素。

【问题3】getNextNum()方法?

  • A: 返回当前被访问元素后不为0的数的列下标,若无满足条件的数返回-1.

【问题4】能否省略nextNotNull == -1的判断

  • A: 可以。在for循环中break的作用是跳出循环,若该判断为真,可知当前遍历元素不会和后面的数字进行计算,因而直接跳出循环效果更好。

【问题5】j–?

  • A: 若能进行到此步,说明该位置原先的数值为0,现被后面第一个不为0的数取代,即该位置上的数并没有与后面的数计算过,因而需要重新访问此位置。

【问题6】changeData()方法?

  • A: 若能进行到此步,说明该位置原先的数值与后面第一个不为0的数值相等,即该数被计算了一次。

游戏设计

定义一个game对象。 其属性有:

  • data:数组类型。保存棋盘上方块数值,如data[i][j]表示棋盘第i-1行j-1列上的方块数值。
  • rowNum、colNum:数值类型。棋盘的规格,默认为4*4棋盘。
  • direct:数值类型。记录按键方向。1为向左,2为向右,3为向上,4位向下。
  • score:数值类型。记录当前得分。
  • RUNNING、GAMEOVER:布尔类型。
  • state:游戏状态。状态为RUNNING表示游戏正在进行,GAMEOVER表示游戏结束。

其方法有:

  • isFull():判断棋盘上是否所有位置都被填满。返回值为布尔类型,若是返回true。
  • isGameOver():判断棋盘是否无可移动的方块或已经出现数值2048。返回值为布尔类型,若是返回true;
  • start():开始游戏,清空棋盘原有数据,分数清零,然后随机产生两个方块。
  • randomNum():产生随机方块。
  • updateView():更新视图并判断游戏是否达到结束条件。
  • changeData(i, j, next):根据方块的移动组合条件改变方块数值和游戏分数。
  • getNextNum(rowIndex, colIndex):返回当前被访问元素后不为0的数的列下标,若无满足条件的数返回-1。
  • move(direct):根据参数direct确定移动方向,具体步骤见游戏逻辑一节。

一点感想

看得多不如自己动手敲。看过去的知识会觉得很简单,因为难的已经自动被过滤掉了😕
而在真的动手coding时才会深切体会该是什么逻辑该用什么方法。突然想起一个词:唯手熟尔
还有就是不要急着去动手写代码,先大致清楚了需要实现什么功能有个大体的框架再一点一点去细化,慢慢完善。
以及,想到再说吧😯

文章目录
  1. 1. 游戏逻辑
  2. 2. 游戏设计
  3. 3. 一点感想
|