《Python 飞机大战》(七)敌人飞机

现在我们的主场景中还没有敌机,我们接下来实现多个敌机从上向屏幕下方移动,并能够随机发射子弹。

1. EnemyPlane 类

该类的定义了单个敌机的实现,主要实现方法如下:

  1. set_used 设置飞机可用,并随机设置其初始位置
  2. set_unused 设置飞机不可用,并将其设置到不合法的位置
  3. calc_position 计算敌机位置、以及敌机发射的子弹位置
  4. draw_element 绘制敌机、以及敌机子弹图像
  5. shoot 敌机发射子弹
class EnemyPlane:

    def __init__(self, scene, speed=10):
        # 游戏主场景
        self.scene = scene
        # 敌机资源
        self.image = pygame.image.load(f'source/plane/enemy-{random.randint(1, 7)}.png')
        # 敌机边框
        self.bbox = self.image.get_rect()
        # 移动速度
        self.speed = speed
        # 是否可见
        self.visible = False
        # 初始化子弹
        self.bullet = Bullet(scene, is_enemy=True)

    def set_used(self, start_x, start_y):
        self.bbox[0] = start_x
        self.bbox[1] = start_y
        self.speed = random.randint(4, 8)
        self.visible = True


    def calc_position(self):

        # 计算飞机位置
        if self.visible:
            self.bbox.move_ip(0, self.speed)
            if self.bbox[1] > SCENE_H:
                self.set_unused()

        # 计算子弹位置
        if self.bullet.visible:
            self.bullet.move(0, self.speed + 5)

    def draw_element(self):
        # 绘制飞机
        if self.visible:
            self.scene.blit(self.image, self.bbox)
        # 绘制子弹
        if self.bullet.visible:
            self.bullet.draw_element()

    def set_unused(self):
        self.visible = False
        self.bbox[0] = -1000
        self.bbox[1] = -1000

    def shoot(self):
        if self.bullet.visible:
            return
        start_x = self.bbox[0] + self.bbox[2]/2 - self.bullet.bbox[2]/2
        start_y = self.bbox[1] + self.bbox[3] - 10
        self.bullet.set_used(start_x, start_y)

测试 EnemyPlane 类:

if __name__ == '__main__':

    pygame.init()
    window = pygame.display.set_mode([512, 768])
    clock = pygame.time.Clock()

    enemy = EnemyPlane(window)
    enemy.set_used(random.randint(0, SCENE_W - enemy.bbox[2]), -enemy.bbox[3])

    index = 0
    while True:
        # 清空窗口
        window.fill((0, 0, 0))
        # 计算位置
        enemy.calc_position()
        # 绘制敌机
        enemy.draw_element()
        # 发射子弹
        index += 1
        if index > 120 and random.randint(1, 100) > 80 and enemy.bbox[1] < 300:
            enemy.shoot()
            index = 0

        # 敌机复活
        if not enemy.visible:
            enemy.set_used(random.randint(0, SCENE_W - enemy.bbox[2]), -enemy.bbox[3])

        pygame.event.get()
        pygame.display.update()
        clock.tick(60)

2. EnemyManager 类

该类负责组织多个敌机的行为,例如:何时、从那个位置进入游戏场景,以及什么时候发射子弹等。

  1. calc_position 计算每一个敌机的位置
  2. draw_element 绘制每一个敌机
  3. set_out 飞机出场的策略,每次随机选择 1-4 个飞机,并从屏幕上方随机位置进场
  4. shoot 控制所有敌机的发射子弹的行为,里面使用了随机数,目的是为了让不同的飞机不要同时发射子弹

上面函数中,set_out 和 shoot 是定时器事件调用,前者每间隔 3 秒调用一次,后者每 1 秒调用一次。

