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链接 飞机大战增强版本

编译环境
python3.7 + pygame1.9

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章