我按照书上写的代码,运行游戏的时候发现飞船碰撞到外星人以后,是在原位置刷新,不会在屏幕底部刷新,不清楚为什么。
主程序:
import pygame
from settings import Settings
from game_stats import GameStats
from button import Button
from ship import Ship
from pygame.sprite import Group
import game_functions as gf
from alien import Alien
def run_game():
"""初始化游戏,设置屏幕对象"""
pygame.init() # 初始化所有导入的pygame模块
ai_settings = Settings() # 用ai_settings代替Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height)) # 设置屏幕的长宽, pygame.display.set_mode((width, height))
pygame.display.set_caption("Alien Invasion") # 设置当前窗口的标题
# 创建Play按钮
play_button = Button(ai_settings, screen, "Play")
# 创建一个用于存储游戏统计信息的实例
stats = GameStats(ai_settings)
ship = Ship(ai_settings, screen) # 将设置好的屏幕参数传给Ship里面,然后用ship代替Ship类
bullets = Group() # 创建一个用于储存子弹的编组,回头会存储bullet模块新建的子弹,Group类用于保存和管理多个Sprite对象的容器类
# 创建一个用于储存外星人的编组
aliens = Group()
# 创建一个外星人群
gf.create_fleet(ai_settings, screen, ship, aliens)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets) # 返回ship是由于gf需要ship的返回值,用于调用ship中update函数
if stats.game_active:
ship.update() # 移动飞船, 更新飞船的位置
gf.update_bullets(ai_settings, screen, ship, aliens, bullets) # 更新子弹位置
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets) # 更新外星人位置
# 每次循环时都会重新绘制屏幕,让最近绘制的屏幕可见
gf.update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button)
run_game()
游戏功能程序:
import sys
from time import sleep
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""响应按键"""
if event.key == pygame.K_RIGHT: # 我们读取属性event.key,查看它是否是键盘右键
ship.moving_right = True # 是的话就向右移动飞船
elif event.key == pygame.K_LEFT: # 我们读取属性event.key,查看它是否是键盘左键
ship.moving_left = True # 是的话就向左移动飞船
elif event.key == pygame.K_UP: # 我们读取属性event.key,查看它是否是键盘上键
ship.moving_up = True # 是的话就向上移动飞船
elif event.key == pygame.K_DOWN: # 我们读取属性event.key,查看它是否是键盘下键
ship.moving_down = True # 是的话就向下移动飞船
elif event.key == pygame.K_SPACE: # 我们读取属性event.key,查看它是否是键盘空格键
fire_bullet(ai_settings, screen, ship, bullets) # 是的话我们将运行函数fire_bullet
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, ship):
"""响应松开"""
if event.key == pygame.K_RIGHT: # 当event按键属性key遇到右键的KEYUP事件时就会让移动标志变成False
ship.moving_right = False
elif event.key == pygame.K_LEFT: # 当event按键属性key遇到左键的KEYUP事件时就会让移动标志变成False
ship.moving_left = False
elif event.key == pygame.K_UP:
ship.moving_up = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
def check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 获得事件储存到event里面
if event.type == pygame.QUIT: # 检查event事件的类型是不是鼠标点×
sys.exit() # 是的话就退出游戏
elif event.type == pygame.KEYDOWN: # 检测event事件是不是KEYDOWN事件
check_keydown_events(event, ai_settings, screen, ship, bullets) # 如果发生按键事件,将各种参数带入到check_keydown_events()
elif event.type == pygame.KEYUP: # 我们添加了一个新的elif模块,用于响应KEYUP事件,让事件type类型知道是按键key
check_keyup_events(event, ship)
elif event.type == pygame.MOUSEBUTTONDOWN: # 检测鼠标按下的事件
mouse_x, mouse_y, = pygame.mouse.get_pos() # 它返回一个元组,其中包含玩家单击时鼠标的x和y的坐标
check_play_button(ai_settings, screen, stats, play_button, ship,
aliens, bullets, mouse_x, mouse_y)
def check_play_button(ai_settings, screen, stats, play_button, ship, aliens,
bullets, mouse_x, mouse_y):
"""在玩家单机Play按钮时开始新的游戏"""
if play_button.rect.collidepoint(mouse_x, mouse_y): # 使用collidepoint()检查鼠标单击的位置是否在Play按钮的rect内
# 重置游戏统计信息
stats.reset_stats()
stats.game_active = True
# 清空外星人列表和子弹列表
aliens.empty()
bullets.empty()
# 创建一群新的外星人,并让飞船居中
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
def fire_bullet(ai_settings, screen, ship, bullets):
"""创建一颗子弹,并将其加入到编组bullets中"""
if len(bullets) < ai_settings.bullets_allowed: # 限制子弹数量在10以内
new_bullet = Bullet(ai_settings, screen, ship) # 新建一个子弹
bullets.add(new_bullet) # 在bullets中再添加一个new_bullet
def update_bullets(ai_settings, screen, ship, aliens, bullets):
"""更新子弹的位置,并删除已经消失的子弹"""
# 更新子弹的位置
bullets.update() # 通过执行这个函数来让我们绘制的东西显示在屏幕上
# 删除已消失的子弹
for bullet in bullets.copy(): # 为了不从列表和编组中删除条目,我们遍历编组的副本
if bullet.rect.bottom <= 0: # 如果子弹飞出屏幕
bullets.remove(bullet) # 将子弹从bullets中删除
check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
# 检查是否有子弹击中了外星人
# 如果是这样就删除外星人和子弹
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
# 删除现有的子弹并且新建一群外星人
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
def create_fleet(ai_settings, screen, ship, aliens):
"""创建外星人群"""
# 创建一个外星人,并计算一行可容纳多少个外星人
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)
# 设置一个for循环,创建相应行数的外星人
for row_number in range(number_rows):
# 创建第一行外星人
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, aliens, alien_number,
row_number)
def get_number_aliens_x(ai_settings, alien_width):
"""计算每行可容纳多少个外星人"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""计算屏幕可以容纳多少行外星人"""
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height) # 计算屏幕的空白高度
number_rows = int(available_space_y / (2 * alien_height)) # 得到空白部分能够放多少行外星人
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""创建一个外星人并将其放在当前行"""
alien = Alien(ai_settings, screen) # 创建一个外星人
alien_width = alien.rect.width # 获得外星人宽度
alien.x = alien_width + 2 * alien_width * alien_number # 设置每个外星人在当前行的位置
alien.rect.x = alien.x # rect.x和rect.y是分别自动等于rect.left和rect.top的
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number # 这相当于将外星人依次下放到各行
aliens.add(alien)
def check_fleet_edges(ai_settings, aliens):
"""有外星人到达边缘时采取相应的措施"""
for alien in aliens.sprites(): # 这里的aliens.sprites()返回一个列表包含所有精灵
if alien.check_edges(): # 如果alien里面的check_edges函数返回是Ture,就执行下面的
change_fleet_direction(ai_settings, aliens) # 下移外星人并改变方向
break
def change_fleet_direction(ai_settings, aliens):
"""将整群外星人下移,并改变它们的方向"""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""响应被外星人撞到的飞船"""
if stats.ships_left > 0:
# 将ships_left减1
stats.ships_left -= 1
# 清空外星人列表和子弹列表
aliens.empty()
bullets.empty()
# 创建一群新的外星人,并将飞船放到屏幕底端中央
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
else:
stats.game_active = False
# 暂停
sleep(0.5)
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""检查是否有外星人到达了屏幕底端"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# 像飞船被撞到一样进行处理
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break
def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
"""检查是否有外星人位于屏幕边缘,更新外星人群中所有外星人的位置"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# 检测外星人和飞船之间的碰撞
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
#检查是否有外星人到达屏幕底端
check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
def update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button):
"""更新屏幕上的图像,并切换到新屏幕上"""
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
# 在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites(): # 这里的bullets.sprites()返回一个列表包含所有精灵
bullet.draw_bullet() # 将遍历的精灵都调用draw_bullet函数
ship.blitme() # 绘制飞船
aliens.draw(screen) # 绘制外星人
# 如果游戏处于非活动状态,就绘制Play按钮
if not stats.game_active:
play_button.draw_button()
# 让最近绘制的屏幕可见
pygame.display.flip()
ship设置程序:
# Ship主要是获取飞船外形,然后
import pygame
class Ship():
def __init__(self, ai_settings, screen): # 形参列表中添加screen让飞船能够获取屏幕设置,添加ai_settings获取速度设置
"""初始化飞船并设置其初始位置"""
self.screen = screen # 我们将形参screen存储在一个属性中,以便能够在update()中使用它
self.ai_settings = ai_settings # 我们将形参ai_settings存储在一个属性中,以便能够在update()中使用它
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp') # 从文件中加载图片,并存储在self.image里面
self.rect = self.image.get_rect() # get_rect()是处理矩形图形的方法,返回值包含矩形的居中属性center centerx centery
# 给这个属性赋值到self.rect中去
self.screen_rect = screen.get_rect() # self.screen也是获得了rect属性,返回值也是矩形的居中属性
# 将每艘船的新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx # self.rect代表的是加载图形的rect属性,将它的x坐标
self.rect.centery = self.screen_rect.centery # 和背景图的x坐标居中属性相等, 意味着加载图片在背景图的中心位置
self.rect.bottom = self.screen_rect.bottom # 加载图片底部和背景图底部相等
# 在飞船的属性center中存储小数值
self.center1 = float(self.rect.centerx)
self.center2 = float(self.rect.centery)
"""其他关于矩形操作的位置关键词有:top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
size, width, height"""
# 移动标志
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值,而不是rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center1 += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0: # 0代表背景的左侧
self.center1 -= self.ai_settings.ship_speed_factor
if self.moving_up and self.rect.top > 0:
self.center2 -= self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.center2 += self.ai_settings.ship_speed_factor
# 根据self.center更新rect对象
self.rect.centerx = self.center1
self.rect.centery = self.center2
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
def center_ship(self):
"""让飞船在屏幕上居中"""
self.center = self.screen_rect.centerx