飞机大战-Python版本

在这里插入图片描述

requirements.txt

pygame==1.9.6

background.py

from image import Image


class Background:
    def __init__(self):
        self.image = Image.background
        self.rect = self.image.get_rect()
        self.y = float(self.rect.y)

    def update(self):
        # move down background image
        if self.y < self.rect.height:
            self.y += 1.5
        else:
            self.y = 0

        self.rect.y = self.y

    def draw(self, display_surface):
        # draw 2 images onto display surface
        display_surface.blit(self.image, (0, self.rect.y))
        display_surface.blit(self.image, (0, self.rect.y - self.rect.height))

bullet.py

import pygame
from pygame.sprite import Sprite

from image import Image


class Bullet(Sprite):
    def __init__(self, hero_rect):
        super().__init__()
        self.image = Image.bullet
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.rect.midtop = hero_rect.midtop

    def update(self):
        self.rect.top -= 12

        # remove it from the group if outside of the screen
        if self.rect.bottom <= 0:
            self.kill()

button.py

import pygame


class Button:
    def __init__(self, screen_rect, text):
        # button border attributes
        self.border_color = (96, 96, 96)
        self.border_rect = pygame.Rect(0, 0, 180, 40)
        self.border_rect.center = screen_rect.center

        if text == "Start":
            self.border_rect.centery += 80
        elif text == "Restart":
            self.border_rect.centery += 80
        elif text == "Exit":
            self.border_rect.centery += 140

        # button text attributes
        text_color = self.border_color
        font = pygame.font.SysFont(None, 40)
        self.text_image = font.render(text, True, text_color)
        self.text_rect = self.text_image.get_rect()
        self.text_rect.center = self.border_rect.center

    def draw(self, display_surface):
        # draw button border
        pygame.draw.rect(display_surface, self.border_color, self.border_rect, 3)

        # draw button text
        display_surface.blit(self.text_image, self.text_rect)

    def is_hit(self, pos):
        return self.border_rect.collidepoint(pos)

enemy.py

import pygame
from pygame.sprite import Sprite

from image import Image


class Enemy(Sprite):
    def __init__(self, topleft, screen_rect, stats):
        super().__init__()

        self.screen_rect = screen_rect
        self.stats = stats

        self.image_index = 0
        self.image = self.images[self.image_index]
        self.mask = pygame.mask.from_surface(self.image)

        self.rect = pygame.Rect(topleft, self.get_max_size())
        self.current_hp = self.max_hp
        self.is_hit_bullet = False

    def update(self):
        self.rect.top += 2

        # remove enemy outside the screen from group
        if self.rect.top >= self.screen_rect.bottom:
            self.kill()

    def hit_by_bullet(self):
        if self.current_hp > 0:
            self.current_hp -= 1
            self.is_hit_bullet = True

    @classmethod
    def get_max_size(cls):
        max_width_image = max(cls.images, key=lambda x: x.get_width())
        max_height_image = max(cls.images, key=lambda x: x.get_height())

        return max_width_image.get_width(), max_height_image.get_height()


class EnemySmall(Enemy):
    type = 1
    max_hp = 1
    score = 2
    images = Image.small_enemies

    def update(self):
        super().update()

        if self.current_hp > 0:
            self.image_index = 0
        else:
            if self.image_index < len(self.images) - 1:
                self.image_index += 1
            else:
                self.kill()
                self.stats.score += self.score

        self.image = self.images[self.image_index]


class EnemyMiddle(Enemy):
    type = 2
    max_hp = 10
    score = 10
    images = Image.mid_enemies

    def update(self):
        super().update()

        if self.current_hp > 0:
            if self.is_hit_bullet:
                self.image_index = 1
                self.is_hit_bullet = False
            else:
                self.image_index = 0
        else:
            if self.image_index < len(self.images) - 1:
                self.image_index += 1
            else:
                self.kill()
                self.stats.score += self.score

        self.image = self.images[self.image_index]


class EnemyBig(Enemy):
    type = 3
    max_hp = 25
    score = 100
    images = Image.big_enemies

    def update(self):
        super().update()

        if self.current_hp > 0:
            if self.is_hit_bullet:
                self.image_index = 2
                self.is_hit_bullet = False
            else:
                self.image_index = (self.image_index + 1) % 2
        else:
            if self.image_index < len(self.images) - 1:
                self.image_index += 1
            else:
                self.kill()
                self.stats.score += self.score

        self.image = self.images[self.image_index]

game.py

import sys
import random

import pygame
from pygame.sprite import Group

