23. 课程_21 飞行的小鸟游戏

飞吧!用你自制的极具挑战性的飞行的小鸟游戏来实现你的管道梦想。这个游戏只需要一块micro:bit主板和一些Python代码。

23.1. 制作目标


我们将要在5x5的LED屏幕上制作一个完整的互动游戏。这个游戏适合各个年龄段的玩家。在这个过程中,你将会学习: 第一步:将micro:bit库导入到Python。 然后,在屏幕上滚动显示‘READY’这条信息,并启动游戏开始时显示的倒计时,。 第1行:导入micro:bit程序。 第4行:在屏幕上滚动显示‘READY’。双引号标志指的是一个字符串(在这个案例中是‘READY’)。 第5-10行:通过使用sleep()函数在屏幕上闪烁每个数字1秒钟(或1000毫秒,测量时间也包括在内)。 第11行:清屏,为后来的小鸟和墙的绘制做准备。

注意: 在你的代码种添加注释来解释代码,有助于你或者其他人理解代码。 你可以添加一个带#的注释。当有必要的时候,你需要将你的代码隔开,把不同的代码划分为不同的部分。

23.2. 制作材料


1 x BBC micro:bit 1 x USB线(真的,这就是你所需要的东西)

温馨提示: 如果你想要以上所有这些元器件,你可以购买我们的micro:bit小小发明家套件

23.3. 为什么选择Python呢?


  • 读起来像英语。Python是一门最容易读懂的语言。它是最适合编程初学者的语言。

  • 灵活多变。Python成为行业标准是有充分理由的。它可以用来做很多事。 这就是为什么谷歌和YouTube使用该语言的一部分作为它的后端软件。

  • 社区活跃。Python对于初学者来说是一门最受欢迎的编程语言。 它有着成千上万的资源,除了可以帮助你查看代码之外,你还可以获得很多帮助。这可以帮助你扫除编程路上的绊脚石。

实际的编程看起来比积木块拖拽式编程看起来更酷。这听起来似乎很难,但是看看这些颜色吧!(Flappy Bird程序文本示例)

23.4. 如何在Python中编程呢?


如果你初学编程,你可能还没有安装Python。别担心!你可以进入到micro:bit Python官方编辑器或者是下载离线Python编辑器mu来编写代码,并把它发送到你的micro:bit。你也可以用你自己的编辑器(Sublime3和Atom),但是你需要将程序闪存到micro:bit。这可能会比较麻烦。此外,你可以使用micro:bit仿真器。它可以有效地测试代码,不需要每次都下载.hex文件,方便你修改程序的错误。

设置完成后,用一根USB线将你的micro:bit连接至电脑。它应该连接micro:bit背面顶部的接口。当准备好闪存程序,micro:bit会发出亮黄色的光。如果你用的是仿真器,你可以忽略这一步。不然,就停止阅读并按照说明将它设置好。别担心,我会等你设置好。

欢迎回来。事不宜迟,让我们开始吧!

让我们从整体的角度出发,来看一看我们正在做的事情。

解决每个编程问题的关键是将它分解成为几个可以解决的小问题。 让我们看看我们将需要什么。 通过视频来观看游戏的演示。 看完视频后时,让我们想一想游戏中有哪些元素。 当开机的时候,屏幕上会会显示‘READY’信息和倒数计时。

创建一个坐标来代表小鸟。 按下按钮A移动小鸟。 跟踪小鸟通过的管道的数量。 创造小鸟将要飞过去墙壁 当小鸟撞墙时,游戏结束。 你可能已经知道如何做这些。 自己先尝试着操作这些步骤。如有必要,你可以将这个步骤进一步分解成几个更小的步骤。 分解之后,顺便检查一下代码,这样可以避免你出错。

步骤 1 – Hello, World!

第一步是将micro:bit代码库加载到Python。然后,让一个‘READY’的信息在屏幕上滚动。当游戏开始的时候,初始化倒计时。

第1行:导入micro:bit程序。 第4行:初始化屏幕的滚动信息‘READY’。双引号代表它是一个字符串(在本案例中用 ‘READY’)。 第5-10行:使用函数sleep()会让屏幕上的每个字母闪烁一秒(或者1000毫秒,包括测量时间在内)。 第11行:清屏,为后来的小鸟和墙的绘制做准备。

