Program Arcade Games
With Python And Pygame

Chapter 1: 创造一个自定义计算器

(嗨!如果你的机器上还没装有Python和Pygame, 那么请 跳转 到“前言”一章去下载并安装好它们,这样你就可以开始了。)

1.1 导言

fig.trajectory

Python有一个最简单的用途,便是我们可以把它当一个非常华丽且复杂的计算器来使用。 等一下,一个计算器可不是一个游戏啊。我们为什么要讨论计算器?好像很无聊...

嘿,但你要知道,计算下坠的物体,飞行的子弹,和游戏得分,我们都需要用到计算。 而且, 任何一个真正的电脑天才都会把计算器当作一个玩具而不是一个工具!所以让我们从计算器开始我们的游戏编程学习。不用着急,我们会从第五章起介绍图形学相关的内容。

一个简单的计算器程序, 既可以用来通过向用户收集些基本信息来计算无聊的数据比如贷款的金额, 也可以计算出更有趣的数据比如小球在空中滑行的轨迹。

图例 1.1 向我们展示了一个计算动能的例子,这是我们在设计游戏的物理引擎时也许会用到的一个部件。

fig.kinetic_energy
Figure 1.1: 使用Python来计算动能

把它写成一个程序的最棒的地方,就是我们可以把复杂的公式给隐藏起来。 用户只需要提供基本的信息,便可以得到用非常简单易懂的形式所表示出来的结果。 任何相似的自定义的计算器都可以运行在手机上,这样一来人们就随时随地都可以得到自己所需的计算结果。

1.2 打印

1.2.1 打印文本

程序是如何把信息打印到屏幕上的呢? 代码非常地简单,只需要一行。

print("Hello World.")
fig.printing_calculator

这个一行的程序把“Hello World”(意为,“你好,世界”) 打印到了屏幕上。 你可以在IDLE的提示符之后把这句代码输入进去,看看是否成功。也可以试着打印其他的字或者词语。电脑会非常开心地为你打印任何你想打印的内容。

在其他计算机语言中, “Hello World”程序是长什么样的呢? 可以去查阅下维基百科。 在那儿有一大组用不同的计算机语言写就的“Hello World”程序:
http://en.wikipedia.org/wiki/Hello_world_program_examples

以此可以了解到世界上原来有这么多不尽相同的计算机语言,真的非常有趣。而且通过如此简单的“Hello World”程序,我们可以对程序背后不同语言的复杂程度略之一二。

Video: The print function

要记得, Python里打印的命令非常地简单明了。只需要使用 print。 在print命令之后是一组圆括号( ). 在括号之中是我们想要打印在屏幕上的内容。使用括号来把信息传递给函数或者方程,是数学和计算机语言中都会用到的标准实现方法。

我们学数学的时候曾学到使用括号来计算表达式,比如 $sin(\theta)=cos(\frac{\pi}{2}-\theta)$. $sin$ 和 $cos$ 是函数. 传入这些函数的数据便存在括号之中。 与我们的程序唯一不同的地方,只是我们传递的是文字信息。

值得注意的是,在将被打印的文字之外使用了双引号 。如果打印的语句发现在文字外有引号,那么计算机便会打印出和引号内一样的内容。比如,这个程序会打印出2+3:

print("2 + 3")

1.2.2 打印出表达式的结果

接下来这个程序在$2+3$之外没有引号, 那么计算机便会把它视作一个数学表达式. 所以它会打印出一个5而不是2+3。

print(2 + 3)

下面这段代码会生成一个错误,因为计算机试图把 “Hello World”当作一个数学表达式去评估, 所以根本不会成功:

print(Hello World)

上面这段代码会打印出这样一个错误SyntaxError: invalid syntax ,意思是计算机在说它不懂“Hello” 和 “World” 是什么意思。

同时, 请注意这里有单引号 ' 和双引号 " 的区分。有一种常见的错误是写成了 "",看起来是一个“双”双引号。

1.2.3 打印多个内容

一个打印的语句可以同时打印出多个内容,每一个都用逗号来隔开。比如,这段代码会打印出Your new score is 1040

print("Your new score is", 1030 + 10)

下面这行代码则会打印出Your new score is 1030+10. 两个数并没有相加是因为它们在引号之中。任何引号之中的内容,计算机会视作是文本。任何引号之外的内容,计算机会当作数学表达式或者计算机代码。