from stats import Stats
from background import Background
from hero import Hero
from bullet import Bullet
from enemy import EnemySmall, EnemyMiddle, EnemyBig
from button import Button
from widgets import Logo, EndPrompt, Scoreboard, PauseResume
from sound import Sound


class Game:
    def __init__(self):
        pygame.init()
        self.surface = pygame.display.set_mode((480, 852))
        pygame.display.set_caption("Plane Wars")

        try:
            icon = pygame.image.load("../res/image/icon.ico")
        except pygame.error:
            pass
        else:
            pygame.display.set_icon(icon)

        self.clock = pygame.time.Clock()

        self.stats = Stats()

        self.bg = Background()
        self.hero = Hero(self.surface.get_rect(), self.stats)
        self.bullets = Group()
        self.enemies = Group()

        buttons_name = ["Start", "Restart", "Exit"]
        self.buttons = {name: Button(self.surface.get_rect(), name) for name in buttons_name}

        widgets_name = ["Logo", "EndPrompt", "Scoreboard", "PauseResume"]
        self.widgets = {name: eval(name)(self.surface.get_rect(), self.stats) for name in widgets_name}

        self.sounds = Sound()

        self.frames = 0

    def reset(self):
        self.stats.reset()
        self.hero.reset()
        self.widgets["PauseResume"].reset()

    def run(self):
        while True:
            self.handle_events()

            if self.stats.state == Stats.RUN:
                self.bg.update()
                self.update_bullets()
                self.update_enemies()
                self.handle_collision()

            self.update_screen()

            self.clock.tick(60)

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                self.handle_mousedown_event(event)
            elif event.type == pygame.MOUSEMOTION:
                self.handle_mousemotion_event(event)

    def handle_mousedown_event(self, event):
        if self.stats.state == Stats.WELCOME:
            if self.buttons["Start"].is_hit(event.pos):
                self.stats.state = Stats.RUN
                # play background music indefinitely
                self.sounds.play("bg")
        elif self.stats.state == Stats.RUN:
            if self.widgets["PauseResume"].is_hit(event.pos):
                self.widgets["PauseResume"].update_click()
                self.stats.state = Stats.PAUSE
                self.sounds.pause("bg")
        elif self.stats.state == Stats.PAUSE:
            if self.widgets["PauseResume"].is_hit(event.pos):
                self.widgets["PauseResume"].update_click()
                self.stats.state = Stats.RUN
                self.sounds.unpause("bg")
        elif self.stats.state == Stats.GAMEOVER:
            if self.buttons["Restart"].is_hit(event.pos):
                self.reset()
                self.stats.state = Stats.RUN
                self.sounds.play("bg")
            elif self.buttons["Exit"].is_hit(event.pos):
                pygame.quit()
                sys.exit()

    def handle_mousemotion_event(self, event):
        if self.stats.state == Stats.RUN:
            if event.buttons[0]:
                self.hero.update(event.pos)

            self.widgets["PauseResume"].update_mouse_motion(event.pos)
        elif self.stats.state == Stats.PAUSE:
            self.widgets["PauseResume"].update_mouse_motion(event.pos)

    def update_bullets(self):
        # create bullet
        self.frames += 1
        if not (self.frames % 5):
            self.bullets.add(Bullet(self.hero.rect))
            self.sounds.play("bullet")

        # move bullets
        self.bullets.update()

    def update_enemies(self):
        if len(self.enemies) < 18:
            screen_rect = self.surface.get_rect()

            weighted_list = [EnemySmall] * 30 + [EnemyMiddle] * 3 + [EnemyBig] * 1
            enemy = random.choice(weighted_list)

            left = random.randint(0, screen_rect.width - enemy.get_max_size()[0])
            top = random.randint(-screen_rect.height, -enemy.get_max_size()[1])

            self.enemies.add(enemy((left, top), screen_rect, self.stats))

        self.enemies.update()

    def handle_collision(self):
        # check bullets & enemies collision
        collide_dict = pygame.sprite.groupcollide(self.bullets, self.enemies, True, False, pygame.sprite.collide_mask)
        collide_enemies_list = []

        if collide_dict:
            for collide_enemies in collide_dict.values():
                collide_enemies_list.extend(collide_enemies)

        if collide_enemies_list:
            for collide_enemy in collide_enemies_list:
                if collide_enemy.current_hp == 1:
                    self.sounds.play("enemy" + str(collide_enemy.type) + "_down")

                collide_enemy.hit_by_bullet()

        # check hero & enemies collision
        enemy = pygame.sprite.spritecollideany(self.hero, self.enemies, pygame.sprite.collide_mask)
        if enemy:
            self.hero.is_collide = True
            self.bullets.empty()
            self.enemies.empty()
            self.widgets["EndPrompt"].update_score_num()
            self.sounds.fadeout("bg")
            self.sounds.play("game_over")

    def update_screen(self):
        # draw background image onto display surface
        self.bg.draw(self.surface)

        if self.stats.state == Stats.WELCOME:
            # welcome state
            self.widgets["Logo"].draw(self.surface)
            self.buttons["Start"].draw(self.surface)
        elif self.stats.state == Stats.RUN or self.stats.state == Stats.PAUSE:
            # run/pause state
            self.hero.draw(self.surface)
            self.bullets.draw(self.surface)
            self.enemies.draw(self.surface)

            # draw pause button and score at last, keeps them above the screen
            self.widgets["PauseResume"].draw(self.surface)
            self.widgets["Scoreboard"].draw(self.surface)
        elif self.stats.state == Stats.GAMEOVER:
            # game over state
            self.widgets["EndPrompt"].draw(self.surface)
            self.buttons["Restart"].draw(self.surface)
            self.buttons["Exit"].draw(self.surface)

        # update display surface
        pygame.display.flip()

