Program Arcade Games
With Python And Pygame

Chapter 5: 图形学入门

fig.ellie

现在你已经能够创造循环,是时候接着学习如何创造图形了。 这个章节包含了:

5.1 计算机的坐标系统

视频:计算机图形学的坐标系统

笛卡尔坐标系, 如所示图例 5.1 (维基百科), 是通常人们绘制图形的系统。这是学校所教的系统。 计算机使用了一个相似都又略有不同的坐标系同。 理解为何有区别得需要回顾下计算机的历史。

fig.Cartesian_coordinates_2D
Figure 5.1: 笛卡尔坐标系

在80年代早期,大多数计算机系统是以文本为基础而不支持图形的。图例Figure 5.2 (维基百科) 显示了一个早期的运行在当时广受欢迎的苹果][计算机上的电子数据表。当在屏幕上给文本定位时,程序员是从顶部开始称之为第一行的。屏幕向下延展到24行共40个字符。

fig.apple3screen
Figure 5.2: 早期苹果上的文本屏幕

即使用纯文本,也是可以通过键盘上的字符来绘制非常初级的图形的。参考这个图例中的猫咪5.3画并仔细观察它是如何画出来的 。 在创作的时候,字符的位置还是从顶部的第一行开始的。

fig.simpleASCII
Figure 5.3: 文本屏幕

之后符号集扩展了,包含了盒子和其他主要绘制的形状。字符可以以不同的颜色表示了。如图例所示5.4图形更高级了。在网络上搜索 “ASCII art”虎发现更读的例子。更多的更多hp figure("fig.spacewar","chapters/05_intro_to_graphics/spacewar.png","太空战役的文本屏幕",50); ?>

一旦计算机发展到可以控制每个图形单独的像素的时候,文本为基础的坐标系统就显得过时了。

fig.Computer_coordinates_2D
Figure 5.5: 计算机的坐标系统

$x$轴和笛卡尔坐标系同中的一样。不过$y$坐标是反过来的。不像笛卡尔坐标中$y$轴的0是在图形的下端。计算机的$y$轴是在屏幕的顶端的。 随着$y$的增加,坐标位置顺着屏幕向下移动,就像多行的文字一样。参看图例5.5

同时,请注意屏幕包含了右下象限,而笛卡尔坐标系通常着重的右上象限。 在负坐标画图也是可以的,但是会不显示在屏幕内。这种技巧也是很用的,当你希望有部分的图形不被显示在屏幕上的时候。

5.2 Pygame库

视频:打开一个窗口

为了让画图更加容易,我们将会使用Pygame。 Pygame是一个其他人写哈的库,在以下方面使用起来非常方便:

第一件Pygame程序需要完成的事情就是载入和初始化Pygame库。 每一个使用Pygame的程序都应该以这几行开头:

# 导入'pygame'库中的函数
import pygame
# 初始化游戏引擎
pygame.init()

如果你还未安装Pygame,安装的指导可以查看在你开始之前章节。 如果Pygame没有被安装,你会在运行import pygame是遇到一个这样的错误:这样

不要把任何文件命名为“pygame.py”

非常重要的一点: import pygame会去寻找一个名叫pygame的库文件。 如果程序员创建了一个新的程序也叫pygame.py, 计算机会导入这个新的文件! 这将会似的任何pygame程序无法工作,直到这个新的pygame.py文件被删除为止。

5.3 颜色

下一步,我们需要添加变量来定义我们程序使用的颜色。 颜色是用三种颜色的列表来定义的:红(R),绿(G),蓝(B)。 你听说过RGB显示器吗?这就是它的来源。 Red-Green-Blue. 使用拉的显示器时,你甚至可以坐得离显示器很近从而数出单独的RGB颜色来,至少在妈妈赶来让你别坐的那么近之前。 对于现在高分辨率的显示器来说这很难做到。

RGB三元群中的每个元素都是一个0到255之间的一个数字。 0表示没有这种颜色,而255则告诉显示器显示尽可能多的这种颜色。 颜色的组合是累加的,所以如果三种颜色都定义成了满值255,显示器上显示的是白色。(这和墨水以及油画的原理不同)的

Python中的列表可以用方括号也可以用圆括号表示。 (第七章举出了两种不同类型的细节。) 列表中单独的数字是以逗号隔开的。 下面这个例子创建了一些变量,并赋予了包含三个数的列表。 这些列表之后就可以用来定义颜色。

# 定义一些颜色
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)
GREEN    = (   0, 255,   0)
RED      = ( 255,   0,   0)

