用 Arduino DIY 70后的复古游戏机

70末80初的小伙伴们们,还记得小时候第一次看到电视游戏机时的兴奋吗?我记得小学的时候,看到邻居借了一个神秘的深色方盒子,用一根线连接到黑白电视机上,那充满雪花和条纹干扰的黑白电视机里居然播放了弹球游戏画面。两个人可以用类似收音机旋钮的东西控制球拍的位置,互相对战弹球,喇叭里还发出叮叮当当的声音。那种惊讶和好奇到现在记忆犹新,的的确确是把“小伙伴们都惊呆了”。5年后,由于考试成绩进入了班级前十名(其实就是第九名,是本学渣有生以来唯一一次超常发挥)父亲奖励了我一台任天堂游戏机。对于我来说,终于有机会可以看看塑料盒子里面的奥秘了,于是当我拿到它后的第一件事就是把他拆开。。。。然而让我吃惊的是里面没有错综复杂的电线,没有形形色色的二极管三极管,没有密密麻麻的线圈变压器,就几个小黑块,像死虫一样的趴着,实在是太难看了。

那时候的我,虽然懂一点无线电方面的电路知识,但是对于计算机的原理是无论如何也理解不了的,为了能明白那几个小黑块,后来借了本什么微机原理的书,结果看完前言和目录,就彻底迷糊了,翻遍了全书,全是像密码一样的mov来mov去,为什么就没有一张像样的电路图呢?一晃二十多年过去来了,电子游戏从当年的像素乒乓发展到了现在的超真实的实时光照引擎,游戏机也从早期的8位机发展到了现在的带体感控制的xbox,我也从一个喜欢焊收音机的小学生变成了喜欢捣鼓技术的设计师。但是儿童时代的好奇心和创造欲望却始终没有改变,而且我总是对艺术和技术混搭的领域非常着迷。于是乎,就有了制作这台游戏机的原始动力。

好了,言归正传,现在就自己动手制作一台70年代风格的电视游戏机,怀念一下黑白像素和方波的音效乐趣吧。让看惯了ipad游戏绚丽画面的小朋友们玩玩咱们小时候的游戏机,顺便用简洁有趣的方式让他们对物理抛物运动知识有一个感性的认识,起到DIY从小培养兴趣的作用。

1准备工作:

经过构思,我画了一张设计稿,对角色造型,布局,玩法进行了大概的设计。

游戏的逻辑非常简单,有一天怪兽袭来,小伙伴们奋不顾身用土豆还击,发射土豆的装置类似于迫击炮,可以调整炮管的仰角,土豆将以抛物线形式发射出去,当土豆砸到怪兽时,怪兽的生命值会减少。所以在怪兽到达炮台前将其消灭即可赢得胜利。但是天有不测风云,风会对土豆的弹道产生影响,所以一个优秀的土豆大炮手能够根据怪兽的距离和风向准确的调整发射仰角以命中目标,这个艰巨的任务就交给玩游戏的小伙伴吧!


为了降低开发难度,快速的实现效果,我选择了Arduino UNO进行了原型机的测试。我的计划是硬件电路尽量的简单廉价,只使用一片mega328P配合极简单的外围电路实现上述游戏的全部功能。这里面包括电视信号的产生,2D图像渲染,音效的产生,弹道的物理计算,游戏逻辑,游戏手柄的控制信号输入。我的创客哲学是:“以快速实现设计目标为原则,不纠缠技术细节浪费时间,充分利用开源分享的力量,不做重复劳动”。这样设计师就可以把精力重点放在创新上,一旦产品原型完成,再交给工程师慢慢的改进吧。可以说Arduino的出现正是符合了这种创客理念。

我们第一步需要解决的是电视信号输出的问题,先上网搜索,看看是否有人做过类似的事情,幸运的是,我找到了开源的TVout库。经过测试,发现它的使用非常方便,不但能够绘制点,线,多边形,还可以生成文字和声音。有了它,电视信号的生成,图像的显示和音效问题就很方便的解决了。TVout库有一个自带的3D旋转程序,测试效果如下图:

2弹道的计算

