【Python实战】飞机大战

开发一个飞机大战游戏是Python学习的经典实战项目,尤其适合结合面向对象编程和游戏框架(如Pygame)进行实践。以下是游戏设计的核心考虑因素和模块划分建议:


一、游戏设计核心考虑因素

  1. 性能优化

    • Python游戏需注意帧率控制(如60FPS)
    • 避免频繁的对象创建/销毁(使用对象池管理子弹、敌机)
    • 减少图像缩放等实时计算操作(预加载缩放后的图片)
  2. 游戏循环(Game Loop)

    • 主循环需要处理:事件监听→状态更新→画面渲染→音效播放
    • 控制循环频率(如pygame.time.Clock().tick(60)
  3. 输入控制

    • 响应键盘事件(WSAD/方向键移动,空格发射子弹)
    • 支持手柄输入(可选扩展)
  4. 碰撞检测

    • 使用矩形碰撞(pygame.Rect.colliderect
    • 优化检测效率(分组检测:玩家子弹 vs 敌机、敌机 vs 玩家)
  5. 游戏状态管理

    • 区分不同场景:开始界面、游戏中、暂停、结束界面
    • 使用状态机或场景堆栈管理(如game_state = "playing"
  6. 资源管理

    • 集中加载图片、音效、字体(避免重复IO操作)
    • 使用相对路径确保跨平台兼容性
  7. 可扩展性

    • 模块化设计,方便新增敌机类型、武器系统
    • 配置数据分离(如敌机属性存于JSON文件)

二、游戏核心模块划分

1. 游戏主模块(Main Game)
# ====================== 游戏主逻辑 ======================
class Game:
    def __init__(self):
        # 初始化显示
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("飞机大战")
        self.clock = pygame.time.Clock()

        # 加载资源
        self.load_resources()

        # 游戏状态
        self.running = True
        self.score = 0

    def load_resources(self):
        """加载所有资源"""
        global shoot_sound, explosion_sound
        shoot_sound = load_sound("pew.wav")
        explosion_sound = load_sound("expl3.wav")

        # 初始化精灵组
        self.all_sprites = pygame.sprite.Group()
        self.enemies = pygame.sprite.Group()
        self.bullets = pygame.sprite.Group()

        # 创建玩家
        self.player = Player()
        self.all_sprites.add(self.player)

        # 创建敌机组
        self.enemy_spawn_timer = pygame.USEREVENT + 1
        pygame.time.set_timer(self.enemy_spawn_timer, 1000)  # 每秒生成敌机

    def handle_events(self):
        """处理事件循环"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.running = False
            elif event.type == self.enemy_spawn_timer:
                enemy = Enemy()
                self.all_sprites.add(enemy)
                self.enemies.add(enemy)

    def check_collisions(self):
        """检测碰撞事件"""
        # 子弹击中敌机
        hits = pygame.sprite.groupcollide(self.enemies, self.bullets, True, True)
        for hit in hits:
            self.score += 50
            explosion_sound.play()

        # 敌机撞击玩家
        hits = pygame.sprite.spritecollide(self.player, self.enemies, True)
        for hit in hits:
            self.player.health -= 1
            if self.player.health <= 0:
                self.game_over()

    def draw_hud(self):
        """绘制游戏界面HUD"""
        # 绘制得分
        font = pygame.font.Font(None, 36)
        score_text = font.render(f"Score: {self.score}", True, WHITE)
        self.screen.blit(score_text, (10, 10))

        # 绘制生命值
        health_text = font.render(f"Health: {self.player.health}", True, RED)
        self.screen.blit(health_text, (10, 50))

    def game_over(self):
        """游戏结束处理"""
        font = pygame.font.Font(None, 74)
        text = font.render("GAME OVER", True, RED)
        text_rect = text.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2))
        self.screen.blit(text, text_rect)
        pygame.display.flip()
        pygame.time.wait(10000)  # 等待2秒
        self.running = False

    def run(self):
        """游戏主循环"""
        while self.running:
            # 控制帧率
            self.clock.tick(FPS)

            # 处理输入
            self.handle_events()
            keys = pygame.key.get_pressed()
            if keys[pygame.K_SPACE]:
                self.player.shoot(self.bullets, self.all_sprites)

            # 更新游戏状态
            self.all_sprites.update(keys)  # 更新所有精灵
            self.check_collisions()

            # 渲染画面
            self.screen.fill(BLACK)
            self.all_sprites.draw(self.screen)
            self.draw_hud()
            pygame.display.flip()

        pygame.quit()
2. 精灵类(Sprites)

使用面向对象设计游戏元素:

  • Player(玩家飞机)

      """玩家飞机类"""
    
      def __init__(self):
          super().__init__()
          self.image = load_image("player.png")
          self.rect = self.image.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 50))
          self.speed = PLAYER_SPEED
          self.health = 3
          self.shoot_delay = 250  # 射击间隔(毫秒)
          self.last_shot = pygame.time.get_ticks()
    
      def update(self, keys):
          """根据键盘输入更新位置"""
          # 水平移动
          if keys[pygame.K_LEFT] or keys[pygame.K_a]:
              self.rect.x -= self.speed
          if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
              self.rect.x += self.speed
    
          # 垂直移动(可选)
          if keys[pygame.K_UP] or keys[pygame.K_w]:
              self.rect.y -= self.speed
          if keys[pygame.K_DOWN] or keys[pygame.K_s]:
              self.rect.y += self.speed
    
          # 边界约束
          self.rect.clamp_ip(pygame.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT))
    
      def shoot(self, bullets, all_sprites):
          """发射子弹"""
          now = pygame.time.get_ticks()
          if now - self.last_shot > self.shoot_delay:
              self.last_shot = now
              bullet = Bullet(self.rect.centerx, self.rect.top)
              all_sprites.add(bullet)
              bullets.add(bullet)
              shoot_sound.play()
    
  • Enemy(敌机)

      def __init__(self):
          super().__init__()
          self.image = load_image("enemy1.png")
          self.rect = self.image.get_rect()
          self.rect = self.image.get_rect(
              x=random.randrange(SCREEN_WIDTH - self.rect.width),
              y=random.randrange(-100, -40),
          )
          self.speed_y = random.randint(*ENEMY_SPEED_RANGE)
          self.speed_x = random.choice([-1, 0, 1])  # 横向移动
    
      def update(self, *args):
          """自动向下移动"""
          self.rect.y += self.speed_y
          self.rect.x += self.speed_x
          # 移出屏幕后删除
          if self.rect.top > SCREEN_HEIGHT + 10:
              self.kill()
    
  • Bullet(子弹)

      def __init__(self, x, y):
          super().__init__()
          self.image = load_image("bullet3.png")
          self.rect = self.image.get_rect(center=(x, y))
          self.speed_y = BULLET_SPEED
    
      def update(self, *args):
          """向上移动"""
          self.rect.y += self.speed_y
          if self.rect.bottom < 0:  # 移出屏幕后删除
              self.kill()
    

三、推荐技术栈

  1. 游戏框架

  2. 素材资源

  3. 代码结构参考


四、开发路线建议

  1. 基础版本(1-3天)

    • 实现玩家移动、发射子弹
    • 随机生成敌机,碰撞检测得分
    • 显示血量和分数
  2. 进阶版本(3-7天)

    • 添加不同敌机类型(普通/快速/BOSS)
    • 实现敌机发射子弹
    • 增加音效和爆炸动画
    • 支持游戏暂停/继续
  3. 扩展版本(可选)

    • 添加道具系统(护盾、连发武器)
    • 实现关卡进度和难度曲线
    • 支持本地高分榜(SQLite存储)
    • 打包为exe/APK文件

五、避坑指南

  1. 不要过早优化:先完成核心玩法,再考虑性能
  2. 善用精灵组(Sprite Groups)pygame.sprite.Group()管理同类对象
  3. 坐标系注意:Pygame的Y轴向下增大(与数学坐标系相反)
  4. 资源路径问题:使用os.path处理路径,避免绝对路径硬编码
  5. 调试技巧:用print输出变量或绘制调试矩形(pygame.draw.rect

通过分阶段实现,你将逐步掌握游戏开发的核心模式(状态机、对象池、事件驱动),同时巩固Python面向对象编程和模块化设计能力。

六、总体代码(单文件)

# -*- coding: utf-8 -*-
import pygame
import random
import os

# 初始化Pygame和混音器
pygame.init()
pygame.mixer.init()

# 游戏常量配置
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 600
FPS = 60
PLAYER_SPEED = 5
ENEMY_SPEED_RANGE = (2, 4)
BULLET_SPEED = -8  # 玩家子弹速度(向上)

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)

# 资源路径配置
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "images")
snd_folder = os.path.join(game_folder, "sounds")


# 加载图形资源
def load_image(file):
    """加载图像并转换格式,返回图像和矩形"""
    path = os.path.join(img_folder, file)
    try:
        image = pygame.image.load(path).convert()
    except pygame.error as e:
        print(f"无法加载图像: {path}")
        raise SystemExit(e)
    image.set_colorkey(BLACK)  # 设置透明色
    return image


# 加载音效资源
def load_sound(file):
    """加载音效文件"""
    path = os.path.join(snd_folder, file)
    try:
        return pygame.mixer.Sound(path)
    except pygame.error as e:
        print(f"无法加载音效: {path}")
        return None


# ====================== 游戏精灵类 ======================
class Player(pygame.sprite.Sprite):
    """玩家飞机类"""

    def __init__(self):
        super().__init__()
        self.image = load_image("player.png")
        self.rect = self.image.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 50))
        self.speed = PLAYER_SPEED
        self.health = 3
        self.shoot_delay = 250  # 射击间隔(毫秒)
        self.last_shot = pygame.time.get_ticks()

    def update(self, keys):
        """根据键盘输入更新位置"""
        # 水平移动
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.rect.x += self.speed

        # 垂直移动(可选)
        if keys[pygame.K_UP] or keys[pygame.K_w]:
            self.rect.y -= self.speed
        if keys[pygame.K_DOWN] or keys[pygame.K_s]:
            self.rect.y += self.speed

        # 边界约束
        self.rect.clamp_ip(pygame.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT))

    def shoot(self, bullets, all_sprites):
        """发射子弹"""
        now = pygame.time.get_ticks()
        if now - self.last_shot > self.shoot_delay:
            self.last_shot = now
            bullet = Bullet(self.rect.centerx, self.rect.top)
            all_sprites.add(bullet)
            bullets.add(bullet)
            shoot_sound.play()


class Enemy(pygame.sprite.Sprite):
    """敌机基类"""

    def __init__(self):
        super().__init__()
        self.image = load_image("enemy1.png")
        self.rect = self.image.get_rect()
        self.rect = self.image.get_rect(
            x=random.randrange(SCREEN_WIDTH - self.rect.width),
            y=random.randrange(-100, -40),
        )
        self.speed_y = random.randint(*ENEMY_SPEED_RANGE)
        self.speed_x = random.choice([-1, 0, 1])  # 横向移动

    def update(self, *args):
        """自动向下移动"""
        self.rect.y += self.speed_y
        self.rect.x += self.speed_x
        # 移出屏幕后删除
        if self.rect.top > SCREEN_HEIGHT + 10:
            self.kill()


class Bullet(pygame.sprite.Sprite):
    """玩家子弹类"""

    def __init__(self, x, y):
        super().__init__()
        self.image = load_image("bullet3.png")
        self.rect = self.image.get_rect(center=(x, y))
        self.speed_y = BULLET_SPEED

    def update(self, *args):
        """向上移动"""
        self.rect.y += self.speed_y
        if self.rect.bottom < 0:  # 移出屏幕后删除
            self.kill()


# ====================== 游戏主逻辑 ======================
class Game:
    def __init__(self):
        # 初始化显示
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("飞机大战")
        self.clock = pygame.time.Clock()

        # 加载资源
        self.load_resources()

        # 游戏状态
        self.running = True
        self.score = 0

    def load_resources(self):
        """加载所有资源"""
        global shoot_sound, explosion_sound
        shoot_sound = load_sound("pew.wav")
        explosion_sound = load_sound("expl3.wav")

        # 初始化精灵组
        self.all_sprites = pygame.sprite.Group()
        self.enemies = pygame.sprite.Group()
        self.bullets = pygame.sprite.Group()

        # 创建玩家
        self.player = Player()
        self.all_sprites.add(self.player)

        # 创建敌机组
        self.enemy_spawn_timer = pygame.USEREVENT + 1
        pygame.time.set_timer(self.enemy_spawn_timer, 1000)  # 每秒生成敌机

    def handle_events(self):
        """处理事件循环"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.running = False
            elif event.type == self.enemy_spawn_timer:
                enemy = Enemy()
                self.all_sprites.add(enemy)
                self.enemies.add(enemy)

    def check_collisions(self):
        """检测碰撞事件"""
        # 子弹击中敌机
        hits = pygame.sprite.groupcollide(self.enemies, self.bullets, True, True)
        for hit in hits:
            self.score += 50
            explosion_sound.play()

        # 敌机撞击玩家
        hits = pygame.sprite.spritecollide(self.player, self.enemies, True)
        for hit in hits:
            self.player.health -= 1
            if self.player.health <= 0:
                self.game_over()

    def draw_hud(self):
        """绘制游戏界面HUD"""
        # 绘制得分
        font = pygame.font.Font(None, 36)
        score_text = font.render(f"Score: {self.score}", True, WHITE)
        self.screen.blit(score_text, (10, 10))

        # 绘制生命值
        health_text = font.render(f"Health: {self.player.health}", True, RED)
        self.screen.blit(health_text, (10, 50))

    def game_over(self):
        """游戏结束处理"""
        font = pygame.font.Font(None, 74)
        text = font.render("GAME OVER", True, RED)
        text_rect = text.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2))
        self.screen.blit(text, text_rect)
        pygame.display.flip()
        pygame.time.wait(10000)  # 等待2秒
        self.running = False

    def run(self):
        """游戏主循环"""
        while self.running:
            # 控制帧率
            self.clock.tick(FPS)

            # 处理输入
            self.handle_events()
            keys = pygame.key.get_pressed()
            if keys[pygame.K_SPACE]:
                self.player.shoot(self.bullets, self.all_sprites)

            # 更新游戏状态
            self.all_sprites.update(keys)  # 更新所有精灵
            self.check_collisions()

            # 渲染画面
            self.screen.fill(BLACK)
            self.all_sprites.draw(self.screen)
            self.draw_hud()
            pygame.display.flip()

        pygame.quit()


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

附件:图片资源

请添加图片描述
请添加图片描述
请添加图片描述

音效资源无法放置在此处,如自行下载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

满怀1015

你的鼓励是我创作最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值