python碰撞检测与鼠标/键盘的输入

碰撞检测与鼠标/键盘的输入

本章的主要内容:
● 碰撞检测;
● 当遍历一个列表时切勿对其进行修改;
● Pygame 中的键盘输入;
● Pygame 中的鼠标输入。

碰撞检测负责计算屏幕上的两个物体何时彼此接触(即发生碰撞)。例如,倘若玩家接触到一个敌人,其可能会损失生命值。抑或,当玩家接触到金币时,程序需要能够知晓此情形,以便玩家能够自动地将金币拾起。碰撞检测能够辅助判断游戏角色是立于地面之上,还是漂浮于空中。

在我们的游戏中,碰撞检测将判别两个矩形是否相互重叠。下一个示例程序将会介绍这种基本的技术。

在本章的后续部分,我们将会看到 Pygame 程序如何接收用户通过键盘和鼠标的输入。这相较于我们在文本程序中调用 input()函数要更为复杂一些。但在 GUI 程序中,使用键盘会更具交互性。而在文本游戏中使用鼠标几乎是不可能的。这两个概念将会使游戏更富有趣味!

19.1 Collision Detection 程序的源代码

众多代码与 Animation 程序相仿,因而我们会略去移动和弹跳这部分代码的阐释(请参阅第 18 章的 Animation 程序)。一个“大块头”将会于窗口中蹦跳。Rect 对象的一个列表将用以表示食物方块。

在游戏循环的每一次迭代中,程序都会读取列表中的每个 Rect 对象,并在窗口上绘制一个绿色的方块。每经过游戏循环的 40 次迭代,就会新增一个 Rect 对象至列表中,以令屏幕中始终有新的食物方块。

用一个字典来表征“大块头”。此字典有一个称作’rect’的键(其值为 pygame.Rect 对象)以及一个称作’dir’的键(其值为方向常量之一,恰似 Animation 程序中所运用的方向常量)。

由于“大块头”在窗口中蹦跳,我们将判别它是否与任何食物方块发生碰撞。若是如此,我们需要删除食物方块,以使得屏幕上不再绘制这个食物方块。看上去,仿若“大块头”“吃掉了”窗口中的食物方块。

将如下内容输入至一个新的文件,并将其保存为 collisionDetection.py 。倘若输入这些代码后出现差错,在线 diff 工具,把您的代码与书中的代码进行对比。

19.1 Collision Detection 程序的源代码 285 collisionDetection.py

1. import pygame, sys, random

2. from pygame.locals import *

3.

4. def doRectsOverlap(rect1, rect2):

5. 对于 a, b 于 [(rect1, rect2), (rect2, rect1)] 中:

6. 检查 a 的角点是否在 b 内部

7. 若 ((isPointInsideRect(a.left, a.top, b)) 或

8. (isPointInsideRect(a.left, a.bottom, b)) 或

9. (isPointInsideRect(a.right, a.top, b)) 或

10. (isPointInsideRect(a.right, a.bottom, b))):

11. 返回 True

12.

13. 返回 False

14.

15. def isPointInsideRect(x, y, rect):

16. 若 (x > rect.left) 且 (x < rect.right) 且 (y > rect.top) 且 (y < rect.bottom):

17. 返回 True

18. 否则:

19. 返回 False

20.

21.

22. 初始化 pygame

23. pygame.init()

24. mainClock = pygame.time.Clock()

25.

26. 设置窗口

27. WINDOWWIDTH = 400

28. WINDOWHEIGHT = 400

29. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)

30. pygame.display.set_caption(‘Collision Detection’)

31.

32.

设置方向变量

1. DOWNLEFT = 1

2. DOWNRIGHT = 3

3. UPLEFT = 7

4. UPRIGHT = 9

5.

6. MOVESPEED = 4

7.

8. 设定颜色

9. BLACK = (0, 0, 0) 碰撞检测与鼠标/键盘的输入 286

10. GREEN = (0, 255, 0)

11. WHITE = (255, 255, 255)

12. 设定弹球和食物的数据结构

13. foodCounter = 0

14. NEWFOOD = 40

15. FOODSIZE = 20

16. bouncer = {‘rect’: pygame.Rect(300, 100, 50, 50), ‘dir’: UPLEFT}

17. foods = []

18. 对于 i 在范围(20)内:

19. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

20. 运行游戏循环

21. 当 True 时:

22. 检查 QUIT 事件

23. 对于 event 在 pygame.event.get() 中:

24. 若 event.type 为 QUIT:

25. pygame.quit()

26. sys.exit()

27.

foodCounter 增加 1

1. 若 foodCounter 大于等于 NEWFOOD:

2. 增添新的食物

3. foodCounter = 0

4. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

5. 将黑色背景绘制于表面之上

6. windowSurface.fill(BLACK)

7. 移动弹球的数据结构

8. 若 bouncer[‘dir’] 为 DOWNLEFT:

9. bouncer[‘rect’].left 减少 MOVESPEED

10. bouncer[‘rect’].top 增加 MOVESPEED

11. 若 bouncer[‘dir’] 为 DOWNRIGHT:

12. bouncer[‘rect’].left 增加 MOVESPEED

13. bouncer[‘rect’].top 增加 MOVESPEED

14. 若 bouncer[‘dir’] 为 UPLEFT:

15. bouncer[‘rect’].left 减少 MOVESPEED

16. bouncer[‘rect’].top 减少 MOVESPEED

17. 若 bouncer[‘dir’] 为 UPRIGHT:

18. bouncer[‘rect’].left 增加 MOVESPEED

19. bouncer[‘rect’].top 减少 MOVESPEED

Collision Detection 程序的源代码 287

1. 检查弹球是否移出了窗口

2. 若 bouncer[‘rect’].top 小于 0:

3. 弹球已越过顶部

4. 若 bouncer[‘dir’] 为 UPLEFT:

5. bouncer[‘dir’] = DOWNLEFT

6. 若 bouncer[‘dir’] 为 UPRIGHT:

7. bouncer[‘dir’] = DOWNRIGHT

8. 若 bouncer[‘rect’].bottom 大于 WINDOWHEIGHT:

9. 弹球已越过底部

10. 若 bouncer[‘dir’] 为 DOWNLEFT:

11. bouncer[‘dir’] = UPLEFT

12. 若 bouncer[‘dir’] 为 DOWNRIGHT:

13. bouncer[‘dir’] = UPRIGHT

14. 若 bouncer[‘rect’].left 小于 0:

15. 弹球已越过左侧

