14.计分_1(添加Play按钮)

1.添加Play按钮:Play按钮在开始游戏前出现,结束游戏后再次出现。需要使游戏一开始处于非活动状态,在game_stats.py中修改game_active为False。
新建button模块,创建Button类,用于创建带标签的实心矩形。该模块导入了pygame.font模块将文本渲染到屏幕上,Button类中pygame.font.SysFont()可指定文本的字体和字号,font.render()方法可以使文本渲染成图像,该方法传递四个实参:字符串文本信息,布尔实参,文本颜色和背景色。布尔实参指定开启或关闭反锯齿功能(反锯齿让文本的边缘更光滑),若不指定背景颜色,Pygame将以透明背景的方式渲染文本。screen.fill()绘制按钮矩形,screen.blit()绘制文本图像。
game.functions模块中新建函数start_game()和check_play_button(),并修改了check_keydown_events()、check_events()、update_screen()和ship_hit()。上述修改是为了实现Play按钮显示在屏幕上,游戏未开始时鼠标点击Play按钮或者按下P键可以开始游戏,并重置游戏。游戏结束后Play按钮重新出现。游戏为活跃状态时隐藏光标。
alien_invasion模块创建Button实例,并增加check_events()和update_screen()的实参。
game_stats.py 代码如下:

# -*- coding: GBK -*- 
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_left = self.ai_settings.ship_limit

button.py 代码如下:

# -*- coding: GBK -*- 
import pygame.font	#用于将文本渲染到屏幕上

class Button():
	
	def __init__(self, ai_settings, screen, msg):
		'''初始化按钮的属性'''
		self.screen = screen
		self.screen_rect = self.screen.get_rect()
		
		# 设置按钮的尺寸和其他属性
		self.width, self.height = 200, 50
		self.button_color = (0, 255, 0)		#按钮颜色
		self.text_color = (255, 255, 255)		#字体颜色
		self.font = pygame.font.SysFont(None, 48)	#选择字体渲染文本,实参None表示使用默认字体,后一个实参表示字号
		
		# 创建按钮的rect对象,并使其居中
		self.rect = pygame.Rect(0, 0, self.width, self.height)
		self.rect.center = self.screen_rect.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中的文本转换为图像,布尔实参指定开启或关闭反锯齿功能(反锯齿让文本的边缘更平滑),后两个实参分别为文本颜色和背景色
		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)	#screen.fill()绘制按钮
		self.screen.blit(self.msg_image, self.msg_image_rect)	#绘制文本图像

game_functions.py 代码如下:

# -*- coding: GBK -*- 
import sys		# 使用模块sys来退出游戏。
import pygame
from bullet import Bullet
from alien import Alien		# 导入alien模块Alien类,控制每个外星人的行为
from time import sleep		# sleep()函数可用来让游戏暂停

def check_keydown_events(event, ai_settings, screen, stats, ship, aliens, bullets):
	'''响应按键'''
	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:	# 空白键
		fire_bullet(ai_settings, screen, ship, bullets)
	elif event.key == pygame.K_q:	# 字母Q键
		sys.exit()
	elif event.key == pygame.K_p:	# 游戏处于非活跃状态时按P键可以开始游戏
		if not stats.game_active:
			start_game(ai_settings, screen, stats, ship, aliens, bullets)

def fire_bullet(ai_settings, screen, ship, bullets):
	'''如果还没有到达限制,就发射一颗子弹'''
	# 创建一颗新子弹,并将其加入到编组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, play_button, ship, aliens, bullets):
	'''响应按键和鼠标事件'''
	# 监视键盘和鼠标事件
	for event in pygame.event.get():	#Pygame检测到的时间
		if event.type == pygame.QUIT:	#玩家单击窗口关闭按钮,则检测到pygame.QUIT事件。
			sys.exit()		#退出游戏
		elif event.type == pygame.KEYDOWN:		#根据按住键修改移动标志的值
			check_keydown_events(event, ai_settings, screen, stats, ship, aliens, bullets)
		elif event.type == pygame.KEYUP:		#根据松开键修改移动标志的值
			check_keyup_events(event, ship)
		elif event.type == pygame.MOUSEBUTTONDOWN:
			mouse_x, mouse_y = pygame.mouse.get_pos()	#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按钮时开始新游戏'''
	button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)	#cllidepoint()检查鼠标单击位置是否在Play按钮的rect内
	if button_clicked and not stats.game_active:		#仅在游戏非活跃状态时点击Play按钮区域才可以重新开始游戏,防止游戏时误点Play按钮区域导致重新开始
		start_game(ai_settings, screen, stats, ship, aliens, bullets)


def start_game(ai_settings, screen, stats, ship, aliens, bullets):
	'''在按Q键或者鼠标点击Play按钮时开始游戏'''
	# 隐藏光标
	pygame.mouse.set_visible(False)
	
	# 重置游戏统计信息
	stats.reset_stats()
	stats.game_active = True
	
	#清空外星人列表和子弹列表
	aliens.empty()
	bullets.empty()
	
	#创建一群新的外星人,并让飞船居中
	create_fleet(ai_settings, screen, ship, aliens)
	ship.center_ship()	