hero.py

import pygame

from stats import Stats
from image import Image


class Hero:
    def __init__(self, screen_rect, stats):
        self.screen_rect = screen_rect
        self.stats = stats

        self.images = Image.heros
        self.image = self.images[0]
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.reset()

    def reset(self):
        self.rect.midbottom = self.screen_rect.midbottom
        self.image_index = 0
        self.is_collide = False

    def draw(self, display_surface):
        if self.is_collide:
            if self.image_index < len(self.images) - 1:
                self.image_index += 1
            else:
                pygame.time.delay(200)
                self.stats.state = Stats.GAMEOVER
        else:
            self.image_index = not self.image_index

        self.image = self.images[self.image_index]

        display_surface.blit(self.image, self.rect)

    def update(self, pos):
        if self.rect.collidepoint(pos):
            self.rect.center = pos

image.py

import pygame


class Image:
    pygame.init()
    pygame.display.set_mode((480, 852))

    heros_name = ["hero1", "hero2", "hero_blowup_n1", "hero_blowup_n2", "hero_blowup_n3", "hero_blowup_n4"]
    small_enemies_name = ["enemy1", "enemy1_down1", "enemy1_down2", "enemy1_down3", "enemy1_down4"]
    mid_enemies_name = ["enemy2", "enemy2_hit", "enemy2_down1", "enemy2_down2", "enemy2_down3", "enemy2_down4"]
    big_enemies_name = ["enemy3_n1", "enemy3_n2", "enemy3_hit", "enemy3_down1", "enemy3_down2", "enemy3_down3",
                        "enemy3_down4", "enemy3_down5", "enemy3_down6"]

    pause_resume_name = ["game_pause_nor", "game_pause_pressed", "game_resume_nor", "game_resume_pressed"]

    try:
        background = pygame.image.load("../res/image/background.png").convert()
        bullet = pygame.image.load("../res/image/bullet1.png").convert_alpha()
        heros = [pygame.image.load(image.join(["../res/image/", ".png"])).convert_alpha() for image in heros_name]
        small_enemies = [pygame.image.load(image.join(["../res/image/", ".png"])).convert_alpha() for image in small_enemies_name]
        mid_enemies = [pygame.image.load(image.join(["../res/image/", ".png"])).convert_alpha() for image in mid_enemies_name]
        big_enemies = [pygame.image.load(image.join(["../res/image/", ".png"])).convert_alpha() for image in big_enemies_name]
        logo = pygame.image.load("../res/image/logo.png").convert_alpha()
        pause_resume = [pygame.image.load(image.join(["../res/image/", ".png"])).convert_alpha() for image in pause_resume_name]
    except pygame.error:
        raise SystemExit(pygame.get_error())

plane_wars.py

from game import Game


if __name__ == "__main__":
    plane_wars_game = Game()
    plane_wars_game.run()

sound.py

from collections import defaultdict

import pygame


class DummySound:
    def play(self, *args):
        pass

    def pause(self):
        pass

    def unpause(self):
        pass

    def fadeout(self, *args):
        pass


