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