2.5    使用Rect对象

看过之前的范例代码后,大家有什么感受呢?想必已经对位置计算和碰撞检测的细微之处感到头疼了吧。接下来,让我们尝试使用Pygame提供的Rect对象来简化这一过程。

Rect对象中存储了矩形的位置和大小信息,格式如下:

Rect(left, top, width, height)

使用Rect对象后,马里奥的变量原本需要四个,现在只需要一个就足够了,代码对比如下:

#旧写法,马里奥位置和大小

marioX=10

marioY=20

marioWidth = blockSize

marioHeight = blockSize

 

#Rect的写法

marioRect = pygame.Rect(10, 20, blockSize, blockSize)

看起来简洁多了,而且还可以使用marioRect.xmarioRect.ymarioRect.widthmarioRect.height来轻松获取位置和宽高信息。最重要的是,Rect对象提供了一个方法用来检测两个矩形是否碰撞,返回True表示碰撞,格式如下:

RectA.colliderect(RectB)

让我们尝试使用Rect对象重新编写程序,代码如下所示:

02\10.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)

 

# 水管的位置和大小

pipeRect = pygame.Rect(400, 300 - blockSize * 2, blockSize, blockSize * 2)

 

# 主循环

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)

        if nextStepRect.colliderect(pipeRect):

            marioWalkStep = marioRect.x - pipeRect.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)

        if nextStepRect.colliderect(pipeRect):

            marioWalkStep = pipeRect.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)  # 画直线

 

    # 障碍物

    pygame.draw.rect(screen, (255, 255, 255), pipeRect, 0)

 

    # 马里奥Y轴步伐

    marioWalkStepY = 4

 

    # 下一步预判断

    nextStepRect = marioRect.move(0, marioWalkStepY)

    if nextStepRect.colliderect(pipeRect):

        marioWalkStepY = pipeRect.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()

在代码中,我们使用了Rect对象的move(x, y)方法来实现位置移动。其中,参数xy可以为负数,x为正值表示向右移动,负值表示向左移动,y为正值表示向下移动,负值表示向上移动。需要注意的是,move方法返回一个新的Rect对象,如果需要修改原来的Rect对象,需要将新的Rect对象重新赋值给自己。

此外,Rect对象提供了一些便捷的属性供我们使用,如marioRect.left等同于marioRect.xmarioRect.right等同于marioRect.x + marioRect.widthmarioRect.bottom等同于marioRect.y + marioRect.height。想要了解更多属性,请阅读官方文档。修改属性可能会导致位置的变化,比如修改right值实际上会改变X坐标的值。

还有一个有用的方法是inflate(x, y),它可以在保持矩形中心不变的情况下放大或缩小矩形。如果参数是正数,则放大矩形,如果是负数,则缩小矩形。这比手动计算位置和大小要方便得多。

当我们运行程序后,会发现效果与之前一样,但代码变得更加简洁了。