class Sound:
    def __init__(self):
        # background music
        try:
            pygame.mixer.music.load("../res/sound/game_music.wav")
        except pygame.error as e:
            self.music = DummySound()
            print(e)
        else:
            self.music = pygame.mixer.music

        # sound effect
        self.sounds = defaultdict(DummySound)

        sounds_name = ["bullet", "enemy1_down", "enemy2_down", "enemy3_down", "game_over"]

        for sound in sounds_name:
            try:
                self.sounds[sound] = pygame.mixer.Sound(sound.join(["../res/sound/", ".wav"]))
            except pygame.error as e:
                print(e)

    def play(self, name):
        if name == "bg":
            self.music.play(-1)
        else:
            self.sounds[name].play()

    def pause(self, name):
        if name == "bg":
            self.music.pause()

    def unpause(self, name):
        if name == "bg":
            self.music.unpause()

    def fadeout(self, name):
        if name == "bg":
            self.music.fadeout(1000)

stats.py

class Stats:
    # Game states
    WELCOME, RUN, GAMEOVER, PAUSE = range(4)

    def __init__(self):
        self.state = Stats.WELCOME
        self.score = 0

    def reset(self):
        self.score = 0

widgets.py

import pygame

from image import Image


class Logo:
    def __init__(self, *args):
        screen_rect = args[0]
        self.image = Image.logo
        self.rect = self.image.get_rect()
        self.rect.centerx = screen_rect.centerx
        self.rect.centery = screen_rect.centery - 150

    def draw(self, display_surface):
        display_surface.blit(self.image, self.rect)


class Scoreboard:
    def __init__(self, *args):
        self.stats = args[1]
        self.score_color = (0, 0, 0)

        try:
            self.font = pygame.font.Font("../res/font/comici.ttf", 35)
        except Exception as e:
            print(e)
            self.font = pygame.font.SysFont(None, 50)

    def draw(self, display_surface):
        score_image = self.font.render(str(self.stats.score), True, self.score_color)
        display_surface.blit(score_image, (80, 0))


class PauseResume:
    # Pause and resume states
    PAUSE_NORMAL, PAUSE_PRESSED, RESUME_NORMAL, RESUME_PRESSED = range(4)

    def __init__(self, *args):
        self.images = Image.pause_resume
        self.state = PauseResume.PAUSE_NORMAL

        self.rect = self.images[self.state].get_rect()
        self.rect.topleft = (0, 5)

    def reset(self):
        self.state = PauseResume.PAUSE_NORMAL

    def draw(self, display_surface):
        display_surface.blit(self.images[self.state], self.rect)

    def is_hit(self, pos):
        return self.rect.collidepoint(pos)

    def update_click(self):
        if self.state == PauseResume.PAUSE_NORMAL or self.state == PauseResume.PAUSE_PRESSED:
            self.state = PauseResume.RESUME_NORMAL
        else:
            self.state = PauseResume.PAUSE_NORMAL

    def update_mouse_motion(self, pos):
        is_mouse_on = self.is_hit(pos)

        if is_mouse_on:
            if self.state == PauseResume.PAUSE_NORMAL:
                self.state = PauseResume.PAUSE_PRESSED
            elif self.state == PauseResume.RESUME_NORMAL:
                self.state = PauseResume.RESUME_PRESSED
        else:
            if self.state == PauseResume.PAUSE_PRESSED:
                self.state = PauseResume.PAUSE_NORMAL
            elif self.state == PauseResume.RESUME_PRESSED:
                self.state = PauseResume.RESUME_NORMAL


class EndPrompt:
    def __init__(self, *args):
        screen_rect = args[0]
        self.stats = args[1]

        # outside border
        self.border_color = (96, 96, 96)
        self.border_rect = pygame.Rect(0, 0, 350, 300)
        self.border_rect.centerx = screen_rect.centerx
        self.border_rect.centery = screen_rect.centery + 40

        # score
        self.score_color = self.border_color
        self.font = pygame.font.SysFont(None, 40)

        # score text
        self.score_text_image = self.font.render("Score:", True, self.score_color)
        self.score_text_top = self.border_rect.top + 30
        self.score_text_left = self.border_rect.left + 30

        # score number
        self.update_score_num()

    def update_score_num(self):
        self.score_num_image = self.font.render(str(self.stats.score), True, self.score_color)
        self.score_num_rect = self.score_num_image.get_rect()
        self.score_num_rect.centerx = self.border_rect.centerx
        self.score_num_rect.centery = self.border_rect.top + 80

    def draw(self, display_surface):
        # draw border
        pygame.draw.rect(display_surface, self.border_color, self.border_rect, 3)

        # draw score text
        display_surface.blit(self.score_text_image, (self.score_text_left, self.score_text_top))

        # draw score number
        display_surface.blit(self.score_num_image, self.score_num_rect)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tui_GuiGe

鼓励一下作者吧,请他喝一瓶啤酒

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值