今天是圣诞节,公司放假一天,趁着有空,学习了一下午,多写一篇博客吧!
接着上节的继续学习,
在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。在本节中,我们将创建一个名为 game_functions 的新模块,它将存储大量让游戏《外星人入侵》运行的函数。通过创建模块 game_functions,可避免 alien_invasion.py 太长,并使其逻辑更容易理解。
将 check_events() 放在一个名为 game_functions 的模块中,在该函数主要是管理事件的功能,通过隔离事件循环,可将事件管理与游戏的其他方面(如更新屏幕)分离。通
- #game_functions.py
- import sys
- import pygame
- def check_events():
- """响应按键和鼠标事件"""
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
为进一步简化 run_game(),下面将更新屏幕的代码移到一个名为 update_screen() 的函数中,并将这个函数放在模块 game_functions.py 中:
- #game_functions.py
- --snip--
- def check_events():
- --snip--
- def update_screen(ai_settings, screen, ship):
- """更新屏幕上的图像,并切换到新屏幕"""
- # 每次循环时都重绘屏幕
- screen.fill(ai_settings.bg_color)
- ship.blitme()
- # 让最近绘制的屏幕可见
- pygame.display.flip()
下面来让玩家能够左右移动飞船:
每当用户按键时,都将在 Pygame 中注册一个事件。事件都是通过方法 pygame.event.get() 获取的,因此在函数 check_events() 中,我们需要指定要检查哪些类型的事件。每次按键都被注册为一个 KEYDOWN 事件。
检测到 KEYDOWN 事件时,我们需要检查按下的是否是特定的键。例如,如果按下的是右箭头键,我们就增大飞船的 rect.centerx 值,将飞船向右移动:
- #game_ functions.py
- def check_events(ship):
- """响应按键和鼠标事件"""
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- elif event.type == pygame.KEYDOWN:
- if event.key == pygame.K_RIGHT:
- #向右移动飞船
- ship.rect.centerx += 1
玩家按住右箭头键不放时,我们希望飞船不断地向右移动,直到玩家松开为止。检测 pygame.KEYUP 事件,以便玩家松开右箭头键时我们能够知道这一点;然后,我们将结合使用 KEYDOWN 和 KEYUP 事件,以及一个名为 moving_right 的标志来实现持续移动。
飞船不动时,标志 moving_right 将为 False。玩家按下右箭头键时,我们将这个标志设置为 True;而玩家松开时,我们将这个标志重新设置为 False。代码见下面:
当前,每次执行 while 循环时,飞船最多移动 1 像素,但我们可以在 Settings 类中添加属性 ship_speed_factor,用于控制飞船的速度。我们将根据这个属性决定飞船在每次循环时最多移动多少距离。下面演示了如何在 settings.py 中添加这个新属性:
当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面,消失得无影无踪。下面来修复这种问题,让飞船到达屏幕边缘后停止移动。为此,我们将修改 Ship 类的方法 update():
- 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.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=float(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 > self.screen_rect.left :
- self.center -=self.ai_settings.ship_speed_factor
- self.rect.centerx = self.center
- def blitme(self):
- """在指定位置绘制飞船"""
- self.screen.blit(self.image, self.rect)
主文件 alien_invasion.py 创建一系列整个游戏都要用到的对象:存储在 ai_settings 中的设置、存储在 screen 中的主显示 surface 以及一个飞船实例。文件 alien_invasion.py 还包含游戏的主循环,这是一个调用 check_events()、ship.update() 和 update_screen() 的 while 循环。
文件 settings.py 包含 Settings 类,这个类只包含方法__init__(),它初始化控制游戏外观和飞船速度的属性。
文件 game_functions.py 包含一系列函数,游戏的大部分工作都是由它们完成的。函数 check_events() 检测相关的事件,如按键和松开,并使用辅助函数 check_keydown_events() 和 check_keyup_events() 来处理这些事件。就目前而言, 这些函数管理飞船的移动。模块 game_functions 还包含函数 update_screen(),它用于在每次执行主循环时都重绘屏幕。
文件 ship.py 包含 Ship 类,这个类包含方法__init__()、管理飞船位置的方法 update() 以及在屏幕上绘制飞船的方法 blitme()。表示飞船的图像存储在文件夹 images 下的文件 ship.bmp 中。
下面来添加射击功能。我们将编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。
首先,更新 settings.py,在其方法__init__() 末尾存储新类 Bullet 所需的值:
- class Settings():
- '''存储《外星人入侵》的所有设置的类'''
- def __init__(self):
- '''初始化游戏的设置'''
- self.screen_width=1200
- self.screen_height=800
- self.bg_color = (230,230,230)
- self.ship_speed_factor =1.5
- #子弹的设置
- self.bullet_speed_factor = 1
- self.bullet_width =10
- self.bullet_height =15
- self.bullet_color =60,60,60
下面来创建存储 Bullet 类的文件 bullet.py,Bullet 类继承了我们从模块 pygame.sprite 中导入的 Sprite 类。通过使用精灵,可将游戏中相关的元素编组,进而同时操作编组中的所有元素。为创建子弹实例,需要向__init__() 传递 i_settings、screen 和 ship 实例,还调用了 super() 来继承 Sprite。我们创建了子弹的属性 rect。子弹并非基于图像的,因此我们必须使用 pygame.Rect() 类从空白开始创建一个矩形。创建这个类的实例时,必须提供矩形左上角的 x 坐标和 y 坐标,还有矩形的宽度和高度。方法 update() 管理子弹的位置。发射出去后,子弹在屏幕中向上移动,这意味着 y 坐标将不断减小,因此为更新子弹的位置,子弹发射后,其 x 坐标始终不变,因此子弹将沿直线垂直地往上穿行。需要绘制子弹时,我们调用 draw_bullet()。函数 draw.rect() 使用存储在 self.color 中的颜色填充表示子弹的 rect 占据的屏幕部分。代码如下:
- import pygame
- from pygame.sprite import Sprite
- 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)
- 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)
定义 Bullet 类和必要的设置后,就可以编写代码了,在玩家每次按空格键时都射出一发子弹。首先,我们将在 alien_invasion.py 中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的所有子弹。这个编组将是 pygame.sprite.Group 类的一个实例;pygame.sprite.Group 类类似于列表,但提供了有助于开发游戏的额外功能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:
- import sys
- from settings import Settings
- from ship import Ship
- import game_functions as gf
- import pygame
- from pygame.sprite import Group
- def run_game():
- # 初始化游戏并建立一个屏幕对象
- pygame.init()
- # screen = pygame.display.set_mode((1200,800))
- ai_settings=Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
- pygame.display.set_caption("Alien Invasion")
- # 创建一艘飞船
- ship = Ship(ai_settings,screen)
- #创建一个用于存储子弹的group
- bullets =Group()
- #开始游戏的主循环
- while True:
- # 监视键盘和鼠标事件
- gf.check_events(ai_settings,screen,ship,bullets)
- ship.update()
- bullets.update()
- bullets.update()
- gf.update_screen(ai_settings,screen,ship,bullets)
- run_game()
在 game_functions.py 中,我们需要修改 check_keydown_events(),以便在玩家按空格键时发射一颗子弹。我们无需修改 check_keyup_events(),因为玩家松开空格键时什么都不会发生。我们还需修改 update_screen(),确保在调用 flip() 前在屏幕上重绘每颗子弹。下面是对 game_functions.py 所做的相关修改:
- import sys
- from bullet import Bullet
- import pygame
- def check_keydown_events(event,ai_settings,screen,ship,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 :
- 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,ship,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)
- #check_keydown_events(event,ship)
- elif event.type == pygame.KEYUP :
- check_keyup_events(event,ship)
- def update_screen(ai_settings,screen,ship,bullets) :
- """更新屏幕上的图像,并切换到新屏幕"""
- # 每次循环时都重绘屏幕
- screen.fill(ai_settings.bg_color)
- #在飞船和外星人后面重新绘制所有子弹
- for bullet in bullets.sprites() :
- bullet.draw_bullet()
- ship.blitme()
- # 让最近绘制的屏幕可见
- pygame.display.flip()
先写到这里吧,不知不觉天已经黑了,太耗时了,要去做饭了,对了最后的效果如下所示(为了截图我把子弹的速度调的非常慢,所以看起来有点怪):
昨天写的了,忘了发布了,今天发布下!
来源: https://www.cnblogs.com/majianchao/p/8110637.html