首页 » 与孩子一起学编程 » 与孩子一起学编程全文在线阅读

《与孩子一起学编程》24.6 电子宠物

关灯直达底部

我们将要建立一个简化了的电子宠物程序,正如前面所说的一样,这是一种仿真。你可以购买电子宠物玩具(比如有一个小屏幕的钥匙链),下载电子宠物软件,还有一些网站(如 Neopets 和 Webkinz),就采用了电子宠物的形式。当然,所有这些也都是仿真。它们会模仿一些真实动物的行为,会饿,会感到孤单,会觉得累。要让它们快乐健康,你必须给它们喂食,和它们玩,还要带它们看病。

我们的电子宠物会简单得多,与你购买或下载的电子宠物相比没有那么真实,因为我只是想让你有一些基本认识,而且我不希望代码太过复杂。不过你可以在这个简化版本的基础上,根据你的想法进行扩展或改进。

我们的程序要具备以下特性。

 
  • 对这个宠物可以有 4 种活动:给它喂食、带它散步、和它玩或者带它看病。

  • 可以监测这个宠物的 3 种统计信息:饥饿感、快乐度和健康度。

  • 宠物可以醒着或者睡觉。

  • 饥饿感会随时间增加。可以通过喂食减少饥饿感。

  • 宠物睡觉时饥饿感的增加会减慢。

  • 如果宠物在睡觉,你做任何活动都会让它醒过来。

  • 如果宠物太饿了,它的快乐度会减少。

  • 如果宠物实在太饿了,它的健康度会减少。

  • 带宠物散步会同时增加它的快乐度和健康度。

  • 与宠物玩会让它的快乐度增加。

  • 带宠物看病会让它的健康度增加。

  • 宠物有 6 个不同的图片:

    • 一个睡觉的图片;

    • 一个醒着但什么也不做的图片;

    • 一个散步的图片;

    • 一个玩耍的图片;

    • 一个进食的图片;

    • 一个看病的图片。

图片可以使用一些简单的动画。后面几节我们将看到如何把所有这些整合在一起构成一个程序。

GUI

Carter 和我为我们的电子宠物程序创建了一个 PythonCard GUI。其中有一些按钮用来完成活动,还有一些计量器显示重要的统计信息。另外还留有一个位置显示宠物的图片(宠物正在做什么)。看起来就像右图这样:

对应活动的按钮是一种 ImageButton 类型的 PythonCard 组件。利用这种组件可以创建带图片的按钮,而不只是文本。各个计量器的组件类型是 Gauge。主图片是一个 Image 组件。标签是 StaticText 组件。

你可以使用 PythonCard 资源编辑器创建这样的 GUI。

算法

要为电子宠物程序写代码,需要更明确地了解宠物的行为。以下是我们要使用的算法。

 
  • 我们把宠物的一“天”分为 60 个部分,每一部分称为一个“滴答”。每个滴答的实际时间是 5 秒钟,所以宠物的“一天”就是我们实际时间的 5 分钟。

  • 宠物在 48 个滴答中都醒着,然后它想睡 12 个滴答。你可以把它叫醒,不过这样会让它很不高兴!

  • 饥饿感、快乐度和健康度的范围都是 0 到 8。

  • 醒着时,饥饿感每个滴答会增加 1 个单位,快乐度每 2 个滴答减少 1 个单位(除非在散步或者玩)。

  • 睡觉时,饥饿感每 3 个滴答增加 1 个单位。

  • 进食时,饥饿感每个滴答减少 1 个单位。

  • 玩时,快乐度每个滴答增加 1 个单位。

  • 散步时,快乐度和健康度每 2 个滴答增加 1 个单位。

  • 看病时,健康度每个滴答增加 1 个单位。

  • 如果饥饿感达到 7,健康度每 2 个滴答减少 1 个单位。

  • 如果饥饿感达到 8,健康度每个滴答减少 1 个单位。

  • 如果睡觉时被叫醒,快乐度减少 4 个单位。

  • 如果程序不在运行,宠物可能醒着(什么也不做),也可能在睡觉。

  • 程序重启时,我们会统计过去了多少滴答,并对应过去的每个滴答更新统计信息。

看起来好像规则很多,不过编写代码其实很容易。实际上,你可能还想增加更多的行为,让它更加有趣。稍后就会给出代码(还会做一些解释)。

简单动画

并不总是需要 Pygame 才能完成动画。我们可以在 PythonCard 中通过使用定时器完成简单的动画。定时器每隔一段时间会创建一个事件。可以编写一个事件处理器,在定时器到时间时让某个事情发生。这就类似于为一个用户动作编写事件处理器,比如说点击一个按钮,只不过定时器事件是由程序(而不是用户)生成的。

我们的电子宠物 GUI 将使用两个定时器:一个用于动画,另一个用于滴答。动画每半秒(0.5 秒)更新一次,滴答每 5 秒发生一次。

