Switch branches/tags
Nothing to show
Clone or download
Latest commit 8390fd5 Feb 19, 2017
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
history channge name to tetris Apr 12, 2016
.editorconfig channge name to tetris Apr 12, 2016
README.md update Feb 19, 2017
index.html up Feb 19, 2017
tetris.css update Feb 19, 2017
tetris.js debug Feb 19, 2017

README.md

俄罗斯方块

体验一把demo

前端时间有空, 还是想找点事干, 正好那天听一个朋友说写一个 俄罗斯方块, 于是就想尝试一下. 经过两天的思考, 我终于在完全不借助外力的情况下想到了一个自己的实现方法. 当然, 如果网上早就有类似方法纯属巧合.

构思和操作

因为以前写过点DOM动画, 这次遇到俄罗斯方块自然也想到了碰撞检测, 让元素移动起来并不难, 难得是在碰撞时候的各种检测, 主要是当前下落方块和已有的复杂结构方块的碰撞, 因为当前下落方块还可以旋转位移. 简单的碰撞检测通常也就是找到元素的Bounding Box, 然后根据Bounding Box四边位置和边界或其他元素进行判断. 比如碰撞动画.

但是, 俄罗斯方块的形状比普通圆形或矩形复杂, 而且它包含的旋转中心也不同. 所以光是直接通过外形判断是否碰撞(尤其是形态不同的方块之间)比较麻烦.

俄罗斯方块是矩阵形式的, 为什么不可以用矩阵表示?

然后我想到了如下数据结构表示整个游戏舞台:

var stage = [
    // 总共20行, 10列
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    // ...
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]

方块矩阵, 如:

var block = [
    [0, 1, 0],
    [1, 1, 1]
]

方块走到哪里, 就和stage相加到哪里, 如果某个位置之前已经有方块存在, 则这个位置会>1, 因此可以判断方块之间发生了碰撞.

可以说, 想到这个方法, 整个俄罗斯方块已经相当于完成了1/3了.

但事情并没完...

既然想到了用矩阵, 为何不直接使用二进制数取代数组表示?

如果使用数组表示矩阵, 那么水平移动, 向下移动, 检测碰撞 会添加大量的代码. 直接用二进制数表示岂不是更方便? 我只需要添加一个pad函数, 即可将10进制数字展开成二进制数, 且前面补0. 比如pad(3, 10)得到0000000011 而且可以直接使用<<>>对数字进行移位, 如果使用数组表示还需要单独添加一大坨函数来做 使用数字表示舞台:

// 因为俄罗斯方块是20行
var stage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

方块例子:

var block = [32, 112];  // 因为方块默认是从舞台顶部中间位置出现

又会有问题...

上一步中block已经和stage对应行数相加(add函数实现)了, 但下一步动画block已经变了一格位置. 根据上面的<=1法则, 难道算是碰撞了? 然而方块并没有和其他方块或墙壁碰撞

所以, 我想到了一个mix函数, mix函数内部先执行minus, 然后进行某些操作, 最后再add.

中间的"某些操作" 可以是平移,向下移动... 如果不操作, 则下一步位置不变.

中间还需要判断方块在边界是否可变换, 方块在方块之间是否可变换....

最终每一步都得到一个修改后的stage都是最终要呈现的model, 然后将model直接投射到dom或者canvas(使用render), over!

遇到的坑

上面说的检测碰撞算是小坑.

检测边界稍微大点, 直接平移的话边界当然非常容易检测, 但一个奇葩的形状在边界还想变换呢? 有可能一部分就跑到边界之外了. 上面使用的是10个二进制数来表示一行, 在左边界自然可以通过判断是否>=1024, 因为第11位如果为1说明方块已经有一部分在左边界之外. 但右边界就不好弄了, 因为不管怎么向右平移得到都是0. 最终只能使用11或12位的二进制表示一行. 虽然展示的只有10列.

总结

  • 涉及到复杂碰撞的效果, 可以使用矩阵(3D空间可以扩展到三维矩阵), 如果矩阵直接相加大于指定的值, 说明已经碰撞.
  • 展示型的效果(不一定必须是游戏, 可以是一个动画, 展示面板等等), 虚拟舞台最好都比实际显示舞台大, 才方便检测各种效果. 不然坑大了...
  • 视图渲染和模型分离, model出来了, render方法无论呈现给DOM还是Canvas都已经非常简单了.