@Python编程从入门到实践 Python项目练习
四、创建Ship类
建立ship.py,创建Ship类,管理飞船行为。
# ship.py import pygame class Ship(): def __init__(self, ai_settings, screen): """初始化飞船并设置其初始位置""" self.screen = screen self.ai_settings = ai_settings # 加载飞船图像 self.image = pygame.image.load('images/ship.bmp') self.image = pygame.transform.scale(self.image, (37*2, 34*2)) # 获取其外接矩形(rect为surface矩形属性) self.rect = self.image.get_rect() self.screen_rect = screen.get_rect() # 将每艘新飞船放在屏幕底部中央 self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom # 在飞船的属性center中存储小数值 self.center_x = float(self.rect.centerx) self.center_y = float(self.rect.centery) # 移动标志 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.center_x += self.ai_settings.ship_speed_factor if self.moving_left and self.rect.left > 0: self.center_x -= self.ai_settings.ship_speed_factor if self.moving_down and self.rect.bottom < self.screen_rect.bottom: self.center_y += self.ai_settings.ship_speed_factor if self.moving_up and self.rect.top > self.screen_rect.top: self.center_y -= self.ai_settings.ship_speed_factor # 根据self.center更新rect对象 self.rect.centerx = self.center_x self.rect.centery = self.center_y def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image, self.rect) def center_ship(self): """让飞船在屏幕上居中""" self.center_x = self.screen_rect.centerx ship_half_width = self.rect.bottom - self.center_y self.center_y = self.screen_rect.bottom - ship_half_width
五、创建Bullet类
建立bullet.py,创建Bullet类,继承pygame.sprite中的Sprite类,作为游戏中的子弹元素。
# settings.py import pygame from pygame.sprite import Sprite class Bullet(Sprite): """一个对子弹发射进行管理的类""" def __init__(self, ai_settings, screen, ship): super(Bullet, self).__init__() self.screen = screen # 在(0, 0)处创建一个表示子弹的矩形,将初始位置放在(0,0) self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height) # 重设子弹位置 self.rect.centerx = ship.rect.centerx # self.rect.centery = ship.rect.centery self.rect.top = ship.rect.top # 存储用小数表示的子弹位置 self.y = float(self.rect.y) self.color = ai_settings.bullet_color self.speed_factor = ai_settings.bullet_speed_factor def update(self): """向上移动子弹""" # 更新表示子弹的小数值 self.y -= self.speed_factor # 更新表示子弹的rect的位置 self.rect.y = self.y def draw_bullet(self): """在屏幕上绘制子弹""" pygame.draw.rect(self.screen, self.color, self.rect)
六、创建game_functions模块
创建game_functions.py 模块,存储函数,让alien_invasion中主代码更易懂。
#game_functions.py import sys import pygame from bullet import Bullet from passenger import Passenger from time import sleep def check_keydown_events(event, ai_settings, screen, ship, bullet_group): """响应按键""" if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = True elif event.key == pygame.K_UP: ship.moving_up = True elif event.key == pygame.K_DOWN: ship.moving_down = True elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, screen, ship, bullet_group) elif event.key == pygame.K_ESCAPE: sys.exit() def fire_bullet(ai_settings, screen, ship, bullet_group): """如果没有到达限制,就发射一颗子弹""" # 创建一颗子弹,并将其加入到编组bullets中 if len(bullet_group) < ai_settings.bullet_group_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullet_group.add(new_bullet) def check_keyup_events(event, ship): """松开按键""" if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT: 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, ship, bullet_group): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 按下 elif event.type == pygame.KEYDOWN: check_keydown_events(event, ai_settings, screen, ship, bullet_group) # 松开 elif event.type == pygame.KEYUP: check_keyup_events(event, ship) def update_screen(ai_settings, screen, ship, passenger_group, bullet_group): """更新屏幕上的图像,并切换到新屏幕""" # 每次循环时(即打开游戏运行)重绘屏幕 screen.fill(ai_settings.bg_color) # 绘制子弹 for bullet in bullet_group: bullet.draw_bullet() ship.blitme() for i in passenger_group: i.blitme() #passenger_group.draw(screen) # 更新屏幕(隐藏旧屏,更新新屏) pygame.display.flip() def update_bullet_group(ai_settings, screen, ship, passenger_group, bullet_group ): """更新子弹的位置,并删除已消失的子弹""" # 更新子弹的位置 bullet_group.update() # 删除已消失的子弹 for bullet in bullet_group.copy(): if bullet.rect.top <= 0: bullet_group.remove(bullet) check_bullet_passenger_collisions(ai_settings, screen, ship, passenger_group, bullet_group) def check_bullet_passenger_collisions(ai_settings,screen, ship, passenger_group, bullet_group): # 检查是否有子弹击中了乘客 # 如果是这样,就删除相应的子弹和乘客 # groupcollide()返回字典,实参为True即删除发生碰撞的子弹或外星人 collisions = pygame.sprite.groupcollide(bullet_group, passenger_group, False, True) if len(passenger_group) == 0: # 删除现有子弹并新建一波新的乘客 bullet_group.empty() create_fleet(ai_settings, screen, ship, passenger_group) def get_number_passenger_x(ai_settings, passenger_width): """计算每行可以容纳多少个乘客""" available_space_x = ai_settings.screen_width - 2 * passenger_width number_passenger_x = int(available_space_x / (2 * passenger_width)) return number_passenger_x def get_number_rows(ai_settings, ship_height, passenger_height): """计算屏幕可以容纳多少行的乘客""" available_space_y = (ai_settings.screen_height - passenger_height - ship_height) number_rows = int(available_space_y / (1.5 * passenger_height)) return number_rows def create_passenger(ai_settings, screen, passenger_group, passenger_number, row_number): """创建一个乘客并将其放在当前行""" passenger = Passenger(ai_settings, screen) passenger_width = passenger.rect.width passenger.x = passenger_width + 2 * passenger_width * passenger_number passenger.rect.x = passenger.x passenger.rect.y = 0.5 * passenger.rect.height + 1.5 * passenger.rect.height * row_number passenger_group.add(passenger) def create_fleet(ai_settings, screen, ship, passgener_group): """创建乘客群""" # 创建一个乘客,并计算一行可以容纳多少人 passenger = Passenger(ai_settings, screen) number_passenger_x = get_number_passenger_x(ai_settings, passenger.rect.width) number_rows = get_number_rows(ai_settings, ship.rect.height, passenger.rect.height) # 创建乘客群 for row_number in range(number_rows): for passenger_number in range(number_passenger_x): create_passenger(ai_settings, screen, passgener_group, passenger_number, row_number) def change_fleet_direction(ai_settings, passenger_group): """将整群乘客下移,并改变它们的方向""" for passenger in passenger_group.sprites(): passenger.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1 def check_fleet_edges(ai_settings, passenger_group): """有外星人到达边缘时采取相应的措施""" for passenger in passenger_group.sprites(): if passenger.check_edges(): change_fleet_direction(ai_settings, passenger_group) break def ship_hit(ai_settings, stats, screen, ship, passenger_group, bullet_group): """响应被乘客撞到的飞船""" if stats.ships_left > 0: # 讲ship_left减一 stats.ships_left -= 1 # 清空乘客列表和子弹列表 passenger_group.empty() bullet_group.empty() # 创建一群新的外星人,并讲飞船放在屏幕底部中央 create_fleet(ai_settings, screen, ship, passenger_group) ship.center_ship() # 暂停 sleep(0.5) else: stats.game_active = False def check_passenger_group_bottom(ai_settings, stats, screen, ship, passenger_group, bullet_group): """检查是否有外星人到达屏幕底端""" screen_rect = screen.get_rect() for passenger in passenger_group.sprites(): if passenger.rect.bottom >= screen_rect.bottom: # 像飞船被撞到一样进行处理 ship_hit(ai_settings, stats, screen, ship, passenger_group, bullet_group) break def update_passenger_group(ai_settings, stats, screen, ship, passenger_group, bullet_group): """检查是否有乘客在边缘,并更新乘客群中所有乘客的位置""" check_fleet_edges(ai_settings, passenger_group) passenger_group.update() # 检测外星人和飞船之间的碰撞 if pygame.sprite.spritecollideany(ship, passenger_group): ship_hit(ai_settings, stats, screen, ship, passenger_group, bullet_group) # 检查是否有外星让到达屏幕底端 check_passenger_group_bottom(ai_settings, stats, screen, ship, passenger_group, bullet_group)