print("Your new score is", "1030 + 10")
逗号应该存放在引号之中还是之外?

下面这段代码根本不会成功。这是因为在所引的文字和数学表达式1030+10之间之间,没有使用逗号来隔开。 第一眼看上去,好像有一个逗号在那儿嘛, 但是这个逗号是位于引号之中的。 用于区隔多个需要打印的条目的逗号,必须位于每一对引号之外。 如果程序员需要打印出一个逗号,那么就把它放在引号里面吧:

print("Your new score is," 1030 + 10)

而下面这段代码可行, 是因为有一个逗号把多个条目隔开来了。它会打印出:
Your new score is, 1040
请注意只有一个逗号被打印了出来。因为在引号之外的逗号只用来隔开条目,引号之内的逗号才会打印出来。第一个逗号打印出来了,第二个逗号只用来隔开条目。

print("Your new score is,", 1030 + 10)

1.3 转义码

如果引号是用来提示计算机你想打印的文本的起始和结束位置,那想真的打印出引号的时候如何实现呢? 比如说:

print("I want to print a double quote " for some reason.")

fig.escape_codes

这行代码不可行。计算机看见字符串中间那个第二个引号以为那是文本的结束, 然后它就不知道命令for some reason是什么,已经后面的引号让它更加困惑了。

所以,我们必须告诉计算机,那个中间的双引号其实是当文字来打印,不是表示字符串结束的提示符号。比如:

print("I want to print a double quote \" for some reason.")

这两个字符的组合 \"被称作一个转义码。 基本上每一种计算机语言都有它。因为反斜线符号是转义码的一部分,所以它本身必须也要转换。 比方说下面这段代码就不可行:

print("The file is stored in C:\new folder")

为什么?因为 \n是一个转义码。要打印出反斜线符号必须这要再使用反斜线来转义:

print("The file is stored in C:\\new folder")

还有一些重要的转义码我们需要知道。下面整理了一个转义码的表格:

转义码描述
\'单引号
\"双引号
\t制表符
\r回车(回到这一行的最左端)
\n新行(回到新一行的最左端)

什么是“回车”和“新行”? 试试这个例子:

print("This\nis\nmy\nsample.")

这行命令的输出结果是这样的:

This
is
my
sample.

这个\n就用来开启新的一行. 它会把计算机的“光标”指到下面的一行。计算机可以把所有的文本信息都存储在一条巨大的行上。 它就是通过这个\n 符号来知道什么时候需要把文字输出到不同的行上。

为了把事情变得更加复杂化, 不同的操作系统对于如何表示一行的结束有不同的标准定义。

转义码描述
\r\n回车+新行: 微软Windows系统
\n新行: 基于UNIX的系统,包括新的Mac系统.
\r回车:旧的Mac系统

通常你的文本编辑器会帮你处理好这些。但是微软的记事本不会,UNIX的文件在记事本中打开会看起来非常糟糕因为行末跟根本没有显示提示的符号,要么就显示成黑色的盒子。 每一个程序员需要安装一个好用的文本编辑器。我推荐Sublime或者Notepad++

1.4 注释

注释非常重要! (即使计算机会忽略它们。)

许多时候代码需要添加一些额外的解释,给读代码的人使用,帮助理解。所以,我们添加“注释”到代码之中。 注释是给读代码的人看的,不是计算机。

有两种方法去创建一条注释。第一种是使用 # 符号。 计算机会无视Python程序里任何出现在#之后的文本信息。比如:

# 这是一个注释, 它是以一个#字符开始的
# 而且计算机会无视它。

print("This is not a comment, the computer will")
print("run this and print it out.")

如果一个程序有一个#字符在引号之间,那么它不会被视作一条注释。程序员如果想让某一行代码不执行的时候就会添加一个#字符在前面。也有可能把一条注释放在行的末尾。

print("A # sign between quotes is not a comment.")

# print("This is a comment, even if it is computer code.")

print("Hi") # This is an end-of-line comment

还有一种办法,是想把多行代码想要注释掉的时候,连续使用三个单引号来划分 delimit 想要注释掉的区域。

print("Hi")
'''
This is
a
multi
line
comment. Nothing
Will run in between these quotes.
print("There")
'''
print("Done")

大多数专业的Python程序员只在用于docstrings的时候使用这种多行的注释方法。 Docstrings允许文档可以和关联的代码一同创建,之后可以自动导出到可打印的文档,网站和集成编译环境(IDE)。通常我们使用的注释,用#字符来标记就足够了。

即使读者只有写代码的你自己,添加注释也会节省时间,提高效率。添加一段注释说“处理外星球的炸弹”可以让你快速地想起那段代码是做什么的而不用去深入解读。

1.5 赋值运算符

视频:赋值运算符

我们如何存储游戏中的得分? 如何追踪敌人的血量? 我们只需要使用赋值运算符

运算符是一种符号,比如 +号和-号。 赋值运算符使用的是=号。它把一个值存到了一个变量之中,以便之后使用。下面这段代码会把数值10存到变量x之中,然后把x之中的数值打印出来。

观察下面这个例子。点击“Step”按钮来查看代码是如何运行的。

# 创建一个变量x
# 存入一个数值10给变量。
x = 10

# 这一行会打印出x中的值。
print(x)

# 这一行会打印出字母x,而不是x中的值。
print("x")

# 这一行会打印出 "x= 10"
print("x=", x)
Variables:
x=
Output:
10
x
x= 10
变量在引号之外,不在其中。

注意: 上述代码证明了打印一个 x在引号之内x在引号之外的区别。 如果一个x实在引号之内, 计算机便会直接打印出字符x。 如果这个x实在引号之外的,那么计算机便会打印出x里存放的值。 被“引号之内,引号之外”问题所困扰是学习编程过程里很常见的现象。

fig.pi

赋值运算符(一行使用= 号的代码) 不同于你在数学中学到的算术的相等概念。不要把它们想成同样的符号。在赋值运算符的左侧有且只能有一个变量。不能出现其他任何东西。

在等号/赋值运算符的右边是一个表达式. 表达式能把任何内容评估成一个值。观察下面这段代码。

x = x + 1

上述这段代码显然不能表达算术上的平等关系。 但是对于计算机来说,它是合法的,因为它是一个赋值的语句。数学等式和赋值语句是截然不同的,即使它们都拥有变量,数值,和一个等号。

上述代码语句先取了现在x的值,然后加上了1,最后把结果存回x。

试着拓展我们例子中的代码,那么下段的程序会打印出6。

x = 5
x = x + 1
print(x)

语句是按顺序执行的。计算机并不会 “往未来看。” 在下面这段代码中,计算机会在第二行打印出5, 然后在第四行打印出6。这是因为在执行到第二行的时候,加一的运算还没有运行。

x = 5
print(x) # Prints 5
x = x + 1
print(x) # Prints 6

下面的语句是合法且可以运行的,但是没有任何意义。计算机会给x加上个1,但是结果永远不会存储起来或者打印出来。

x + 1

下面的代码会打印出5而不是6,因为程序员忘记把x + 1的结果存回到变量x中了。

x = 5
x + 1
print(x)

下面的语句并不合法,因为等号的左侧不只有一个变量:

x + 1 = x

Python还有其他类型的赋值运算符。这样就可以让程序员轻松地修改变量了。比如:

x += 1

上述代码和下面这一句是等效的:

x = x + 1

相类似地,也有减法、乘法和除法的赋值运算符。

1.6 变量

变量以一个小写字母开头。

变量应该以一个小写字母来开头。 变量 能够 以一个大写字母或是下划线开头, 但这些是特殊的情况,不应当作常规命名方法。 在第一个小写字母之后,变量可以包含大写字母、小写字母,或是数字和下划线。变量不可以包含空格符。

变量名对于大小写是敏感的。这对一些没有准备的程序员可能会造成困惑。 在下面这段代码中,输出结果是6而不是5,因为有两个不同的变量xX

x = 6
X = 5
print(x)

Python的官方编程风格指南(没错, 程序员们写了本关于风格的书)上说,在Python中多个词语组成的变量名称该用下划线隔开。 例如,使用hair_style而不是hairStyle。就个人而言,如果你是我的学生,我不是很在乎这条规则, 因为像我们下一个将会介绍的语言,Java,有正好相反的规则。 我曾试着教导使用Java的风格,然后我就开始接收到Python爱好者的投诉信。这些朋友跑到我的网站然后震惊了,真的因我糟糕的风格震惊了。

琼·里弗斯和这些人无关,所以我放弃了,并开始使用更恰当的编程风格。

这里有一些变量命名的例子,有些合法,有些不合法不该被采用:

合法的变量名称 非法的变量名称 合法,但不恰当
first_name first name FirstName
distance 9ds firstName
ds9 %correct X

所有大写字母构成的变量名称像MAX_SPEED只在变量的值永远不会改变的情况下才被允许使用。 变量的值不会变化被称作常数

1.7 运算符

对于更加复杂的数学运算, 常规的数学符号都可以使用。还有一些并不常用的符号:

operator operation example equation example code
+ 加法 $3 + 2$ a = 3 + 2
- 减法 $3 - 2$ a = 3 - 2
* 乘法 $3 \cdot 2$ a = 3 * 2
/ 除法 $\frac{10}{2}$ a = 10 / 2
// Floor除法 N/A a = 10 // 3
** 指数 $2^3$ a = 2 ** 3
% 取模 N/A a = 8 % 3
Video: Operators

“Floor除法” 总会把结果近似到最近的整数。 例如, 11//2 结果是5,而不是5.5,而 99//100 会得到0。

并置的乘法在Python中不可用。下面这两行代码就无法运行:

# These do not work
x = 5y
x = 5(3/2)

我们必须使用乘法运算符来使得这两行成功执行:

# These do work
x = 5 * y
x = 5 * (3 / 2)

1.7.1 运算符间隔

在一个运算符前后可以有任意数量的空格, 计算机还是可以理解的所以请放心。比如下面三行是完全等效的:

x=5*(3/2)
x = 5 * (3 / 2)
x      =5     *(    3/   2)

Python的官方风格指南说在每一个操作符前后应该有一个空格符。 (你一定想进一步了解, 对不对? 好, Python官方的风格指南被叫做: PEP-8. 点进去会发现更多惊喜。) 在上面三行代码中, 最“有型”的莫过于第二行.

1.8 运算的顺序

Python会在评估表达式时,采用和标准数学表达式一样的运算顺序。例如这个表达式并不能正确地计算平均值:

average = 90 + 86 + 71 + 100 + 98 / 5

第一个执行的运算是98/5。所以计算机实际计算的是:
$90+86+71+100+\frac{98}{5}$

而不是我们想要的:

$\dfrac{90+86+71+100+98}{5}$

使用括号这个问题就可以被轻松解决:

average = (90 + 86 + 71 + 100 + 98) / 5

1.9 三角方程

三角方程常被用来计算正弦和余弦值。在默认环境中,Python是不知道如何计算正弦和余弦的。 但是一旦导入了相关的库之后就可以了。计算的单位是弧度。

# 导入数学库
# 这一行只在程序的顶端执行一次。

from math import *

# 用正弦和余弦计算x的值
x = sin(0) + cos(0)

1.10 自定义等式计算器

我们可以用Python写一个程序,去计算一辆开了294英里、消耗了10.5加仑汽油的平均里程数。

m = 294 / 10.5
print(m)

这个程序还可以通过使用变量来改进。这样我们就可以轻松地改变其中的数值而不用改写等式。

m = 294
g = 10.5
m2 = m / g # 这里使用了变量
print(m2)
好的变量名称非常重要

光看程序本身, 其实非常难去理解。变量 mg 没有上下文的话没有太多意义。 我们可以通过使用更确切的变量名称来使程序更加容易理解:

miles_driven = 294
gallons_used = 10.5
mpg = miles_driven / gallons_used
print(mpg)

现在, 即使一个不懂程序的人可以通过观察程序知道它在做什么。 另一个关于好的变量名和坏的变量名的差别的例子是这样的:

# 难以理解
ir = 0.12
b = 12123.34
i = ir * b

# 易于理解
interest_rate = 0.12
account_balance = 12123.34
interest_amount = interest_rate * account_balance
Video: Creating a custom calculator program

在IDLE编辑器中,想要编辑先前的某一行可以不用重新输入。只要把鼠标移到那一行并敲击“回车”键。那一行便会复制到当前的一行。

>>>提示符输入Python的代码是很慢的,而且每次只能输入一行。而且还不能保存代码,以后让别人运行。 幸好,有一种更好的办法来输入Python代码。

Python代码可以输入在脚本之中。脚本是一系列Python代码组成,并且可以一次执行。想创建一个脚本,打开一个新的窗口如所示图例 1.2.

fig.entering_a_script
Figure 1.2: Entering a script

)你可以使用其他的程序来创建你的脚本, 比如Wing IDE,或者PyCharm。这些程序,比起Python自带的IDLE编辑器,使用更加简单,而且功能更加强大。)

