Python基础教程书籍案例:使用python制作游戏(DIY街机游戏)【下】
这一篇教程,我们来完成游戏的主程序模块。
最终编写完的游戏会有如下界面。
游戏界面汇总图:
接下来,根据上一篇教程的结构,我们编写每一个类的代码。
一、State类
从游戏界面汇总图中,我们能够看出,游戏中会有暂停状态(除关卡界面之外的界面)和运行状态(关卡界面)。
State类就是将这两个状态进行抽象,包括。
- 无论那个状态都需要对退出游戏的指令进行处理;
- 无论哪个状态都要显示界面内容。
对于退出游戏指令的处理,我们需要定义一个处理方法;
而显示内容我们需要定义两个方法,因为两个状态的内容显示要有不同的处理。
示例代码:
'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
class State:
"""泛型游戏状态类,可以处理事件并在给定的表面上显示自身。"""
def handle(self, event):
"""处理退出事件。"""
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
def paused_display(self, screen):
"""暂停时显示。"""
r = config.getint('Screen', 'red')
b = config.getint('Screen', 'blue')
g = config.getint('Screen', 'green')
screen.fill((r, b, g)) # 屏幕填充颜色
pygame.display.flip() # 刷新显示屏幕
def display(self, screen): # 默认不作处理,由子类Level重写。
"""刷新显示时。"""
pass
二、Level类
这个类继承State类,主要对关卡运行时进行相应的处理。
具体实现过程,大家可以通过注释理解。
示例代码:
'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
class Level(State):
"""游戏等级,用于计算共有多少敌机落下,移动游戏对象以及其他与游戏相关的逻辑任务。"""
def __init__(self, number=1):
"""初始化关卡的等级和游戏对象。"""
self.number = number # 初始化关卡级别
self.remaining = config.getint('Level', 'pre_level') # 初始化关卡敌机数量
speed = config.getint('Speed', 'drop_speed') # 获取设置文件中的敌机速度数值
speed += (self.number - 1) * config.getint('Speed', 'speed_increase') # 计算当前关卡敌机速度数值
self.enemy = objects.Enemy(speed) # 创建敌方飞机游戏对象
self.plane = objects.Plane() # 创建己方飞机对象
self.sprites = pygame.sprite.RenderUpdates(self.enemy, self.plane) # 创建游戏对象集添加游戏对象
def update(self, game):
"""关卡运行,并进行游戏结束或通过关卡的处理。"""
self.sprites.update() # 刷新游戏对象
if self.plane.touches(self.enemy): # 如果碰撞到敌方飞机
game.next_state = GameOver() # 变更状态为游戏结束
elif self.enemy.landed: # 如果敌方飞机从屏幕底边移出
self.enemy.reset() # 重置敌方飞机位置
self.remaining -= 1 # 敌机数量减少1架
if self.remaining == 0: # 如果敌机数量为0
game.next_state = LevelCleared(self.number) # 变更状态为清空关卡
def display(self, screen):
"""关卡运行时的屏幕显示。"""
r = config.getint('Screen', 'red')
b = config.getint('Screen', 'blue')
g = config.getint('Screen', 'green')
screen.fill((r, b, g)) # 关卡运行时为屏幕填充颜色
updates = self.sprites.draw(screen) # 绘制屏幕外观
pygame.display.update(updates) # 刷新屏幕外观
三、Paused类
这个类继承自State类,是其它暂停状态类的超类。
暂停状态基本都是白色的屏幕和黑色的文字内容。
当然,也有例外。
欢迎界面做了两种不同的方案,方案1只有一张图片,方案2既有文字又有图片。
根据这些情况,我们分析一下如何处理。
- 如果只有图片直接绘制图片;
- 如果只有文字则在屏幕中心绘制文字;
- 如果既有文字又有图片,将图片绘制在文字上方,间隔20像素。
具体实现过程,请大家参考代码中的注释理解。
示例代码:
class Paused(State):
"""暂停游戏的状态,按任意键或点击鼠标退出暂停状态。"""
next_state = None # 存储下一个游戏状态
finish = False # 记录是否完成按键或鼠标点击
image = None # 存储屏幕中显示的图片
text = '' # 存储屏幕中显示的文字
def handle(self, event):
"""处理按任意键继续游戏。"""
State.handle(self, event)
if event.type in [MOUSEBUTTONDOWN, KEYDOWN]: # 如果捕获鼠标点击和按键的事件
self.finish = True # 记录完成操作
def update(self, game):
"""按任意键时进入下一个游戏状态。"""
if self.finish: # 如果按键或鼠标点击完成
game.next_state = self.next_state() # 将变量next_state中类的实例化,存入Game类进行处理。
def paused_display(self, screen):
"""暂停时显示的处理。"""
State.paused_display(self, screen) # 重载超类中的方法
font = pygame.font.SysFont('SimHei', config.getint('Screen', 'font_size')) # 设置使用系统字体(用于支持中文)
lines = self.text.strip().splitlines() # 获取子类中设置的文字内容并分行
height = len(lines) * font.get_linesize() # 计算文字高度
center, middle = screen.get_rect().center # 获取屏幕中心坐标值
top = middle - height // 2 # 计算显示内容中心点的y轴坐标
if self.image: # 如果有图片
image = pygame.image.load(self.image).convert_alpha() # 载入图片
img_rect = image.get_rect() # 创建矩形
top += img_rect.height // 2 # 重新计算顶部y轴坐标
if self.text: # 如果有文字
img_rect.midbottom = center, top - 20 # 设置图片位置为中心且底边与文字顶部间隔20像素。
else: # 否则
img_rect.center = center, middle # 设置图片中心点为屏幕中心点
screen.blit(image, img_rect) # 屏幕填充图片
antialias = True # 设置抗锯齿(平滑文字)
black = 0, 0, 0 # 设置文字颜色
for line in lines:
text = font.render(line.strip(), antialias, black) # 创建单行文字对象
txt_rect = text.get_rect() # 创建矩形
txt_rect.midtop = center, top # 设置文字显示位置
screen.blit(text, txt_rect) # 屏幕填充文字
top += font.get_linesize() # 逐行下移文字位置
pygame.display.flip() # 刷新屏幕显示
四、Paused类的子类(StartUp、Info、LevelCleared、GameOver)
这几个子类都比较简单,主要是定义暂停状态下屏幕中显示的内容。
其中,略有不同的是LevelCleared类,在这个类中要进行下一个关卡的实例化。
另外,为了能够方便更换欢迎界面的方案,在配置文件(config.ini)中我们新增一项配置内容。
示例代码:
[Welcome] skin = 1
各个Paused类的子类实现,大家可以根据下方代码中的注释理解。
示例代码:
class Info(Paused):
"""游戏信息。"""
next_state = Level # 将类存入变量,以便在Paused类中实例化。
text = '''控制移动你的飞机,
不要被敌机撞到它。'''
class StartUp(Paused):
"""进入游戏。"""
next_state = Info # 将类存入变量,以便在Paused类中实例化。
if config.getint('Welcome', 'skin'): # 读取配置文件中欢迎界面的方案设置
text = ''
image = config.get('Image', 'welcome')
else:
text = '老司机开飞机'
image = config.get('Image', 'plane')
class LevelCleared(Paused):
"""游戏过关。"""
def __init__(self, number):
"""过关信息。"""
self.number = number # 获取当前关卡级别
self.text = '''恭喜闯过第 %d 关,
点击继续下一关。''' % self.number
def next_state(self):
"""创建下一关卡。"""
return Level(self.number + 1) # 返回下移关卡的对象
class GameOver(Paused):
"""游戏结束。"""
next_state = Level # 将类存入变量,以便在Paused类中实例化。
text = '游戏结束!'
五、Game类
这个类负责处理游戏的整个运行过程。
class Game:
"""负责主事件循环的游戏对象,完成在不同状态间切换的任务。"""
def __init__(self):
"""初始化。"""
self.state = None # 初始化当前状态
self.next_state = StartUp() # 初始化下一个状态为开始游戏的状态
def run(self):
"""运行游戏。"""
pygame.init() # 游戏初始化
flag = 0 # 记录是否全屏的标志
if config.getint('Screen', 'full_screen'): # 如果配置文件中设置为全屏
flag = FULLSCREEN # 标志设置为全屏
screen_size = (config.getint('Screen', 'width'), config.getint('Screen', 'height')) # 读取配置文件中的屏幕尺寸设置
screen = pygame.display.set_mode(screen_size, flag) # 设置屏幕外观模式
pygame.display.set_caption('老司机开飞机') # 设置游戏窗口标题(非全屏时显示)
pygame.mouse.set_visible(False) # 隐藏鼠标指针显示
while True:
pygame.time.Clock().tick(200) # 设置每秒帧数
if self.state != self.next_state: # 如果有新的游戏状态
self.state = self.next_state # 切换到新的状态
self.state.paused_display(screen) # 屏幕中显示暂停状态的内容(切换状态会进入暂停状态)
for event in pygame.event.get(): # 获取事件
self.state.handle(event) # 处理事件
self.state.update(self) # 游戏对象的当前状态内容刷新
self.state.display(screen) # 屏幕中显示刷新后的状态内容
到这里,我们就完成了这个项目所有代码的编写。
大家可以运行程序,查看一下是否能够正确执行。
示例代码:
if __name__ == '__main__':
game = Game()
game.run()