我们将要建立一个简化了的电子宠物程序,正如前面所说的一样,这是一种仿真。你可以购买电子宠物玩具(比如有一个小屏幕的钥匙链),下载电子宠物软件,还有一些网站(如 Neopets 和 Webkinz),就采用了电子宠物的形式。当然,所有这些也都是仿真。它们会模仿一些真实动物的行为,会饿,会感到孤单,会觉得累。要让它们快乐健康,你必须给它们喂食,和它们玩,还要带它们看病。
我们的电子宠物会简单得多,与你购买或在线玩的电子宠物相比没有那么真实,因为我只是想让你有一些基本认识,而且我不希望代码太过复杂。不过你可以在这个简化版本的基础上,根据你的想法进行扩展或改进。
我们的程序要具备以下特性。
对这个宠物可以有 4 种活动:给它喂食、带它散步、和它玩或者带它看病。
可以监测这个宠物的 3 种统计信息:饥饿感、快乐度和健康度。
宠物可以醒着或者睡觉。
饥饿感会随时间增加。可以通过喂食减少饥饿感。
宠物睡觉时饥饿感的增加会减慢。
如果宠物在睡觉,你做任何活动都会让它醒过来。
如果宠物太饿了,它的快乐度会减少。
如果宠物实在太饿了,它的健康度会减少。
带宠物散步会同时增加它的快乐度和健康度。
与宠物玩会让它的快乐度增加。
带宠物看病会让它的健康度增加。
宠物有 6 个不同的图片:
一个睡觉的图片;
一个醒着但什么也不做的图片;
一个散步的图片;
一个玩耍的图片;
一个进食的图片;
一个看病的图片。
图片可以使用一些简单的动画。后面几节我们将看到如何把所有这些整合在一起构成一个程序。
GUI
卡特和我为我们的电子宠物程序创建了一个 PyQt GUI。其中有一些按钮(实际上是工具条上的图标)用来完成活动,还有一些进度条显示重要的统计信息。另外还留有一个位置显示宠物的图片(宠物正在做什么)。
注意,窗口的标题栏上写着 Virtual Pet(虚拟宠物)。怎么设置窗口标题呢?在 Qt Designer 应用中新建一个窗口,然后在对象检查器中点击 MainWindow 对象。之后,在属性编辑器中找到 windowTitle
属性,将它改为 Virtual Pet(或者你想在标题栏中显示的任意文字)。
用来控制宠物活动的一组按钮是 PyQt 中一种叫做工具栏(Toolbar)的组件。工具栏和菜单栏一样也有行为,但不同之处在于,工具栏中的每个行为都有一个与之关联的图标。
要添加一个工具栏,右键点击主窗口,然后选择 Add Toolbar(添加工具栏)。这会在窗口顶部创建一个非常小的工具栏。在对象检查器中找到工具栏,点击它。然后在属性编辑器中找到 minimumSize
属性,将它的宽设为 100,高设为 50。
要将行为(图标)添加到工具栏上,点击 Qt Designer 右下角的 Action Editor(行为编辑器)标签。在 Action Editor 面板中任意位置点击右键,然后选择 New(新建)。你会看到一个用来添加新行为的对话框。你唯一需要输入的是 Text,Qt Designer 会自动填写对象名称。然后在中间找到三个点(...)的小按钮,点击右边的向下箭头,选择 Choose File,接着选择你想在工具栏按钮上使用的图片文件。
要在工具栏中添加新图标还有最后一步要做。一旦你创建了新的行为,就能在 Action Editor 的列表中看到它。现在你需要将它拖到工具栏上。当你拖过来时,你为新行为选择的图像将会作为工具栏的新图标出现。Qt Designer 会自动缩放图片以适应工具栏的大小。
血量是名为 Progress Bar(进度条)的组件类型。主图像是一个 Push Button(我们之前用过),通过设置它的属性,可以让它看起来不像普通的按钮,而是显示一幅图像。
窗口中其余部分的文本是 Label 组件。
你可以像这样使用 PyQt Designer 创建一个 GUI。你也可以(从示例文件夹中)将我们创建好的 GUI 加载到 Qt Desinger 中,以检查这些组件和它们的属性。
算法
要为电子宠物程序写代码,需要更明确地了解宠物的行为。以下是我们要使用的算法。
我们把宠物的一“天”分为 60 个部分,每一部分称为一个“滴答”。每个滴答的实际时间是 5 秒钟,所以宠物的“一天”就是我们实际时间的 5 分钟。
宠物在 48 个滴答中都醒着,然后它想睡 12 个滴答。你可以把它叫醒,不过这样会让它很不高兴!
饥饿感、快乐度和健康度的范围都是 0 到 8。
醒着时,饥饿感每个滴答会增加 1 个单位,快乐度每 2 个滴答减少 1 个单位(除非在散步或者玩)。
睡觉时,饥饿感每 3 个滴答增加 1 个单位。
进食时,饥饿感每个滴答减少 2 个单位。
玩时,快乐度每个滴答增加 1 个单位。
散步时,快乐度和健康度每 2 个滴答增加 1 个单位。
看病时,健康度每个滴答增加 1 个单位。
如果饥饿感达到 7,健康度每 2 个滴答减少 1 个单位。
如果饥饿感达到 8,健康度每个滴答减少 1 个单位。
如果睡觉时被叫醒,快乐度减少 4 个单位。
如果程序不在运行,宠物可能醒着(什么也不做),也可能在睡觉。
程序重启时,我们会统计过去了多少滴答,并对应过去的每个滴答更新统计信息。
看起来好像规则很多,不过编写代码其实很容易。实际上,你可能还想增加更多的行为,让它更加有趣。稍后就会给出代码(还会做一些解释)。
简单动画
并不总是需要 Pygame 才能完成动画。我们可以在 PyQt 中通过使用定时器完成简单的动画。定时器每隔一段时间会创建一个事件。可以编写一个事件处理器,在定时器到时间时让某个事情发生。这就类似于为一个用户动作编写事件处理器,比如说点击一个按钮,只不过定时器事件是由程序(而不是用户)生成的。定时器到时间时生成的事件类型是 timeout
事件。
我们的电子宠物 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.helloworldbook2.com)下载。PyQt UI 文件和所有图片也都已经提供。试着运行这个程序,然后再看代码,确保你能理解它是如何工作的。
代码清单 24-4 VirtualPet.py
sleep_test
函数使用了 PyQt 的“警告信息”对话框。一系列的参数告诉它要显示哪些按钮,以及哪个是默认按钮。代码清单 24-4 中的注释解释了这一点。当对话框弹出来时(你试图叫醒你的宠物时),看起来是这样的:
即使你不能完全读懂这个代码也不用担心。如果你希望学习更多有关 PyQt 的内容,可以先看看 PyQt 网站:www.riverbankcomputing.co.uk/software/pyqt/into。
在本章中,我们只是稍稍了解了计算机仿真的一点皮毛,知道了模拟真实世界中一些方面的基本思想,比如重力和时间。实际上,计算机仿真在科学、工程、医药、金融和很多其他领域都得到了广泛使用。其中很多仿真非常复杂,即使用最快的计算机运行也需要花费几天甚至几个星期。不过钥匙链上的小电子宠物也是一种仿真,有时最简单的仿真也是最有意思的。
你学到了什么
在这一章,你学到了以下内容。
什么是计算机仿真,为什么使用计算机仿真。
如何模拟重力、加速度和作用力。
如何跟踪和模拟时间。
如何使用
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 文件中保存“暂停”状态。)