把刚才计算里程数的Python程序输进去,并保存好文件。把文件存到闪存,网络硬盘,或者其他地方。Python程序应该总是以.py结尾。 如所示图例1.3.

fig.saving_mpg_calculator
Figure 1.3: Saving a script

通过点击 “Run”菜单里的“Run Module”来运行刚输入的程序。试着在程序中更新不同的数值。

警惕, 常见错误!

从现在起, 几乎所有的代码都应该输入在脚本/模块之中。 请不要把你的程序输在IDLE的>>>提示符下。 这样代码无法保存。许多工作需要从头再来。 这是一个编程新手常会犯的错误。

如果可以和用户交互,向用户询问总里程数和使用的汽油量,这个程序会变得更加有用。 我们可以通过input语句来完成。 请看下面这个示例:

# 这段代码还差一点就可以工作
miles_driven = input("Enter miles driven:")
gallons_used = input("Enter gallons used:")
mpg = miles_driven / gallons_used
print("Miles per gallon:", mpg)

运行这个程序会向用户征询里程数和汽油数,但是它会产生一个奇怪的错误,如所示图例1.4.

fig.mpg_program_error
Figure 1.4: Error running MPG program

我们通过改变一点点程序来发现生成错误的理由:

miles_driven = input("Enter miles driven:")
gallons_used = input("Enter gallons used:")
x = miles_driven + gallons_used
print("Sum of m + g:", x)

