现在我们有了一个 Pygame 窗口,在我们优雅地将它关闭之前它会一直打开。代码清单 16-2 的第 3 行中的 [640, 480] 是窗口的大小,表示 640 像素宽、480 像素高。下面就在这里面画一些图形。按照代码清单 16-4 修改你的程序。
代码清单 16-3 画一个圆
什么是翻转
对于 Pygame 窗口中显示的所有内容,Pygame 中的显示对象(我们的显示对象名为 screen
,这在代码清单 16-3 的第 3 行创建)都会有这些内容的两个副本。这样做的原因是,开始动画时,我们希望让动画尽可能流畅,速度尽可能快。所以不必在每次对图形做一个小小的修改时都更新显示,可以做很多修改后再“翻转”(flip)到图形的新版本。这样就会一次显示所有这些修改,而不是一个接一个地出现。这样一来,我们就不会显示出只画了一半的圆(或外星人,或者其他任何东西)。
可以把这两个副本当作一个“当前屏”和一个“下一屏”。当前屏就是我们现在看到的,下一屏是完成“翻转”之后看到的。做完“下一屏”上的所有修改后,再翻转到下一屏,就能看到这些改变。
如何建立一个圆
运行代码清单 16-3 中的程序时,应该能看到如下图这样,靠近窗口左上角有一个红色的圆。
毫不奇怪,pygame.draw.circle
函数会画一个圆。必须告诉它以下 5 件事。
在哪个表面(surface)画这个圆。(在这里,要在第 3 行定义的表面上画圆,名为
screen
,这就是显示表面。)用什么颜色来画。(在这里要用红色,对应的值为 [255, 0, 0]。)
在什么位置画。(在这里要位于 [100, 100],这表示从左上角向下 100 像素再向右 100 像素的位置)。
圆的大小。(这里是 30,这是圆的半径,也就是圆心到外围边界的距离,单位是像素。)
线宽。(如果
width = 0
,圆是完全填充的,这里就采用了完全填充。)
下面再来更详细地学习这 5 个方面。
术语箱
像素(pixel)这个词是“图像元素”(picture element)的简写。这表示屏幕上或图像中的一点。如果在一个图像浏览器中查看图片,充分放大(让图像非常大),就可以看到单个的像素。下面是一张照片的正常视图和放大视图,在放大视图中可以看到像素。
哇,你眼力真好!这些小线条实际上就是像素行。一般的计算机屏幕可能有 768 行像素,每行有 1024 个像素。我们就会说这个屏幕“分辨率是 1024×768”。有些屏幕的像素更多,有些可能比这要少。
Pygame 表面
在实际生活中如果我让你画一幅画,你可能会先问“我在哪儿画?”在 Pygame 中,我们要在一个表面上画图。显示表面就是我们在屏幕上看到的表面,也就是代码清单 16-3 中的 screen
。不过 Pygame 程序可以有多个表面,可以把图像从一个表面复制到另一个表面。还可以对表面做一些处理,比如旋转表面或者调整它们的大小(让它们更大或更小)。
前面提到过,显示表面有两个副本。按软件术语来讲,我们说显示表面是双缓冲的(double-buffered)。正是因为这个原因,我们不会在屏幕上看到只画了一半的形状和图像。我们会在缓冲区里画圆、外星人或者任何东西,然后“翻转”显示表面,来显示已经完全绘制的图像。
Pygame 中的颜色
Pygame 中使用的颜色系统是很多计算机语言和程序中通用的系统,称为 RGB。这里的 R、G 和 B 分别代表红、绿和蓝。
你可能在自然课上已经学过,通过结合或混合光的三原色(红、绿和蓝)可以得到任何颜色。计算机上也采用了同样的做法。每个颜色(红、绿和蓝)对应一个从 0 到 255 的数。由一个包含 3 个整数的列表来给出颜色,每个数的范围在 0 到 255 之间。如果所有数都是 0,就没有任何颜色,这就是全黑,所以会得到黑色。如果三个数都是 255,会将 3 种颜色以最大亮度混合在一起,这就是白色。如果颜色是 [255, 0, 0],这就是纯红色,没有绿色和蓝色。纯绿就是 [0, 255, 0],纯蓝是 [0, 0, 255]。如果所有 3 个数都一样,比如 [150, 150,150],你会得到某种灰度。数字越小,灰度就越深,数字越大,灰度就越浅。
颜色名
Pygame 提供了一个命名颜色列表,如果你不想使用 [R, G, B] 记法,就可以使用这些命名颜色。定义好的颜色名有 600 多个。我不想在这里全部列出,不过如果你想看看到底有哪些颜色,可以在你的硬盘上搜索一个名为 colordict.py 的文件,然后在文本编辑器中打开这个文件。
如果你想使用这些颜色名,必须在程序最前面增加下面这行代码:
from pygame.color import THECOLORS然后,使用某个命名颜色时,可以这样做(我们的画圆例子中就是这样做的):
pygame.draw.circle(screen, THECOLORS[/"red/"],[100,100], 30, 0)
到底怎么回事?
为什么是 255 呢?每个三原色(红、绿、蓝)都有 0 到 255 共 256 个不同的值。256 这个数字有什么特别的呢?为什么不是 200、300 或者 500 呢?
8 位总共正好能表示 256 个不同的值,也就是由 1 和 0 构成的 八位数的所有可能的组合。8 位也称为一个字节,字节是有自己的地址的最小内存块。计算机就是利用地址来查找某段内存的。
这就像在街道上一样。你的房子或公寓有一个地址,不过你的房间没有地址。房子就是街道上最小的“可寻址单位”。字节则是计算机内存中最小的“可寻址单位”。
也可以用 8 位以上来表示每种颜色,不过,8 位之后可用的位数可能就到 16 位(2 个字节)了,因为不完整的字节使用起来会不太方便。事实证明,根据人眼识别颜色的方式,用 8 位来表示实际可见的颜色完全足够了。
由于有 3 个值(红、绿、蓝),每个值有 8 位,总共就是 24 位,所以这种表示颜色的方法也称为“24 位颜色表示法”。对每个像素使用 24 位,每个三原色分别使用 8 位。
如果你想试验一下,看看红色、绿色和蓝色如何结合来生成不同的颜色,可以试试 colormixer.py 程序,运行本书的安装程序时会把这个程序放在 examples 文件夹下。利用这个程序,你可以尝试红、绿、蓝的任意组合,看看能得到什么颜色。
位置——屏幕坐标
如果想在屏幕上画个东西或者放上一个东西,需要指定这个东西应当放在屏幕上的哪个位置。这里有两个数:一个对应 x 轴(水平方向),还有一个对应 y 轴(垂直方向)。在 Pygame 中,这两个数从窗口左上角的 [0, 0] 坐标开始。
看到类似 [320, 240] 的一对数时,要知道第一个数表示水平方向,也就是相对于左边界的距离。第二个数表示垂直方向,也就是相对于顶边的距离。在数学和编程中,字母 x 通常用来表示水平距离,y 常用来表示垂直距离。
我们建立了一个 640 像素宽、480 像素高的窗口。如果希望在窗口正中间画圆,需要在 [320, 240] 上绘制。这个位置离左边界 320 像素,离上边界 240 像素。
下面尝试在窗口中间画圆。运行代码清单 16-4 中的程序。
代码清单 16-4 把圆放在窗口中间
这里使用坐标 [320, 240] 作为圆心。把运行代码清单 16-3 的结果与运行代码清单 16-4 时看到的结果做个比较,看看有什么差别。
形状大小
使用 Pygame 的 draw
函数画形状时,必须指定形状的尺寸。对于圆来说,只有一个尺寸,也就是半径。而像矩形之类的形状,则必须指定长和宽。
Pygame 有一种特殊的对象,名为 rect
(这是“rectangle”(矩形)的简写),用来定义矩形区域。rect
要使用左上角坐标、宽和高来定义:
Rect(left, top, width, height)
这里同时定义了位置和大小。下面是一个例子:
my_rect = Rect(250, 150, 300, 200)
这会创建一个矩形,它的左上角距离窗口左边界 250 像素,距离窗口上边界 150 像素,宽为 300 像素,高为 200 像素。下面来试试看。
用下面这行代码替换代码清单 16-4 中的第 5 行,看看结果是什么:
矩形的位置和大小可以是一个简单的数字列表(或元组),也可以是一个 Pygame 的 Rect
对象。所以还可以把前面一行替换为下面这两行代码:
my_list = [250, 150, 300, 200]pygame.draw.rect(screen, [255,0,0], my_list, 0)
或者
my_rect = pygame.Rect(250, 150, 300, 200)pygame.draw.rect(screen, [255,0,0], my_rect, 0)
这就是最后得到的矩形。我增加了一些尺寸标注来说明每个数字分别表示什么含义。
注意这里只向 pygame.draw.rect
传递了 4 个参数,因为 rect
用一个参数就表示了位置和大小。在 pygame.draw.circle
中,位置和大小分别由两个不同的参数表示,所以需要传递 5 个参数。
像(Pygame)程序员一样思考
如果用
Rect(left, top, width, height)
创建一个矩形,还可以使用其他一些属性来移动和对齐这个 Rect:
4 条边:
top
、left
、bottom
、right
4 个角
: topleft
、bottomleft
、topright
、bottomright
每条边的中点:
midtop
、midleft
、midbottom
、midright
中心:
center
、centerx
、centery
尺寸:
size
、width
、height
这些属性只是为了提供方便。所以,如果你想移动一个矩形,让它的中心位于某个点,不必得出上坐标和左坐标分别是什么;可以直接访问中心位置。
线宽
画形状时最后需要指定的一点是线的粗细。在之前的例子中,我们使用的线宽都是 0,这会填充整个形状。如果使用不同的线宽,会看到形状的轮廓。
试着把线宽改为 2:
试试看有什么结果。再试试其他线宽。
现代艺术
想不想让计算机生成某种现代艺术?玩玩呗,试试代码清单 16-5。也可以在代码清单 16-4 的基础上做些修改,或者干脆从头开始键入。
代码清单 16-5 使用
draw.rect
实现艺术创作
import pygame, sys, randompygame.initscreen = pygame.display.set_mode([640,480])screen.fill([255, 255, 255])for i in range (100): width = random.randint(0, 250) height = random.randint(0, 100) top = random.randint(0, 400) left = random.randint(0, 500) pygame.draw.rect(screen, [0,0,0], [left, top, width, height], 1)pygame.display.fliprunning = Truewhile running: for event in pygame.event.get:if event.type == pygame.QUIT: running = Falsepygame.quit
运行这个程序,看看会得到什么。应该能得到如下图所示的结果:
你明白这个程序是怎么工作的吗?它会随机画 100 个大小不等、位置不同的矩形。为了让它更有“艺术性”,再增加一些颜色,另外将线宽也设为随机,如代码清单 16-7 所示。
代码清单 16-6 带颜色的现代艺术
运行这个程序时,每次你都会看到不同的东西。如果有看着不错的,可以给它起个富有想象力的名字,比如“机器之声”,看看能不能把它卖到你们当地的美术馆 !