16. 若 bouncer[‘dir’] 为 DOWNLEFT:

17. bouncer[‘dir’] = DOWNRIGHT

18. 若 bouncer[‘dir’] 为 UPLEFT:

19. bouncer[‘dir’] = UPRIGHT

20. 若 bouncer[‘rect’].right 大于 WINDOWWIDTH:

21. 弹球已越过右侧

22. 若 bouncer[‘dir’] 为 DOWNRIGHT:

23. bouncer[‘dir’] = DOWNLEFT

24.

若 bouncer[‘dir’] 为 UPRIGHT:
109. bouncer[‘dir’] = UPLEFT

1. 将弹球绘制于表面之上

2. pygame.draw.rect(windowSurface, WHITE, bouncer[‘rect’])

3. 检查弹球是否与任何食物方块相交。

4. 对于 foods 中的 food:

5. 若 doRectsOverlap(bouncer[‘rect’], food):

6. foods.remove(food)

7. 绘制食物

8. 对于范围(len(foods))内的 i:

9. pygame.draw.rect(windowSurface, GREEN, foods[i])

10. 将窗口绘制于屏幕之上

11. pygame.display.update()

12. mainClock.tick(40)

程序运行情况如图 19 - 1 所示。“大块头”方块将会在窗口中往复弹跳。当它与绿色的食物方块碰撞时,食物方块会从屏幕上消失。

碰撞检测与鼠标/键盘的输入 288

图 19 - 1 Collision Detection 程序的动态截图

导入模块

1. import pygame, sys, random

2. from pygame.locals import *

Collision Detection 程序导入的内容和第 18 章中

Animation 程序一样,此外,它还导入了 random 模块。

19.2 Collision Detection 算法

1. def doRectsOverlap(rect1, rect2):
要实施碰撞检测,需存在一个能够判别两个矩形是否相互碰撞的函数。图 19 - 2 展示了发生碰撞的矩形与未发生碰撞的矩形。doRectsOverlap() 接收两个 pygame.Rect 对象作为参数。倘若两个矩形发生碰撞,函数返回 True;若两个矩形未发生碰撞,函数则返回 False。存在一条简单的规则用于判定矩形是否碰撞。审视两个矩形的 4 个角中的每一个角。倘若这 8 个角中至少有一个角处于另外一个矩形之内,那么,我们便可知晓两个矩形发生了碰撞。我们能够依据此事实来确定 doRectsOverlap() 函数返回 True 还是 False。

2. for a, b in [(rect1, rect2), (rect2, rect1)]:

3. 检查 a 的角点是否在 b 内部

4. 若 ((isPointInsideRect(a.left, a.top, b)) 或
图 19 - 2 碰撞的矩形(左边)和没有碰撞的矩形(右边)的示例

Collision Detection 算法 289

1. (isPointInsideRect(a.left, a.bottom, b)) 或

2. (isPointInsideRect(a.right, a.top, b)) 或

3. (isPointInsideRect(a.right, a.bottom, b))):

1. 返回 True

第 5 行到第 11 行的代码用于判别一个矩形的角是否处于另一个矩形之中。稍后,我们将创建一个名为 isPointInsideRect() 的函数,若点的 XY 坐标位于矩形内,该函数则返回 True。针对 8 个角中的每一个角调用此函数,倘若任何一次调用返回 True,or 操作符便会致使整个条件为 True。

doRectsOverlap() 的参数为 rect1 和 rect2。首先判别 rect1 的角是否在 rect2 中,而后判别 rect2 的角是否在 rect1 中。无需重复这段代码以判别 rect1 和 rect2 中的所有 4 个角。仅需重复运用第 7 到第 10 行的 a 和 b 即可。第 5 行 for 的循环采用了多变量赋值。在第 1 次迭代中,将 rect1 设为 a,将 rect2 设为 b。在循环的第 2 次迭代中,反之将 rect2 设为 a,将 rect1 设为 b。

1. 返回 False

若第 11 行代码未返回 True,那么 8 个角均不在其他矩形之中。在此示例中,矩形未发生碰撞,第 13 行代码返回 False。

19.2.1 判断一个点是否在一个矩形中

1. def isPointInsideRect(x, y, rect):

2. 若 (x 大于 rect.left) 且 (x 小于 rect.right) 且 (y 大于 rect.top) 且 (y 小于 rect.bottom):

3. 返回 True

doRectsOverlap() 函数调用了 isPointInside Rect() 函数。

若传入的参数中 XY 坐标处于作为第 3 个参数传入的 pygame.Rect 对象之内,isPointInsideRect() 函数将返回 True。反之,此函数返回 False。

图 19 - 3 为一个矩形和若干点的示例图。这些点和矩形角的坐标分别被标记了出来。

若以下 4 个条件均为 True,那么点便在矩形之中:
● 点的 X 坐标大于矩形左边的 X 坐标;
● 点的 X 坐标小于矩形右边的 X 坐标;
● 点的 Y 坐标大于矩形上边的 Y 坐标;

图 19 - 3 矩形之内和之外的点的坐标示例。点(50, 30)、(85, 30)和(50, 50)皆在矩形之内,其他点均在矩形之外

● 点的 Y 坐标小于矩形下边的 Y 坐标。

若任意一个条件为 False,那么点处于矩形之外。第 16 行运用 and 操作符,将所有这 4 个条件组合至一条 if 语句之中。

1. 否则:

2. 返回 False

doRectsOverlap() 函数调用 isPointInsideRect() 函数,以判别两个 pygame.Rect 对象中的任何角是否处于另一个矩形之中。这两个函数提供了在两个矩形之间进行碰撞检测的功能。

19.2.2 pygame.time.Clock 对象和 tick() 方法

第 22 行到第 43 行所做之事与第 18 章中的 Animation 程序所做之事相同:初始化 Pygame、设定 WINDOWHEIGHT 和 WINDOWWIDTH 并指定颜色和方向常量。

然而,第 24 行是新的代码:

1. mainClock = pygame.time.Clock()

在之前的 Animation 程序中,一次 time.sleep(0.02) 函数调用能够减缓程序的执行速度,使得程序不会运行得过快。

time.sleep() 的弊端在于,对于较慢的计算机,其暂停时间过长,而对于较快的计算机,暂停时间又过短。pygame.time.Clock 对象能够针对任何计算机暂停适宜的时间。第 125 行于游戏循环中调用了 mainClock.tick(40) 函数。此次对 Clock 对象的 tick() 方法的调用,会等待足够长的时间,从而无论计算机速度快慢,均能每秒迭代 40 次。这切实保障了游戏的运行速度不会超出预期。在游戏循环中仅能调用 tick() 一次。