运行以上程序的话,输出的结果会如图例所示1.5.

fig.mpg_incorrect_addition
Figure 1.5: Incorrect Addition

这个程序没有把两个数相加,它只是把一个数放在另一个后面。这是因为程序并不知道用户会输入数字。 用户可能会输入“Bob”和“Mary”,这两者相加会得到“BobMary” The user might enter “Bob” and “Mary”;这看起来也合理。

输入必须转换成数字

为了告诉计算机这些输入是数字, 我们有必要在input方程之外用一个 int( )或者一个float( )方法。 前者用于整数,后者用于浮点小数。

最后成功的程序是这样的:

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Explanation video: http://youtu.be/JK5ht5_m6Mk

# Calculate Miles Per Gallon
print("This program calculates mpg.")

# Get miles driven from the user
miles_driven = input("Enter miles driven:")
# Convert text entered to a
# floating point number
miles_driven = float(miles_driven)

# Get gallons used from the user
gallons_used = input("Enter gallons used:")
# Convert text entered to a
# floating point number
gallons_used = float(gallons_used)

# Calculate and print the answer
mpg = miles_driven / gallons_used
print("Miles per gallon:", mpg)

Variables:
milesDriven=
gallonsUsed=
mpg=
Output:
This program calculates mpg.
Enter miles driven: 288
Enter gallons used: 15
Miles per gallon: 19.2

这里是另一个例子, 计算一个物理的动能:

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Calculate Kinetic Energy

print("This program calculates the kinetic energy of a moving object.")
m_string = input("Enter the object's mass in kilograms: ")
m = float(m_string)
v_string = input("Enter the object's speed in meters per second: ")
v = float(v_string)

e = 0.5 * m * v * v
print("The object has " + str(e) + " joules of energy.")

为了简化程序, 我们可以把input语句嵌套在float语句之中。 范例的代码如下:

milesDriven = input("Enter miles driven:")
milesDriven = float(milesDriven)

如果只用一行代码:

milesDriven = float(input("Enter miles driven:"))

在这种情况下, input方法的输出结果直接导入到了float方法里。 两种写法都可行,只取决于程序员自己的喜好。 最重要的是,我们要能够理解和掌握两种形式。

1.11 复习检查

1.11.1 选择题小测验

Click here for a multiple-choice quiz.

1.11.2 练习表

Click here for the chapter worksheet.

1.11.3 实验

Click here for the chapter lab.


You are not logged in. Log in here and track your progress.