动画定时器时间到时,我们会所显示宠物的图像。每个活动(进食、玩等)都有自己的一组图像来实现动画,每组图像将存储在一个列表中。动画会循环显示这个列表中的所有图像。程序将根据正在进行的活动来确定使用哪个列表。

试一试,再试一试

这个程序中还要使用一个新内容,这称为 try-except 块。

如果程序要做一件事情,而且这个事情有可能导致错误,那么最好提供一种办法来收集错误消息并进行处理,而不是让程序直接停止。这可以利用 try-except 块来做到。

例如,如果想打开一个文件,但是这个文件并不存在,你就会得到一条错误消息。如果你没有处理这个错误,程序会在这里停止。不过,也许你想让用户重新输入文件名(没准她只是敲错了)。利用 try-except 块,你可以获取到错误信息并继续执行。

对于打开文件的例子,try-except 块如下所示:

try:    file = open(/"somefile.txt/", /"r/")except:    print /"Couldn/'t open the file.  Do you want to reenter the filename?/"

你想尝试的部分(可能导致一个错误)要放在 try 块中。在这个例子中就是尝试打开一个文件。如果可以打开文件而不会导致错误,就会跳过 except 部分。

如果 try 块中的代码确实导致一个错误,就会运行 except 块中的代码 except 块中的代码告诉程序一旦出现错误该做些什么。你可以这样来考虑:

try:    做这件事 ( 不做其他事情...)except:    如果有错误,就做这件事

try-except 语句是 Python 处理错误所采用的方法,这通常称为错误处理(error handling)。错误处理允许你编写可能出错的代码(甚至是很严重的错误,倘若没有错误处理,这些错误在正常情况下甚至会让你的程序停止),使程序仍能继续运行。我们不打算在这本书里更详细地讨论错误处理,不过我希望你能了解一些基础知识,因为在电子宠物代码中就会看到错误处理。

下面来看这个代码,见代码清单 24-4。这里的说明已经对大部分工作做了解释。这个代码有点长,所以如果你不想自己键入,可以在 examplesVirtualPet 文件夹找到这个程序(如果你运行了本书的安装程序)。也可以从这本书的网站(www.helloworldbook.com)下载。PythonCard 资源文件和所有图片也都已经提供。试着运行这个程序,然后再看代码,确保你能理解它是如何工作的。

代码清单 24-4 VirtualPet.py

sleeptest 函数使用了一个 PythonCard 对话框,不过稍做了调整。你可能记得,PythonCard 要基于另一个名为 wxPython 的 Python 模块。正是因为这个原因,安装 PythonCard 时要安装 wxPython。有时可以使用特殊的 wxPython 参数来改变 PythonCard 的行为。在这里,我们改变了标准 PythonCard 消息框,如右图所示。

我们把它变成一个有感叹号的对话框,还有 Yes 和 No 按钮,就像右图这样。

即使你不能完全读懂这个代码也不用担心。如果你希望学习更多有关 PythonCard 和 wxPython 的内容,可以先看看 PythonCard 网站:http://pythoncard.sourceforge.net/。

在本章中,我们只是稍稍了解了计算机仿真的一点皮毛,知道了模拟真实世界中一些方面的基本思想,比如重力和时间。实际上,计算机仿真在科学、工程、医药和很多其他领域都得到了广泛使用。其中很多仿真非常复杂,即使用最快的超级计算机运行也需要花费几天甚至几个星期。不过钥匙链上的小电子宠物也是一种仿真,有时最简单的仿真也是最有意思的。

你学到了什么

在这一章,你学到了以下内容。

 
  • 什么是计算机仿真,为什么使用计算机仿真。

  • 如何模拟重力、加速度和作用力。

  • 如何跟踪和模拟时间。

  • 如何使用 pickle 将时间戳保存到文件。

  • 关于错误处理的一点知识(try-except)。

  • 如何使用定时器生成周期性的事件。

测试题

 
  1. 列出使用计算机仿真的 3 个原因。

  2. 列出你见过或知道的 3 种计算机仿真。

  3. 使用哪种对象来存储两个日期或时间之差?

动手试一试

 
  1. 为 Lunar Lander 程序增加一个“脱离轨道”测试。如果飞船飞出了窗口顶边,而且速度超过 +100m/s,就停止程序,并显示一条消息,比如“You have escaped the moon/'s gravity. No landing today!”(你已经脱离月球重力,无法着陆!)

  2. 为 Lunar Lander 用户增加一个选项,可以在飞船着陆后继续玩这个游戏,而不必重启程序。

  3. 为电子宠物 GUI 增加一个 Pause 按钮。这会让宠物的时间停止,不论程序是否在运行。(提示:这说明可能需要在 pickle 文件中保存“暂停”状态。)