Python游戏开发-超级海盗!!!

开发环境配置

安装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))

图片

项目源码获取在下面文章末尾获取

猜你喜欢

转载自blog.csdn.net/m0_59595915/article/details/132622404