第二部分:飞机射击、子弹、和敌方飞机。
第一部分的实现过程点击下方链接
Python实现飞机大战-第一部分(附源码、素材、超详细教程)
实现飞机射击,首先需要有子弹,为了方便后期对子弹的长度、宽度、颜色和速度的改变,需要在settings.py内添加子弹这些信息。紧接着创建一个Bullet类,用来存放子弹的属性和方法。
为了方便设置,在Settings类中添加子弹信息的相关设置,速度、长度、宽度、颜色。
settings.py
class Settings():
'''游戏设置的类'''
def __init__(self):
--跳过其他代码--
'''子弹设置'''
# 子弹速度
self.bullet_speed = 1.5
# 子弹宽度
self.bullet_width = 3
# 子弹高度
self.bullet_height = 10
# 子弹颜色
self.bullet_color = 0, 0, 255
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
'''控制子弹射击的类'''
def __init__(self, player_settings, screen, player_plane):
# super()函数,既可以继承父类的所有属性、方法,又可添加或重写父类的方法、属性。
super().__init__()
self.screen = screen
# 创建矩形
self.rect = pygame.Rect(0, 0, player_settings.bullet_width, player_settings.bullet_height)
# 子弹的centerx = 飞机的centerx 目的是让子弹从飞机的top发出
self.rect.centerx = player_plane.rect.centerx
self.rect.top = player_plane.rect.top
# 子弹图像的矩形方框的位置
self.y = float(self.rect.y)
# 子弹的速度
self.bullet_speed = player_settings.bullet_speed
# 子弹的颜色
self.bullet_color = player_settings.bullet_color
def update(self):
'''子弹自动上移'''
# 原理是让子弹的矩形图像的y左边每次刷新都减去子弹的速度
self.y -= self.bullet_speed
# 更新当前坐标
self.rect.y = self.y
def draw_bullet(self):
'''绘制子弹'''
pygame.draw.rect(self.screen, self.bullet_color, self.rect)
用空格键控制飞机发射子弹,需要修改check_keyboard函数,和update_screen函数
--省略部分代码--
from bullet import Bullet
def check_keyboard(players_plane, screen, player_settings, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# KEYDOWN检测键盘是否按下
elif event.type == pygame.KEYDOWN:
--省略部分代码--
# 空格是否按下
elif event.key == pygame.K_SPACE:
print("空格键按下")
new_bullet = Bullet(player_settings, screen, players_plane)
bullets.add(new_bullet)
--省略部分代码--
def update_screen(player_settings,players_plane, screen, bullets):
--省略部分代码--
for bullet in bullets.sprites():
bullet.draw_bullet()
def start_game():
--省略部分代码--
# 用于存放子弹
bullets = Group()
while True:
'''游戏主循环'''
# 调用文件game_functions内的check_keyboard函数,是一个响应键鼠的函数
game_functions.check_keyboard(players_plane, screen, player_settings, bullets)
players_plane.change_position()
bullets.update()
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings, players_plane, screen, bullets)
子弹连发的原理其实和飞机连续移动的原理相同。不过效果好像不是那么理想,看一下。
简单解释一下,代码一共需要修改五个地方:
class Plane():
'''玩家的飞机的类'''
def __init__(self, screen, player_settings):
--省略部分代码--
self.sign_launch = False
def launch_bullet(players_plane, screen, player_settings, bullets):
if players_plane.sign_launch:
new_bullet = Bullet(player_settings, screen, players_plane)
bullets.add(new_bullet)
elif event.type == pygame.KEYDOWN:
--省略部分代码--
elif event.key == pygame.K_SPACE:
print("空格键按下")
players_plane.sign_launch = True
elif event.type == pygame.KEYUP:
--省略部分代码--
elif event.key == pygame.K_SPACE:
print("空格键松开")
players_plane.sign_launch = False
while True:
'''游戏主循环'''
# 调用文件game_functions内的check_keyboard函数,是一个响应键鼠的函数
game_functions.check_keyboard(players_plane, screen, player_settings, bullets)
game_functions.launch_bullet(players_plane, screen, player_settings, bullets)
players_plane.change_position()
bullets.update()
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings, players_plane, screen, bullets)
效果不是太好,小伙伴们有更好的方法,欢迎不吝赐教。
创建的子弹被存放在Group()当中,当子弹到达屏幕的顶端,实际上子弹依旧存在于Group,只不过是超出屏幕范围无法绘制它们。为了避免计算做这些无用功,就需要把超出屏幕的子弹删除。
在game_functions.py中新建一个删除子弹的函数
def delete_bullet(bullets):
'''删除超出屏幕的子弹'''
bullets.update()
for bullet in bullets.copy:
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
main.py
调用函数delete_bullet()
while True:
'''游戏主循环'''
# 调用文件game_functions内的check_keyboard函数,是一个响应键鼠的函数
game_functions.check_keyboard(players_plane, screen, player_settings, bullets)
#game_functions.launch_bullet(players_plane, screen, player_settings, bullets)
players_plane.change_position()
# 刷新子弹位置,子弹超出屏幕后将其删除。
game_functions.delete_bullet(bullets)
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings, players_plane, screen, bullets)
start_game()
同样新建一个enemy_plane.py的文件,用于存放敌方飞机的类。
import pygame
from pygame.sprite import Sprite
class Enemy(Sprite):
'''敌方飞机的类'''
def __init__(self, screen, player_settings):
super().__init__()
self.screen = screen
self.player_settings = player_settings
# 获取敌方飞机图像、rect属性
self.image = pygame.image.load('image/enemy1.png')
self.rect = self.image.get_rect()
# 敌方飞机的坐标
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# 储存敌方飞机水平方向准确位置
self.x = float(self.rect.x)
def blitme(self):
'''绘制敌方飞机'''
self.screen.blit(self.image, self.rect)
然后实例化这个类,其中需要传递参数enemy_plane给update_screen()函数,后面绘制图像时需要用到。
注意:实例化类的时候一定要按顺序传递参数。
main.py
def start_game():
--省略部分代码--
# 实例化敌方飞机,
enemy_plane = Enemy(screen, player_settings)
while True:
--省略部分代码--
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings, players_plane, screen, bullets, enemy_plane)
因为需要在屏幕上显示敌方飞机的图像,所以需要在game_functions.py中的update_screen()添加代码。
game_functions.py
def update_screen(player_settings,players_plane, screen, bullets, enemy_plane):
'''更新窗口图像'''
--省略部分代码--
# 绘制玩家飞机
players_plane.blitme()
# 绘制敌方飞机
enemy_plane.blitme()
pygame.display.flip()
那么如何生成一行敌机呢
每个敌机都是有宽度、高度的,那就可以计算出一行可以放下几个敌机。
game_functions
首先添加一个生成敌机机群的函数
def create_enemys(player_settinngs, screen, enemys):
'''创建敌方机群'''
# 实例化敌机
enemy = Enemy(screen, player_settinngs)
# 计算一行可以容下几个敌机
enemy_width = enemy.rect.width
available_space_x = player_settinngs.screen_weight - 2 * enemy_width
number_enemys_x = int(available_space_x / (2 * enemy_width))
# 创建一行敌机
for enemy_number in range(number_enemys_x):
enemy = Enemy(screen, player_settinngs)
enemy.x = enemy_width + 2 * enemy_width * enemy_number
enemy.rect.x = enemy.x
enemys.add(enemy)
修改函数update_screen
def update_screen(player_settings,players_plane, screen, bullets, enemys):
--省略部分代码--
# 绘制玩家飞机
players_plane.blitme()
# 绘制敌方飞机
#enemy_plane.blitme()
enemys.draw(screen)
pygame.display.flip()
main.py
def start_game():
--省略部分代码--
# 实例化飞机类,对象为players_plane
players_plane = Plane(screen, player_settings)
# 存放子弹
bullets = Group()
# 实例化敌方飞机,
enemy_plane = Enemy(screen, player_settings)
# 存放敌机
enemys = Group()
# 创建敌机群
game_functions.create_enemys(player_settings, screen, enemys)
while True:
--省略部分代码--
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings, players_plane, screen, bullets, enemys)
为了是函数结构更清晰,这里对create_enemys()函数重构,分为三个函数,
create_enemys:重复调用create_enemy,创建一行飞机。
def create_enemys(player_settings, screen, enemys):
'''创建敌方机群'''
# 实例化敌机
enemy = Enemy(screen, player_settings)
number_enemys_x = get_number_enemys_x(player_settings, enemy.rect.width)
# 创建一行敌机
for enemy_number in range(number_enemys_x):
create_enemy(player_settings, screen, enemys, enemy_number)
create_enemy:创建一个飞机
def create_enemy(player_settings, screen, enemys, enemy_number):
'''创建一个敌机'''
enemy = Enemy(screen, player_settings)
enemy_width = enemy.rect.width
enemy.x = enemy_width + 2 * enemy_width * enemy_number
enemy.rect.x = enemy.x
enemys.add(enemy)
get_number_enemys_x:计算出一行容纳飞机个数
def get_number_enemys_x(player_settings, enemy_width):
'''计算出每行可生成几个敌机'''
available_space_x = player_settings.screen_weight - 2 * enemy_width
number_enemys_x = int(available_space_x / (2 * enemy_width))
return number_enemys_x
前面生成了一行飞机,原理相同,需要计算出屏幕垂直方向上可以放几行飞机。先创建一个计算垂直方向容纳飞机数的函数。
game_functions
def get_number_enemys_y(player_settings, players_plane_height, enemy_height):
'''获取屏幕可生成多少行'''
available_space_y = (player_settings.screen_height - (3 * enemy_height) - players_plane_height)
number_y = int(available_space_y / (2 * enemy_height))
return number_y
def create_enemy(player_settings, screen, enemys, enemy_number, row_number):
'''创建一个敌机'''
enemy = Enemy(screen, player_settings)
enemy_width = enemy.rect.width
enemy.x = enemy_width + 2 * enemy_width * enemy_number
enemy.rect.x = enemy.x
enemy.rect.y = enemy.rect.height + 2* enemy.rect.height * row_number
enemys.add(enemy)
def create_enemys(player_settings, screen, players_plane, enemys):
'''创建敌方机群'''
# 实例化敌机
enemy = Enemy(screen, player_settings)
number_enemys_x = get_number_enemys_x(player_settings, enemy.rect.width)
number_rows = get_number_enemys_y(player_settings, players_plane.rect.height, enemy.rect.height)
# 创建一pingmu敌机
for row_number in range(number_rows):
for enemy_number in range(number_enemys_x):
create_enemy(player_settings, screen, enemys, enemy_number, row_number)
main.py
def start_game():
--省略部分代码--
game_functions.create_enemys(player_settings, screen, players_plane,enemys)
main.py
--省略部分代码--
while True:
'''游戏主循环'''
# 调用文件game_functions内的check_keyboard函数,是一个响应键鼠的函数
game_functions.check_keyboard(players_plane, screen, player_settings, bullets)
#game_functions.launch_bullet(players_plane, screen, player_settings, bullets)
players_plane.change_position()
# 刷新子弹位置,子弹超出屏幕后将其删除。
game_functions.delete_bullet(bullets)
# 敌机移动
game_functions.update_enemys(player_settings, enemys)
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings,players_plane, screen, bullets, enemys)
enemy_plane.py
--省略部分代码--
def blitme(self):
'''绘制敌方飞机'''
self.screen.blit(self.image, self.rect)
def change_position(self):
'''敌机位置变化'''
self.x += (self.player_settings.enemy_change_speed * self.player_settings.fleet_direction)
self.rect.x = self.x
def check_edges(self):
'''触碰屏幕边缘'''
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
game_functions
--省略部分代码--
def check_fleet_edges(player_settings, enemys):
'''敌机触碰边缘响应'''
for enemy in enemys.sprites():
if enemy.check_edges():
change_fleet_direction(player_settings, enemys)
break
def change_fleet_direction(player_settings, enemys):
for enemy in enemys.sprites():
enemy.rect.y += player_settings.fleet_drop_speed
player_settings.fleet_direction *= -1
def update_enemys(player_settings, enemys):
'''更新敌机位置'''
check_fleet_edges(player_settings, enemys)
enemys.update()
settings.py
'''敌机设置'''
# 敌机速度
self.enemy_change_speed = 1
self.fleet_drop_speed = 10
# fleet_direction为1表示向右,为-1表示向左
self.fleet_direction = 1
main.py
while True:
'''游戏主循环'''
# 调用文件game_functions内的check_keyboard函数,是一个响应键鼠的函数
game_functions.check_keyboard(players_plane, screen, player_settings, bullets)
#game_functions.launch_bullet(players_plane, screen, player_settings, bullets)
players_plane.change_position()
# 刷新子弹位置,子弹超出屏幕后将其删除。
game_functions.delete_bullet(player_settings, screen, players_plane, enemys,bullets)
# 敌机移动
game_functions.update_enemys(player_settings, enemys)
# 调用update_screen函数,更新窗口
game_functions.update_screen(player_settings,players_plane, screen, bullets, enemys)
game_functions
def delete_bullet(player_settings, screen, players_plane, enemys, bullets):
'''删除超出屏幕的子弹'''
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
# 子弹碰到敌人就消失
collisions = pygame.sprite.groupcollide(bullets, enemys, True, True)
if len(enemys) == 0:
bullets.empty()
create_enemys(player_settings, screen, players_plane, enemys)