pygame飛機大戰遊戲

使用python的pygame模塊寫了一個窗口的飛機大戰遊戲,效果如圖。

使用上下左右控制,獲得藍色星星火力+20,黃色星星生命+1

以下是實現代碼,運行環境python3。

此代碼僅供學習與交流。請勿用於商業用途。

import random
import threading
from abc import ABCMeta, abstractmethod
from enum import Enum

import pygame

# 窗口寬高
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 654
# 背景,開始,暫停,遊戲結束圖片
background_image = pygame.image.load('image/background.png')
start_image = pygame.image.load('image/start.png')
pause_image = pygame.image.load('image/pause.png')
gameover_image = pygame.image.load('image/gameover.png')
# 英雄機圖片,兩張圖片切換
hero0_image = pygame.image.load('image/hero0.png')
hero1_image = pygame.image.load('image/hero1.png')
# 子彈圖片
bullet_image = pygame.image.load('image/bullet.png')
# 敵機,大敵機,敵機爆炸圖片
airplane_image = pygame.image.load('image/airplane.png')
airplane_rect = airplane_image.get_rect()
big_airplane_image = pygame.transform.scale(airplane_image, (airplane_rect.width * 2, airplane_rect.height * 2))
bomb_image = pygame.image.load('image/bomb.png')
bomb_rect = bomb_image.get_rect()
# 黃色星星,藍色星星圖片
yellow_star_image = pygame.image.load('image/star.png')
blue_star_image = pygame.image.load('image/blue_star.png')


class GameState(Enum):
    """遊戲狀態"""
    start = 0
    running = 1
    gameover = 2
    pause = 3


class Award(Enum):
    """獎勵類型"""
    life = 0
    bullet = 1

    def hint(self):
        """獲得獎勵的提示"""
        if self == Award.life:
            return '生命+1'
        elif self == Award.bullet:
            return '火力+20'


class FlyingObject(pygame.sprite.Sprite):
    """飛行物"""
    __metaclass__ = ABCMeta

    def __init__(self):
        super(FlyingObject, self).__init__()
        self.image = None
        self.rect = None
        self.speed = 0
        self.life = 1

    @abstractmethod
    def update(self):
        """更新當前對象狀態的方法,供子類實現"""
        pass


class Hint(FlyingObject):
    """提示"""

    def __init__(self, hint_str, pos):
        super(Hint, self).__init__()
        self.hint_str = hint_str
        # 元組不能更改,轉換爲list
        self.start_pos = list(pos)
        self.pos = self.start_pos.copy()

    def update(self):
        """上升一小段距離後消失"""
        self.pos[1] -= 1
        if self.start_pos[1] - self.pos[1] >= 40:
            self.kill()


class Bullet(FlyingObject):
    """子彈"""

    def __init__(self, pos):
        super(Bullet, self).__init__()
        self.image = bullet_image
        self.rect = self.image.get_rect()
        self.rect.topleft = (pos[0] - self.rect.width / 2, pos[1])
        self.speed = 8

    def update(self):
        """向上移動,出上邊界消失"""
        self.rect.top -= self.speed
        if self.rect.bottom < 0:
            self.kill()


class Enemy(FlyingObject):
    """敵人"""
    __metaclass__ = ABCMeta

    def __init__(self):
        super(Enemy, self).__init__()
        self.score = 0

    def update(self):
        """向下移動,出下邊界消失"""
        self.rect.top += self.speed
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()

    def destroy(self):
        """置爲毀壞狀態"""
        # 將圖片置爲bomb圖片,座標移動到正中間
        x = self.rect.left + ((self.rect.width - bomb_rect.width) / 2)
        y = self.rect.top + ((self.rect.height - bomb_rect.height) / 2)
        self.rect.topleft = (x, y)
        self.image = bomb_image
        # 0.3秒後消失
        timer = threading.Timer(0.3, self.kill)
        timer.start()


class Airplane(Enemy):
    """敵機,敵人的子類"""

    def __init__(self):
        super(Airplane, self).__init__()
        self.image = airplane_image
        self.rect = self.image.get_rect()
        self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
        self.speed = 4
        self.score = 10


class BigAirplane(Enemy):
    """大敵機,敵人的子類"""

    def __init__(self):
        super(BigAirplane, self).__init__()
        self.image = big_airplane_image
        self.rect = self.image.get_rect()
        self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
        self.speed = 3
        self.life = 2
        self.score = 30


