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

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