19.2.3 创建窗口和数据结构

1. 构建弹球和食物的数据结构

2. foodCounter = 0

3. NEWFOOD = 40

4. FOODSIZE = 20

第 46 行到第 48 行的代码为屏幕上的食物方块创建了一些变量。foodCounter 的初始值设定为 0,NEWFOOD 的初始值为 40,FOODSIZE 的初始值为 20。

1. bouncer = {‘rect’: pygame.Rect(300, 100, 50, 50), ‘dir’: UPLEFT}

第 49 行创建了一个名为 bouncer 的新数据结构,它是包含两个键的一个字典。’rect’ 键拥有表示“大块头”的大小和位置的一个 pygame.Rect 对象。’dir’ 键所拥有的值表示当前“大块头”移动的一个方向。“大块头”的移动方式与第 18 章的 Animation 程序中的积木的移动方式相同。

1. foods = []

2. 对于范围(20)内的 i:

3. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

程序将运用 foods 中的 Rect 对象的一个列表来记录每一个食物方块。

第 51 行和第 52 行的代码创建了在屏幕上随机分布的 20 个食物方块。能够运用 random.randint() 函数获取随机的 XY 坐标。

在第 52 行,我们将调用 pygame.Rect() 构造函数以返回一个新的 pygame.Rect 对象。该对象将表征食物方块的位置与大小。pygame.Rect() 函数的前两个参数为左上角的 XY 坐标。我们期望获取一个随机坐标,其处于 0 和窗口大小减去食物方块大小所得结果之间。倘若使用了处于 0 到窗口大小之间的随机坐标,那么可能会将食物方块完全置于窗口之外,如图 19 - 4 所示。

图 19 - 4 一个宽 20 像素、高 20 像素的矩形,若放置于宽 400 像素、高 400 像素的窗口中的左上角,其坐标位置为(400, 200),那么此矩形将会放置在窗口之外。要将其置于窗口之内,左上角的位置应当是(380, 200)

pygame.Rect() 函数的第 3 个参数是包含食物方块宽和高的一个元组。宽和高皆为常量 FOODSIZE 中的值。

19.2.4 在屏幕上绘制“大块头”

第 71 行到第 91 行的代码创建了在窗口中移动并且会从窗口边缘弹回的“大块头”。这些代码与第 18 章 Animation 程序中的第 44 行到第 83 行代码相仿,此处不再赘述。

碰撞检测与鼠标/键盘的输入 292

1. 将“大块头”绘制于表面之上

2. pygame.draw.rect(windowSurface, WHITE, bouncer[‘rect’])

在移动了“大块头”之后,第 112 行代码于新的位置绘制它。windowSurface 作为第一个参数传递,告知 Python 要将矩形绘制于哪个 Surface 对象之上。变量 WHITE(存储的值为(255, 255, 255))告知 Python 要绘制一个白色的矩形。

存储于 bouncer 字典中键为’rect’的 Rect 对象,指明了要绘制的矩形的位置与大小。

19.2.5 食物方块的碰撞

1. 检查“大块头”是否与任何食物方块相交。

2. 对于 foods 中的每个食物方块(food):

在绘制食物方块之前,判别“大块头”是否已与任何食物方块发生重叠(碰撞)。若有,则从 foods 列表中移除食物方块。这是由于 Python 不会再绘制“大块头”已经“吃掉”的任何食物方块。

在 for 循环的每次迭代中,foods(复数)列表中当前的食物方块存于变量 food(单数)中。

19.3 当遍历一个列表时,切勿修改该列表

请注意,这个 for 循环存在一个细微的差异。倘若仔细审视第 116 行代码,会发现其并非对 foods 进行迭代,实际上是对 foods[:] 进行迭代。

还记得切片是如何运作的吗?foods[:2] 会获取从索引为 2 的元素起始(但不涵盖该元素)的元素列表的一个副本。foods[3:] 会获取从索引为 3 的元素至列表末尾的一个列表副本。foods[:] 将会获取从起始元素至结束元素的元素列表的一个副本。

基本上,foods[:]创建了一个全新的列表,其中的元素乃是 foods 中所有元素的副本。相较于在 Tic Tac Toe 游戏中 getBoardCopy() 函数的操作方式,此乃复制列表的一种更为简易的方法。

当对列表进行迭代时,不可向列表中增添元素或者从列表中移除元素。倘若 foods 列表始终处于变化状态,那么 Python 或许无法记录 food 变量的下一个值。试想一下,若在数一个罐子中的糖豆数量之时,同时有人向其中添加糖豆或者从中取出糖豆,要数清罐子中的糖豆将会是何等艰难。

然而,倘若对列表的一个副本进行迭代,那么向原始列表中添加元素或者从中删除元素皆不成问题。

键盘输入程序的源代码 293

19.3.1 删除食物方块

1. 若 doRectsOverlap(bouncer[‘rect’], food):

2. foods.remove(food)

第 116 行运用了 doRectsOverlap() 函数。若表示“大块头”和当前食物方块的两个矩形发生重叠,doRectsOverlap() 函数将返回 True,并且第 117 行将会从 foods 列表中删除产生重叠的食物方块。

19.3.2 在屏幕上绘制食物方块

1. 绘制食物

2. 对于范围(len(foods))内的 i:

3. pygame.draw.rect(windowSurface, GREEN, foods[i])

第 120 行与第 121 行的代码和我们为玩家绘制白色方块的代码类似。第 120 行遍历了 foods 列表中的每个食物方块。第 121 行将食物方块绘制至 windowSurface 的 Surface 对象上。此程序与第 18 章的弹跳积木的程序相仿,只不过此处“大块头”将“吃掉”其碰到的其他方块。

前边的几个程序看似饶有趣味,但是用户无法进行任何操控。在下一个程序中,我们将介绍如何从键盘获取输入。

19.4 键盘输入程序的源代码

将如下内容输入至一个新的文件,并将其保存为 pygameInput.py 。若输入这些代码后出现错误,在线 diff 工具,将您的代码与书中的代码进行对比。

pygameInput.py

1. 导入 pygame、sys、random

2. 从 pygame.locals 导入 *

3.

4. 初始化 pygame

5. pygame.init()

6. mainClock = pygame.time.Clock()

7.

8. 设定窗口

9. WINDOWWIDTH = 400