第二个问题就是弹道的模拟了。这里有必要稍微多用一点文字来探讨这个问题。一般情况下各类游戏引擎中都有一个模块就是专门用来计算这个的,他有一个专业的名字叫做“刚体引擎”,可以用来计算重力,摩擦力,碰撞,反弹等各种物理效果。我需要的仅仅是简单的2D抛物线计算而已,完全没有必要兴师动众的去移植一款大型的游戏引擎,所以就自己动手写一个吧。

首先在不考虑空气阻力的理想情况下简化的土豆抛射运动如下图:

如果土豆不是垂直向上发射,而是与地平面呈φ角度射出(最迷糊的小伙伴也知道如果把大炮垂直向上发射会有什么结果,所以这个假设成立),那么,这颗土豆将会按照抛物线轨迹移动,它的水平运动与垂直运动可以通过下式计算。

其中,R代表土豆的抛射距离,Vo代表抛射的初速度。

为了帮助像我自己这样学渣背景的创客们理解这一坨坨的公式,我们可以取一个具体的实例数字代入公式计算一下,就茅厕顿开了。如果假设,土豆是以最初速率 50米/秒,与地平面呈30度角度射出。根据公式,不考虑任何干扰因素,它会飞到220.7米远的地方。如果这块土豆半斤重,真砸到怪兽,估计会很痛。

理解了这一坨坨的公式后思路就清晰了。在这个游戏程序中我们需要解决的问题就是如何写一个程序来计算任意φ时的土豆运动轨迹,然后把它绘制在屏幕上。一旦这个土豆在某时刻的位置达到怪兽的边界坐标内,那么即说明怪兽被击中,否则当土豆运动到0高度时,则说明碰撞到地面,没有击中目标。

为了能够让程序实时的计算出土豆的位置,我们回顾一下那个学渣们不知所云学霸们不知所用的牛顿定律。通常线性的运动方程表示如下(“线性”就暂且理解成不像无头苍蝇那样毫无规律的乱飞乱撞,我们的土豆运动简化了所有诸如气压,阻力等的复杂情况,就像标准的初中教科书里的抛物曲线一样光滑,因此肯定是满足线性的)

F=m dv/dt

换个形式让他可以被积分

Dv/dt=F/m
Dv=(F/m)dt

可以认为速度上的无穷小的变化量dv等于(F/m)乘以时间无穷小的变化量。可是在计算机中我们是无法让时间无穷小的,因此我们只能取一个较小的离散时间增量Δt,那么Δv就可以通过下式表示

Δv是在离散的时间片段内速度的改变值,因此当前的速度取决于之前的速度与速度变化之和

在初始条件下,Vt为土豆离开炮筒的速度。同理位置的计算也可以用类似的方法表示。理解了这些我们可以再发挥一下,加上风力对土豆弹道的影响,以增加娱乐性。我们可以把它简化为在水平方向上的恒定加速度,然后用与垂直方向相同的方法来处理。

经过这样的转化和简化,以上微分方程问题就适合Arduino来计算了。这就是游戏引擎中常用的所谓“欧拉积分法”。名字很高大上吧!虽然这种方式的计算精度不高,只是对函数曲线进行了多边形近似,但是如果把离散时间尽量取得小一些,对付这种简单的小游戏还是绰绰有余了。搞明白了上面的内容,就剩下堆一堆代码的工作。这里给出相关的片段如下:

xspeed=xspeed (float(wind_force)-50.0)*0.0004;//计算离散时间内风对x速度分量的影响
yspeed=yspeed force;//计算离散时间内重力对速度y分量的影响

xpos = xpos xspeed;//计算速度对位置x坐标的影响 ;
ypos = ypos yspeed;//计算速度对位置y坐标的影响 ;

deg=(analogRead(A0)*0.0005) offset;//通过ADC采集电位器的角度信息,经过转换后用于控制发射方向

xspeed_p = cos( deg ) 6; //计算初始速度x分量 
yspeed_p = sin( deg ) 6; //计算初始速度y分量 

if(digitalRead(2)==0)//按下发射按钮 { 
xspeed=xspeed_p;//给初始速度x分量赋值 
yspeed=yspeed_p;//给初始速度y分量赋值 
}

