游戏地址:
俄罗斯方块 | Tetriszhangxiaoleiwk.gitee.io只支持电脑端,不知道键位可以点击设置查看。
游戏用纯 JavaScript、HTML、CSS 实现,图形用 canvas。
支持 SRS、DAS、ARR。
有一个设置面板,可以修改键位以及相关参数。
有一个本地的排行榜,可以记录自己的最好的十次战绩。
farter 在游戏细节上给予许多指导,但受限于作者自身技术水平,以及对游戏的理解,许多地方做的并不好。
作者并非码农,代码以能实现功能为主,比较随意。
后记:
虽然游戏的功能简单,但是玩了有一段时间,感觉稳定性不错,在 Edge chromium 浏览器上表现很好。
分辨率
每次调整屏幕分辨率,所有图形都会进行重绘,游戏在全屏模式下体验最好,为此,我还专门做了一个全屏的按钮。
过度动画
游戏有个我特别喜欢的特性,就是下落动画,看起来很简单,实现起来还是费了一番功夫。能实现这个,是因为游戏本身采用了双 canvas 设计。就像 Ps 的图层一样,背景和移动方块、阴影是分开的,这样不用每次更新图形重绘整个区域。这个设计是借鉴 tetr.js 的,但是它是采用三个图层,我看它 dom 的名字,应该是把 背景、移动方块、阴影全部分离。我在设计的时候做了一些简化,只做两个图层,这为消行动画的实现打下了基础。
消行动画是采用上下图层遮挡实现的,大致原理是,每次消行的时候,把背景图层消除部分以上的图形区域一帧一帧的绘制到前面图层,每一帧都移动一个距离,这样就达到一个动画效果。我最满意的地方是,在应对隔行消除的时候,它会依次从下往上显示过度动画,这样就达到一个很自然下落的过程,这个算法的实现我很喜欢。虽然从玩的角度,这个功能是拖累体验的,但是我实在喜欢这个实现。
更离谱的是,因为图形刷新采用 requestanimationframe 这个 API,根据官方的介绍,我不太确定这个API执行和屏幕刷新之间的确切关系,比如 144 hz的屏幕,这个 API 会以 1000 / 144的毫秒的间隔刷新吗?之所以在乎这个,是因为我把图形动画的移动计算也放在这个 API 里了。如果刷新的速度不一致,则意味着图形动画的移动速度不一致,因为动画帧数帧数分配是移动距离除以帧率,虽然多数电脑似乎都是 60hz,但是如果有 144hz呢,这个API的频率是多少呢?所以我搞了一个特别奇葩的设计,就是玩家第一次点开游戏的第一秒种里,程序会计算玩家电脑这个 API 的刷新率,然后和键位设置游戏数据等保存在本地。说了这么多,这其实是个很简单的东西。
在 Edge chromium 下,动画看着不错,但在最新的 Firefox 下,表现明显不如前者流畅。
SRS 旋转系统
我在中文网上几乎没有看到有关 SRS 的详细介绍,这里我说一说我所实现的 SRS。
所谓 SRS,就是方块的旋转系统。最开始我不懂 SRS,所以一直搞不懂 farter 说的方块钻来钻去。后来过了一年多,无意中在 B 站看到的一个介绍 SRS 的视频,突然明白了,这也是我在一年后又去完善游戏的原因。
流程大致是这样:方块预旋转完成,检测是否发生碰撞,如果没有发生碰撞,则旋转完成;如果发生碰撞,则开始进行所谓踢墙。把预旋转的方块和数组里面的数据依次计算,就是加、减X, Y轴。比如 [-1, 0] 就是把方块向左移动一格,以次类推就可以理解。一个数组有四个数据,依次尝试,尝试成功则旋转成功,四个尝试全部失败,则把用来保存上一个状态的方块的数据替换掉现在的状态,这意味啥也没变。
所谓预旋转方块是说,一般程序会用两个数组保存移动的方块。比如你要移动方块,可以先移动一个数组的数据(作为预旋转),如果移动成功,则把数据复制到另一个数组(后一个状态),如果失败(比如移动的墙里去了,判定冲突),则把后一个状态的数组复制过来,就是啥也没动。程序绘制图形的时候,只需要绘制后一个状态的数组。
每个数组前面的类似 '0R' 的数据,是表示这个数据是用在某一个状态的,所以程序自身应该记录方块的状态,以匹配应该使用哪个数据。具体的内容可以看图片里的网址,虽然是英文的,但教程里的图片解释的很清楚。
更新:2020-7-13
- 做了一个消除方块的过度动画,虽然没有什么用,但是觉得这样的实现非常有意思。尤其是在应对隔行消除方块的时候,可以模仿自然的下落过程,感觉很有趣。
- 每次移动或者旋转,都会重置延时锁定的计时。
- 锁定或得分时有音效效果。
- 视觉效果以及一些计算细节的改进。
更新:2020-5-11
- 方块绘制改为提前绘制,浏览器尺寸变化,方块会进行重绘,以适应屏幕分辨率。
- 图形用两层 canvas 实现,一层为死方块,一层为移动的方块和阴影。避免每一帧的绘制都需要重绘整个图形。
- 候选方块由一个增加到六个。
- 设置面板做了一些补充,修改 DAS ARR 时,把以前的直接输入时间改为 1 ~ 10 的等级调整,感觉毫秒这个时间单位不容易感知。