class Star(FlyingObject):
    """星星,英雄機可以吃星星獲得獎勵"""

    def __init__(self):
        super(Star, self).__init__()
        # 初始化獎勵類型
        self.award = random.choices(list(Award), [1, 3])[0]
        # 生命獎勵爲黃色星星,火力獎勵爲藍色星星
        self.image = yellow_star_image if self.award == Award.life else blue_star_image
        self.rect = self.image.get_rect()
        self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
        self.speed_x = 2
        self.speed_y = 4

    def update(self):
        """斜向下移動"""
        self.rect.left += self.speed_x
        self.rect.top += self.speed_y
        # 碰到左右邊界speed_x取反
        if self.rect.left < 0 or self.rect.left > SCREEN_WIDTH - self.rect.width:
            self.speed_x = -self.speed_x
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()


class Hero(FlyingObject):
    """英雄機"""

    def __init__(self):
        super(Hero, self).__init__()
        self.images = [hero0_image, hero1_image]
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.rect.topleft = (int(SCREEN_WIDTH / 2 - self.rect.width / 2), SCREEN_HEIGHT - self.rect.height - 30)
        self.speed = 6
        self.life = 3
        self.init_directions()
        self.double_fire = 0
        # 控制圖片切換
        self.index = 0
        # 子彈列表
        self.bullets = pygame.sprite.Group()

    def shoot(self):
        """射擊"""
        step_x = self.rect.width / 4
        # 火力大於0時二連發
        if self.double_fire > 0:
            self.bullets.add(Bullet((self.rect.left + 1 * step_x, self.rect.top)))
            self.bullets.add(Bullet((self.rect.left + 3 * step_x, self.rect.top)))
            self.double_fire -= 2
        # 沒有火力單發子彈
        else:
            self.bullets.add(Bullet(self.rect.midtop))

    def set_direction_enabled(self, direction, enabled):
        """設置某個方向有效或無效"""
        self.directions[direction] = enabled

    def init_directions(self):
        """初始化方向的字典"""
        self.directions = {pygame.K_LEFT: False, pygame.K_RIGHT: False, pygame.K_UP: False, pygame.K_DOWN: False}

    def receive_award(self, award):
        """接受獎勵"""
        if award == Award.life:
            self.life += 1
        elif award == Award.bullet:
            self.double_fire += 20

    def update(self):
        # 切換圖片
        self.image = self.images[self.index // 10 % len(self.images)]
        self.index += 1
        # 移動,不能超出邊界
        x = self.rect.left + (self.directions[pygame.K_RIGHT] - self.directions[pygame.K_LEFT]) * self.speed
        y = self.rect.top + (self.directions[pygame.K_DOWN] - self.directions[pygame.K_UP]) * self.speed
        if x < 0:
            x = 0
        elif x > SCREEN_WIDTH - self.rect.width:
            x = SCREEN_WIDTH - self.rect.width
        if y < 0:
            y = 0
        elif y > SCREEN_HEIGHT - self.rect.height:
            y = SCREEN_HEIGHT - self.rect.height
        self.rect.left = x
        self.rect.top = y


class Game:
    """遊戲類"""

    def __init__(self):
        """初始化窗口"""
        pygame.init()

        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
        pygame.display.set_caption('shoot game')

        self.state = GameState.start
        self.init()

    def init(self):
        """初始化數據,供遊戲第一次開始和死亡後重新開始時調用"""
        # 計數
        self.tick = 0
        # 分數
        self.point = 0
        # 英雄機對象
        self.hero = Hero()
        # 敵機組
        self.enemy_group = pygame.sprite.Group()
        # 星星組
        self.star_group = pygame.sprite.Group()
        # 提示組
        self.hint_group = pygame.sprite.Group()

    def handle_event(self, event):
        """處理事件"""
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        # 如果不是運行中,點擊回車,置爲運行中
        if self.state != GameState.running:
            if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
                if self.state == GameState.gameover:
                    self.init()
                self.hero.init_directions()
                self.state = GameState.running
            return
            # 若在運行中,上下左右控制英雄方向,回車暫停
        else:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    self.state = GameState.pause
                elif event.key in self.hero.directions:
                    self.hero.set_direction_enabled(event.key, True)
            elif event.type == pygame.KEYUP:
                if event.key in self.hero.directions:
                    self.hero.set_direction_enabled(event.key, False)

    def produce_flying_object(self):
        """產生飛行物"""
        fly_class = random.choices([Airplane, BigAirplane, Star], [4, 2, 1])[0]
        if fly_class == Airplane or fly_class == BigAirplane:
            self.enemy_group.add(fly_class())
        elif fly_class == Star:
            self.star_group.add(fly_class())

    def update(self):
        """子彈,敵機,提示,英雄 更新位置"""
        self.hero.bullets.update()
        self.enemy_group.update()
        self.star_group.update()
        for hint in self.hint_group.sprites():
            hint.update()
        self.hero.update()

    def enemy_and_bullet_collide(self):
        """敵人和子彈碰撞 敵人生命-他撞到的子彈數量,若敵人生命=0則敵機毀滅然後獲得分數"""
        enemy_and_bullect_list_dict = pygame.sprite.groupcollide(self.enemy_group, self.hero.bullets, False,
                                                                 False)
        if enemy_and_bullect_list_dict:
            for enemy, bullect_list in enemy_and_bullect_list_dict.items():
                # 敵機已經毀滅,則跳過
                if enemy.life <= 0:
                    continue
                for bullet in bullect_list:
                    enemy.life -= 1
                    bullet.kill()
                if enemy.life <= 0:
                    enemy.destroy()
                    score = enemy.score
                    # 添加提示
                    self.hint_group.add(Hint('分數+%d' % score, enemy.rect.topleft))
                    self.point += score

    def hero_and_star_collide(self):
        """星星和英雄機碰撞,獲得獎勵"""
        star_list = pygame.sprite.spritecollide(self.hero, self.star_group, True)
        if star_list:
            for star in star_list:
                # 添加提示
                self.hint_group.add(Hint(star.award.hint(), star.rect.topleft))
                # 英雄機獲得獎勵
                self.hero.receive_award(star.award)

    def hero_and_enemy_collide(self):
        """敵機和英雄碰撞,英雄機生命-1,敵機消失,若英雄機生命小於等於0遊戲結束"""
        enemy_list = pygame.sprite.spritecollide(self.hero, self.enemy_group, True)
        if enemy_list:
            for enemy in enemy_list:
                if enemy.life > 0:
                    self.hint_group.add(Hint('生命-1', self.hero.rect.topleft))
                    self.hero.life -= 1
                    enemy.kill()
            if self.hero.life <= 0:
                self.state = GameState.gameover

    def draw_flying_object(self):
        """繪製飛行物(子彈,敵機,星星,提示,英雄機)"""
        self.hero.bullets.draw(self.screen)
        self.enemy_group.draw(self.screen)
        self.star_group.draw(self.screen)
        for hint in self.hint_group.sprites():
            self.draw_string(hint.hint_str, 16, hint.pos)
        self.screen.blit(self.hero.image, self.hero.rect)

    def draw_string(self, s, size, pos, color=(255, 0, 0)):
        """繪製字符串"""
        font = pygame.font.SysFont("SimHei", size)
        text_surface = font.render(s, True, color)
        self.screen.blit(text_surface, pos)

    def draw_info(self):
        """繪製左上角的分數、生命、火力等信息"""
        self.draw_string("分數:%d" % self.point, 16, (0, 0))
        self.draw_string("生命:%d" % self.hero.life, 16, (80, 0))
        self.draw_string("火力:%d" % self.hero.double_fire, 16, (150, 0))

    def start(self):
        """遊戲開始"""
        while True:
            """事件處理"""
            for event in pygame.event.get():
                self.handle_event(event)
            """畫背景後,判斷四個狀態分別繪製"""
            # 畫背景
            self.screen.blit(background_image, background_image.get_rect())
            # 若爲開始界面,畫開始圖,寫提示
            if self.state == GameState.start:
                self.screen.blit(start_image, start_image.get_rect())
                self.draw_string("press enter key to start", 30, (20, SCREEN_HEIGHT / 2 + 50), (150, 150, 150))
            # 若爲暫停界面,畫暫停圖
            elif self.state == GameState.pause:
                self.screen.blit(pause_image, pause_image.get_rect())
            # 若爲遊戲結束界面,畫遊戲結束圖、顯示得分
            elif self.state == GameState.gameover:
                self.screen.blit(gameover_image, gameover_image.get_rect())
                self.draw_string("分數:%d" % self.point, 24, (140, SCREEN_HEIGHT / 2), (255, 0, 255))
            # 若爲運行中
            elif self.state == GameState.running:
                # 每20個tick英雄射擊,每50個tick產生飛行物
                if self.tick % 20 == 0:
                    self.hero.shoot()
                if self.tick % 50 == 0:
                    self.produce_flying_object()
                # 子彈,敵機,提示,英雄 更新位置
                self.update()
                # 敵人和子彈碰撞 敵人生命-1,若敵人生命=0則敵機毀滅然後獲得分數
                self.enemy_and_bullet_collide()
                # 星星和英雄機碰撞,獲得獎勵
                self.hero_and_star_collide()
                # 敵機和英雄碰撞,英雄機生命-1,敵機消失,若英雄機生命小於等於0遊戲結束
                self.hero_and_enemy_collide()
                # 繪製子彈,敵機,星星,提示,英雄機
                self.draw_flying_object()
                # 繪製左上角的分數、生命、火力
                self.draw_info()
                # 計數
                self.tick += 1

            pygame.display.flip()


if __name__ == '__main__':
    Game().start()

 

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