为什么这些变量都是大写的? 如果记得第一章所提到的,一个不会改变其值的变量我们称作常数。 我们不会改变去改变黑色的值,所以它是一个常数。我们通过把变量设为大写来强调它们是常数。 如果我们准备去改变一个颜色的值,比如我们有个sky_color会随着太阳的生落改变颜色,那么就把它设置成全小写。

使用IDLE, 来试试定义这些变量并打印出来。 如果上述的五种颜色并不是你想要的,你可以自己定义。挑选一个颜色,可以上网去找一个“color picker”,如图例所示: 5.6. One such color picker is at:
http://www.colorpicker.com/

fig.colorpicker
Figure 5.6: Color picker

另外: 有些颜色是以16进制来定义的。你也可以以0x为开头定义十六进制的数。例如:

WHITE = (0xFF, 0xFF, 0xFF)

之后的程序在画弧形的时候需要用到$\pi$的值,所以我们可以来学习下定义一个变量来包含$\pi$的值。 (也可以从数学库中导入,例如math.pi.)

PI = 3.141592653

5.4 打开一个窗口

目前为止,我们创建的程序都只会把结果打印到屏幕上。 这些程序并不会向现代的程序一样打开任何新的窗口。 打开新窗口的代码并不复杂。下面就是一段所需的代码,它会创建一个宽700像素、高500像素的窗口:

size = (700, 500)
screen = pygame.display.set_mode(size)

为什么set_mode?为什么不是open_window?原因是这个命令比起打开一个窗口能完成更多事情。 它可以创建一个全屏幕运行的游戏。这样就可以移除开始菜单,标题栏,给游戏控制全屏幕的自由。 因为这种模式要稍微复杂一些,而且许多人更喜欢窗口化的游戏,所以我们在这儿先就跳过全屏游戏的讨论。 如果你想了解更多,可以查看pygame文档中的display命令。

另外,为什么是size = (700, 500)而不是size = 700, 500?这和我们定义颜色的时候使用圆括号是一样的道理。 Python一般情况下不能够同时把两个数字(宽和高)存入一个变量。唯一的办法就是把数字存入一个列表。 列表需要圆括号或者方括号。(技术上准确地讲, 用圆括号包围一组数字被称作一个元组或者是不可变的列表. 用方括号围绕的列表就叫列表。 一个经验丰富的Python开发者仍会把用圆括号包围的叫做列表而不是元组。 另外你能够真的用size = 700, 500,程序会把它默认成元组,但我是推荐使用圆括号。) 列别会在第七章具体讨论。

要设置一个窗口的标题(会被显示在标题栏),请使用以下的代码:

pygame.display.set_caption("Professor Craven's Cool Game")

5.5 与用户交互

只用现在所写过的代码, 程序会创建一个窗口然后就立即停摆。 用户无法和窗口交互,更别提关掉它了。 这些都是需要编写代码的。 需要添加一段让程序在循环中等待直到用户按下了“退出”。

这是程序里最复杂的一段,但是现在还不需要你完全理解。 但是有必要了解对程序的执行一个大体的概念, 所以花一些时间来学习它并发现一些不明白的问题。

# 循环直到用户退出
done = False

# 管理屏幕刷新的速率
clock = pygame.time.Clock()

# -------- 程序主循环 -----------
while not done:
    # --- 事件处理的循环
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done = True # Flag that we are done so we exit this loop

    # --- 游戏逻辑写在这

    # --- 绘图逻辑写在这

    # 首先,把屏幕清理成白色。在这之前别使用任何绘图的命令;
    # 否则也会被清除。
    screen.fill(WHITE)

    # --- 把我们画的更新到屏幕上
    pygame.display.flip()

    # --- 限制到每秒60帧
    clock.tick(60)

最终我们会添加代码来处理键盘和鼠标的点击。 这些代码会放在主循环起第九行注释的下方。 决定子弹何时发射以及物体如何移动的代码会在14行关于游戏逻辑的注释下方。 我们会在之后的章节来讨论它们。 绘图的代码会在20行屏幕涂成白色之后展开。

5.5.1 事件处理的循环

把处理的循环内容放在一起

