外星人游戏主程序alien_invasion
import pygame
#import sys,当前只在模块game_functions中使用它
from settings import Settings #导入类
from ship import Ship #导入类
#from alien import Alien 由于我们不在本模块中直接创建alien所以不要导入Alien模块
import game_functions as gf #导入模块game_functions
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import ScoreBoard
def run_game():
#初始化pygame,settings和屏幕对象
pygame.init()
ai_settings=Settings()#实例化设置类
screen=pygame.display.set_mode((ai_settings.screen_width,
ai_settings.screen_height))
#创建一个宽1200,高800的屏幕
pygame.display.set_caption("Alien Invasion")#设置标题
#创建一艘飞船
ship=Ship(ai_settings,screen)
#创建一个用于存储子弹的编组
bullets=Group()
#创建一个外星人编组
aliens =Group() #存储所有外星人
#创建外星人群
gf.create_fleet(ai_settings,screen,ship,aliens)#将空编组传给他
#创建一个用于存储游戏统计信息的实例,并创建记分牌
stats=GameStats(ai_settings)
sb=ScoreBoard(ai_settings,screen,stats)
#创建游戏开始按钮
play_button = Button(ai_settings,screen,"Play")
#开始主循环
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,screen,stats,sb,ship,aliens,bullets)
gf.update_aliens(ai_settings,stats,screen,sb,ship,aliens,bullets)
#print(len(bullets))#核实子弹确实已经被删除,以后本句话就可以删除,占内存
gf.update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,play_button)
run_game()
设置模块
class Settings():
'''存储alien所有设置'''
def __init__(self):
'''初始化游戏的静态设置'''
#屏幕设置
self.screen_width=1200
self.screen_height=600
self.bg_color = (230,230,230)#RGB值,0~255,(255,0,0)红色 (0,255,0)绿色 (0,0,255)蓝色
#飞船的设置
#rect的centerx属性只能存放整数
self.ship_limit = 3
#子弹的设置
self.bullet_width=300
self.bullet_height=15
self.bullet_color = (255,0,0)#深灰色子弹速度比飞船速度低
self.bullets_allowed=8#将未消失的子弹数量设置为3颗
#外星人设置
self.fleet_drop_speed = 10 #撞击屏幕的之后下降的速度
#以什么样的速度加快游戏节奏
self.speedup_scale=1.1
#外星人点数的提高速度
self.score_scale=1.5
self.initalize_dynamic_settings()
def initalize_dynamic_settings(self):
'''初始化随游戏变化的设置'''
self.ship_speed_factor=8.5 #飞船移动时,移动1.5个像素,小数可以更加细致的控制飞船速度
self.bullet_speed_factor = 10
self.alien_speed_factor = 5
self.fleet_direction = 1 #为1表示向右移,-1 表示向左移动
#记分
self.alien_points =50
def increase_speed(self):
'''提高速度设置和外星人点数'''
self.ship_speed_factor *=self.speedup_scale
self.bullet_speed_factor *=self.speedup_scale
self.alien_speed_factor *= self.speedup_scale
self.alien_points =int(self.alien_points*self.score_scale)
#print(self.alien_points)#确认点数不断增加之后这句话可以删掉
所有的函数模块
import pygame
import sys
from bullet import Bullet
from alien import Alien
from time import sleep
def check_keydown_events(event,ai_settings,screen,ship,bullets):#编组传给check_keydown_events
'''响应按键'''
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_SPACE: #按下空格键创建新子弹
file_bullet(ai_settings,screen,ship,bullets)#调用发射函数
elif event.key==pygame.K_q:
sys.exit()
def file_bullet(ai_settings,screen,ship,bullets):
'''如果没有达到限制就发射一颗子弹'''
if len(bullets)<ai_settings.bullets_allowed: #现在子弹的数量
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
elif event.key==pygame.K_LEFT:#判断是否按得是左箭头
ship.moving_left=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.KEYDOWN: #按键
check_keydown_events(event,ai_settings,screen,ship,bullets)
elif event.type==pygame.KEYUP:#松开
check_keyup_events(event,ship)
elif event.type == pygame.MOUSEBUTTONDOWN: #检查鼠标事件
mouse_x,mouse_y=pygame.mouse.get_pos()#pygame.mouse.get_pos返回一个元组,包含鼠标的想,和y坐标
check_play_button(ai_settings,screen,stats,sb,play_button,ship,aliens,bullets,mouse_x,mouse_y)
#调用函数,处理单击鼠标之后的反应
def check_play_button(ai_settings,screen,stats,sb,play_button,ship,aliens,bullets,
mouse_x,mouse_y):
'''在玩家单击play按钮时开始游戏'''
button_clicked=play_button.rect.collidepoint(mouse_x,mouse_y)#检查鼠标时是否在play_button.rect中,结果为True False
if button_clicked and not stats.game_active: #当且仅当玩家按按钮,且游戏处于非活跃状态,游戏才重新开始,不然玩家在游戏过程按中原本按钮的位置游戏也会重新开始
#重置游戏
ai_settings.initalize_dynamic_settings()
#隐藏光标
pygame.mouse.set_visible(False)
#重置游戏统计信息
stats.reset_stats()
stats.game_active=True
#重置记分牌图像
sb.prep_score()
sb.prep_high_score()
sb.prep_level()
sb.prep_ships()#在开始游戏时调用
#清空外星人和子弹列表
aliens.empty()
bullets.empty()
#创建一批新的外星人,并让飞船居中
create_fleet(ai_settings,screen,ship,aliens)
ship.center_ship()
def update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,play_button):#更新屏幕
'''更新屏幕上的图像,并切换新屏幕'''
'''每次循环都重绘屏幕'''
screen.fill(ai_settings.bg_color)#背景色来填满屏幕
#在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():#方法bullets.sprites返回一个列表,包含编组bullets中所有精灵
bullet.draw_bullet()#对每个精灵调用draw_bullet函数画图
ship.blitme()###填充背景之后才将飞船绘制到屏幕上,确保飞船出现在屏幕前面
#在飞船和子弹后面绘制外星人
aliens.draw(screen)#对编组调用draw时,pygame自动绘制编组中的每个元素,位置有rect决定,在屏幕上绘制每一个外星人
#显示得分
sb.show_score()
#如果屏幕属于非活动状态就运行游戏
if not stats.game_active:
play_button.draw_button()
#让最近屏幕可见
pygame.display.flip() #图像翻转,,绘制一个空屏幕并不断擦去旧屏幕,使得只有新屏幕可见
def update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets): #函数用来管理子弹
'''更新子弹的位置,删除已经消失的子弹'''
bullets.update()#子弹位置更新
#删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collision(ai_settings,screen,stats,sb,ship,aliens,bullets)
def check_bullet_alien_collision(ai_settings,screen,stats,sb,ship,aliens,bullets):
#检查是否有子弹击中了外星人,如果是这样就删除相应的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets,aliens,True,True)
#方法sprite.groupcollide(),将子弹的rect与外星人的rect比较,并返回一个字典,其中包含发生碰撞重叠的子弹和外星人,键是子弹,值是外星人,两个True告诉pygame删除发生碰撞的子弹和外星人
if collisions:
for aliens in collisions.values():
stats.score += ai_settings.alien_points*len(aliens)
sb.prep_score()
check_high_score(stats,sb)
#j检查aliens是否为空,如果是,删除剩下的子弹,并再次创建一群外星人
if len(aliens) == 0:
#如果整群外星人都被消灭,就提高一个等级
bullets.empty()
ai_settings.increase_speed()
#提高等级
stats.level +=1
sb.prep_level()
create_fleet(ai_settings,screen,ship,aliens)
def check_high_score(stats,sb):
'''检查是否诞生了最高分'''
if stats.score>stats.high_score:
stats.high_score=stats.score
sb.prep_high_score()
def check__fleet_edges(ai_settings,aliens):
'''有外星人到达边缘时采取相应的措施'''
for alien in aliens.sprites():
if alien.check_edges():
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,sb,ship,aliens,bullets):
'''响应被外星人撞击之后的飞船'''
if stats.ships_left > 0:
#将ship_left -1
stats.ships_left -=1
#更新记分牌
sb.prep_ships()
#清空外星人列表和子弹列表
aliens.empty()
bullets.empty()
#创建一群新的外星人,并将飞船放在屏幕底部中央
create_fleet(ai_settings,screen,ship,aliens)
ship.center_ship()
#暂停,为了让用户看清飞船与外星人碰撞了
sleep(0.5)
else:
stats.game_active =False #玩家没有飞船,就将标准设置为false 结束游戏
pygame.mouse.set_visible(True)
def check_aliens_bottom(ai_settings,stats,screen,sb,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,sb,ship,aliens,bullets)
break #有一个到达底部就无需检查其他
def update_aliens(ai_settings,stats,screen,sb,ship,aliens,bullets):
'''检查是否有外星人到达边缘,并更新外星人的位置'''
check__fleet_edges(ai_settings,aliens)
aliens.update()
#检查是否有外星人到达屏幕底端
check_aliens_bottom(ai_settings,stats,screen,sb,ship,aliens,bullets)
#检查外星人和飞船之间的碰撞
if pygame.sprite.spritecollideany(ship,aliens):#spritecollideany()接受两个实参,一个精灵和一个编组,检查编组里面是否有成员跟精灵碰撞,如果有则返回编组里的成员,执行接下来的操作,没有则返回None。
ship_hit(ai_settings,stats,screen,sb,ship,aliens,bullets)
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#每个外星人的位置都是通过设置x坐标实现,占用一个宽度的空白
alien.rect.x=alien.x
alien.rect.y=alien.rect.height+2*alien.rect.height*row_number
aliens.add(alien)#将创建好的加到编组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 row_number in range(number_rows):#重复次数,行数
for alien_number in range(number_aliens_x):
#从0开始到要创建的外星人数,。每一行的个数
create_alien(ai_settings,screen,aliens,alien_number,row_number)
子弹
#创建子弹类
import pygame
from pygame.sprite import Sprite #Sprint精灵,通过使用精灵,可以将游戏中相关元素编组,相当于列表
class Bullet(Sprite):
'''一个对飞船发射的子弹进行管理的类'''
def __init__(self,ai_settings,screen,ship):
super().__init__()
self.screen=screen
#在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
self.rect =pygame.Rect(0,0,ai_settings.bullet_width,
ai_settings.bullet_height)
self.rect.centerx=ship.rect.centerx
self.rect.top=ship.rect.top#子弹应该从飞船顶部射出
#存储用小数表的子弹的位置
self.y=float(self.rect.y) #y可以微调子弹的设计速度
self.color=ai_settings.bullet_color
self.speed_factor=ai_settings.bullet_speed_factor
def update(self):
'''向上移动子弹'''
self.y-= self.speed_factor #更新表示子弹的小数值,子弹向上运动,y坐标不断减小
#更新表示子弹的rect位置
self.rect.y=self.y
def draw_bullet(self):
'''在屏幕上绘制子弹'''
pygame.draw.rect(self.screen,self.color,self.rect)#用color填空表示子弹的矩形rect占screen的部分
按钮
import pygame.font #导入字体模块,让pygame能够将文本渲染到屏幕上
class Button():
def __init__(self,ai_settings,screen,msg): #msg是要在按钮上显示的文本
'''初始化按钮属性'''
self.screen =screen
self.screen_rect = screen.get_rect()
#设置按钮尺寸和其他属性
self.width,self.height = 200,50
self.button_color=(0,255,0) #按钮的rect为绿色
self.text_color=(255,255,255)#文本为白色
self.font =pygame.font.SysFont(None,48) #指定使用默认字体,48号大小的字体渲染文本
#创建按钮的rect对象,并使其居中
self.rect = pygame.Rect(0,0,self.width,self.height) #pygame.Rect创建一个新的矩阵对象,在原点处,指定高,宽
self.rect.center =self.screen_rect.center#将center属性设置为屏幕的center属性
#按钮标签只需要创建一次
self.prep_msg(msg) #调用方法prep_msg()
def prep_msg(self,msg):
'''将msg渲染为图像,并使其在按钮上居中'''
self.msg_image = self.font.render(msg,True,self.text_color,
self.button_color)#font.render将存储在msg中的文本转化为图像,True开启反锯齿功能,文本背景颜色为按钮颜色
self.msg_image_rect =self.msg_image.get_rect()#创建文本图像的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)#传递图像,已经图像相关连的rect对象 在屏幕上绘制文本
```
外星人模块
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
'''表示单个外星人的类'''
def __init__(self,ai_settings,screen):
'''初始化外星人并设置初始位置'''
super().__init__()
self.ai_settings=ai_settings
self.screen=screen
#加载外星人图像,并设置rect属性
self.image=pygame.image.load("images/alien.bmp") #加载图像
self.rect=self.image.get_rect()#获取图像alien的矩阵对象
#每个外星人初始位置都在屏幕左上角附近
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)
#检查外星人是否撞击屏幕
def check_edges(self):
'''如果外星人位于屏幕边缘,返回true'''
screen_rect =self.screen.get_rect()#获取屏幕的rect属性
if self.rect.right >=screen_rect.right:#rect右属性大于屏幕rect右属性或者,左rect属性小于0,都说明在屏幕边缘
return True
elif self.rect.left <= 0:
return True
def update(self):
'''向左或者向右移动外星人'''
self.x+=(self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x=self.x
飞船
import pygame
from pygame.sprite import Sprite#创建飞船编组
class Ship(Sprite):#创建飞船类,负责飞船的大部分行为
def __init__(self,ai_settings,screen): #添加ai——settings,让飞船可以获得其速度设置,后者指定要将飞船放在什么位置
'''初始化飞船,并设置初始位置'''
super().__init__()
self.screen=screen
self.ai_settings=ai_settings
#加载飞船图,并获取外接矩阵
self.image=pygame.image.load("images/ship.bmp") #加载图像
self.rect=self.image.get_rect()#获取图像ship的矩阵对象
self.screen_rect=screen.get_rect()#获取屏幕的矩阵对象
#将每艘新飞船放在屏幕底部中央
self.rect.centerx=self.screen_rect.centerx #将图像ship的中心x坐标放在屏幕中心x坐标
self.rect.bottom=self.screen_rect.bottom #将ship的底放在屏幕的底部
self.center=float(self.rect.centerx)#重新定义属性,将self.rect.centerx的值转化为小数
#移动标志
self.moving_right=False
self.moving_left=False
def update(self):
'''根据移动标志调整飞船的位置'''
if self.moving_right and self.rect.right < self.screen_rect.right: #限制活动范围
self.center+=self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left> 0:
self.center-=self.ai_settings.ship_speed_factor
#根据self.center更新rect对象
self.rect.centerx=self.center
def blitme(self): #方法blit 实现位块传输
'''在指定位置绘制飞船'''
self.screen.blit(self.image,self.rect) #它根据self.rect 指定的位置将图像绘制到屏幕上
def center_ship(self):
'''让飞船在屏幕中央'''
self.center =self.screen_rect.centerx
统计信息
class GameStats():
'''跟踪游戏的统计信息'''
def __init__(self,ai_settings):
'''初始化统计信息'''
self.ai_settings=ai_settings
self.reset_stats() #调用方法reset_stats()
#游戏启动时处于活动状态
self.game_active = False#将游戏一开始状态设置为非活动状态,按下play按钮才开始游戏
#在任何情况下都不应该重置高分
self.high_score = 0
def reset_stats(self):
'''初始化在游戏运行期间可能变化的统计信息'''
self.ships_left=self.ai_settings.ship_limit
self.score = 0
self.level =1
得分信息
import pygame.font
from pygame.sprite import Group
from ship import Ship
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)
#准备初始得分图像h和最高分的图像
self.prep_score()
self.prep_high_score()
self.prep_level()
self.prep_ships()
def prep_score(self):
'''将得分转化为一幅渲染的图像'''
rounded_score = round(self.stats.score,-1)#round函数是让小数精确到小数点后面多少位,-1表示圆整到最近的10的整数倍
score_str='{:,}'.format(rounded_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 prep_high_score(self):
'''将最高分转化为一幅渲染的图像'''
high_score= round(self.stats.high_score,-1)
high_score_str='{:,}'.format(high_score)
self.high_score_image=self.font.render(high_score_str,True,self.text_color,
self.ai_settings.bg_color)
#将最高分放在屏幕顶端中央
self.high_score_rect=self.high_score_image.get_rect()
self.high_score_rect.centerx=self.screen_rect.centerx
self.high_score_rect.top =self.score_rect.top
def prep_level(self):
'''将等级渲染为图像'''
self.level_image=self.font.render(str(self.stats.level),True,self.text_color,self.ai_settings.bg_color)
#将等级放在得分下面
self.level_rect=self.level_image.get_rect()
self.level_rect.right=self.score_rect.right
self.level_rect.top =self.score_rect.bottom+10
def prep_ships(self):
'''显示还剩下多少飞船'''
self.ships =Group()#存储用来显示还有多少剩余飞船实例
for ship_number in range(self.stats.ships_left):
ship =Ship(self.ai_settings,self.screen)
ship.rect.x=10+ship_number*ship.rect.width
ship.rect.y =10
self.ships.add(ship)
def show_score(self):
'''在屏幕上显示得分和最高得分'''
self.screen.blit(self.score_image,self.score_rect)
self.screen.blit(self.high_score_image,self.high_score_rect)
self.screen.blit(self.level_image,self.level_rect)
#绘制飞船
self.ships.draw(self.screen)#对编组调用draw,即可在屏幕上绘制飞船
两张位图:外星人和飞船
游戏运行结果