def update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button):
	'''更新屏幕上的图像,并切换到新屏幕'''
	# 每次循环时都重绘屏幕
	screen.fill(ai_settings.bg_color)	#screen.fill()只接受一个实参:一种颜色
	# 在飞船和外星人后面重绘所有子弹
	for bullet in bullets.sprites():	#方法bullets.sprites()返回一个列表,其中包含编组bullets中的所有精灵
		bullet.draw_bullet()
	ship.blitme()	#在指定位置绘制飞船
	aliens.draw(screen)		#对编组调用draw(),Pygame自动绘制编组的每个元素,绘制的位置由元素的属性rect决定。此处在屏幕上绘制编组中每个外星人。
	
	#如果游戏处于非活跃状态,就绘制Play按钮
	if not stats.game_active:
		play_button.draw_button()
	
	# 让最近绘制的屏幕可见
	pygame.display.flip()	#不断更新屏幕,以显示元素的新位置	

def update_bullets(ai_settings, screen, ship, aliens, bullets):
	'''更新子弹的位置,并删除已消失子弹'''
	# 更新每一粒子弹的位置
	bullets.update()	#对编组调用update(),编组自动对其中每个精灵调用update(),即为每一粒子弹调用bullet.update()
	
	# 删除已消失子弹
	for bullet in bullets.copy():	# 遍历编组的副本
		if bullet.rect.bottom <= 0:
			bullets.remove(bullet)
	# print(len(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)	#每当子弹和外星人的rect重叠时,Pygame删除发生碰撞的子弹和外星人
	
	if len(aliens) == 0:
		# 若外星人群被消灭干净,删除现有的子弹并新建一群外星人
		bullets.empty()		#方法empty()可删除编组中剩余所有精灵
		create_fleet(ai_settings, screen, ship, aliens)

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)	#此处减去的2个alien_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	#确定生成外星人的y坐标
	aliens.add(alien)	#将生成的外星人添加至编组	

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):		#此处alien_number从0开始数起,range()也需要整数
			create_alien(ai_settings, screen, aliens, alien_number, row_number)		

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, ship, aliens, bullets):
	'''响应被外星人撞到的飞船'''
	if stats.ship_left > 0:
		# 讲ships_left减1
		stats.ship_left -= 1

		# 清空外星人列表和子弹列表
		aliens.empty()
		bullets.empty()

		# 创建一群新的外星人,并将飞船放到屏幕底部*
		create_fleet(ai_settings, screen, ship, aliens)
		ship.center_ship()

		# 暂停
		sleep(0.5)
	else:
		stats.game_active = False
		pygame.mouse.set_visible(True)	# 游戏不活跃状态后会显示Play按钮,并显示光标

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()		#对编组调用update(),自动对每个外星人调用方法update()
	
	# 检测外星人和飞船之间的碰撞
	if pygame.sprite.spritecollideany(ship, aliens):	#接受两个实参,检查编组是否有成员与精灵发生了碰撞,若有停止遍历编组,返回发生碰撞的那个外星人,否则返回None
		ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
	
	# 检查是否有外星人到达屏幕底端
	check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)

alien_invasion.py 执行代码如下:

# -*- coding: GBK -*- 
import pygame		# 模块pygame包含开发游戏所需的功能。
from settings import Settings	# 导入设置模块的Setting类
from ship import Ship	# 导入ship模块的Ship类,它负责管理飞船的大部分行为。
import game_functions as gf	# 导入game_functions模块
from pygame.sprite import Group	#用于创建子弹编组
from game_stats import GameStats	#导入跟踪游戏统计信息的类GameStats
from button import Button	#导入Button类,用于创建带标签的实心矩形

def run_game():
	# 初始化游戏并创建一个屏幕对象
	pygame.init()	#初始化背景设置,让Pygame能够正确地工作。
	ai_settings = Settings()	#创建Settings实例,并使用它来访问设置
	screen = pygame.display.set_mode(
		(ai_settings.screen_width, ai_settings.screen_height))	#调用pygame.display.set_mode()来创建一个窗口,实参是一个元祖,指定游戏窗口尺寸。
	pygame.display.set_caption("Alien Invasion")	#设置窗口的标题
	
	
	# 创建Play按钮,创建Button实例
	play_button = Button(ai_settings, screen, "Play")
	
	# 创建一个用于存储游戏统计信息的实例
	stats = GameStats(ai_settings)
	
	# 创建一艘飞船
	ship = Ship(ai_settings, screen)
	# 创建一个用于存储子弹的编组
	bullets = Group()
	# 创建一个外星人编组
	aliens = Group()
	
	# 创建外星人群
	gf.create_fleet(ai_settings, screen, ship, aliens)
	
	# 开始游戏的主循环
	while True:
		# 响应按键和鼠标事件
		gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets)
		
		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()

14.计分_1(添加Play按钮)

上一篇:动态选择屏幕


下一篇:第七篇:linux之基本权限、进程管理、系统服务