俄罗斯方块游戏机屏幕显示原理(用ESP32制作俄罗斯方块掌上游戏机)
1984 年,一位名叫阿列克谢·帕基特诺夫(Alexey Pajitnov) 的工程师,在当时的苏联科学院计算机中心工作。他的工作任务是测试新硬件设备的兼容性,相比单位分配的正式任务,更吸引他的是研究如何使用计算机进行娱乐。
为此,帕基特诺夫开始尝试在计算机上开发一些简单的游戏,经过一段时间的尝试后,他从一款拼图游戏上获得了灵感,考虑让不同形状的图形依次下落,在矩形底部堆叠起来,使之排列成完整的一行后消除。
在另外两位同伴的协助下,帕基特诺夫在Electronika 60 计算机上完成了这款名为“Tetris”(俄语:Тетрис)的游戏,因为这个型号的计算机没有图形接口,所以游戏只使用空格和实心方块来表示形状(见图 1)。
图 1 第一版《俄罗斯方块》游戏,运行在 Electronika 60 上
根据另一位当事人的回忆,“Tetris”这个单词是阿列克谢自己发明并坚持使用的,来自反映俄罗斯方块图案基本结构的“四”(希腊语:tetra)和阿列克谢自己最喜爱的运动“网球”(tennis)的组合。今天我们更习惯称呼这个游戏为《俄罗斯方块》。
1985 年,《俄罗斯方块》的开发者之一 ——瓦丁·格拉西莫夫在 MS-DOS 下移植了《俄罗斯方块》(见图 2),伴随着 PC 的推广,游戏得以迅速普及。
图 2 1986 年 IBM PC 版本的《俄罗斯方块》
对于“80 后”来说,图 3 所示的《俄罗斯方块》掌上游戏机承载了很多快乐的回忆。
图 3 《俄罗斯方块》掌上游戏机
硬件设计
我这次使用ESP32 搭配 ILI9341TFT LCD 来制作一个《俄罗斯方块》掌上游戏机,选择的硬件如附表所示。
硬件中最为复杂的是 ILI9341 TFT 显示屏,它使用 SPI 接口进行通信。丝印上的 SCL 和 SDA 分别是 CLK 和 MOSI(显示屏可以看作一个单纯的输出设备,因此MISO 是可以省略的);RES 用于显示屏复位,在初始化显示屏时是必需的;DC用于通知显示屏当前传输的是数据(Data)还是命令(Command);CS 是 SPI 接口用于片选的信号,如果当前 SPI 总线上还有其他设备,可以用于选中需要的通信设备;BLK 用于控制显示屏背光是否开启,在这个项目中没有关闭显示屏的需求,因此直接悬空,显示屏会一直保持背光打开的状态。
图 4 ILI9341 TFT 显示屏
确定使用的硬件之后,就可以开始着手设计电路了,我使用立创 EDA 绘制电路图和 PCB,结果如图 5、图 6 所示。可以看到电路很简单,除了前面提到的 TFT 显示屏就是 PS/2 摇杆,后者输出 2 路模拟信号和一个数字信号,分别是x 轴和y 轴坐标、按键信号。
图 5 使用立创 EDA 绘制的电路图
图 6 PCB 图
立创 EDA 提供在线预览工具,PCB 绘制完成后可以直接查看最终效果(见图 7)。
图 7 预览 PCB 效果
软件设计
硬件设计完成后即可进行软件设计,相比硬件,软件设计要复杂得多。代码整体可以看作两部分,一部分是在 screen[Width][Height]数组中计算界面的形态, 代码在Get Next PosRot() 和 Revise Screen() 函数中;另外一部分是通过 ILI9341 TFT 显示屏将前面的数组显示出来,代码在 Draw() 函数中。这样设计的好处是逻辑和硬件分离,便于调试和移植到不同显示接口的设备上。
代码中最重要的数据结构是关于形状的存放。游戏中共有 7 种形状(见图 8),可以看到每种形状都由 4 个方块组成,因此可以用方块的坐标表示形状。
图 8 游戏中的 7 种形状
比如,图 8 所示的这个形状,以记录坐标的方式,可用 {{-1,1},{0,1}, {0,0},{1, 0}} 来表示。
图 8 形状和对应的坐标
当它旋转后, 变成图9所示的形状,以记录坐标的方式,可表示为 {{0,-1},{0,0}, {1,0},{1, 1}}。
图 9 旋转后的形状和对应的坐标
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一嵌入式物联网学习资料(头条)个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理0元领取:
具体到代码中,我用 blocks[ ] 数组记录了每一种形状的全部变换结果,旋转就是指向下一个内容。例如 blocks[ ] 数组中的一个形状定义如下:
{{{{-1,0},{0,0}, {1,0},{2,0}}, {{0,-1}, {0,0},{0,1},{0,2}},{{0,0},{0,0},{0,0},{0,0}}, {{0,0},{0,0},{0,0},{0,0}}},2,1},
其中末尾处的深绿色 1 表示这个形状的颜色索引,浅绿色 2 表示这个形状有 2 个形态。{{-1,0},{0,0}, {1,0},{2,0}} 对应图10所示的形态,其中的 x 表示 (0,0) 这个原点。
图 10 形状的形态 1
{{0,-1}, {0,0},{0,1},{0,2}} 对应图 11 所示的形态。
图 11 形状的形态 2
了解了上面的数据结构,就不难理解GetNextPosRot() 函数了。这个函数的功能是放置新生成形状、响应按键动作(比如,左右移动和旋转)和触发下落。游戏的难度不同体现为图形不同的下降速度。游戏根据当前积分的不同使用不同的下降速度,具体代码在 GetNextPosRot() 函数中。
Score2Level() 函数返回当前分数对应的延时,当前分数越高,下落速度越快。
ReviseScreen() 函数处理下落的过程,此外计分功能也在该函数中:在消除行时,根据一次性消除的行数计算分数,具体在 DeleteLine() 函数中。每次获得的分数 = 消除层数的平方。
Draw() 函数将前面 screen[ ] 数组中的界面形态展现在 ILI9341 TFT 显示屏上。为了驱动这个显示屏,我选用了 Adafruit_GFX 显示库;注意这个库对 ESP32 有兼容性问题,编译时会出现错误,可以修改\Adafruit_BusIO\Adafruit_SPIDevice.cpp 这个文件解决。
为了便于使用,我在 IL9341 的库中增加了一个填充图像的函数。
此外,《俄罗斯方块》还要具有显示“下一个图形”的功能。为此,我在 Draw()函数中实现了对应的绘制“下一个图形”的功能。这里有特别需要注意的地方,比如, 我们定 义了一个 image[20][20] 图像,但是想将它里面 10×20 的内容显示出来,是不可以直接使用 tft.fillImage() 的,会出现混乱,因为这个函数会默认将显示Buffer 认为是 20×20 大小的。解决方法是创建另外的 Buffer,先把数据存进去,再使用 fillImage() 函数绘制。
装配电路、烧写程序后,你就可以开启游戏之旅了!
正版《俄罗斯方块》发售了 1.25 亿份,受到 50 多个国家和地区的玩家喜爱,有超过50 种语言的版本,运行在街机、家用游戏机、掌上游戏机、PC、手机等几十种游戏平台上。历经近 40 年,小方块的魅力经久不衰,《俄罗斯方块》游戏可以称得上是传奇。
声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。
------------ END ------------
文章链接:https://mp.weixin.qq.com/s/2zje32Vks2ECafzKsjiqtg
转载自:嵌入式专栏
文章来源:无线电杂志 ,作者王岩柏
文章链接:用 ESP32 制作《俄罗斯方块》掌上游戏机
版权申明:本文来源于网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com