警告! 一个程序员会遇到的最沮丧的问题之一就是把事件处理的循环搞砸了。 这些“事件处理”的代码处理所有键盘的敲击,鼠标的点击,和一些其他类型的事件。 例如你的循环可能是这个样子的:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("User asked to quit.")
        elif event.type == pygame.KEYDOWN:
            print("User pressed a key.")
        elif event.type == pygame.KEYUP:
            print("User let go of a key.")
        elif event.type == pygame.MOUSEBUTTONDOWN:
            print("User pressed a mouse button")

事件们(例如按下按键) 都存在一个列表里。程序使用了一个forloop来循环每个事件。然后使用一系列if语句来判断哪种类型的事情发生了, 那么具体那个事件的处理器就放在那个if语句内。

所有的if语句应该放在一起,在同一个for循环之内。 在复制和粘贴代码的时候,容易犯的一个错误就是没有把不同程序里的循环合并起来,而是存在了两个不同的事件循环。

    # 这是一个循环
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("User asked to quit.")
        elif event.type == pygame.KEYDOWN:
            print("User pressed a key.")
        elif event.type == pygame.KEYUP:
            print("User let go of a key.")

    # 这里程序员复制来了另一个事件循环。这就出问题了。
    # 这就出问题了。
    # 有些事件已经处理过了。
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("User asked to quit.")
        elif event.type == pygame.MOUSEBUTTONDOWN:
            print("User pressed a mouse button")

第二行中的for循环抓取了所有的用户事件。 第十三行中的for循环获取不了任何事件因为它们已经被先前的循环处理过了。

另一个典型的问题是先开始绘图,然后试着在事件循环里完成:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("User asked to quit.")
        elif event.type == pygame.KEYDOWN:
            print("User pressed a key.")


    pygame.draw.rect(screen, GREEN, [50,50,100,100])

    # T这是处理事件的代码。但是它并不在
    # 'for'循环里。所以它运行起来并不可靠。
    if event.type == pygame.KEYUP:
        print("User let go of a key.")
    elif event.type == pygame.MOUSEBUTTONDOWN:
        print("User pressed a mouse button")

这就会产生忽略了一些键盘和鼠标命令的问题。为什么? for循环处理列表中的所有事件。所以如果有两个键命中了,for循环将会处理两次。但是在上面的例子中。 if语句并不在for里面。如果有多个事件,if语句只会执行最后一次事件,而不是所有的事件。

5.5.2 每一帧的处理

处理游戏每一帧的基本逻辑和顺序是:

如果这些步骤不混在一起,会使得程序更加容易阅读和理解。 不要做一些计算,画一些图,再做一些计算,再画一些图。 另外,注意观察这和第一章中的计算器有哪些相似之处。 获得用户输入,运行计算,输出答案。样式在这里也是适用的。

在屏幕上绘制图形的代码发生砸while循环内。 把时钟设成10, 窗口内的内容便会每一秒绘制十次。 如果发生的太快那么计算机会被拖慢因为计算机一直在更新屏幕。 如果这不在循环内,那么屏幕无法重新绘制。 如果绘制是在循环之外,屏幕也许会先显示图形,但是一旦窗口最小化或者另一个窗口把这这个窗口盖住了,图形不会重新出现。

5.6 结束程序

现在,在IDLE里运行这个Pygame程序时,点击“close”按钮仍然会造成程序崩溃。 这就很不方便,因为需要点击无数多次才能关闭一个崩溃的程序。

问题在于,即使循环已经退出了,程序还是没有告诉计算机去关闭窗口。 使用下面这条命令,程序会关闭任意开着的窗口并退出。

pygame.quit()

5.7 清理屏幕

下面的代码会用一个白色的背景清理窗口中的任何物体。 请记得变量WHITE是用3个RGB值组成的列表。

# 清理屏幕并设置背景色
screen.fill(WHITE)
视频:绘制线条

这一步需要在任何绘制命令执行前完成。在程序画完图形之后清理屏幕会造成用户只看到一个空白的屏幕。

当一个窗口创建的时候它有一个黑色的背景。但还是有必要去清理屏幕,因为可能还有一些事件需要先清理。一个程序不应该在黑色的画布上绘图。

5.8 更新屏幕

非常重要的一点!你必须在画完之后更新显示屏。 计算机在你画图的时候不会显示图形因为那样会造成屏幕持续闪烁。 它会等到全部画完之后再显示到屏幕上。 下面的命令会把图形“翻转”到屏幕上。

如果遗漏了这条命令就意味着程序依然只显示一个空把的屏幕。任何在这次翻转之后画的代码都不会被显示。

