跟着《Python从入门到实践》这本书学习的Python,这本书最后的实践部分第一个就是实现一个飞机打外星人的小游戏,跟着书的步骤一步步做最终实现了,并扩展了飞机可以上下移动的功能。
游戏有9个py文件,分别是Alien_invasion.py游戏主程序,settings.py游戏设定,alien.py外星人,ship.py飞船,bullet.py子弹,game_status.py游戏启动与否状态,game_function游戏的函数,scoreborad游戏计数板,button.py游戏窗口的按钮。
下面简单说一下各个文件的作用。
首先是游戏的主程序Alien_invasion.py,主要是new一个游戏设定setting,new一个飞船,new存储子弹list,new存储外星人list,new计数板,设定初始游戏状态等游戏的初始化工作。
import sys import pygame from pygame.sprite import Group from settings import Settings from game_stats import GameStats from scoreboard import Scoreboard from button import Button from ship import Ship from alien import Alien import game_function as gf def run_game(): #初始化游戏+创建一个屏幕 pygame.init() ai_settings=Settings() screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") stats=GameStats(ai_settings) sb=Scoreboard(ai_settings,screen,stats) #Play按钮 play_button=Button(ai_settings,screen,"Play") #创建一艘飞船 ship=Ship(ai_settings,screen) #创建用于存储子弹的编组 bullets=Group() #创建一个外星人编组 aliens=Group() gf.creat_fleet(ai_settings,screen,ship,aliens) #游戏主循环 while True: gf.check_events(ai_settings, screen, stats,sb, play_button, ship,aliens,bullets) #监视键盘和鼠标事件 if stats.game_active: ship.update() gf.update_bullets(ai_settings,stats,sb,screen,ship,aliens,bullets) gf.update_aliens(ai_settings,stats,screen,ship,aliens,bullets) gf.update_screen(ai_settings, screen,sb,stats,ship,aliens, bullets,play_button) #更新屏幕 run_game()
settings.py,游戏的设定程序,包括:游戏窗口大小,生命个数,飞船飞行速度,同屏子弹个数,子弹飞速等等。
设定类在游戏主程序的时候调用有参构造函数new一个设定类。
class Settings(): #存储《外星人入侵》游戏的设置 def __init__(self): #屏幕及飞船 self.screen_width=1200 self.screen_height=700 self.bg_color=(230,230,230) self.ship_speed=3 self.ship_lives=2 #子弹 self.bullet_width=5 self.bullet_height=10 self.bullet_speed=3 self.bullet_color=60,60,60 self.bullets_total=5 #敌人 self.alien_speed=0.5 self.fleet_drop_speed=50 #敌人撞壁之后向下移动速度 self.fleet_direction=1 #取1/-1为向右/向左 self.alien_points=10
alien.py,外星人类,存储外星人的一些函数,外星人移动等。
import pygame from pygame.sprite import Sprite class Alien(Sprite): def __init__(self,ai_settings,screen): super(Alien,self).__init__() self.screen=screen self.ai_settings=ai_settings self.image=pygame.image.load('images/alien.bmp') self.image=pygame.transform.scale(self.image,(50,50)) #缩放图像 self.rect=self.image.get_rect() #把飞船初始位置设为左上角 self.rect.x=self.rect.width self.rect.y=self.rect.height self.x,self.y=0.0,0.0 def update(self): self.x+=self.ai_settings.alien_speed * self.ai_settings.fleet_direction self.rect.x=int(self.x) def check_edges(self): screen_rect=self.screen.get_rect() if self.rect.right>=screen_rect.right: return True elif self.rect.right<=0: return True else: return False def blitme(self): self.screen.blit(self.image,self.rect)
ship.py,飞船类,存储有关飞船的一些函数:把飞船绘制到窗口中,飞船移动,飞船发射子弹等。
#飞船类:管理飞船的行为 import pygame class Ship(): def __init__(self,ai_settings,screen): #init初始化飞船并设置其初始位置 self.screen=screen self.ai_seettings=ai_settings #self.rect取得飞船矩形 self.screen_rect取得屏幕矩形 self.image=pygame.image.load("images/ship.bmp") self.image=pygame.transform.scale(self.image,(50,50)) #缩放图像 self.rect=self.image.get_rect() self.screen_rect=screen.get_rect() ''' 可设置相应 rect 对象的属性center,centerx,centery 要让游戏元素与屏幕边缘对齐,可使用属性 top,bottom,left,right ; 要调整游戏元素的水平或垂直位置,可使用属性 x,y ,它们分别是相应矩形左上角的 x 和 y 坐标。 ''' #将飞船放在屏幕最下方中央 self.rect.centerx=self.screen_rect.centerx self.rect.bottom=self.screen_rect.bottom #移动标记 self.moving_right=False self.moving_left=False self.moving_up=False self.moving_down=False #移动速度x/y self.center=float(self.rect.centerx) self.recty=float(self.rect.bottom) def update(self): #根据移动标记调整飞船位置(横轴) if self.moving_right and self.center<self.ai_seettings.screen_ self.center+=self.ai_seettings.ship_speed if self.moving_left and self.center>0: self.center-=self.ai_seettings.ship_speed self.rect.centerx=int(self.center) #根据移动标记调整飞船位置(纵轴) if self.moving_up and self.recty>0: self.recty-=self.ai_seettings.ship_speed if self.moving_down and self.recty<self.ai_seettings.screen_height: self.recty+=self.ai_seettings.ship_speed self.rect.centery=int(self.recty) def blitme(self): #根据self.rect的指定位置将飞船绘制到屏幕上 self.screen.blit(self.image,self.rect) def center_ship(self): self.center=float(self.screen_rect.centerx) self.recty=float(self.screen_rect.bottom)
bullet.py,子弹类,存储有关子弹的一些函数:子弹飞行,子弹与外星人碰撞等。
import pygame from pygame.sprite import Sprite #子弹类:对飞船子弹进行管理,继承自Sprite class Bullet(Sprite): #__init__在飞船位置创建一颗子弹 def __init__(self,ai_settings,screen,ship): super(Bullet,self).__init__() self.screen=screen #在 (0,0) 处创建一个表示子弹的矩形,再设置正确的位置 self.rect=pygame.Rect(0, 0, ai_settings.bullet_width,ai_settings.bullet_height) #x轴设为飞船x轴 self.rect.centerx = ship.rect.centerx self.rect.top = ship.rect.top # #记录self.y值用于以后改变,self.x不会变不用记录 self.y=float(self.rect.y) #子弹颜色和速度 self.color=ai_settings.bullet_color self.bullet_speed=ai_settings.bullet_speed #update将子弹向上移动 def update(self): self.y-=self.bullet_speed self.rect.y=int(self.y) def draw_bullet(self): #用self.color的颜色填充self.screen的self.rect的矩形部分 pygame.draw.rect(self.screen,self.color,self.rect)
game_status.py,游戏状态类,主要是为了游戏重新开始
class GameStats(): def __init__(self,ai_settings): self.ai_settings=ai_settings self.reset_stats() self.game_active=False def reset_stats(self): self.ship_lives=self.ai_settings.ship_lives self.score=0
game_function,调控各个函数作用的主函数。
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: ship.moving_right=True if event.key==pygame.K_LEFT: ship.moving_left=True if event.key==pygame.K_UP: ship.moving_up=True if event.key==pygame.K_DOWN: ship.moving_down=True if event.key==pygame.K_SPACE: fire_bullet(ai_settings,screen,ship,bullets) if event.key==pygame.K_q: sys.exit() def fire_bullet(ai_settings,screen,ship,bullets): #监测到按下空格键:创建一颗子弹 if len(bullets)<ai_settings.bullets_total: new_bullet=Bullet(ai_settings,screen,ship) bullets.add(new_bullet) #响应键盘回弹事件 def check_keyup_events(event,ship): if event.key==pygame.K_RIGHT: ship.moving_right=False if event.key==pygame.K_LEFT: ship.moving_left=False if event.key==pygame.K_UP: ship.moving_up=False if event.key==pygame.K_LEFT: ship.moving_down=False #监测并响应键盘鼠标事件 def check_events(ai_settings, screen, stats,sb, play_button, ship,aliens,bullets): for event in pygame.event.get(): if event.type==pygame.QUIT: sys.exit() elif event.type==pygame.MOUSEBUTTONDOWN: mouse_x,mouse_y=pygame.mouse.get_pos() check_play_buttons(ai_settings,screen,stats,sb,play_button,mouse_x,mouse_y,ship,aliens,bullets) elif event.type==pygame.KEYDOWN: check_keydown_events(event,ai_settings,screen,ship,bullets) elif event.type==pygame.KEYUP: check_keyup_events(event,ship) def check_play_buttons(ai_settings,screen,stats,sb,play_button,mouse_x,mouse_y,ship,aliens,bullets): if not stats.game_active and play_button.rect.collidepoint(mouse_x, mouse_y): pygame.mouse.set_visible(False) stats.reset_stats() stats.game_active = True aliens.empty() bullets.empty() creat_fleet(ai_settings,screen,ship,aliens) ship.center_ship() sb.prep_score() #功能:更新屏幕图像,绘制新的图像 def update_screen(ai_settings, screen,stats,ship,aliens, bullets,play_button): #每次循环都重绘屏幕 screen.fill(ai_setting.bg_color) #更新子弹group内子弹位置 for bullet in bullets.sprites(): bullet.draw_bullet() ship.blitme() aliens.draw(screen) # 如果游戏处于非活动状态,就绘制 Play 按钮 if not stats.game_active: play_button.draw_button() #让最近绘制的屏幕可见 pygame.display.flip() #更新子弹 def update_bullets(ai_settings,stats,sb,screen,ship,aliens,bullets): bullets.update() for bullet in bullets.copy(): if bullet.rect.bottom<=0: bullets.remove(bullet) #print(len(bullets)) #内置函数:一行就双重遍历子弹和敌人,矩形重叠就消失 collisions=pygame.sprite.groupcollide(bullets,aliens,True,True) #更新分数 if collisions: for aliens in collisions.values(): stats.score+=ai_settings.alien_points*len(aliens) sb.prep_score() #检测现有敌人是否为0了 if len(aliens)==0: bullets.empty() creat_fleet(ai_settings,screen,ship,aliens) #创建外星人舰队: def creat_fleet(ai_settings,screen,ship,aliens): #创建一个外星人,并计算每行可容纳多少个外星人 alien=Alien(ai_settings,screen) number_aliens_x=calc_aliens_number(ai_settings,alien.rect.width) row_numbers=calc_row_number(ai_settings, ship.rect.height, alien.rect.height) for row_number in range(row_numbers-1): for alien_number in range(number_aliens_x-1): creat_an_alien(ai_settings,screen,aliens,alien_number,row_number) def creat_an_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 alien.rect.y=alien.rect.height + 2 * alien.rect.height * row_number alien.x=float(alien.rect.x) alien.y=float(alien.rect.y) aliens.add(alien) def calc_aliens_number(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 calc_row_number(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 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) #print("Xiba!!!") def check_fleet_edges(ai_settings,aliens): for alien in aliens.sprites(): if alien.check_edges(): change_direction(ai_settings,aliens) break def change_direction(ai_settings,aliens): for alien in aliens.sprites(): alien.y+=ai_settings.fleet_drop_speed alien.rect.y=int(alien.y) ai_settings.fleet_direction*=-1 def ship_hit(ai_settings,stats,screen,ship,aliens,bullets): if stats.ship_lives>0: stats.ship_lives-=1 aliens.empty() bullets.empty() creat_fleet(ai_settings,screen,ship,aliens) ship.center_ship() sleep(0.5) else: stats.game_active=False pygame.mouse.set_visible(True) """ 更新屏幕上的图像,并切换到新屏幕 """ def update_screen(ai_settings, screen, sb,stats, ship, aliens, bullets,play_button): #每次循环都重绘屏幕 screen.fill(ai_settings.bg_color) #更新子弹group内子弹位置 for bullet in bullets.sprites(): bullet.draw_bullet() ship.blitme() aliens.draw(screen) sb.show_score() # 如果游戏处于非活动状态,就绘制 Play 按钮 if not stats.game_active: sb.prep_score() play_button.draw_button() # 让最近绘制的屏幕可见 pygame.display.flip()
scoreboard.py,计分板。
import pygame.font class Scoreboard(): def __init__(self,ai_settings,screen,stats): self.screen=screen self.screen_rect=screen.get_rect() self.ai_settings=ai_settings self.stats=stats # self.text_color=(30,30,30) self.font=pygame.font.SysFont(None,48) # self.prep_score() def prep_score(self): score_str=str(self.stats.score) self.score_image=self.font.render(score_str, True, self.text_color,self.ai_settings.bg_color) self.score_rect = self.score_image.get_rect() self.score_rect.right = self.screen_rect.right - 20 self.score_rect.top = 20 def show_score(self): self.screen.blit(self.score_image,self.score_rect)
button.py,按钮,只有游戏结束重新开始的按钮。
import pygame.font class Button: def __init__(self,ai_settings,screen,msg): self.screen=screen self.screen_rect=screen.get_rect() #设置按钮的大小/颜色/字体 self.width,self.height=200,50 self.button_color=(100,100,100) self.text_color=(255,255,255) self.font=pygame.font.SysFont(None,48) #创建按钮的矩形 self.rect=pygame.Rect(0,0,self.width,self.height) self.rect.center=self.screen_rect.center #调用prep_msg函数绘制按钮 self.prep_msg(msg) def prep_msg(self,msg): #将 msg 渲染为图像,并使其在按钮上居中 self.msg_image=self.font.render(msg,True,self.text_color,self.button_color) self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center def draw_button(self): #绘制一个用颜色填充的按钮,再绘制文本 self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)