**注意: ** 在代码中添加注释是一种好习惯。这可以让你或者其他人返回这段代码的时候,能更好地理解它。添加注释一般会带一个“#”。而且,当有必要表明不同的部分做不同的事情的时候,你需要将你的代码隔开。

你正在做的是将函数应用到目标显示器上,例如:将LED屏幕点亮的函数。 在Python中,你也可以自由地调节第4行文字的滚动速度。

display.scroll(“READY”, delay = 200)的文字滚动速度快2倍,而display.scroll(“READY”, delay = 800)的文字滚动速度则是速度的一半。标准的延时设置是400。增加数值会减缓文字滚动的速度,而减少数值将会增加文字滚动的速度。

祝贺你!你完成了游戏前的消息设置哦!接下来,我们需要正式进入到游戏的设置,让玩家可以玩这个游戏。

步骤 2 – 小鸟,飞吧!

接着,我们需要创建小鸟的图像。对于这些从未分析过游戏的人,Flappy Bird只允许小鸟上下飞行,并且匀速将小鸟朝着墙的方向推进。当然,我们的屏幕只有5排LED灯,因此屏幕的显示区域非常有限。为了使小鸟的飞行变得更加切实可行,我们将把这5个部分拆分成100个不同的位置。这给我们后来添加下降的速度带来了更多的灵活性。在这个案例中,屏幕顶部的位置位于y=0,按钮的位置位于y=99,因此这里面有100个位置。初始位置是y=50。

第13行: 因为y=0是顶部,而y=99是在底部,所以小鸟的初始位置刚好被设置在中间。 第17行: 这决定了小鸟在屏幕上的实际位置,因为屏幕上有100个位置和5排LED灯。因此,当你将存储在变量y中的数值除以20的时候,小鸟就会被你吓跑到屏幕的下方。 第18行: 将小鸟显示在屏幕上。它使用了函数 display.set_pixel。这个函数有3个参数:x、y 和亮度。x轴是1,因此它将会显示在第二栏。y轴当前是2,因为我们用50除以20,并进行了四舍五入。那就是第三排。(注释:在电脑编程中,指数通常从0开始。因此从上到下,你有0-4排;从左到右你有0-4列。) 亮度可以是从0到9的任意整数,并且9代表最亮。在这个案例中,亮度为7就足够了,这样可以防止眼睛疲劳。 我们添加一个while循环,告诉micro:bit一直运行被缩进的代码区域。(Python用缩进来分隔代码。)

sleep代码告诉micro:bit每20秒运行这个循环一次,这样会使你的游戏更加容易管理,并且让你的CPU也不会过度运转以至浏览器崩溃(这也是有可能发生的)。

游戏检查:在这个时候,一条欢迎的消息应该会出现,接着它又消失了,随后一只小鸟出现了。

步骤 3 – 离开巢穴

上一步只显示了小鸟的图像,但是小鸟还不能移动哦!这就是我们接下来将要做的–通过模拟真实的重力来实现小鸟的移动。

首先,让我们在y轴的正下方添加一个新的变量“speed”(速度)。 将display.clear()移动到while循环中。这样它不仅能清除欢迎的消息,还能在每次生成小鸟的新位置的时候,清除小鸟之前的位置。 第25-29行: 在边界(最大值:y=99, 最小值 y=0)内设置了小鸟的一个新的y坐标。这个坐标是基于那一点的“gravity”(重力)。 为什么要把这些都放到while循环中呢?因为你想让这个代码区域每几毫秒(精确地来说是20毫秒)持续更新小鸟的位置,所以这个代码区域将不断地重复运行。

终端速度:为了让小鸟的运动变得更加实际,让它的速度达到常量速率2,但是只在speed = 0变成了speed =2的两次代码迭代之后。函数if确保小鸟的速度不会大于2。你可以运用if函数来设置不同的下降速度。

步骤 4 – 克服重力

现在,我们通过按下按钮A来让小鸟进行跳跃。在这一步,我们也会添加一个新的变量“score”(得分)来追踪小鸟飞过的墙的数量。这在任何时候都可以用按钮B来实现。

