开发环境配置
安装python环境后,下载pygame模块,使用如下命令
pip install pygame
注:该项目使用了一些新特性,使用3.10以上的版本
游戏项目介绍
游戏分为两个模块,分别是编辑模块和关卡模块,在编辑模块下,玩家可拖动地图菜单中的不同地图元素,对游戏界面进行地图的布局,在关卡模式下,玩家可控制角色对自己布局的地图关卡进行闯关
该游戏是一款类似于马里奥的闯关游戏,不同的是,玩家可自己设计游戏关卡进行闯关
游戏项目演示
项目源码获取在下面文章末尾获取
游戏部分功能介绍
游戏模式切换
main.py为该程序的入口文件,editor.py为编辑模式的功能实现,level.py为关卡模式的功能实现,三者的关系如下
在main.py中定义了一个标识符(self.editor_active)用于游戏两种模式的切换,默认该值为True,即运行程序,先进入编辑模式,编辑完成后,按下Enter键,则调用toggle()函数,将该值改为False,在main程序的循环体执行中,进入关卡模式
#main.py中的Main类
def run(self):
while True:
dt = self.clock.tick() / 1000
# 通过检查self.editor_active的值,决定是运行编辑器还是关卡
if self.editor_active:
self.editor.run(dt)
else:
self.level.run(dt)
self.transition.display(dt)
pygame.display.update
#main.py中的Main类
def toggle(self):
print('进入toggle,切换游戏的模式状态')
self.editor_active = not self.editor_active
# 通过检查self.editor_active的值,判断是否需要启动编辑器音乐
if self.editor_active:
self.editor.editor_music.play()
toggle函数的调用过程是较复杂的,程序运行后,首先调用了editor.run(dt),进入编辑模式,后调用Editor类中的event_loop()函数,该函数会及时响应玩家的事件操作(鼠标和键盘),当玩家在键盘中输入Enter键时,调用switch函数改变游戏模式状态
Translation类作为main类的一个属性,在执行display()方法时,若属性transition.ative的值为True时(默认为False),进入if分支内,调用了main中的toggle()函数,改变了editor_active属性的值
#Translation类
#用于在游戏窗口中绘制过渡效果
def display(self, dt):
if self.active:
self.border_width += 1000 * dt * self.direction
if self.border_width >= self.threshold:
self.direction = -1
self.toggle()
if self.border_width < 0:
self.active = False
self.border_width = 0
self.direction = 1
pygame.draw.circle(self.display_surface, 'black',self.center, self.radius, int(self.border_width))
游戏分数显示
为了提升游戏的体验感,增加游戏分数显示的功能,当玩家对关卡设计完成后,进行关卡挑战,玩家控制的角色只要接触到硬币,则右上角的游戏分数增加,按金币增加两分,银币增加一分进行计算
在level.py中的Level类的__init__()方法中添加属性score,用于记录游戏分数
# 游戏分数
self.score = 0
Player(玩家角色Sprite)类,在创建Level类时,作为level对象中的一个类属性被创建,同时地图上的其它(Sprite)类也被创建,这些精灵类都继承了通用的角色类Generic,而Generic继承Sprite
"""
通用的角色类,包含了角色的基本属性和方法
"""
class Generic(pygam,ere.sprite.Sprite):
def __init__(self, pos, surf, group, z = LEVEL_LAYERS['main']):
super().__init__(group)
self.image = surf
self.rect = self.image.get_rect(topleft = pos)
self.z = z
这些精灵类都交给pygame.sprite.Group进行管理,它是一个可以容纳多个精灵对象的容器,作为Level类的一个属性,方便进行对象的获取
self.coin_sprites = pygame.sprite.Group() # 硬币
self.shell_sprites = pygame.sprite.Group() # 敌人
在Level类中声明get_coin()方法,用于检测角色是否触碰到硬币,pygame.sprite.spritecollide用于检测两个精灵对象是否发生碰撞,并返回碰撞列表,之所以调用两次,第一次用于获取碰撞的硬币对象信息,第二次设置为为True用于将碰撞的精灵删除
def get_coins(self):
# pygame.sprite.spritecollide:检测玩家和硬币的碰撞
# 返回和玩家发生碰撞的硬币精灵的列表,同时将这些硬币精灵从 self.coin_sprites 精灵组中移除(设置碰撞为 True)
collided_coins1 = pygame.sprite.spritecollide(self.player, self.coin_sprites, False)
len = collided_coins1.__len__()
if(len > 0):
coin_type = collided_coins1[0].coin_type
print(f'碰撞1:{collided_coins1[0].coin_type}')
if coin_type == 'silver':
print(self.score)
self.score += 1
elif coin_type == 'gold':
print(self.score)
self.score += 2
else:
self.player.life += 1
print(f'生命数:{self.player.life}')
collided_coins = pygame.sprite.spritecollide(self.player, self.coin_sprites, True)
for sprite in collided_coins:
print('碰撞')
self.coin_sound.play()
Particle(self.particle_surfs, sprite.rect.center, self.all_sprites)
此时游戏分数功能的记录已经完善,但还需要在游戏窗口中展示,在level.py中定义了CameraGroup类,继承自pygame.sprite.Group用于管理精灵对象,并在游戏窗口中进行精灵对象的绘制
游戏关卡模式运行时,会调用level.run方法,而run方法中,则调用了CameraGroup类中的update方法,对游戏窗口进行了更新,在调用该方法时,应将游戏分数score传给cameraGroup对象
#level.run
def run(self, dt):
# update
self.event_loop()
life = self.player.life
self.all_sprites.score = self.score
self.all_sprites.life = life
self.all_sprites.update(dt)
self.get_coins()
self.get_damage()
故可在CameraGroup类的draw_horizon()添加游戏分数的显示
# 绘制游戏分数
text = pygame.font.Font("../font/LycheeSoda.ttf", 36).render(f'SCORE:{self.score}', True, (255, 255, 255))
self.display_surface.blit(text, (20, 20))
运行结果
游戏生命显示
这里设计为红色宝石为角色的生命数,当触碰到敌人或者被敌人攻击击中后,生命数量减少1,当在游戏地图中触碰到红色宝石,则生命数量增加1
判断用户是否接触到红宝石,在上面的get_coin方法中已经完成,在Player类的属性中添加life属性,设置游戏角色的血量,同时将该属性值传给cameraGroup对象,与上面游戏分数的做法一样
#sprite.py中的Player类
# 设置血条
self.life = 2
在Player类中声明damage方法,用于减少角色生命数量,而真正角色碰撞受伤的逻辑在level中实现
"""
Player类
受到伤害时的操作,使玩家向上移动并启动无敌计时器
"""
def damage(self):
if not self.invul_timer.active:
print('受伤')
self.invul_timer.activate()
self.direction.y -= 1.5
self.life -= 1
#Level类
def get_damage(self):
# 检测角色是否与敌人发生碰撞
collision_sprites = pygame.sprite.spritecollide(self.player, self.damage_sprites, False, pygame.sprite.collide_mask)
if collision_sprites:
self.hit_sound.play()
self.player.damage()
运行结果如上图所示
游戏结束
当玩家生命数量为0时,则游戏结束,同时在游戏窗口中显示GAME OVER字体提示,在CameraGroup类中判断life的值即可
# 绘制生命(用宝石数量代表生命数量)
image = pygame.image.load("../graphics/items/diamond/0.png")
image_rect = image.get_rect()
image_rect.center = (30,80)
text2 = pygame.font.Font("../font/LycheeSoda.ttf", 36).render(f':× {self.life}', True, (255, 255, 255))
self.display_surface.blit(text2, (50, 65))
self.display_surface.blit(image, image_rect)
if self.life <= 0:
text = pygame.font.Font('../font/LycheeSoda.ttf', 120).render('GAME OVER', True, (215, 55, 74))
self.display_surface.blit(text, (400, 250))
项目源码获取在下面文章末尾获取