下面尝试增加多个水管,因为马里奥的世界里到处都是砖头和水管。我们可以直接使用一个列表来存储多个Rect对象,代码如下所示:
02\11.py
|
import pygame
# 初始化pygame pygame.init()
# 设置窗口大小 WIDTH = 600 HEIGHT = 400 screen = pygame.display.set_mode((WIDTH, HEIGHT))
# 设置窗口标题 pygame.display.set_caption('super mario bros')
# 避免输入法影响按键 pygame.key.stop_text_input()
# 时钟 clock = pygame.time.Clock()
# 方块大小 blockSize = 30
# 马里奥位置和大小 marioRect = pygame.Rect(10, 20, blockSize, blockSize)
# 水管数组 pipeArray = [] pipeArray.append(pygame.Rect(200, 300 - blockSize, blockSize, blockSize)) pipeArray.append(pygame.Rect(300, 100, blockSize * 4, blockSize)) pipeArray.append(pygame.Rect(400, 300 - blockSize * 2, blockSize, blockSize * 2)) pipeArray.append(pygame.Rect(500, 300 - blockSize * 3, blockSize, blockSize * 3))
# 主循环 isRunning = True while isRunning: for event in pygame.event.get(): if event.type == pygame.QUIT: isRunning = False
# 马里奥的步伐 marioWalkStep = 18
# 获取按键状态 keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: print("按了左方向键")
# 窗口边界判断 if marioRect.x - marioWalkStep <= 0: marioWalkStep = marioRect.x
# 下一步预判断 nextStepRect = marioRect.move(-1 * marioWalkStep, 0) pipeIndex = nextStepRect.collidelist(pipeArray) # 是否碰撞 if pipeIndex != -1: marioWalkStep = marioRect.x - pipeArray[pipeIndex].right marioRect = marioRect.move(-1 * marioWalkStep, 0) if keys[pygame.K_RIGHT]: print("按了右方向键")
# 窗口边界判断 if marioRect.right + marioWalkStep >= WIDTH: marioWalkStep = WIDTH - marioRect.right
# 下一步预判断 nextStepRect = marioRect.move(marioWalkStep, 0) pipeIndex = nextStepRect.collidelist(pipeArray) if pipeIndex != -1: marioWalkStep = pipeArray[pipeIndex].x - marioRect.right marioRect = marioRect.move(marioWalkStep, 0) if keys[pygame.K_g]: print("按了跳跃键") if marioRect.y > 0: marioRect = marioRect.move(0, -10)
# 全屏擦除 screen.fill((0, 0, 0))
# 地平线 pygame.draw.line(screen, (255, 255, 255), (0, 300), (WIDTH, 300), 1) # 画直线
# 障碍物 for i in range(len(pipeArray)): pygame.draw.rect(screen, (255, 255, 255), pipeArray[i], 0)
# 马里奥Y轴步伐 marioWalkStepY = 4
# 下一步预判断 nextStepRect = marioRect.move(0, marioWalkStepY) pipeIndex = nextStepRect.collidelist(pipeArray) if pipeIndex != -1: marioWalkStepY = pipeArray[pipeIndex].y - marioRect.bottom marioRect = marioRect.move(0, marioWalkStepY) if marioRect.y >= 300 - marioRect.height: marioRect.y = 300 - marioRect.height
# 显示马里奥 pygame.draw.rect(screen, (0, 0, 0), marioRect, 1) # 边框 pygame.draw.rect(screen, (255, 255, 255), marioRect.inflate(-2, -2), 0) # 边框内部白色填充 pygame.draw.rect(screen, (0, 0, 0), marioRect.inflate(-10, -10), 0) # 中心黑色填充
# 刷新显示 pygame.display.flip()
# 每秒60帧 clock.tick(60) # 退出 Pygame pygame.quit() |
代码改动并不大,关键是碰撞检测改成了使用 nextStepRect.collidelist(pipeArray) 方法。这个方法可以直接检测一个矩形与一个矩形列表的碰撞情况。如果有碰撞,则返回列表中第一个碰撞的矩形的索引,如果没有碰撞,则返回-1。
运行程序,画面如图2‑12所示。

图2‑12 多个水管的碰撞检测
在测试中可以发现,地面上的三个水管的碰撞检测都正常,但当马里奥从下往上顶向空中的砖块时,他会直接穿过砖块,站在砖块的上方。这是因为在执行跳跃动作时,没有进行Y轴方向的碰撞检测。让我们添加这部分代码,具体如下所示。
02\12.py
|
if keys[pygame.K_g]: print("按了跳跃键")
# 跳跃步伐 jumpStep = 10
# 窗口边界判断 if marioRect.y - jumpStep <= 0: jumpStep = marioRect.y
# 下一步预判断 nextStepRect = marioRect.move(0, -1 * jumpStep) pipeIndex = nextStepRect.collidelist(pipeArray) if pipeIndex != -1: jumpStep = (pipeArray[pipeIndex].bottom - marioRect.y) * -1 marioRect = marioRect.move(0, -1 * jumpStep) |
代码逻辑基本相同,运行程序,画面如图2‑13所示。

图2‑13 跳跃增加碰撞检测
按住跳跃键,会发现马里奥最高只能到达图中的位置,距离砖块还有几个像素的距离。这是因为在处理跳跃时,马里奥的Y坐标会被更新,接着又会被重力拉回一些像素。这个问题影响不大,暂时保留,以后再进行优化。