class EnemyManager:

    ENEMY_START_EVENT = pygame.USEREVENT + 1
    ENEMY_SHOOT_EVENT = pygame.USEREVENT + 2

    def __init__(self, scene):
        # 初始化敌机
        self.enemies = [EnemyPlane(scene) for _ in range(8)]
        # 定时器事件
        pygame.time.set_timer(EnemyManager.ENEMY_START_EVENT, 2000)
        pygame.time.set_timer(EnemyManager.ENEMY_SHOOT_EVENT, 1000)

    def calc_position(self):
        for enemy in self.enemies:
            enemy.calc_position()

    def draw_element(self):
        for enemy in self.enemies:
            enemy.draw_element()

    def set_out(self):

        # 随机选择 1- 4 个飞机
        number = random.randint(1, 4)

        # 候选敌机
        wait_for_out = []
        for enemy in self.enemies:
            if not enemy.visible:
                wait_for_out.append(enemy)
            if len(wait_for_out) == number:
                break

        # 敌机出发
        if len(wait_for_out) == number:

            # 敌机位置
            position_xs = []
            range_distance = int((SCENE_W - 100) / number)
            for index in range(number):
                x = random.randint(index * range_distance, index * range_distance + range_distance - 100)
                position_xs.append(x)

            # 敌机出发
            for enemy, x in zip(wait_for_out, position_xs):
                enemy.set_used(x, -enemy.bbox[3])

    def shoot(self):
        for enemy in self.enemies:
            if not enemy.visible:
                continue
            if random.randint(1, 100) > 50 and enemy.bbox[1] < 500:
                enemy.shoot()

测试 EnemyManager 类:

if __name__ == '__main__':

    pygame.init()
    window = pygame.display.set_mode([512, 768])
    clock = pygame.time.Clock()

    # 初始化敌机管理器
    enemy = EnemyManager(window)

    while True:

        # 清空窗口
        window.fill((0, 0, 0))
        # 敌机位置
        enemy.calc_position()
        # 绘制敌机
        enemy.draw_element()
        # 处理定时器事件
        events = pygame.event.get()
        for event in events:
            if event.type == EnemyManager.ENEMY_START_EVENT:
                enemy.set_out()
            if event.type == EnemyManager.ENEMY_SHOOT_EVENT:
                enemy.shoot()

        pygame.display.update()
        clock.tick(60)

3. 加入主场景

敌机创建好之后,接下来,我们将其初始化到主场景中。

import random
import pygame
from Config import *
from GameMap import GameMap
from HeroPlane import HeroPlane
from EnemyManager import EnemyManager
import sys


# 主场景
class MainScene:

    # 初始化主场景
    def __init__(self):

        # 初始化组件
        pygame.init()
        # 初始化时钟
        self.clock = pygame.time.Clock()
        # 初始化游戏窗口
        self.scene = pygame.display.set_mode((SCENE_W, SCENE_H))
        # 设置窗口标题
        pygame.display.set_caption("飞机大战-v1.0 作者: 孟宝亮")
        # 初始化游戏元素
        self.init_elements()


    # 初始化游戏元素
    def init_elements(self):
        # 初始化游戏地图
        self.map = GameMap(self.scene)
        # 初始化英雄飞机
        self.hero = HeroPlane(self.scene)
        # 初始化敌机序列
        self.enemy = EnemyManager(self.scene)

    # 计算坐标
    def calc_position(self):
        # 计算地图坐标
        self.map.calc_position()
        # 计算英雄弹夹坐标
        self.hero.calc_position()
        # 计算敌机位置
        self.enemy.calc_position()


    # 绘制元素
    def draw_elements(self):
        # 绘制滚动地图
        self.map.draw_element()
        # 绘制英雄飞机
        self.hero.draw_element()
        # 绘制敌机
        self.enemy.draw_element()

    # 处理事件
    def handle_events(self):

        # 窗口关闭事件
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            # 敌机出发事件
            if event.type == EnemyManager.ENEMY_START_EVENT:
                self.enemy.set_out()

            # 敌机发射子弹
            if event.type == EnemyManager.ENEMY_SHOOT_EVENT:
                self.enemy.shoot()

        # 获得当前按下的键
        keys = pygame.key.get_pressed()
        # 射击
        if keys[pygame.K_j]:
            self.hero.shoot(1)

        if keys[pygame.K_k]:
            self.hero.shoot(3)

        if keys[pygame.K_l]:
            self.hero.shoot(5)
        # 上
        if keys[pygame.K_w]:
            self.hero.top()
        # 下
        if keys[pygame.K_s]:
            self.hero.bottom()
        # 左
        if keys[pygame.K_a]:
            self.hero.left()
        # 右
        if keys[pygame.K_d]:
            self.hero.right()

    # 碰撞检测
    def detect_conlision(self):
        pass

    # 主循环
    def run(self):

        while True:
            # 碰撞检测
            self.detect_conlision()
            # 计算元素坐标
            self.calc_position()
            # 绘制元素图片
            self.draw_elements()
            # 处理事件
            self.handle_events()
            # 刷新显示
            pygame.display.update()
            # 控制帧率
            self.clock.tick(60)

未经允许不得转载:一亩三分地 » 《Python 飞机大战》(七)敌人飞机
评论 (0)

2 + 2 =