【FlappyBird小游戏】编写游戏逻辑(四)——控制Bird和碰撞检测【源码】

提示:如果本文对您有帮助,欢迎点赞支持!


文章目录

1、玩家控制Bird

(1)更改游戏主循环

(2)更改游戏帧渲染

2、Bird的碰撞检测

(1)更改游戏帧渲染

(2)碰撞检测函数

(3)像素级碰撞检查

(4)最终效果


1、玩家控制Bird

在该游戏中玩家只需要按空格或者up键来控制输入,玩家每按一次,小鸟就向上飞行一小段距离,这也是该游戏的特点

(1)更改游戏主循环

我们在游戏主循环中添加接收玩家输入事件的判断程序,如下:

 def run_game(self):
        while True:
            is_flap = False # 是否让小鸟飞翔
            # 遍历系统的事件列表
            for event in pygame.event.get():
                if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):  # 退出事件,Esc
                    pygame.quit()
                    sys.exit()
                if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):  # 按下空格或者上方向键
                    actions = np.array([0, 1])  # 让小鸟飞翔
                    self.frame_step(input_actions=actions)
                    is_flap = True
            # 如果没有接收到输入就让小鸟降落
            if not is_flap:
                actions = np.array([1, 0])  # 让小鸟降落
                self.frame_step(input_actions=actions)

从程序中我们可以看到,我们输入一个OneHot形式的数组来区别两个不同的动作:

# input_actions[0] == 1: 小鸟下降
# input_actions[1] == 1: 小鸟上升

(2)更改游戏帧渲染

我们然后我们在Bird类的帧渲染函数中如下修改:

 def frame_step(self, input_actions,BASEY):
        # 检查输入是否正确
        if sum(input_actions) != 1:
            raise ValueError('Multiple input actions!')
        if input_actions[1] == 1:
            if self.PlayerInfo['y'] > -2 * self.PlayerInfo['h']:
                self.playerVelY = self.playerFlapAcc # 速度方向一下变为朝上
                self.playerFlapped = True
                # SOUNDS['wing'].play()
        # 玩家的移动
        if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: #如果y速度没有最大且没有上升时
            self.playerVelY += self.playerAccY
        if self.playerFlapped:
            self.playerFlapped = False
        self.PlayerInfo['y'] += min(self.playerVelY, BASEY - self.PlayerInfo['y'] - self.PlayerInfo['h'] )# 在y的速度和离地面的距离中取一个最小值
        if self.PlayerInfo['y'] < 0:  # 玩家的最高位置
            self.PlayerInfo['y'] = 0
        # 计算玩家序列帧
        if (self.loopIter + 1) % 3 == 0:
            self.PlayerInfo['index'] = next(self.PLAYER_INDEX_GEN)
        self.loopIter = (self.loopIter + 1) % 30
        # 绘制玩家图像
        self.screen.blit(self.Player_Sprite[self.PlayerInfo['index']],
                         (self.PlayerInfo['x'], self.PlayerInfo['y']))  # 玩家

此时我们就可以判断用户的输入,并控制小鸟的动作了

2、Bird的碰撞检测

(1)更改游戏帧渲染

在上述的代码中,我们虽然已经实现了小鸟的运动,但是并没有实现Bird的碰撞检测,此时的Bird可以穿过Pipe,所以我们要在游戏的帧渲染函数中继续添加碰撞检测的代码。

        # 绘制玩家图像
        self.bird.frame_step(input_actions, self.BASEY)
        # 检查是否发生碰撞
        isCrash = self.checkCrash(self.bird.PlayerInfo,self.upperPipes, self.lowerPipes)
        if isCrash:
            #self.SOUNDS['hit'].play()
            #self.SOUNDS['die'].play()
            self.reset()  # 重置

(2)碰撞检测函数

def checkCrash(self,player, upperPipes, lowerPipes):
        """检查玩家是否碰撞到管道和地面"""
        pi = player['index']
        # 检查是否碰到地面
        if player['y'] + player['h'] >= self.BASEY - 1:
            return True
        else:

            playerRect = pygame.Rect(player['x'], player['y'],
                                     player['w'], player['h'])
            for uPipe, lPipe in zip(upperPipes, lowerPipes):
                # upper and lower pipe rects
                uPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], self.PIPE_WIDTH, self.PIPE_HEIGHT)
                lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], self.PIPE_WIDTH, self.PIPE_HEIGHT)

                # player and upper/lower pipe hitmasks
                pHitMask = self.HITMASKS['player'][pi]
                uHitmask = self.HITMASKS['pipe'][0]
                lHitmask = self.HITMASKS['pipe'][1]

                # if bird collided with upipe or lpipe
                uCollide = tool.pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask)
                lCollide = tool.pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask)
                if uCollide or lCollide:
                    return True

        return False

(3)像素级碰撞检查

def pixelCollision(rect1, rect2, hitmask1, hitmask2):
    """检查两个矩形是否发生像素级碰撞"""
    rect = rect1.clip(rect2) #获取两个Rect对象互相重叠的部分
    # 如果重叠部分不存在则没有发生碰撞
    if rect.width == 0 or rect.height == 0:
        return False
    # 遍历两个碰撞遮罩的重叠部分的alpha的值都不为0则发生了碰撞
    x1, y1 = rect.x - rect1.x, rect.y - rect1.y
    x2, y2 = rect.x - rect2.x, rect.y - rect2.y
    for x in range(rect.width):
        for y in range(rect.height):
            if hitmask1[x1 + x][y1 + y] and hitmask2[x2 + x][y2 + y]:
                return True
    return False

(4)最终效果

20210502_212711.gif

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔法攻城狮MRL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值