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()

 

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