2.6    多个水管

下面尝试增加多个水管,因为马里奥的世界里到处都是砖头和水管。我们可以直接使用一个列表来存储多个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所示。

212 多个水管的碰撞检测

         在测试中可以发现,地面上的三个水管的碰撞检测都正常,但当马里奥从下往上顶向空中的砖块时,他会直接穿过砖块,站在砖块的上方。这是因为在执行跳跃动作时,没有进行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所示。

213 跳跃增加碰撞检测

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