为了响应按钮A被按下,在一个if循环中运行button_a.was_pressed(),就像第21行一样。如果,在那次迭代中,按钮A在任何时候被按下,我们会让小鸟回升到原来的位置,重置下降速率,然后增加小鸟的飞行速度,让小鸟以原来的飞行速度飞向地面,以此来给出一个上升和下降的运动。 改变小鸟向上飞行的速度(当前的速度是-8)来观察小鸟下降速率的改变。 添加变量“score = 0”(得分=0),将新的变量score设置为0,并设置下方的速度以及变量y。出于编程的一个习惯,试着将你所有的变量放在变量代码的上方一个地方,这样它就更容易追随,并且实际上可以被输入,来达到使用的目的。 通过创建一个和按钮A类似的if循环,当按下按钮B,就会显示得分。display.show(score)在任何时候都能及时显示得分。我们将会学习统计并更新每堵墙被穿过之后的得分。

游戏检查: 欢迎消息出现、消失,然后向下飞行的小鸟出现。按下按钮A,让小鸟向上飞行;按下按钮B来检查得分(现在应该还是0分)。

步骤 5 – 管道冲击波

我们将用一个make_pipe(制作管道)函数来创作我们的第一个管道。 然后,我们将它赋值为变量i,并且在while循环中显示管道。我知道这很复杂,但是这将会是我们的游戏最终变得完整的开始哦! 函数是以函数名字进行便捷运行的代码块。通过调用一个函数,我们可以运行它的内部的整个代码块。这就便于我们理解每一步我们在做的事。在这个案例中,我们将给我们的函数make_pipe()命名。每次运行函数make_pipe()中的代码的时候就会生成一个新的管道。让我们将函数make_pipe()的每一步进行分解。 在第19行,我们用def make_pipe(): 定义函数。它是make up the function(形成函数)下方缩进的代码块。 在第20行,绘制了一个定制的图像。“0”代表每个坐标的0亮度。它从第1排第1列开始,然后是第1排第2列,以此类推。当亮度等于4的时候,它就会点亮最后一列的所有LED灯。(你可以根据自己的喜好进行调整。我个人喜欢小鸟比墙更亮,这样你就可以识别出它的位置。) 在第21行,我们使用随机代码库来调用一个在0到3之间,且包含0和3的随机数,也就是0、1、2和3。我们不使用4,因为这样会使2个孔爆炸。其中一个是gap+1。如果选择了4,我们就会使第5排第4列的孔爆炸。但是这里没有第5排,所以它就返回了一个错误。 我们需要返回这个图像,这样它就可以作为后面的i的数值被调用。 通过设置间隔位置及其上方的LED灯的位置为0,就会使孔爆炸。是不是非常酷呢?那就是你的第一个函数。干得不错! 注意:你需要时常定义实际代码上方和变量下方的函数。这只是一个惯例,但是它可以让你的程序变得更加可读哦! 让我们给函数赋值变量i,就像第27行一样。现在,在while循环中,如果我们添加了一个display.show(i),屏幕上就会显示管道(和一个孔)i。 加油!我们基本上快要完成了。现在,我们需要让墙移动,统计得分以及为小鸟和墙的碰撞做出反应。 游戏检查: 和第4步一样,现在我们有了静止的带孔的墙哦!检查一下之前的步骤,看看是否不小心遗漏了什么吧!

步骤 6 – 帧率

这一步我们需要设置游戏常量。这里,变量frame(帧)从0开始,然后每20分钟增加1,这样变量frame增加到20就需要400ms或者0.4s.记住,这将会使接下来的数学变得更加简单。这些常量在步骤7才会被用到,但是先让我们把它们设置好吧。 第15行 表示frame增加1所花的时间(单位:ms)。这会添加到第37行(frame += 1)的while循环中。你可以改变底部的sleep(20)让它sleep(DELAY),因此它就会做出反应。 第16行 设置墙移动1列所花费的时间。目前这个时间是0.4s或者20 frames(帧)。 第17行 设置另一堵墙出现的间隔时间。当前这个时间是2.0或者100 frames(帧)。 第18行 设置得分增加所花费的时间。这应该一直等于FRAMES_PER_NEW_WALL的数值,因此你经过的每堵墙等于1个额外的得分。 为了增加游戏的难度,你需要调整这些游戏的常量,或者减少每堵墙之间的距离,以便于显示更多墙(但是相应地,你需要改变 FRAMES_PER_SCORE)。目前,这个游戏在屏幕上只显示了一堵墙,但是你可以通过这些数值让它变得更加热闹。 注意:游戏的常量需要大写,这是为了和其他使用过的变量区分开来。这只是Python编程的规范。虽然不遵循这个规范代码也可以运行,但是你遵循了它,你的代码会变得更加可读。

