俄罗斯方块的逻辑稍微有些复杂,实现的方法也有很多,这里分享其中一种。
首先我们得有数据驱动的思维,把这个游戏抽象成两组数据:
方块活动的范围是一组数据,方块可以在这个范围向左、向右、向下移动,我们可以把这个范围标记为一个二维数组,类似于一个xy坐标轴:
gameAreaData: [
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0] ,
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0] ,
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ,
[2, 2, 2, 2, 2, 2, 2, 2, 2, 0]
…
]
没有方块的时候该坐标单位的值为0,有方块并且方块可以移动的时候值为1,方块不能移动的时候值为2。
方块有不同的形状,每种形状又可以旋转,同样可以用一个二维数组来保存这些形状,比如长条:
teris: {
origin: [
[1, 1, 1, 1]
],
rotate: [
[
[1, 1, 1, 1]
],
[
[1],
[1],
[1],
[1]
],
[
[1, 1, 1, 1]
],
[
[1],
[1],
[1],
[1]
]
]
…
}
另外方块在移动时,我们需要知道方块移动的位置,是向下移动一个单位,还是向右/向左移动了一个单位。我们可以用一个对象来保存当前位置:
position: {
x: 0,
y: 0
};
现在两组数据我们都有了,要做的就是在方块位置移动的时候或者形状变化(旋转)的时候,把第二组数据的值赋给第一组数据:
function updateData() {
for (let i = 0; i < teris.origin.length; i++) {
for (let j = 0; j < teris.origin[0].length; j++) {
gameAreaData[teris.position.x + 1][teris.position.y + j] = teris.origin[i][j];
}
}
}
这样俄罗斯方块的基本逻辑我们就有了,现在可以来考虑一下代码结构的问题。
首先七种方块由于形状不同,内部的数据也不同,我们可以把它们分成七个类:
class IBlock ;
class Square;
class RLblock;
class TBolck;
class Swagerly;
class RSwagerly;
class LinePiece;
操作这其中方块的方法是共享的,可以把这些方法放在一个共同的类里,并让这个类继承七种方块的属性(数据):
class Shapes() {
constructor: {
this.shapesData = [
lBlock,
Square,
RLblock,
TBolck,
Swagerly,
RSwagerly,
LinePiece
]
}
}
为了让Shapes每次被实例化的时候系统不必分配内存给它的方法,我们把这些方法写在原型链上:
class Shapes() {
constructor: {
this.shapesData = [
lBlock,
Square,
RLblock,
TBolck,
Swagerly,
RSwagerly,
LinePiece
]
}
}
Shapes.prototype.updateData = function() {
for (let i = 0; i < teris.origin.length; i++) {
for (let j = 0; j < teris.origin[0].length; j++) {
gameAreaData[teris.position.x + 1][teris.position.y + j] = teris.origin[i][j];
}
}
}
……
最后到游戏的具体操作部分,我们把这些操作都放在一个JS文件里。这一篇先讲解思路,下一篇再续上完整的实现过程。
网友评论