你可以试着改变各种参数,实现不同重力情况下的效果,这样土豆防御战就可以在月球上进行了。精心调整好各项参数之后,别忘了存盘。

3游戏画面的绘制

设计好角色后就可以用简单的几何图形来建模了,比如,首先根据画好的怪兽图形测量出每个定点的坐标然后用直线函数绘制,获得坐标的方法很多,比如可以借助2D或3D软件直接生成,或者干脆在纸上画上格子数一下,我是在processing里绘制的,调整非常方便,直接把坐标和函数修改一下就可以用在Arduino的程序中。由于TVout库提供了绘制直线和圆的函数,所以,可以方便的直接调用。代码片段如下

TV.draw_line(enmey_pos,74,enmey_pos 15,74,WHITE); 
TV.draw_line(enmey_pos 15,74,enmey_pos 15,94,WHITE); 
TV.draw_line(enmey_pos,94,enmey_pos 15,94,WHITE); 
TV.draw_line(enmey_pos,90,enmey_pos,94,WHITE); 
TV.draw_line(enmey_pos,90,enmey_pos 10,87,WHITE); 
TV.draw_line(enmey_pos,84,enmey_pos 10,87,WHITE); 
TV.draw_line(enmey_pos,74,enmey_pos,84,WHITE); 
TV.draw_circle(enmey_pos 5,79,2,WHITE);
TV.draw_line(enmey_pos 3,74,enmey_pos 3,68,WHITE); 
TV.draw_line(enmey_pos 6,74,enmey_pos 6,68,WHITE); 
TV.draw_line(enmey_pos 9,74,enmey_pos 9,68,WHITE); 
TV.draw_line(enmey_pos 12,74,enmey_pos 12,68,WHITE);

enmey_pos是控制怪兽移动的变量。

这里要注意的是在绘制角色的时候要注意图像缓冲区不要设置太大,否则会导致内存溢出,必定Arduino有限的的小脑袋是思考不了浩瀚宇宙的问题的。经过几次尝试,我设置的是120X96像素大小。代码片段如下:

TV.begin(PAL,120,96);

4游戏逻辑

这个回合制小游戏的逻辑并不复杂,目前也不完善,仅有一个关卡:每发射一颗土豆,怪兽就向前走一步,所以必须在有限的时间内将足够多的土豆准确的扔向怪兽。一旦怪兽到达大炮的位置,游戏失败结束,否则怪兽被足够多的土豆砸中就一命呜呼了,这时可以播放一段怪兽被击败的胜利凯歌,然后再次重新开始。。。对于命中怪兽的检测方法可以通过条件语句判断在当前时间内,土豆的坐标是否落在怪兽的坐标范围内就可以。代码片段如下:

if(xpos>enmey_pos-5 && xpos82)

5游戏的文字和声效

TVout库的声音函数也非常好用,只要一条代码就可以生成方波的声音。如果嫌音质不好,可以加一个简单的滤波器,把高频谐波去掉,正弦波的声音会稍微动听点。文字的绘制也非常简单,只需要先设置字体,然后直接调用print函数即可。

片头文字和音效部分代码如下:

TV.select_font(font6x8);//选择字体 
TV.print(22,24,”Monster will be back…”);//在指定坐标绘制文字 
TV.tone(100, 500);//产生100Hz的音频持续500ms 
TV.delay(1000);//延时1000ms 
TV.tone(200, 500);//产生200Hz的音频持续500ms 
TV.delay(1000);//延时1000ms 
TV.tone(300, 500);//产生300Hz的音频持续500ms 
TV.delay(1000); //延时1000ms 
TV.clear_screen();//清屏 
TV.select_font(font8x8);//选择字体 
TV.print(12,35,”POTATO GUN !”);//在指定坐标绘制文字 
TV.tone(100, 1000);//产生100Hz的音频持续1000ms 
TV.delay(1000); //延时1000ms

5硬件电路

说了半天,硬件电路一点没提,其实游戏机的电路非常简单,以至于不需要什么特别的讲解。游戏主机用arduino加两个电阻和一些跳线即可,手柄使用一个电位器,一个电阻和一个按钮。硬件连接如下

