pygame为创建一款游戏提供了很多方便地操作,下面将会用此写一款打飞机游戏。使用的版本为python3。笔记可能有些许错误,请见谅。
最后游戏界面如下:
游戏大致想法:外星人水平移动,碰到左右边缘x轴的速度方向改变,同时y轴会运动。当外星人碰到飞船或到达边缘时,扣除生命值,重置外星人和飞船位置。每清除完一波外星人,游戏难度提升。
设置类模块:我们可以将游戏基本设置属性放在里面,例如窗口的宽和长,背景颜色,又例如飞船速度,外星人运动速度等游戏难度相关的属性
功能函数模块:里面装着显示窗口,更新飞船位置等等函数,在主模块中通过调用功能函数模块便基本实现游戏
飞船类模块:定义了一个飞船类,在这里存储着飞船的图片,以及通过飞船图片获取得来的飞船矩形。还有更新飞船位置的函数,在pygame中,可以通过更新矩形位置,来实现物体的移动
子弹类模块:我们用创建有色矩形的方式来创建子弹,类中存储着子弹颜色,子弹速度,子弹宽度,其中一部分可以从设置类对象中获取。还有更新子弹位置的函数,绘制子弹的函数
外星人类模块:定义一个外星人类,里面包含着外星人的图片,代表外星人的矩形,更新外星人位置的函数等
游戏统计类模块:存储着游戏进程中,玩家得分,历史高分记录,当前生命值等
按钮类模块:可用于生成开始按钮的类,类中存储着显示的字体,将文字渲染后的图片等
显示板类:可用于生成显示的分数,生命条,历史高分条
游戏功能函数模块几乎包含运行整个游戏所需的函数,我们可以先完善其他模块的内容,最后再完善游戏功能模块。
class Settings():
def __init__(self):
self.bgc = (230,230,230) #背景颜色
self.screen_width = 1200
self.screen_height = 700 #屏幕大小
self.ship_hp = 3 #飞船血条
self.bullet_width = 20
self.bullet_height = 3
self.bullet_color = (0x66,0xcc,0xff) #子弹大小颜色
self.bullet_max = 5 #允许射出的子弹最大数目
self.alien_yspeed = 10 #外星人y轴移动速度
self.alien_xdirection = 1 #外星人x轴移动方向
self.speedup_scale = 1.1 #速度提升倍率和分数提升倍率
self.initial_speed()#初始化飞船,子弹,外星人水平速度。
def speed_up(self):
self.alien_xspeed *= self.speedup_scale
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_score += 20
def initial_speed(self):
'''初始化设置'''
self.alien_xspeed = 1
self.ship_speed = 3
self.bullet_speed = 1.5
self.alien_score = 50 #射杀一个外星人后有50分
def initial_ship_life(self):
self.ship_hp = 3 #重置飞船生命值
在游戏设置类模块中,我们编写了游戏很多的参数。如果想改变窗口大小,游戏难度可以通过游戏设置类来改变。
考虑玩法,因为会有难度提升的元素,所以我们需要有一个速度提升速率,然后在一个函数中调节游戏难度相关元素,在清除一波外星人后调用。同时如果生命值全用掉,需要重来,我们需要初始化这些设置,于是便有初始速率的函数。
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
def __init__(self,screen,ai_settings):
super().__init__()
self.image = pygame.image.load('bship.png') #加载图形
self.screen = screen #获取屏幕的surface
self.screen_rect = self.screen.get_rect() #获取屏幕矩形边框
self.ship_rect = self.image.get_rect() #获取飞船初始边框
self.ship_rect.centerx = self.screen_rect.centerx
self.ship_rect.bottom = self.screen_rect.bottom #将飞船位置移动到屏幕底部终点
self.rect = self.ship_rect
self.ship_width = self.rect.width
self.left_flag = False
self.right_flag = False #飞船的移动标志
self.settings = ai_settings #引入系统设置
def blitme(self):
'''绘制飞船的函数'''
self.screen.blit(self.image,self.ship_rect) #绘制飞船
def update(self):
'''更新飞船移动标志的函数'''
if self.left_flag and self.ship_rect.left > 0:
self.rect.centerx -= self.settings.ship_speed
if self.right_flag and self.ship_rect.right < self.screen_rect.right:
self.rect.centerx += self.settings.ship_speed
def reset_ship(self):
self.ship_rect.centerx = self.screen_rect.centerx
#重新放置飞船
游戏的一帧就是一幅图像,我们绘制一幅图像会有图层叠放顺序,而一个图层在pygame里面就是一个surface
函数 | 功能 |
---|---|
pygame.image.load(图片路径) | 返回一个对应的surface |
surface.get_rect() | 返回surface对应的矩形 |
rect的相关参数
参数 | 意义 |
---|---|
rect.center | rect的中心 |
rect.centerx | rect的中心x坐标 |
rect.centery | rect的中心y坐标 |
rect.bottom/top/left/right | rect的各边界 |
rect.width | 矩形的宽度 |
rect.height | 矩形的高度 |
rect.x | 矩形左上角x坐标 |
rect.y | 矩形左上角y坐标 |
在上面的飞船类模块中,有两个移动标志,是为了在游戏功能函数模块中检测按钮事件,改变移动标志,让飞船移动的。
Sprite是从pygame.sprite引入的一个类,主要方便对继承该类的类对象进行编组,进行多元素管理。飞船类继承Sprite类是为了制作左上方的血条。
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self,ai_setting,screen,ship):
super().__init__()
self.screen = screen #获取当前屏幕
self.rect = pygame.Rect(0,0,ai_setting.bullet_width,ai_setting.bullet_height) #新建代表子弹的矩形
self.rect.centerx = ship.ship_rect.centerx
self.rect.top = ship.ship_rect.top #将位置调整至飞船顶部
self.color = ai_setting.bullet_color #设置子弹颜色
self.speed = ai_setting.bullet_speed #设置子弹速度
def update(self):
self.rect.y -= self.speed
def draw_bullet(self):
pygame.draw.rect(self.screen,self.color,self.rect) #绘制子弹
函数 | 功能 |
---|---|
pygame.Rect(x,y,width,height) | 以(x,y)作为将创作矩形的左上角坐标,创建一个宽为width,高为height的矩形 |
pygame.draw.rect(surface,颜色,rect) | 在surface中绘制指定颜色的矩形 |
子弹类模块中,用pygame绘制一个简单的矩形来代表子弹。
在update()函数中,通过更改子弹代表矩形的左上角y坐标来让其移动。另外,子弹类要继承Sprite类,是为了对其进行编组,其必要性比飞船类继承Sprite类更大。后面通过向编组中添加元素,调用编组.update()
就能对里面的元素全部调用update函数。以及检测碰撞可以使用。update()函数有点类似C++中的虚函数,名字不能变。
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
self.image = pygame.image.load("aship.png") #加载敌人图片
self.rect = self.image.get_rect() #获取矩形
self.rect.x = self.rect.width
self.rect.y = self.rect.height #开始矩形默认坐标位左上角,移动一个身位
def check_edge(self):
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right or self.rect.left <=0:
return True #检测是否到达边界
def update(self):
'''向左或向右移动外星人'''
self.rect.x += (self.ai_settings.alien_xspeed *
self.ai_settings.alien_xdirection)
外星人类同样要继承Sprite类,方便后面编组。
update()函数用来更新其x坐标,因为update()函数几乎每一帧都会调用,而y轴的移动只有外星人碰到边缘后才会调用。
类中还有一个检查是否碰到窗口左右两边的函数,返回bool型值。从而在游戏功能函数模块中的函数调用检测是否到达边缘并改变alien_xdirection即外星人的x轴移动方向。
import json
class Game_stats():
'''跟踪游戏的统计信息'''
def __init__(self,ai_settings):
self.ai_settings = ai_settings
self.ship_hp = ai_settings.ship_hp #获取飞船血条个数
self.score = 0 #统计得分
self.game_active = False
self.level = 1 #统计等级
with open("high_score.json",'r') as score_file:
self.high_score = json.load(score_file)
def reset_ship_hp(self):
self.ship_hp = self.ai_settings.ship_hp #重新设置生命
self.score = 0 #重新设置得分
def update_high_score(self):
if self.score > self.high_score:
self.high_score = self.score #更新最高分
with open("high_score.json",'w') as score_file:
json.dump(self.high_score,score_file)
该类的主要操作就是统计生命值,分数等等。
有个重置生命值的函数,在生命值耗尽重新开始游戏时调用,更新最高分函数,可以在每击杀一个外星人时调用一次,里面还会判断是否更新。
这里用了json文件来存储高分记录。我们可以先在当前文件夹创建一个json文件,并写入0值,什么都不写会报错。
import pygame.font
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) #设定按钮背景颜色
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#设定按钮矩形,并移动到屏幕中心
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)
函数 | 功能 |
---|---|
pygame.font.SysFont(字体,字号,bool1,bool2) | 创建字体,bool1为是否粗体,bool2为是否斜体,默认False。字体None为默认字体。 |
Font对象.render(文本,bool,c1,c2) | 将文本以对应字体渲染成图片,bool型值为是否开启抗锯齿功能,c1为文本颜色,c2为背景颜色。返回surface |
surface1.fill(color,rect) | 在surface1上的rect填充颜色 |
surface1.blit(surface2,rect) | 在surface1上对应矩形处绘制surface2 |
font模块详细:https://blog.csdn.net/qq_41556318/article/details/86303502
在这里我们主要是对play按钮的新建,先用pygame.font.SysFont(None,48)
创建字体,然后用font.render(...)
来得到文本的surface。最后绘制时先绘制背景矩形,在背景surface上绘制文本的surface。绘制背景矩形也可以用子弹类用的pygame.draw.rect()
。这里要先绘制背景矩形是因为,文本surface的矩形往往事前较难知道多大。可能偏大,可能偏小。
下面第一幅图为不绘制按钮背景矩形,第二幅为绘制后。
from Ship import Ship
import pygame
from pygame.sprite import Group
class Show_board():
def __init__(self,ai_settings,screen,stats):
self.screen = screen
self.screen_rect = screen.get_rect()
self.ai_settings = ai_settings
self.text_color = (30,30,30)
self.font = pygame.font.SysFont(None,48) #设置字体字号
self.prep(stats)
def prep_score(self,stats):
'''准备当前得分文本surface'''
self.score = int(round(stats.score,-1)) #精确分数到十位
self.total_score = "{:,}".format(self.score)#数字格式化,每隔三位加一个逗号
self.total_score_image = self.font.render(self.total_score,
True,self.text_color,self.ai_settings.bgc) #将分数渲染成图片
self.total_score_image_rect = self.total_score_image.get_rect() #获取图片矩形
self.total_score_image_rect.right = self.screen_rect.right -20
self.total_score_image_rect.top = self.screen_rect.top #设置矩形位置
def prep_high_score(self,stats):
'''准备最高得分文本surface'''
self.high_score = int(round(stats.high_score,-1))
self._high_score = "{:,}".format(self.high_score)
self.high_score_image = self.font.render(self._high_score,True,
self.text_color,self.ai_settings.bgc)
self.high_score_image_rect = self.high_score_image.get_rect() #获取矩形
self.high_score_image_rect.centerx = self.screen_rect.centerx
self.high_score_image_rect.top = self.screen_rect.top #设置矩形位置
def prep_level(self,stats):
'''准备当前难度文本surface'''
self.level = stats.level
self.level_str = "level:" + str(self.level)
self.level_image = self.font.render(self.level_str,True,
self.text_color,self.ai_settings.bgc)
self.level_image_rect = self.level_image.get_rect() #获取矩形
self.level_image_rect.right = self.screen_rect.right-20
self.level_image_rect.top = self.screen_rect.top + 32 #设置矩形位置
def prep_hp(self,stats):
'''准备生命值的图像'''
self.hp = stats.ship_hp
self.ships = Group()
for number in range(self.hp):
ship = Ship(self.screen,self.ai_settings) #新建一个飞船图标
ship.rect.x = 10 + number * ship.ship_width
ship.rect.y = 10 #设置图标位置
self.ships.add(ship)
def prep(self,stats):
self.prep_score(stats)
self.prep_high_score(stats)
self.prep_level(stats)
self.prep_hp(stats)
def draw_board(self):
'''绘制文本的函数'''
self.screen.blit(self.total_score_image,self.total_score_image_rect)
self.screen.blit(self.high_score_image,self.high_score_image_rect)
self.screen.blit(self.level_image,self.level_image_rect)
self.ships.draw(self.screen)
函数 | 功能 |
---|---|
round(number,精确位数) | 返回number对应精确位数的float型数,1位小数点后一位,-1为精确到十位,-2为百位,以此类推。 |
“:,”.format(number) | 返回字符串,内容为number每隔3位加逗号。如1000000会变成1,000,000 |
显示板类只要是在屏幕上显示生命值,当前分数,最高分数,当前游戏难度。做法都是得到渲染文本后的surface,然后进行绘制,这里文本背景颜色用一开始设置类中的窗口背景颜色就可以了。
很多模块都需要读取窗口的surface,来根据surface的某些参数确定自己的位置。
import pygame
from Settings import Settings
from Ship import Ship
import GameFunction as gf
from pygame.sprite import Group
from Alien import Alien
from Game_stats import Game_stats
from button import Button
from Show_board import Show_board
def run_game():
'''运行游戏'''
settings = Settings() #新建设置类对象
pygame.init() #初始化
screen = pygame.display.set_mode((settings.screen_width,settings.screen_height)) #新建一个屏幕并设置屏幕宽和高度
pygame.display.set_caption("打飞机") #设置窗口标题
ship = Ship(screen,settings) #新建飞船对象
bullets = Group() #创建存储子弹的编组
aliens = Group() #创建存储外星人的编组
gf.create_aliens(settings,screen,ship,aliens) #添加元素
stats = Game_stats(settings) #新建统计类
play_button = Button(settings,screen,"PLAY") #新建play按钮
show_board = Show_board(settings,screen,stats) #新建显示面板
while True:
gf.check_event(settings,screen,ship,bullets,stats,play_button)#检查按键事件
if stats.game_active == True:
ship.update() #更新飞船位置
gf.update_bullets(bullets,aliens,settings,screen,ship,stats,show_board)#更新和绘画子弹的函数
gf.update_aliens(settings,screen,bullets,aliens,ship,stats,show_board)#更新外星人位置并检查碰撞
gf.update_Screen(settings,screen,ship,bullets,aliens,stats,play_button,
show_board)#更新屏幕
run_game()
函数 | 功能 |
---|---|
pygame.display.set_mode((width,height)) | 设置主窗口大小,返回surface |
pygame.display.set_caption(标题) | 设置标题 |
Group()
为新建一个编组,可以往编组里面添加元素
函数 | 功能 |
---|---|
group.update() | 对里面的每个精灵调用update(),精灵的update()需要自己定义 |
group.draw(surface) | 在surface中自动调用surface.blit(…)函数 |
group.add(a) | 添加元素a |
group.remove(a) | 删除元素a |
group.sprites() | 以列表形式返回编组中的精灵 |
group.empty() | 判断编组中是否含有元素 |
update(),draw()相关定义:https://www.cnblogs.com/huwt/p/10333500.html
在主模块中我们会做一下事情:
1.创建主窗口
2.创建飞船对象
3.创建子弹空编组
4. 创建外星人空编组
5. 调用创建一波外星人函数
6. 创建统计类对象
7. 创建play按钮
8. 创建显示板类对象
9. 主循环:
1.检查事件按钮函数
2.如果游戏状态活跃,执行更新飞船,子弹,外星人的函数
3.更新屏幕内容函数
创建一波外星人的函数,及主循环的函数都存储在游戏功能函数模块中。
我们在游戏设置类会有个游戏状态的变量,开始时为False,要允许窗口有内容显示及点击退出,检查事件按钮函数和更新屏幕内容函数不检测游戏状态。其他则未点击play时不执行。
引入模块:
import pygame
import sys
from Bullets import Bullet
from Alien import Alien
from time import sleep
def check_event(ai_setting,screen,ship,bullets,stats,play_button):
'''按钮事件检查'''
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.right_flag = True
elif event.key == pygame.K_LEFT:
ship.left_flag = True #按钮按下相关操作
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(ai_setting,screen,ship)
if len(bullets) < ai_setting.bullet_max:
bullets.add(new_bullet) #按下空格添加子弹
elif not stats.game_active and event.type == pygame.MOUSEBUTTONDOWN:
mouse_x,mouse_y = pygame.mouse.get_pos() #获取鼠标位置
flag = play_button.rect.collidepoint(mouse_x,mouse_y) #检测鼠标是否在矩形中
if not stats.game_active and flag:
stats.game_active = True #将游戏状态设置为可以开始
pygame.mouse.set_visible(False) #隐藏鼠标
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.right_flag = False
if event.key == pygame.K_LEFT:
ship.left_flag = False #松开按钮相关操作
函数 | 功能 |
---|---|
pygame.event.get() | 获取键盘和鼠标的所有事件 |
sys.exit() | 程序终止 |
rect.collidepoint(x,y) | 检查(x,y)点是否在rect中 |
pygame.mouse.get_pos() | 获取当前鼠标的x,y值 |
pygame.mouse.get_visible(bool) | 设置鼠标在窗口内可见或不可见 |
event.type 事件类型
事件类型 | 意义 |
---|---|
pygame.QUIT | 点击窗口关闭按钮 |
pygame.KEYDOWN | 按钮按下 |
pygame.KEYUP | 按钮松开 |
pygame.MOUSEBUTTONDOWN | 鼠标点击(左/右键) |
event.key按钮类型
按钮类型 | 意义 |
---|---|
pygame.K_RIGHT | 方向右键 |
pygame.K_LEFT | 方向左键 |
pygame.K_SPACE | 空格键 |
在这个检测按钮事件的函数中,我们先用for event in pygame.event.get()
来获取全部事件,然后先判断event.type
,如果是游戏非活跃状态下点击鼠标,则先用pygame.mouse.get_pos()
获取鼠标坐标检测play_button.rect.collidepoint(x,y)
来检测是否有点击到正确的矩形,如果有则设置游戏活跃状态为True
如果是按下键盘按钮,则检测是按下哪个按钮,如果是方向键则设置飞船的允许移动标志为True
,如果是空格键,我们限定最多射出5发子弹,满足条件时,才往子弹编组中添加新的子弹。
如果是松开键盘按钮,则检测是松开哪个按钮,这时我们只用考虑方向键,并将对应的移动标志设置为False
就可以了。
def update_Screen(ai_settings,screen,ship,bullets,aliens,stats,play_button,
show_board):
'''绘制屏幕内容的函数'''
screen.fill(ai_settings.bgc) #先图刷背景颜色
ship.blitme() #画好飞船位置
for bullet in bullets.sprites():
bullet.draw_bullet()
aliens.draw(screen) #对编组每一个元素进行绘制
if not stats.game_active:
play_button.draw_button()
show_board.draw_board()
pygame.display.flip() #显示出来
函数 | 功能 |
---|---|
pygame.display.flip() | 更新待显示的surface到屏幕上 |
这个函数里面,我们如同将各个图层组合起来形成一幅图像。
我们先用screen.fill(...)
来填充背景颜色,然后调用ship.blitme()
函数来绘制飞船,因为子弹我们是通过绘制矩形的方式来呈现,没有image这个属性,所以我们不能直接用bullets.draw(screen)
函数而需要逐个绘制子弹。接着直接aliens.draw(screen)
就能直接绘制外星人,相当于调用了screen.blit(alien.image,alien.rect)
,所以类定义时需要有这两个数据成员。
接着,在游戏活跃状态为False
时调用play_button.draw_button
绘制PLAY按钮,接着则是调用show_boadrd.draw_board()
将显示板的其他元素显示出来。最后则是调用pygame.display.flip()
来让所有surface显示
def create_alien(ai_settings,screen,aliens,number_x,number_y):
'''创建一个外星人的函数'''
alien = Alien(ai_settings,screen)
alien.rect.x = alien.rect.width + 2 * alien.rect.width * number_x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * number_y
#计算这个外星人的坐标
aliens.add(alien) #将该外星人添加进编组中
def get_alien_numberx(ai_settings,alien_width):
'''计算一行能有多少个外星人的函数'''
total_space_x = ai_settings.screen_width - 2 * alien_width #一行总长度
number = int(total_space_x/(2 * alien_width)) #一行外星人个数
return number
def get_alien_numbery(ai_settings,ship_height,alien_height):
'''计算一列能有多少个外星人的函数'''
total_space_y = ai_settings.screen_height - ship_height - 3*alien_height
number = int(total_space_y/(2 * alien_height))
return number
def create_aliens(ai_settings,screen,ship,aliens):
'''创建外星人群的函数'''
alien = Alien(ai_settings,screen)
alien_width = alien.rect.width
alien_height = alien.rect.height #获取一个外星人的宽度与高度
total_number_x = get_alien_numberx(ai_settings,alien_width)
total_number_y = get_alien_numbery(ai_settings,ship.ship_rect.height,alien_height)
#获取行和列
for number_y in range(total_number_y):
for number_x in range(total_number_x):
create_alien(ai_settings,screen,aliens,number_x,number_y)
#创建外星人
在创建一个外星人的函数中,我们用了alien.rect.x = alien.rect.width + 2 * alien.rect.width * number_x
这样的语句来创建对应位置的外星人,这里2*...
可以改变,这里主要是为了在两个外星人之间留着一个外星人的空隙。
在计算外星人一行,一列有多少个外星人的函数中,我们想让一行中左右两边各差一个外星人才到达窗口左右边缘。一列中应考虑与飞船的距离及与窗口上边界差多少个外星人。这些可以自己调整。在计算number时都是2*...
则是考虑两个外星人之间差一个外星人的身位。
在创建外星人群的函数中,我们先创建一个临时用的外星人,但不添加进编组以获取一个外星人的长和宽。接着调用计算一列、一行中有多少个外星人函数,最后用循环一个个创建外星人便可。
def update_bullets(bullets,aliens,ai_settings,screen,ship,stats,show_board):
'''更新子弹显示的函数'''
bullets.update() #调用编组中元素的所有update函数
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet) #将超出屏幕边界的子弹删除
collision = pygame.sprite.groupcollide(bullets,aliens,True,True)
if collision:
for alien in collision.values():
stats.score += ai_settings.alien_score #射中后加分
stats.update_high_score() #检查是否需要更新最高分
show_board.prep_score(stats) #更新分数显示
show_board.prep_high_score(stats) #更新最高分显示
if len(aliens) == 0:
bullets.empty()
ai_settings.speed_up()
create_aliens(ai_settings,screen,ship,aliens)
stats.level += 1
show_board.prep_level(stats)
#射杀完外星人后重新新建
函数 | 功能 |
---|---|
pygame.sprite.groupcollide(g1,g2,bool1,bool2) | 检查g1和g2之间是否有碰撞,如果有则根据布尔型值来确定是否删除编组对应发生碰撞的sprite,bool1对应g1,返回字典。 |
在更新子弹位置的函数中,我们先调用bullets.update()
来更新每颗子弹的位置,然后检查是否有超出边界的,如果有则从编组中删除。接着我们调用pygame.sprite.groupcollide(...)
来检查子弹和外星人两个编组间是否有碰撞,如果有碰撞删除对应的子弹和外星人,并且实现加分,判断是否超过历史高分,更新需要分数显示等等的功能。最后用len(aliens)
判断是否击杀全部外星人,没有了则调用创建外星人群函数,并且调用ai_settings.speed_up()
来提升难度并修改stats.level
,最后更新游戏等级显示。
def change_aliens_move(ai_settings,aliens):
'''改变外星人群x轴移动方向,并让其在y轴移动一段距离'''
for alien in aliens.sprites():
alien.rect.y += ai_settings.alien_yspeed
ai_settings.alien_xdirection *= -1
在这个函数中我们改变alien.rect.y
并且改变外星人的x轴移动方向,在下一个检测左右边缘函数中,如果碰到左右边缘我们调用这个上面的函数。
def check_aliens_edge(ai_settings,aliens):
'''检查外星人群是否有接触边缘的现象并改变运动方向'''
for alien in aliens.sprites():
if alien.check_edge():
change_aliens_move(ai_settings,aliens) #确一改变一群
break
在这里我们遍历编组的sprite,并且调用alien.check_edge()
来检测是否到达边缘,如果有一个到达则改变一整群的运动状态。
def after_ship_hit(ai_settings,screen,bullets,aliens,ship,stats,show_board):
'''飞船被击中后的函数'''
stats.ship_hp -= 1 #减少生命值
ship.reset_ship() #重新放置飞船位置
bullets.empty()
aliens.empty() #清空外星人和子弹
create_aliens(ai_settings,screen,ship,aliens)#重新放置子弹
show_board.prep_hp(stats) #更新生命值显示
sleep(0.5)
在这里我们定义了外星人碰到飞船或到达屏幕底部后的行为,扣除生命值,重置飞船位置,清空当前外星人和子弹,再新建一群外星人,然后调用show_board.prep_hp(stats)
来更新生命值面板,前面已有from time import sleep
,最后我们调用sleep(0.5)
来使其停顿一会。
def check_aliens_bottom(ai_settings,screen,bullets,aliens,ship,stats):
'''检查外星人是否到达底部'''
for alien in aliens.sprites():
screen_rect = screen.get_rect()
if alien.rect.bottom >= screen_rect.bottom:
after_ship_hit(ai_settings,screen,bullets,aliens,ship,stats)
break
def update_aliens(ai_settings,screen,bullets,aliens,ship,stats,show_board):
'''检查是否有外星人处于屏幕边缘并更新位置的函数'''
check_aliens_edge(ai_settings,aliens)
aliens.update()
if pygame.sprite.spritecollideany(ship,aliens):
after_ship_hit(ai_settings,screen,bullets,aliens,ship,stats,show_board)
check_aliens_bottom(ai_settings,screen,bullets,aliens,ship,stats)
if stats.ship_hp < 1 :
stats.game_active = False
ai_settings.initial_speed() #重置速度
ai_settings.initial_ship_life() #重置飞船生命
stats.level = 1 #重置等级
函数 | 功能 |
---|---|
pygame.sprite.spritecollideany(sprite,group) | 检测sprite和group之间的碰撞,返回布尔值 |
pygame中的碰撞检测:https://www.cnblogs.com/msxh/p/5027688.html
在这个函数中,我们先调用检查外星人碰到左右边缘的函数,然后调用aliens.update()
来更新外星人的水平位置,接着通过pygame.sprite.spritecollideany(...)
来检测飞船和外星人是否有碰撞(这里飞船类中如果没有继承Sprite也是可以的。可能在类属性定义中包含需要的标识符)如果有碰撞调用飞船被击中的函数,然后再检测外星人是否到达底部,如果这里有到达,在函数中也会调用飞船被击中的函数。最后则是判断生命值是否小于1,小于则重置游戏。