10. WINDOWHEIGHT = 400

碰撞检测与鼠标/键盘的输入 294

1. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)

2. pygame.display.set_caption(‘Input’)

3.

4. 设定颜色

5. BLACK = (0, 0, 0)

6. GREEN = (0, 255, 0)

7. WHITE = (255, 255, 255)

8.

9. 设定玩家和食物的数据结构

10. foodCounter = 0

11. NEWFOOD = 40

12. FOODSIZE = 20

1. player = pygame.Rect(300, 100, 50, 50)

2. foods = []

3. 于范围(20)内:

4. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

5.

6. 设定移动变量

7. moveLeft = False

8. moveRight = False

9. moveUp = False

10. moveDown = False

11.

12. MOVESPEED = 6

13.

14.

15. 运行游戏循环

16. 当 True 时:

17. 检查事件

18. 对于 pygame.event.get() 中的事件:

19. 若事件类型为 QUIT:

20. pygame.quit()

21. sys.exit()

22. 若事件类型为 KEYDOWN:

23. 更改键盘变量

24. 若事件键为 K_LEFT 或事件键为 ord(‘a’):

25. moveRight = False

26. moveLeft = True

27. 若事件键为 K_RIGHT 或事件键为 ord(‘d’):

28. moveLeft = False

29. moveRight = True

30. 若事件键为 K_UP 或事件键为 ord(‘w’):

键盘输入程序的源代码 295

1. moveDown = False

2. moveUp = True

3. 若事件键为 K_DOWN 或事件键为 ord(’s’):

4. moveUp = False

5. moveDown = True

6. 若事件类型为 KEYUP:

7. 若事件键为 K_ESCAPE:

8. pygame.quit()

9. sys.exit()

10. 若事件键为 K_LEFT 或事件键为 ord(‘a’):

11. moveLeft = False

12. 若事件键为 K_RIGHT 或事件键为 ord(‘d’):

13. moveRight = False

14. 若事件键为 K_UP 或事件键为 ord(‘w’):

15. moveUp = False

16. 若事件键为 K_DOWN 或事件键为 ord(’s’):

17. moveDown = False

18. 若事件键为 ord(‘x’):

19. player.top = random.randint(0, WINDOWHEIGHT - player.height)

20. player.left = random.randint(0, WINDOWWIDTH - player.width)

21.

22. 若事件类型为 MOUSEBUTTONUP:

23. foods.append(pygame.Rect(event.pos[0], event.pos[1], FOODSIZE, FOODSIZE))

24.

25. foodCounter += 1

26. 若 foodCounter >= NEWFOOD:

27. 增添新的食物

28. foodCounter = 0

29. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

30.

31. 将黑色背景绘制于表面之上

32. windowSurface.fill(BLACK)

33.

34. 移动玩家

35. 若 moveDown 且 player.bottom < WINDOWHEIGHT:

36. player.top += MOVESPEED

37. 若 moveUp 且 player.top > 0:

38. player.top -= MOVESPEED

39. 若 moveLeft 且 player.left > 0:

40. player.left -= MOVESPEED

41. 若 moveRight 且 player.right < WINDOWWIDTH:

42. player.right += MOVESPEED

碰撞检测与鼠标/键盘的输入 296
95.
96. # 将玩家绘制于表面之上
97. pygame.draw.rect(windowSurface, WHITE, player)
98.
99. # 检查玩家是否与任何食物方块相交
100. 对于 foods[:] 中的 food:

1. 若 player.colliderect(food):

2. foods.remove(food)

3.

4. 绘制食物

5. 于范围(len(foods))内:

6. pygame.draw.rect(windowSurface, GREEN, foods[i])

7.

8. 将窗口绘制至屏幕

9. pygame.display.update()

10. mainClock.tick(40)

这个程序与 Collision Detection 程序近乎相同。然而,在此程序中,唯有当用户按下键盘的方向键时,“大块头”才会移动。亦能够在窗口中的任何一处点击鼠标,以创建新的食物对象。另外,按下 ESC 键将退出游戏,按下“X”键会将玩家(“大块头”)转移至屏幕上的一个随机位置。

19.4.1 设置窗口和数据结构

自第 29 行起始的代码,创建了若干用于记录“大块头”移动情况的变量。

1. 设定移动变量

2. moveLeft = False

3. moveRight = False

4. moveUp = False

5. moveDown = False

这 4 个布尔值变量用以记录按下了哪个方向键。譬如,当用户按下键盘上向左的方向键时,将 moveLeft 设为 True 。当松开此键时,把 moveLeft 重置为 False 。

第 34 行至第 43 行的代码与之前的 Pygame 程序代码相同。这些代码行处理了游戏循环起始时以及当用户退出程序时所需进行的操作。鉴于在第 18 章已有所介绍,故而此处我们不再对这部分代码予以解释。

19.4.2 事件和 KEYDOWN 事件的处理

处理按下键和释放键的事件的代码始于第 44 行。在程序开端,将记录“大块头”移动的布尔变量皆设为 False 。

键盘输入程序的源代码 297

1. 若 event.type == KEYDOWN:

Pygame 存在一种名为 KEYDOWN 的事件类型。此乃 Pygame 能够创建的众多事件之一。调用 pygame.event.get() 函数,能够返回一个简短的事件列表,正如表 19 - 1 所示。

表 19 - 1 事件及何时创建这些事件

事件类型 描述

QUIT 当用户关闭窗口时所触发的事件

KEYDOWN 当用户按下键盘时所触发的事件。具备一个 key 属性以识别按下的是何键。还有一个 mode 属性用以表明是否有 Shift、Ctrl、Alt 或其他键与该键同时按下

KEYUP 当用户释放一个按键时所触发的事件。拥有一个 key 属性和一个 mod 属性,它们与 KEYDOWN 中的属性相类似

MOUSEMOTION 任何时刻,当鼠标移动经过窗口时均会触发该事件。具有一个 pos 属性,其返回鼠标在窗口中的坐标的元组(x, y)。rel 属性也返回一个(x, y)元组,然而它给出的是相对于上一次的 MOUSEMOTION 事件的坐标。例如,倘若鼠标从(200, 200)向左移动 4 个像素至(196, 200),那么 rel 即为元组值(-4, 0)。buttons 属性返回包含 3 个整数的一个元组。元组中的第一个整数是鼠标左键,第二个整数是鼠标中间键(若存在中间键的话),第三个整数是鼠标右键。当鼠标移动时,倘若未按下这些键,这些整数值为 0;若按下这些键,其对应的整数值即为 1