# 把我们所画的更新到屏幕上。
pygame.display.flip()

5.9 打开一个空白窗口

让我们把所有讨论过的内容放到一个完成的程序里。 下面的代码可以用作一个Pygame程序的基本模板。 它会打开一个空把的窗口然后等待用户按下关闭按钮。

在网站上,如果你点击“举例”按钮你可以选择 “图形举例”并找到这个文件 pygame_base_template.py.

"""
 Pygame base template for opening a window

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

 Explanation video: http://youtu.be/vRB_983kUMc
"""

import pygame

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

pygame.init()

# Set the width and height of the screen [width, height]
size = (700, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("My Game")

# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# -------- Main Program Loop -----------
while not done:
    # --- Main event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    # --- Game logic should go here

    # --- Screen-clearing code goes here

    # Here, we clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.

    # If you want a background image, replace this clear with blit'ing the
    # background image.
    screen.fill(WHITE)

    # --- Drawing code should go here

    # --- Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # --- Limit to 60 frames per second
    clock.tick(60)

# Close the window and quit.
pygame.quit()

5.10 绘图入门

fig.castle

这有一个你可以绘制的物体的清单:
http://www.pygame.org/docs/ref/draw.html
程序能够画出矩形,多边形,圆形,椭圆形,弧线和直线。 我们也会谈到如果显示带文字的图形。位图图形比如图片会在第十一章介绍。 如果你决定去看了那个pygame的参考介绍,你可呢会看到一个类似的函数定义:

pygame.draw.rect(Surface, color, Rect, width=0): return Rect

一个常见的造成困惑的地方就是这一行代码中所说的width=0。 这里的意思是如果你不提供一个宽度,默认会设置成0。因此这个函数会调用:

pygame.draw.rect(screen, RED, [55, 500, 10, 5])

和下面这个函数的调用是一样的:

pygame.draw.rect(screen, RED, [55, 500, 10, 5], 0)

: return Rect在告诉你函数会返回一个矩形,和你传递进去的类型一样。 你也可以忽视它。

不会工作的,则是试着去复制这一杨并把width=0放在了引号里。

# 这段代码会失败,而且计算机提示给你的错误
# 是非常的难理解。
pygame.draw.rect(screen, RED, [55, 500, 10, 5], width=0)

5.11 绘制直线

下面这段代码展示了如果在屏幕上画出一条直线。 It will draw on the screen a green line from 它会在屏幕上的(0, 0)到(100, 100)画出一条绿色的直线,5个像素宽。 请记得GREEN是一个三个RGB值组成的列表变量。

# 从(0, 0)到(100, 100)画出一条绿色的直线
# 5个像素宽
pygame.draw.line(screen, GREEN, [0, 0], [100, 100], 5)

使用先前的基本模板,并添加一些代码便可以画出直线。 阅读注释来了解安排代码的具体位置。 试着多条不同粗细、颜色和位置的直线。

5.12 使用循环和偏置来画线

程序可以不停地重复。 下面这段代码就用一个循环不停地在画线。 用这个办法就可以画出多行直线,甚至一辆完整的汽车。

把画直线的命令放在一个循环只在可以画出多条直线到屏幕上。但这里有一个关键点。 如果每条先都是相同的起始和结束的位置,那么每条新的线就会覆盖在其他线之上。 这样寄会看起来只画了一条线出来。

为了解决这个问题,我们需要在每一次循环的时候给坐标点设置一些偏置值。 所以第一次经过循环的时候变量的 y_offset是0。下面的这行代码就会从(0,10)画到(100, 110). 下一次经过循环的时候y_offset会增加10。这样会似的下一条线会从新的坐标 (0, 20)到(100, 120)。这样子继续下去每条线都会向下偏移10个像素。

# 从(0, 10)到(100, 110)绘制多条直线
# 5个像素宽,使用while循环
y_offset = 0
while y_offset < 100:
    pygame.draw.line(screen,RED,[0,10+y_offset],[100,110+y_offset],5)
    y_offset = y_offset + 10

同样地,如果用for循环来写会更简单:

# 从(0, 10)到(100, 110)绘制多条直线
# 5个像素宽,使用while循环
for y_offset in range(0, 100, 10):
    pygame.draw.line(screen,RED,[0,10+y_offset],[100,110+y_offset],5)

运行这段代码并试着使用不同的偏置值。 尝试不同的值直到最佳效果。

例如,这是一个使用正弦和余弦以及更复杂的一组偏置值来绘制如下的图形5.7

for i in range(200):

	radians_x = i / 20
	radians_y = i / 6

	x = int( 75 * math.sin(radians_x)) + 200
	y = int( 75 * math.cos(radians_y)) + 200

	pygame.draw.line(screen, BLACK, [x,y], [x+5,y], 5)
fig.complex_offsets
Figure 5.7: 复杂的偏置值

多个元素可以在同一个for循环中完成,如下例所示5.8.

    for x_offset in range(30, 300, 30):
        pygame.draw.line(screen,BLACK,[x_offset,100],[x_offset-10,90],2)
        pygame.draw.line(screen,BLACK,[x_offset,90],[x_offset-10,100],2)
fig.multiple_x
Figure 5.8: 多个X

5.13 绘制矩形

视频:如何绘制矩形和椭圆

在绘制矩形时,计算机需要知道左上角的坐标(原点),以及高度和宽度。

图例5.9展示了一个矩形(和一个椭圆,稍微会介绍到),原点在 (20, 20),宽度是250,高度是100。当定义一个矩形的时候,计算机就需要这样4个按照(x, y, width, height)构成的列表参数。

fig.ellipse
Figure 5.9: 绘制一个椭圆

下面这段代码绘制了这个矩形。 列表中的前两个数字(20, 20)定义了左上角的位置。 接下来的两个数字定义了宽度250像素,以及高度100像素。

最后的2定义了一条宽为2像素的直线。数字越大,围绕矩形的直线越粗。 如果这个数是0,那么矩形就没有边。而只由定义的颜色填满。

# Draw a rectangle
pygame.draw.rect(screen,BLACK,[20,20,250,100],2)

5.14 绘制一个椭圆

绘制椭圆就想绘制矩形类似。 一个矩形的边界先定义和,然后计算机会在这些边界里绘制椭圆。

最画椭圆时最常见的错误是以椭圆的中心作为圆始的点 实际上,没有一样物体是从中心作为起始点开始画的;而是包含椭圆的矩形的左上角。

回顾下图例5.9,我们可以看到一个宽为250像素高为100像素的椭圆。 左上角的坐标是(20, 20);但实际上没有东西画在这个(20, 20)坐标上。 如果同时画两个相互覆盖的椭圆,我们可以更容易看明白它是如何定义的。

# 绘制一个椭圆,使用一个矩形来作为外围边界
pygame.draw.ellipse(screen, BLACK, [20,20,250,100], 2)

5.15 绘制一条弧线

视频:如何绘制弧线

如果只需要椭圆的一个部分该如何绘制? 我们可以使用arc command。 这个名利类似于命令kbd>ellipse命令,只不过它包含了要画弧线的起始和结束角度。 角度是以弧度作为单位制的。

fig.arc
Figure 5.10: 弧形

下面这段代码绘制了一个圆形在四个象限中的四段弧线。 每一个象限使用了不同的颜色来区分。结果如图例所示5.10.

# 绘制椭圆中的部分弧线。
# 使用了弧度拉决定弧线的角度。来pygame.draw.arc(screen, GREEN, [100,100,250,200],  PI/2,     PI, 2)
pygame.draw.arc(screen, BLACK, [100,100,250,200],     0,   PI/2, 2)
pygame.draw.arc(screen, RED,   [100,100,250,200],3*PI/2,   2*PI, 2)
pygame.draw.arc(screen, BLUE,  [100,100,250,200],    PI, 3*PI/2, 2)

5.16 绘制多边形

视频:如何绘制多边形入会

下面这行代码绘制了一个多边形。三角形是以(100, 100) (0, 200)和(200, 200)三个点定义的。 我们可以列出任意个我们需要的点。值得注意的是每个点是如何表示的。每个点是一个两个数字组成的列表,这些点本身又嵌套在一个包含它们的另一个列表中。 这段代码的效果如下图所示5.11.

fig.triangle
Figure 5.11: 多边形
# 使用多边形命令绘制三角形使用ygame.draw.polygon(screen, BLACK, [[100,100], [0,200], [200,200]], 5)

5.17 绘制文本

视频:如何绘制文本

文本稍稍复杂些。有三件事情需要完成。 首先,程序需要创建一个包含字型(大小,字体)信息的变量。

然后,程序需要创建一个文本的图像。 一种办法是是把它想成程序刻好了一个所需要字母构成的"印章", 准备印刻在纸上。

第三步就是告诉程序这个印章该印(blit)在画布(也就锁屏幕也就是个位置。

这里是一个例子是以个

# Select the font to use, size, bold, italics
font = pygame.font.SysFont('Calibri', 25, True, False)

# 渲染文本。"True"表示反锯齿的文本。
# Black锁颜色。 变量BLACK是之前定义的
# 列表变量[0, 0, 0]
# 注意: 这一杭创建了一行字母构成的图像,
# 但是还没有显示在屏幕上。
text = font.render("My text",True,BLACK)

# 把图像以250x250的大小绘制在屏幕上
screen.blit(text, [250, 250])

想在屏幕锁打印出分数屏幕这会稍微复杂一些。遮掩是不工作的:

text = font.render("Score: ", score, True, BLACK)

为什么? 程序并不能像print语句一样往font.render里添加额外的参数。 只嫩传递一个字符串,因此实际的分数值需要附在“Score: ”字符串之后。但是这样仍然无法工作:

text = font.render("Score: " + score, True, BLACK)

如果分数是一个整数变量,计算机并不知道如何把它添加到一个字符串里。 你,程序作者,必须把分数转换成字符串。像这样把字符串连在一起:

text = font.render("Score: " + str(score), True, BLACK)

现在你知道了如何打印分数。入股你想打印一个计时器,那就需要打印出一些格式,之后的章节会讨论到。 可以查看在线的example code中的中kbd>timer.py 例子:
ProgramArcadeGames.com/python_examples/f.php?file=timer.py

5.18 完整程序的清单

这是一份这一章所讨论的所以程序的清单。这个程序,连同其他程序,可以在这里下载:
ProgramArcadeGames.com/index.php?chapter=example_code

fig.program_result
Figure 5.12: 示例程序的结果
"""
 Simple graphics demo

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

"""

# Import a library of functions called 'pygame'
import pygame

# Initialize the game engine
pygame.init()

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

PI = 3.141592653

# Set the height and width of the screen
size = (400, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Professor Craven's Cool Game")

# Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()

# Loop as long as done == False
while not done:

    for event in pygame.event.get():  # User did something
        if event.type == pygame.QUIT:  # If user clicked close
            done = True  # Flag that we are done so we exit this loop

    # All drawing code happens after the for loop and but
    # inside the main while not done loop.

    # Clear the screen and set the screen background
    screen.fill(WHITE)

    # Draw on the screen a line from (0,0) to (100,100)
    # 5 pixels wide.
    pygame.draw.line(screen, GREEN, [0, 0], [100, 100], 5)

    # Draw on the screen several lines from (0,10) to (100,110)
    # 5 pixels wide using a loop
    for y_offset in range(0, 100, 10):
        pygame.draw.line(screen, RED, [0, 10 + y_offset], [100, 110 + y_offset], 5)


    # Draw a rectangle
    pygame.draw.rect(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an ellipse, using a rectangle as the outside boundaries
    pygame.draw.ellipse(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an arc as part of an ellipse.
    # Use radians to determine what angle to draw.
    pygame.draw.arc(screen, BLACK, [20, 220, 250, 200], 0, PI / 2, 2)
    pygame.draw.arc(screen, GREEN, [20, 220, 250, 200], PI / 2, PI, 2)
    pygame.draw.arc(screen, BLUE, [20, 220, 250, 200], PI, 3 * PI / 2, 2)
    pygame.draw.arc(screen, RED, [20, 220, 250, 200], 3 * PI / 2, 2 * PI, 2)

    # This draws a triangle using the polygon command
    pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)

    # Select the font to use, size, bold, italics
    font = pygame.font.SysFont('Calibri', 25, True, False)

    # Render the text. "True" means anti-aliased text.
    # Black is the color. This creates an image of the
    # letters, but does not put it on the screen
    text = font.render("My text", True, BLACK)

    # Put the image of the text on the screen at 250x250
    screen.blit(text, [250, 250])

    # Go ahead and update the screen with what we've drawn.
    # This MUST happen after all the other drawing commands.
    pygame.display.flip()

    # This limits the while loop to a max of 60 times per second.
    # Leave this out and we will use all CPU we can.
    clock.tick(60)

# Be IDLE friendly
pygame.quit()

5.19 复习

5.19.1 选择题小测验

Click here for a multiple-choice quiz.

5.19.2 练习表

Click here for the chapter worksheet.

5.19.3 实验

Click here for the chapter lab.


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