现在我们的主场景中还没有敌机,我们接下来实现多个敌机从上向屏幕下方移动,并能够随机发射子弹。
1. EnemyPlane 类
该类的定义了单个敌机的实现,主要实现方法如下:
- set_used 设置飞机可用,并随机设置其初始位置
- set_unused 设置飞机不可用,并将其设置到不合法的位置
- calc_position 计算敌机位置、以及敌机发射的子弹位置
- draw_element 绘制敌机、以及敌机子弹图像
- 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 类
该类负责组织多个敌机的行为,例如:何时、从那个位置进入游戏场景,以及什么时候发射子弹等。
- calc_position 计算每一个敌机的位置
- draw_element 绘制每一个敌机
- set_out 飞机出场的策略,每次随机选择 1-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)


冀公网安备13050302001966号