MOUSEBUTTONDOWN 当在窗口中按下鼠标时所触发的事件。此事件拥有一个 pos 属性,它是当按下鼠标键时,鼠标所在位置的坐标的(x, y)元组。其亦有一个 button 属性,以整数 1 至 5 分别表示按下了哪个键,如表 19 - 2 所示

MOUSEBUTTONUP 当释放鼠标时所触发的事件。

它具备与 MOUSEBUTTONDOWN 相同的属性

表 19 - 2 button 属性和 mouse 属性

键值 鼠标键

1 左键

2 中间键

3 右键

4 向上滚轮

5 向下滚轮

碰撞检测与鼠标/键盘的输入 298

19.4.3 设置 4 个键盘变量

1. 更改键盘变量

2. 若 event.key == K_LEFT 或 event.key == ord(‘a’):

3. moveRight = False

4. moveLeft = True

5. 若 event.key == K_RIGHT 或 event.key == ord(‘d’):

6. moveLeft = False

7. moveRight = True

8. 若 event.key == K_UP 或 event.key == ord(‘w’):

9. moveDown = False

10. moveUp = True

11. 若 event.key == K_DOWN 或 event.key == ord(’s’):

12. moveUp = False

13. moveDown = True

倘若事件类型为 KEYDOWN,那么事件对象将拥有一个 key 属性以识别按下的是何键。第 46 行代码将此属性与 K_LEFT 进行比较,K_LEFT 乃是表示键盘上向左方向键的 pygame.locals 常量。第 46 行至第 57 行代码针对其他的每个方向键(K_LEFT、K_RIGHT、K_UP 以及 K_DOWN)进行类似的判断。

当按下这些键中的某一按键时,将相应的移动变量设定为 True,并将相反方向的移动变量设定为 False。

例如,当按下左方向键时,程序执行第 47 行和第 48 行代码。在此情形下,把 moveLeft 设定为 True,把 moveRight 设定为 False(即便 moveRight 或许已然是 False,仍需确保将其设定为 False)。

在第 46 行中,event.key 中的值既可能等同于 K_LEFT,也可能等同于 ord(‘a’)。将 event.key 中的值设定为键盘上按下的那个键的顺序编码(方向键不存在顺序编码,此乃为何要运用常量 K_LEFT)。能够使用 ord() 函数获取任何单个字符的编码,并将其与 event.key 进行比对。

倘若按键是 K_LEFT 或 ord(‘a’),通过执行第 47 行和第 48 行代码,便可让左方向键和 A 键达成相同的效果。W 键、A 键、S 键和 D 键皆可用作修改移动变量的替代键。左手能够使用 WASD 键。右手能够使用方向键。

图 19 - 5 可以编程令 WASD 键和方向键达成相同的事宜

键盘输入程序的源代码 299

19.4.4 处理 KEYUP 事件

1. 若 event.type == KEYUP: 当用户释放按下的键时,会触发 KEYUP 事件。

2. 若 event.key == K_ESCAPE:

3. pygame.quit()

4. sys.exit()

若用户释放的是 ESC 键,那么程序终止。切记,在 Pygame 中,在调用 sys.exit() 函数之前,必须先调用 pygame.quit() 函数。

倘若释放的是某一方向键,第 62 行至 69 行代码会将一个移动变量设定为 False。

1. 若 event.key == K_LEFT 或 event.key == ord(‘a’):

2. moveLeft = False

3. 若 event.key == K_RIGHT 或 event.key == ord(‘d’):

4. moveRight = False

5. 若 event.key == K_UP 或 event.key == ord(‘w’):

6. moveUp = False

7. 若 event.key == K_DOWN 或 event.key == ord(’s’):

8. moveDown = False

19.4.5 转移玩家

1. 若 event.key == ord(‘x’):

2. player.top = random.randint(0, WINDOWHEIGHT - player.height)

3. player.left = random.randint(0, WINDOWWIDTH - player.width)

亦可为游戏增添转移玩家的功能。倘若用户按下“X”键,那么第 71 行和第 72 行代码会将用户的方块之位置设定为窗口中的一个随机位置。借由按下“X”键,将为用户赋予在窗口中转移的能力。然而,对于将会把用户转移至何处,是难以掌控的,此完全是随机之举。

19.4.6 处理 MOUSEBUTTONUP 事件

1. 若 event.type == MOUSEBUTTONUP:

2. foods.append(pygame.Rect(event.pos[0], event.pos[1], FOODSIZE, FOODSIZE))

碰撞检测与鼠标/键盘的输入 300

处理鼠标输入事件与处理键盘输入事件相仿。当用户释放点击的鼠标时,会触发 MOUSEBUTTONUP 事件。Event 对象的 pos 属性被设定为包含两个整数的一个元组,用以表示点击鼠标时其光标所在的 XY 坐标。

在第 75 行中,将 X 坐标存储于 event.pos[0]中,把 Y 坐标存储至 event.pos[1]中。第 75 行代码创建了一个新的 Rect 对象以表示新的食物,并将其放置于 MOUSEBUTTONUP 事件发生之处。通过为 foods 列表增添一个新的 Rect 对象,代码将会于屏幕上呈现一个新的食物方块。

19.4.7 在屏幕上移动玩家

1. 移动玩家

2. 若 moveDown 且 player.bottom < WINDOWHEIGHT:

3. player.top += MOVESPEED

4. 若 moveUp 且 player.top > 0:

5. player.top -= MOVESPEED

6. 若 moveLeft 且 player.left > 0:

7. player.left -= MOVESPEED

8. 若 moveRight 且 player.right < WINDOWWIDTH:

9. player.right += MOVESPEED

我们已然依据用户的按键,设定了移动变量(moveDown、moveUp、moveLeft 以及 moveRight)。当下,通过调整玩家的 XY 坐标,来移动玩家的方块(以存储在 player 中的 pygame.Rect 对象来表示)。

倘若将 moveDown 设定为 True(并且玩家方块的底部不低于窗口的底部),那么第 88 行通过给玩家的当前 top 属性增添 MOVESPEED,将玩家的方块向下移动。第 89 行至 94 行针对其他 3 个方向做了相同之事。

19.5 colliderect()方法 99. # 检查玩家是否与任何食物方块相交。
100. 对于 foods 中的每一个元素:
101. 若 player.colliderect(food):
102. foods.remove(food)

