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)最终效果
图片来源:http://www.laoshoucun.com/ 页游