步骤 7 – 管道梦想

这里,我们将会比较frame的数值和游戏常量来使墙往左移动,创造一堵新墙以及增加得分。这些都在while循环中,因此它每20ms就会检查一次。准备好了吗?让我们开始吧! 在这一步,我们将使用模数标识(%)。当一个数除以另一个数时,它会产生余数。因此4%2等于0,但是4%3等于3. 这里,我们将用它来检查变量frame是否等于游戏的任意一个常量。 墙往左移动:查看第65-67行代码。这意味着当frame等于20、40、60…的时候,墙就会移动。因为这些数字都能被FRAMES_PER_WALL_SHIFT 的数值20整除。你可以改变数值让墙移动得更快一些,从而增加游戏的额难度。目前,墙每0.4s移动一次。 创造新墙:查看第69-71行。每100 frames(帧),或者2秒钟,通过调用函数 calling the make_pipe() i次就会生成一个新的管道。这就是创造和移动墙的常量。 增加得分:查看第73-75行。这意味着当小鸟飞行了2秒或者飞过了一堵墙,就会加一分。这个数值和墙与墙之间的距离相对应,因此每经过一堵墙就会加一分。

游戏检查: 游戏基本上可以玩了。在欢迎的消息滚动经过屏幕后,按下按钮A就可以让小鸟移动。你可以通过按下按钮B来查看得分。因为游戏中小鸟受到重力作用,因此它随时会掉下来。然后向右移动,就可以穿过随机生成的墙。哇哦,你基本上已经成功了哦! 现在,我们只需要对管道的碰撞做出反应,结束游戏并在小鸟撞上管道的时候显示得分。

步骤 8 – 碰撞课程

你终于到达了最后一步哦! 准备起飞了吗?现在,我们只需要添加一个碰撞反应。我们用函数get_pixel来返回那个位置的LED灯的亮度。 同时也使用了‘!=‘(函数NOT)。下面解释了它是如何被使用。 添加这个检查碰撞的代码到while循环中,将它放在绘制小鸟和移动墙的代码之间。这意味着它在新墙生成之前会检查碰撞,因此这里不会因为错误而产生额外的分数。 正如在第66行所显示的,我们使用一个if循环。“i.get_pixel(1, led_y) != 0” 检查一个管道是否位于第1列的位置(小鸟所在的位置),特别是位于led_y(显示小鸟的位置)。如果在与小鸟坐标相同的位置有一个管道的像素,i.get_pixel(1, led_y)就会返回到4(墙的亮度)。这个数字不是0,所以下面执行碰撞检查的函数就会运行。 第67-68行 显示内置的哭脸图像0.5秒。 你可以改变哭脸显示的时长,以及你喜欢的其他任何图像。 在Python中,你可以输入很多图像。你可以在此查看完整的清单。 第69行: 将得分显示为“Score:”后面的一个字符串。 第70行: 结束while循环,也就结束了游戏。这意味着game over(游戏结束)。

开始游戏!

游戏的制作到此就结束了。你现在已经完成了这个游戏。你的游戏应该能够运行和结束,并在游戏结束的时候显示得分。它现在是一个完整的游戏,非常简单,但是也具有一定的挑战性。给自己一点儿鼓励吧! 这个游戏的编程充满了很多复杂的代码和新概念。看看你的程序,试着找到每一行代码。如果有必要的话,添加评论来解释它。这会有助于你在几个月后回顾你的代码的时候,能够轻松读懂你自己的代码 。

干的不错! 和你的朋友们一起在这个新鲜界面上玩这个游戏吧!现在,你就像一只小鸟一样自由地寻找其他项目,并且进一步理解了Python代码。 扩展:添加一个游戏循环,这样你就可以再次玩这个游戏,而无须重置micro:bit。我建议将while循环的要求true变为一个特定的变量,通过按下一个按钮就可以改变函数play_again。看看其他的Python游戏循环以寻找灵感,例如:剪刀石头布的游戏。

23.5. 常见问题