在先前的Collision Detection 程序中,doRectsOverlap()函数用于判定一个矩形是否与另一个矩形发生碰撞。本书涵盖了此函数,以便我们能够理解碰撞检测背后的代码是如何运作的。

在这个程序中,我们能够运用 Pygame 的碰撞检测函数。pygame.Rect 对象的 colliderect() 方法接纳另一个 pygame.Rect 作为参数,并且倘若这两个矩形发生碰撞,该函数返回 True;若未发生碰撞,则返回 False。

1. mainClock.tick(40)

剩余的代码与 Input 程序和 Collision Detection 程序中的代码近似。

19.6 本章小结

本章所介绍的碰撞检测之概念,于众多图形游戏中皆会被运用。两个矩形的碰撞检测颇为简单:判定一个矩形的 4 个角是否处于另一个矩形之中。由于此类检测频繁进行,所以 Pygame 为 pygame.Rect 对象提供了自身的碰撞检测方法,此方法名为 colliderect()。

本书中的前几个程序是基于文本的。程序的输出乃是打印至屏幕上的文本,程序输入为用户在键盘上的输入。而图形化的程序能够接受键盘和鼠标的输入。

更进一步而言,当用户按下或释放单独一个键时,这些程序能够对单独的按键做出响应。用户无需输入完整的响应并按下回车键。这便允许即时反馈,并由此造就更具交互性的游戏。

● 声音文件与图像文件;
● 绘制精灵;
● pygame.image.load()函数;
● pygame.mixer.Sound 数据类型;
● pygame.mixer.music 模块。

在第 18 章和第 19 章中,我们阐述了如何创建具备图形且能够接收键盘和鼠标输入的 GUI 程序。我们亦介绍了如何绘制各类形状。于本章中,我们将讲述如何在游戏中展示图像和精灵图形、播放声音与音乐。

精灵(sprite)指的是用作屏幕上图形之一部分的单独二维图像。图 20 - 1 呈现了一些精灵的示例。

图 20 - 1 精灵的若干示例

在完整场景中运用精灵的示例,如图 20 - 2 所示。

将精灵图像绘制于背景之上。需留意,我们能够在水平方向反转精灵图像,以使精灵朝向其他方向。能够在相同的窗口中多次绘制相同的精灵图像。也能够重新调整精灵的大小,令其比初始的精灵图像更大或更小。可以把背景图像视作一个很大的精灵。

下面的程序将会描述如何运用 Pygame 播放声音和绘制精灵。

20.2 精灵和声音程序 303

图 20 - 2 完整场景的一个示例,于背景之上绘制了精灵

20.1 声音文件和图像文件

精灵是存储于计算机上的图像文件。Pygame 能够运用数种不同的图像格式。能够通过查看文件末尾(最后的点之后)的名称来辨别图像文件的格式。将此名称称作文件扩展名(file extension)。例如,player.png 是 PNG 格式。Pygame 支持的图像格式包含 BMP、PNG、JPG 和 GIF。

能够从 Web 浏览器下载图像。在大多数 Web 浏览器上,需要用鼠标右键点击 Web 页面上的图像,从弹出的菜单中选择“Save”。

记住图像文件在硬盘上的存储方位。将此下载的图像文件复制至与您的 Python 程序的.py 文件相同的文件夹之中。亦能够使用诸如 MS Paint 或 Tux Paint 这类程序来创建属于自己的图像。

Pygame 支持的声音文件格式涵盖 MID、WAV 和 MP3。能够从互联网下载声音效果,如同下载图像文件一般。它们必须为这 3 种格式之一。倘若计算机配有一个麦克风,您还能够进行录音,并且于游戏中运用自己的 WAV 文件。

20.2 精灵和声音程序

这个程序与第 19 章中的键盘输入程序相同。然而,在此程序中,我们运用精灵,而非看上去颇为普通的方块。 第 20 章 声音和图像 304 替绿色的食物方块。当玩家精灵吃掉一个樱桃精灵时,我们还能够播放背景音乐和声音效果。该程序的运行成效,如图 20 - 3 所示。

图 20 - 3 Sprites and Sounds 程序的动态截图

20.3 Sprites and Sounds 程序的源代码

若您知晓如何使用诸如 Photoshop 或 MS Paint 这类图像软件,便能够自行绘制图像。倘若您不知晓如何使用这些软件,能够从网站下载图形并运用这些图像文件。这对于音乐文件和声音文件同样适用。也能够从网站或数码相机中获取图像文件。

若输入这些代码后产生错误,在线 diff 工具,将您的代码与书中的代码进行对比。

spritesAndSounds.py

1. import pygame, sys, time, random

2. from pygame.locals import *

3.

4. 初始化 pygame

5. pygame.init()

6. mainClock = pygame.time.Clock()

7.

8. 设定窗口

9. WINDOWWIDTH = 400

10. WINDOWHEIGHT = 400

11. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32) Sprites and Sounds 程序的源代码 305

12. pygame.display.set_caption(‘Sprites and Sound’)

13.

14. 设定颜色

15. BLACK = (0, 0, 0)

16.

17. 设定方块的数据结构

18. player = pygame.Rect(300, 100, 40, 40)

19. playerImage = pygame.image.load(‘player.png’)

20. playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))

21.

foodImage = pygame.image.load(‘cherry.png’)
22. foods = []
23. 于 range(20) 内:
24. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - 20), random.randint(0, WINDOWHEIGHT - 20), 20, 20))
25.
26. foodCounter = 0
27. NEWFOOD = 40
28.
29. # 设定键盘变量
30. moveLeft = False
31. moveRight = False
32. moveUp = False
33. moveDown = False
34.
35. MOVESPEED = 6
36.
37. # 设定音乐
38. pickUpSound = pygame.mixer.Sound(‘pickup.wav’)
39. pygame.mixer.music.load(‘background.mid’)
40. pygame.mixer.music.play(-1, 0.0)
41. musicPlaying = True
42.
43. # 运行游戏循环
44. 当 True 时:
45. # 检查 QUIT 事件
46. 对于 pygame.event.get() 中的事件:
47. 若事件类型为 QUIT:
48. pygame.quit()
49. sys.exit()
50. 若事件类型为 KEYDOWN:
51. # 更改键盘变量
52. 若事件键为 K_LEFT 或事件键为 ord(‘a’):
53. moveRight = False
54. moveLeft = True
55. 若事件键为 K_RIGHT 或事件键为 ord(‘d’):

声音和图像 306

1. moveLeft = False

2. moveRight = True

3. 若事件键为 K_UP 或事件键为 ord(‘w’):