sync连接到D9,用以产生同步信号,video连接到D7,用以产生视频的像素信号。

连接好后照片如下,面包板上的那两个电阻,实际有点误差也没有太大关系。后面那个黄色的同轴电缆线是电视机的AV输入端子,我懒得弄插座直接胶带固定。D11脚直接连接了一个小扬声器,用以产生游戏音效。

游戏手柄的电路就是个标准的可变电阻器和按钮的连接方法,电位器和开关分别接在A0和D2上面,开关加上拉电阻。(其实可以省略这个电阻,直接使用程序内部上拉)。当旋转电位器,A0脚的电压随即就发生变化,按下按钮,D2脚就从高电平变成了低电平。

到此为止这个像素游戏机的原型机就完成了。先不用管太多细节,找出面包板搭好电路,烧入代码享受一下吧。

6改进

只有永无休止的折腾才符合创客精神!在面包板上搭建完成了原型机后,我开始着手改进工作。改进的主要目标就是将其做成一个简单易制,物美价廉的开源产品,可以让大家只花一顿快餐钱购买材料,用一下午的时间就可制作出来。

首先是使用体积更小的Arduino mini 代替了UNO。这样可以把电池和主板都塞到游戏手柄里。为了减小体积,电池使用了小直升飞机玩具里的可充电锂电池,把扬声器替换成了压电陶瓷蜂鸣器,把视频输出的那两个电阻换成了贴片的微调电阻。原来的手柄电位器有点占地方,所以我把它换成了四个微动开关,并且使用了我的最爱-—漆包线与洞洞板代替了插接线和面包板。最后,利用了一晚上的时间拼命回忆起上个世纪读大学时玩的Rhinoceros工业设计软件,设计了一款略有复古气息的外壳。不得不感叹,年轻真好啊,当年真应该多背背单词。。。十几年过去了,那些变态的建模命令居然还能记得!

我使用的Arduino mini

洞洞板和利用废导线做的连接跳线

Ardino mini,锂电池,压电陶瓷,洞洞板,插座和开关,组装好的集体照。按钮在背面。

3D打印的外壳和按钮,使用适合小朋友玩的PLA材料。技术太差,略有变形。

最终效果照片,使用了带AV输入的小液晶屏代替电视机做显示。

总结:
在这个小制作中,我们用忘得差不多的中学物理知识,干了点有用的事情—用欧拉积分法写了一个简单的抛体计算引擎,并且利用它来击中怪兽的脑袋。这不就是传说中的弹道计算机吗?没错,二战时的战列舰上如果有这货绝对是超级高科技。用像素坐标和函数创作艺术图形和音乐很有怀旧的感觉,当年乔布斯等人在地下车库里也就是折腾这些,艺术与技术结合到极致的皮克斯动画公司就是这样诞生的。无突然想起了那句话:“创客改变世界”。不得不承认,创客文化使我等学渣们找到了另类的乐趣和自信。

回到现实,由于自己脑力实在有限,程序做得比较粗糙。本来设想了一些好玩的情节,比如,土豆炮可以换不同性质的弹药,怪兽会发射炮弹反击,每一关会有生命力,速度,进攻力不同的怪兽出现,增加双人对战模式等等。。。但目前时间有限暂时无法完成了。所以把程序,电路,3D打印模型文件都开源分享给大家,如果感兴趣可以在这个基础上继续折腾下去。或在这个小平台上开发自己的小游戏。

图中的怪兽被砸中的次数太多,脑袋出现了裂痕。有时还会砸出星星小鸟之类的。只要ROM空间足够,很多小细节都可以用前面讲到的方法在主程序中插入的图像绘制代码得到。

祝大家玩得开心,如果有什么新改进,开发了新关卡,或者折腾出了新玩法,别忘了第一时间新浪微博@超级亚敏….大家一起来娱乐娱乐。或许以后可以组个臭味相投的小聚会。这才体现创客的精神吗,哈哈。

源代码在这里下载


所需要的tvout库在这里下载

3D打印外壳模型文件在这里

下载1234

1 评论

发表评论

你的邮件地址不会公开


*