Python 飞机大战 增强版本实现
飞机大战游戏介绍
基于github上Python实现的一款飞机射击游戏PythonShootGmae
原始版本比较简单,只有一种敌机。但是其中资源文件夹包含有三种敌机的图片和声效,所以基于已有资源进行了功能增强。
增强版本在这个基础上增加了下面功能
- 支持三种类型的敌机
- 支持敌机发射子弹
- 添加飞机的武器类型(炸弹,同时发射多行子弹)
- 敌机可以从左边,上方和右边三个方向出现
- 支持飞机有多条生命
- 添加道具获取(可以获取炸弹,和增强子弹)
代码介绍
代码实现有4个文件
- shooter.py: 进行游戏的初始化和游戏主函数的循环
- gameRole.py: 包含有游戏中敌机,子弹,道具和英雄(我方飞机)的类,和管理 这些类的辅助类定义
- resource.py: 负责图片和声音的初始化
- config.py: 可以设置游戏的难度参数
gameRole.py
这里定义的敌机,子弹,道具和英雄类都继承了pygame 的精灵类(pygame.sprite.Sprite),因为要用到精灵类支持的碰撞检测,在有多个敌人,多个子弹的情况下,使用精灵类,可以简化代码的复杂度。
这里举例说明下 Enemy类 和它的辅助类 EnemyGroup 类 的关系。
按照pygame.sprite.Sprite的说明文档,子类需要实现update函数,和给Sprite的类成员变量image,rect 赋值。
The base class for visible game objects. Derived classes will want to override the Sprite.update() and assign a Sprite.image and Sprite.rect attributes.
Enemy 类
初始化函数设置了 pygame 精灵类在绘图时需要的类成员变量 image,rect ,飞行的方向,和武器。
update函数中会更新敌机的位置,如果拥有武器的话,还会定期发射子弹。
class Enemy(pygame.sprite.Sprite):
def __init__(self, enemy_surface, enemy_init_pos, direction, weapon_group):
pygame.sprite.Sprite.__init__(self)
self.image = enemy_surface
self.rect = self.image.get_rect()
self.rect.topleft = enemy_init_pos
self.direction = direction
self.down_index = 0
self.damage = 0
self.is_down = 0
self.is_hit = 0
self.ticks = 0
self.weapon_group = weapon_group
def update(self, enemy_surface, hit_surface=0):
def shootWeapon(weapon_group, position, direction):
weapon_group.shootWeapon(position, direction)
#direction[0]:x , direction[1]:y
should_kill = False
self.rect.x += self.direction[0]
self.rect.y += self.direction[1]
if self.rect.x > SCREEN_WIDTH or self.rect.x < -self.image.get_width():
should_kill = True
if self.rect.y > SCREEN_HEIGHT or self.rect.y < -self.image.get_height():
should_kill = True
if should_kill:
self.kill()
else:
if self.ticks >= ENEMY_SHOOT_CYCLE:
self.ticks = 0
if self.is_hit:
self.is_hit -= 1
self.image = hit_surface
elif len(enemy_surface) >= 2:
self.image = enemy_surface[self.ticks//(ENEMY_SHOOT_CYCLE//2)]
else:
self.image = enemy_surface[0]
self.ticks += 1
if self.weapon_group is not None:
if self.ticks % ENEMY_SHOOT_CYCLE == 0:
shootWeapon(self.weapon_group, [self.rect.centerx, self.rect.y + self.image.get_height()], [0, ENEMY_SHOOT_SPEED])
EnemyGroup类
目前支持三种类型的敌机,在初始化函数会设置敌机的类型enemy_type,创建一个精灵组 group,用来保存和管理所有生成的该类型敌机,设置敌机拥有的武器。
createEnemy函数会随机创建一个从某个方向(左边,上方,左边)出现的敌机,并添加到group中。
checkBulletCollide函数会检测是否有英雄发射的子弹和这个类型的敌机相撞,每次相撞会造成一定的伤害,当敌机的生命小于累计的伤害时,就判定被消灭。
checkBulletCollide函数会检测英雄是否和这个类型的敌机,或敌机发射的子弹相撞。
class EnemyGroup():
def __init__(self, surface, hit_surface, down_surface, down_sound, score, health, speed, enemy_type, weapon_group):
self.surface = surface
self.hit_surface = hit_surface
self.down_surface = down_surface
self.group = pygame.sprite.Group()
self.down_group = pygame.sprite.Group()
self.down_sound = down_sound
self.score = score
self.health = health
self.speed = speed
self.enemy_type = enemy_type
self.weapon_group = weapon_group
def createEnemy(self):
def getDirection(surface, speed, enemy_type):
if enemy_type == EnemyType.EnemyType3:
enemy_init_pos = [randint(0, SCREEN_WIDTH - surface.get_width()), -surface.get_height()]
direction = [0, speed]
else:
# enemy can appear from top side, left side, and right side
appearSide = randint(0, 2)
if appearSide == 0: # from top side
enemy_init_pos = [randint(0, SCREEN_WIDTH - surface.get_width()), -surface.get_height()]
direction = [0, speed]
elif appearSide == 1: # from left side
enemy_init_pos = [-surface.get_width(), randint(0, (ENEMY_APPEAR_HEIGHT - surface.get_height()))]
direction = [randint(1, speed), randint(1, speed)]
elif appearSide == 2: # from right side
enemy_init_pos = [SCREEN_WIDTH, randint(0, (ENEMY_APPEAR_HEIGHT - surface.get_height()))]
direction = [randint(-speed, -1), randint(1, speed)]
return (enemy_init_pos, direction)
(enemy_init_pos, direction) = getDirection(self.surface[0], self.speed, self.enemy_type)
enemy = Enemy(self.surface[0], enemy_init_pos, direction, self.weapon_group)
self.group.add(enemy)
def update(self):
self.group.update(self.surface, self.hit_surface)
if self.weapon_group is not None:
self.weapon_group.update()
def draw(self, screen):
self.group.draw(screen)
if self.weapon_group is not None:
self.weapon_group.draw(screen)
def checkBulletCollide(self, bullets, screen, ticks):
score = 0
self.down_group.add(pygame.sprite.groupcollide(self.group, bullets.group, False, True))
for enemy_down in self.down_group:
if enemy_down.is_down:
screen.blit(self.down_surface[enemy_down.down_index], enemy_down.rect)
if ticks % (ANIMATE_CYCLE//2) == 0:
if enemy_down.down_index < (len(self.down_surface)-1):
if enemy_down.down_index == 0:
self.down_sound.play()
enemy_down.down_index += 1
else:
self.down_group.remove(enemy_down)
score += self.score
else:
enemy_down.damage += bullets.damage
enemy_down.is_hit = ANIMATE_CYCLE//3
if enemy_down.damage >= self.health:
enemy_down.is_down = 1
self.group.remove(enemy_down)
else:
self.down_group.remove(enemy_down)
return score
def checkHeroCollide(self, hero):
enemy_down_list = pygame.sprite.spritecollide(hero, self.group, False)
collide = False
if len(enemy_down_list) > 0:
for enemy_down in enemy_down_list:
if pygame.sprite.collide_circle_ratio(0.7)(enemy_down, hero):
self.group.remove(enemy_down)
self.down_group.add(enemy_down)
enemy_down.is_down = 1
collide = True
if not collide and self.weapon_group is not None:
bullet_hit_list = pygame.sprite.spritecollide(hero, self.weapon_group.group, False)
if len(bullet_hit_list) > 0:
for bullet_hit in bullet_hit_list:
if pygame.sprite.collide_circle_ratio(0.7)(bullet_hit, hero):
self.weapon_group.group.remove(bullet_hit)
collide = True
return collide
shooter.py
定义了一个Game类,其中的paly() 是游戏主循环的处理函数,负责更新背景图片,每隔一段时间生成敌机和道具,检测子弹和飞机是否相撞,检测英雄和敌机是否相撞。并调用敌机,英雄各自的类来刷新绘图。
class Game():
def __init__(self, caption, hero, screen_info):
self.screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption(caption)
self.hero = hero
self.clock = pygame.time.Clock()
self.screen_info = screen_info
self.ticks = 0
self.pause = False
self.backgound_y = SCREEN_HEIGHT - background.get_height() # height of background image must be larger than SCREEN_HEIGHT
def play(self, enemy_groups, gift_groups):
def updateBackground(screen, image_height, current_y):
if current_y <= 0:
screen.blit(background, (0, 0), (0, -current_y, SCREEN_WIDTH, SCREEN_HEIGHT))
elif current_y < SCREEN_HEIGHT:
screen.blit(background, (0, 0), (0, image_height - current_y, SCREEN_WIDTH, current_y))
screen.blit(background, (0, current_y), (0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - current_y))
def checkBulletCollide(enemy_group, bullets_group, screen, ticks):
score = 0
for group in enemy_group:
for bullet_group in bullets_group:
score += group.checkBulletCollide(bullet_group, screen, ticks)
return score
def checkHeroCollide(hero, enemy_group):
collide = False
for group in enemy_group:
if group.checkHeroCollide(hero):
collide = True
break;
return collide
self.clock.tick(FRAME_RATE)
if self.backgound_y == SCREEN_HEIGHT:
self.backgound_y = SCREEN_HEIGHT - background.get_height()
updateBackground(self.screen, background.get_height(), self.backgound_y)
self.backgound_y += 1
if self.ticks >= FRAME_RATE:
self.ticks = 0
self.hero.play()
self.createEnemy(enemy_groups, self.ticks, self.screen_info.getScore())
self.createGift(gift_groups, self.ticks, self.screen_info)
self.screen_info.addScore(checkBulletCollide(enemy_groups, self.hero.weapon_groups, self.screen, self.ticks))
if checkHeroCollide(self.hero, enemy_groups):
if self.hero.isHeroCrash():
game_over_sound.play()
for gift_group in gift_groups:
gift_group.checkHeroCollide(self.hero)
for weapon_group in self.hero.weapon_groups:
weapon_group.draw(self.screen)
for enemy_group in enemy_groups:
enemy_group.update()
enemy_group.draw(self.screen)
for gift_group in gift_groups:
gift_group.update()
gift_group.draw(self.screen)
self.screen.blit(self.hero.image, self.hero.rect)
self.ticks += 1
self.screen_info.displayInfo(self.screen, 0, self.hero.bomb_num, self.hero.life)
游戏的主循环如下
- 判断游戏状态
- 如果游戏结束,显示结束画面
- 如果游戏暂停,不做操作
- 如果游戏没有暂停,进行处理
- 获取pygame事件,读取键盘输入并处理
- 如果有方向键输入,则移动英雄
while True:
if myGame.isGameOver():
myGame.showGameOver()
elif not myGame.isPause():
myGame.play(enemy_groups, gift_groups)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# get keyboard input
if event.type == pygame.KEYDOWN:
if event.key in offset:
offset[event.key] = 3
elif event.key == pygame.K_SPACE:
myGame.hero.useBomb()
# press z to pause or resume game
elif event.key == pygame.K_z:
myGame.setPause()
elif event.type == pygame.KEYUP:
if event.key in offset:
offset[event.key] = 0
if not myGame.hero.is_hit:
myGame.hero.move(offset)
游戏效果
游戏执行的截图如下:
完整代码
完整实现代码的github链接 飞机大战增强版本
这边是csdn的下载链接 飞机大战增强版本
编译环境
python3.7 + pygame1.9