4. moveDown = False

5. moveUp = True

6. 若事件键为 K_DOWN 或事件键为 ord(’s’):

7. moveUp = False

8. moveDown = True

9. 若事件类型为 KEYUP:

10. 若事件键为 K_ESCAPE:

11. pygame.quit()

12. sys.exit()

13. 若事件键为 K_LEFT 或事件键为 ord(‘a’):

14. moveLeft = False

15. 若事件键为 K_RIGHT 或事件键为 ord(‘d’):

16. moveRight = False

17. 若事件键为 K_UP 或事件键为 ord(‘w’):

18. moveUp = False

19. 若事件键为 K_DOWN 或事件键为 ord(’s’):

20. moveDown = False

21. 若事件键为 ord(‘x’):

22. player.top = random.randint(0, WINDOWHEIGHT - player.height)

23. player.left = random.randint(0, WINDOWWIDTH - player.width)

24. 若事件键为 ord(‘m’):

25. 若 musicPlaying 为真:

26. pygame.mixer.music.stop()

27. 否则:

28. pygame.mixer.music.play(-1, 0.0)

29.

musicPlaying = not musicPlaying

1.

2. 若事件类型为 MOUSEBUTTONUP:

3. foods.append(pygame.Rect(event.pos[0] - 10, event.pos[1] - 10, 20, 20))

4.

5. foodCounter += 1

6. 若 foodCounter >= NEWFOOD:

7. 增添新的食物

8. foodCounter = 0

9. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - 20), random.randint(0, WINDOWHEIGHT - 20), 20, 20))

10.

11. 在表面绘制黑色背景

12. windowSurface.fill(BLACK) 20.3 Sprites and Sounds 程序的源代码 307

13.

14. 移动玩家

15. 若 moveDown 且 player.bottom < WINDOWHEIGHT:

16. player.top += MOVESPEED

17. 若 moveUp 且 player.top > 0:

18. player.top -= MOVESPEED

19. 若 moveLeft 且 player.left > 0:

20. player.left -= MOVESPEED

21. 若 moveRight 且 player.right < WINDOWWIDTH:

22. player.right += MOVESPEED

23.

1.

2. 将方块绘制到表面上

3. windowSurface.blit(playerStretchedImage, player)

4.

5. 检查方块是否与任何食物方块相交。

6. 对于 foods 中的每个 food :

7. 若 player 与 food 发生碰撞:

8. foods 移除 food

9. player = pygame.Rect(player.left, player.top, player.width + 2, player.height + 2)

10. playerStretchedImage = pygame.transform.scale(playerImage, (player.width, player.height))

11. 若 musicPlaying 为真:

12. pickUpSound.play()

13.

14. 绘制食物

15. 对于 foods 中的每个 food :

16. windowSurface.blit(foodImage, food)

17.

18. 将窗口绘制到屏幕上

19. pygame.display.update()

20. mainClock.tick(40)

创建窗口和数据结构

程序中的大部分代码与第 19 章中的 Collision Detection 程序相同。我们仅着重介绍精灵和声音部分。

1. pygame.display.set_caption(‘Sprites and Sound’)

首先,我们在第 12 行将描述此程序的字符串设置为标题栏的名称。

向 pygame.display.set_caption() 函数传递字符串 ‘Sprites and Sound’。 声音和图像 308

1. 设定方块的数据结构

2. player = pygame.Rect(300, 100, 40, 40)

3. playerImage = pygame.image.load(‘player.png’)

4. playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))

5. foodImage = pygame.image.load(‘cherry.png’)

我们将运用三个不同的变量来表征玩家,而非如第 19 章那般仅使用一个变量。第 18 行的 player 变量会存储一个 Rect 对象,用以记录玩家的位置以及玩家的尺寸。player 变量未涵盖玩家的图像,仅包含玩家的大小和位置。在程序起始处,玩家的左上角处于坐标 (300, 100) 处,起初玩家具有 40 个像素的高度和 40 个像素的宽度。

用以表示玩家的第二个变量是第 19 行代码中的 playerImage 。pygame.image.load() 函数接收了一个字符串参数,此为要加载的图像的文件名。其返回值为一个 Surface 对象,将图像文件中的图形绘制至该对象上。我们把这个 Surface 对象保存至 playerImage 中。

第三个变量将在下一节中介绍。 20.4

pygame.transform.scale() 函数

在第 20 行代码中,我们运用了 pygame.transform 模块中的一个全新函数。pygame.transform.scale() 函数能够对一个精灵进行缩小或放大操作。第一个参数为绘制有图像的 pygame.Surface 对象。第二个参数是一个元组,用以表示第一个参数中图像的全新宽度与高度。pygame.transform.scale() 函数会返回一个 pygame.Surface 对象,图像会以新的尺寸绘制于其上。我们于 playerImage 变量中保存了初始图像,而在 playerStretchedImage 变量中保存了拉伸后的图像。

在第 121 行代码中,我们再度调用 pygame.image.load() 函数以创建在其上绘制了樱桃图像的 Surface 对象。务必要确保在与 spritesAndSounds.py 文件相同的目录下存在 player.png 文件和 cherry.png 文件,否则 Pygame 无法寻找到它们,并会给出错误。

20.4.1 创建音乐和声音

1. 设定音乐

2. pickUpSound = pygame.mixer.Sound(‘pickup.wav’)

3. pygame.mixer.music.load(‘background.mid’)

4. pygame.mixer.music.play(-1, 0.0)

5. musicPlaying = True

接下来需要加载声音文件。在 Pygame 中存在两个声音模块。pygame.mixer 模块能够在游戏中播放简短音效。pygame.mixer.music 模块能够播放背景音乐。调用 pygame.mixer.Sound() 构造函数来创建一个 pygame.mixer.Sound 对象(简称 Sound 对象)。此对象具备一个 play() 方法,调用该方法能够播放音效。

第 39 行调用 pygame.mixer.music.load() 函数以加载背景音乐。第 40 行调用 pygame.mixer.music.play() 函数来开启播放背景音乐。第一个参数向 Pygame 告知在首次播放音乐之后还需再度播放背景音乐的次数。故而,传入参数 5 会致使 Pygame 播放背景音乐 6 次。-1 为一个特殊值,将其作为第一个参数传入时,会循环播放此首背景音乐。

pygame.mixer.music.play() 函数的第二个参数为开始播放声音文件的位置。传入参数 0.0 会从背景音乐的起始位置开始播放。若第二个参数为 2.5,则表示将从音乐开头的 2.5 秒处开始播放背景音乐。

最后,musicPlaying 变量将拥有一个布尔值,向程序表明是否应当播放背景音乐与音效。为玩家提供一个选项,使其能够自行决定在运行程序时是否播放声音,这将甚为出色。

20.4.2 切换和关闭声音

1. 若事件键为 ord(‘m’):

2. 若 musicPlaying 为真:

3. pygame.mixer.music.stop()

4. 否则:

5. pygame.mixer.music.play(-1, 0.0)

6. musicPlaying = not musicPlaying

M 键将开启和关闭背景音乐。倘若将 musicPlaying 设置为 True,那么当下正在播放背景音乐,我们应当通过调用 pygame.mixer.music.stop() 来停止音乐。倘若将 musicPlaying 设置为 False,那么当前未播放背景音乐,应当通过调用 pygame.mixer.music.play() 开始播放音乐。

最后,无论开启还是关闭音乐,我们都期望切换 musicPlaying 中的值。切换一个布尔值,意味着要将其当前值设定为相反的值。

若 musicPlaying 当前为 True,代码行 musicPlaying = not musicPlaying 会将此变量设为 False;若 musicPlaying 当前为 False,代码行 musicPlaying = not musicPlaying 会将此变量设为 True。思考一下开关电灯时的切换情形:切换电灯开关,使其变为相反的设置。

20.4.3 把玩家绘制到窗口上

1. 将方块绘制到表面上

2. windowSurface.blit(playerStretchedImage, player)

请记住,在 playerStretchedImage 中存储的值是一个 Surface 对象。第 110 行代码把玩家的精灵绘制到窗口的 Surface 对象上(存储于 windowSurface 中)。blit() 方法的第二个参数是一个 Rect 对象,它指明了将精灵渲染至 Surface 对象的何处。存储于 player 中的 Rect 对象,记录了玩家在窗口中的位置。

310

20.4.4 判断玩家是否和樱桃有碰撞

1. 若 player 与 food 发生碰撞:

2. foods 移除 food

3. player = pygame.Rect(player.left, player.top, player.width + 2, player.height + 2)

4. playerStretchedImage = pygame.transform.scale(playerImage, (player.width, player.height))

5. 若 musicPlaying 为真:

6. pickUpSound.play()

这部分代码与之前程序中的代码类似。然而,存在一些新的代码行。存储在 pickUpSound 变量中的 Sound 对象,调用了 play() 方法。但仅当把 musicPlaying 设置为 True 时(这意味着开启声音),才会如此操作。

当玩家吃掉一个樱桃时,玩家的高度和宽度均会增加 2 个像素。在第 116 行中,新的 Rect 对象相较于旧的 Rect 对象要大两个像素,它将成为 player 的新值。Rect 对象用以表示玩家的位置和大小,玩家的图片以 Surface 对象的形式存储于 playerStretchedImage 中。通过调用 pygame.transform.scale() 来创建一张新的拉伸图像。务必确保传递的是 playerImage 中初始的 Surface 对象,而非 playerStretchedImage 中的对象。

拉伸一张图像通常会使其稍有扭曲变形。倘若一遍又一遍地重新拉伸一张已然拉伸过的图像,其扭曲变形会很快出现。然而,将原始图像拉伸至新的大小,则仅会扭曲一次。正因如此,我们才将 playerImage 作为 pygame.transform.scale() 函数的第一个参数。

20.4.5 在窗口中绘制樱桃

1. 绘制食物

2. 对于 foods 中的每一个 food :

3. windowSurface.blit(foodImage, food)

在之前的程序中,我们调用了 pygame.draw.rect() 函数为存储在 foods 列表中的每一个 Rect 对象绘制一个绿色的方块。然而,在这个程序中,我们期望绘制樱桃精灵。调用 blit() 方法,并将存储在 foodImage 中的 Surface(这是在其上绘制了樱桃图像的 Surface 对象)传递给该函数。food 变量(通过 for 循环中的每次迭代,将包含 foods 中的每一个 Rect 对象)告知 blit() 方法应在何处绘制 foodImage 。

311

20.5 本章小结

这个游戏增添了图像和声音。图像(称为精灵)相较于前边程序中所使用的简单形状,看上去要出色许多。本章所介绍的游戏还具备背景音乐的播放功能,并且能够播放音效。

精灵能够缩放(即拉伸)为更大或更小的尺寸。如此一来,我们能够以任意的大小来展示精灵。在第 21 章所介绍的游戏中,此功能将会大有用处。

当下,我们已然知晓如何创建一个窗口、展示精灵、绘制基本图元、收集键盘和鼠标输入、播放声音以及实现碰撞检测,这便已然做好在 Pygame 中创建一个图形游戏的准备。在下一章中,我们会将所有这些元素整合在一起,创建一个于本书中最为高级的游戏。

Dodger

本章的主要内容:

● pygame.FULLSCREEN 标志;
● Pygame 键盘值的常量;
● Rect 的 Move_ip() 方法;
● pygame.mouse.set_pos() 函数;
● 实现游戏作弊的代码;
● 修改 Dodger 程序。

在第 18 章、第 19 章和第 20 章中,我们介绍了 Pygame 模块,并展示了如何运用它的各项功能。在本章中,我们将运用这些所学知识来创建一个名为 Dodger 的图形游戏。

相关文章

python字典中如何添加键值对

添加键值对首先定义一个空字典 1>>> dic={}直接对字典中不存在的key进行赋值来添加123>>> dic['name']='zhangsan'>>...

在 Python 中使用 JSON 和 JSON 键值

数据序列化是将数据转换为可以存储或传输的格式,然后在以后重建的过程。JSON(JavaScript 对象表示法)由于其可读性和易用性而成为最流行的序列化格式之一。 在 Python 中,json 模...

什么是Python 之 ? 22 dict字典键值对

Python Dictionaries 字典,其实也叫键值对俗话说 男女搭配干活不累,九台怎么男女形成配对呢?key是不能重复的{ "key1": value1, "key2&...

字典操作(键值对) - 看这些就够了

1、初始化:大括号、dict、fromkeys初始化2、访问:单个访问、多个访问 单个访问-->[]、get; 多个访问-->items、keys、values;访问3、编辑:增加、修改、...

python 字典插入新的健值对的方法

在 Python 中,可以使用下标运算符([])或 update() 方法向字典中插入新的键值对。使用下标运算符插入键值对的语法如下所示:my_dict = {'apple': 1,...