首页 > 编程语言 >Python游戏开发实践项目-小恐龙躲避游戏——一个适合python新手练手的项目

Python游戏开发实践项目-小恐龙躲避游戏——一个适合python新手练手的项目

时间:2024-07-20 15:56:03浏览次数:10  
标签:练手 游戏 Python self def score pygame image rect

今天我们就来给大家演示下,用Python来自己做一个仿制的“小恐龙游戏”!

废话不多说,让我们愉快地开始吧~
相关模块:

pygame模块;以及一些python自带的模块。

环境搭建

安装Python并添加到环境变量,pip安装需要的相关模块即可。

先睹为快

在终端运行如下命令即可:

python Game7.py

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码介绍

这里介绍一下游戏的实现原理。

首先,我们对游戏进行一些必要的初始化工作:

# 游戏初始化
pygame.init()
screen = pygame.display.set\_mode(cfg.SCREENSIZE)
pygame.display.set\_caption('T-Rex Rush —— Charles的皮卡丘')
# 导入所有声音文件
sounds = {}
for key, value in cfg.AUDIO\_PATHS.items():
  sounds\[key\] = pygame.mixer.Sound(value)

接着,我们来考虑一下,游戏中有哪些游戏元素:

小恐龙:由玩家控制以躲避路上的障碍物;

路面:游戏的背景;

云:游戏的背景;

飞龙:路上的障碍物之一,小恐龙碰上就会死掉;

仙人掌:路上的障碍物之一,小恐龙碰上就会死掉;

记分板:记录当前的分数和历史最高分。

让我们来依次定义一下这些游戏元素类。对于云,路面以及仙人掌来说,定义起来很简单,我们只需要加载对应的游戏元素图片:
在这里插入图片描述
在这里插入图片描述
然后写两个类内部方法update和draw就ok了。两个方法分别用于将场景不断向左移动以实现小恐龙不断向前移动的动画效果和将场景显示在游戏界面的对应位置上。具体而言,代码实现如下:

'''地板'''
class Ground(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepath, position, \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入图片
    self.image\_0 = pygame.image.load(imagepath)
    self.rect\_0 = self.image\_0.get\_rect()
    self.rect\_0.left, self.rect\_0.bottom = position
    self.image\_1 = pygame.image.load(imagepath)
    self.rect\_1 = self.image\_1.get\_rect()
    self.rect\_1.left, self.rect\_1.bottom = self.rect\_0.right, self.rect\_0.bottom
    # 定义一些必要的参数
    self.speed = -10
  '''更新地板'''
  def update(self):
    self.rect\_0.left += self.speed
    self.rect\_1.left += self.speed
    if self.rect\_0.right < 0:
      self.rect\_0.left = self.rect\_1.right
    if self.rect\_1.right < 0:
      self.rect\_1.left = self.rect\_0.right
  '''将地板画到屏幕'''
  def draw(self, screen):
    screen.blit(self.image\_0, self.rect\_0)
    screen.blit(self.image\_1, self.rect\_1)

'''云'''
class Cloud(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepath, position, \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入图片
    self.image = pygame.image.load(imagepath)
    self.rect = self.image.get\_rect()
    self.rect.left, self.rect.top = position
    # 定义一些必要的参数
    self.speed = -1
  '''将云画到屏幕上'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''更新云'''
  def update(self):
    self.rect = self.rect.move(\[self.speed, 0\])
    if self.rect.right < 0:
      self.kill()

'''仙人掌'''
class Cactus(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepaths, position=(600, 147), sizes=\[(40, 40), (40, 40)\], \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入图片
    self.images = \[\]
    image = pygame.image.load(imagepaths\[0\])
    for i in range(3):
      self.images.append(pygame.transform.scale(image.subsurface((i\*101, 0), (101, 101)), sizes\[0\]))
    image = pygame.image.load(imagepaths\[1\])
    for i in range(3):
      self.images.append(pygame.transform.scale(image.subsurface((i\*68, 0), (68, 70)), sizes\[1\]))
    self.image = random.choice(self.images)
    self.rect = self.image.get\_rect()
    self.rect.left, self.rect.bottom = position
    self.mask = pygame.mask.from\_surface(self.image)
    # 定义一些必要的变量
    self.speed = -10
  '''画到屏幕上'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''更新'''
  def update(self):
    self.rect = self.rect.move(\[self.speed, 0\])
    if self.rect.right < 0:
      self.kill()

记分板的定义也类似,只不过它不需要移动,但是需要实时地更新当前 的分数:

'''记分板'''
class Scoreboard(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepath, position, size=(11, 13), is\_highest=False, bg\_color=None, \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入图片
    self.images = \[\]
    image = pygame.image.load(imagepath)
    for i in range(12):
      self.images.append(pygame.transform.scale(image.subsurface((i\*20, 0), (20, 24)), size))
    if is\_highest:
      self.image = pygame.Surface((size\[0\]\*8, size\[1\]))
    else:
      self.image = pygame.Surface((size\[0\]\*5, size\[1\]))
    self.rect = self.image.get\_rect()
    self.rect.left, self.rect.top = position
    # 一些必要的变量
    self.is\_highest = is\_highest
    self.bg\_color = bg\_color
    self.score = '00000'
  '''设置得分'''
  def set(self, score):
    self.score = str(score).zfill(5)
  '''画到屏幕上'''
  def draw(self, screen):
    self.image.fill(self.bg\_color)
    for idx, digital in enumerate(list(self.score)):
      digital\_image = self.images\[int(digital)\]
      if self.is\_highest:
        self.image.blit(digital\_image, ((idx+3)\*digital\_image.get\_rect().width, 0))
      else:
        self.image.blit(digital\_image, (idx\*digital\_image.get\_rect().width, 0))
    if self.is\_highest:
      self.image.blit(self.images\[-2\], (0, 0))
      self.image.blit(self.images\[-1\], (digital\_image.get\_rect().width, 0))
    screen.blit(self.image, self.rect)

上面代码用is_highest变量来区分该记分板是否用于记录游戏最高分,还是只是记录当前的分数,做该区分的原因是游戏最高分前面有HI标识,所以占的空间更大:
在这里插入图片描述
飞龙的定义就稍微复杂一些了,因为它不仅需要向左移动,还需要做出不停扇动翅膀的效果。具体而言,飞龙有两张图:
在这里插入图片描述
你需要做的就是每隔一段时间就切换一次当前的飞龙图片,以实现飞龙扇动翅膀的效果:

'''飞龙'''
class Ptera(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepath, position, size=(46, 40), \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入图片
    self.images = \[\]
    image = pygame.image.load(imagepath)
    for i in range(2):
      self.images.append(pygame.transform.scale(image.subsurface((i\*92, 0), (92, 81)), size))
    self.image\_idx = 0
    self.image = self.images\[self.image\_idx\]
    self.rect = self.image.get\_rect()
    self.rect.left, self.rect.centery = position
    self.mask = pygame.mask.from\_surface(self.image)
    # 定义一些必要的变量
    self.speed = -10
    self.refresh\_rate = 10
    self.refresh\_counter = 0
  '''画到屏幕上'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''更新'''
  def update(self):
    if self.refresh\_counter % self.refresh\_rate == 0:
      self.refresh\_counter = 0
      self.image\_idx = (self.image\_idx + 1) % len(self.images)
      self.loadImage()
    self.rect = self.rect.move(\[self.speed, 0\])
    if self.rect.right < 0:
      self.kill()
    self.refresh\_counter += 1
  '''载入当前状态的图片'''
  def loadImage(self):
    self.image = self.images\[self.image\_idx\]
    rect = self.image.get\_rect()
    rect.left, rect.top = self.rect.left, self.rect.top
    self.rect = rect
    self.mask = pygame.mask.from\_surface(self.image)

最后,我们需要定义一下小恐龙类,也就是最复杂的一个游戏精灵类。它有低头,跳跃,普通前进三种状态。对于低头来说:
在这里插入图片描述
你只需要和飞龙扇动翅膀一样,不断切换两张低头的图片以实现小恐龙跑动的效果就可以了。对于普通状态也是类似的:
在这里插入图片描述
对于跳跃状态,我们则可以通过初中学的上抛和自由落体运动公式来建模,从而计算小恐龙在竖直方向上的位置。具体而言,代码实现如下:

'''小恐龙'''
class Dinosaur(pygame.sprite.Sprite):
  def \_\_init\_\_(self, imagepaths, position=(40, 147), size=\[(44, 47), (59, 47)\], \*\*kwargs):
    pygame.sprite.Sprite.\_\_init\_\_(self)
    # 导入所有图片
    self.images = \[\]
    image = pygame.image.load(imagepaths\[0\])
    for i in range(5):
      self.images.append(pygame.transform.scale(image.subsurface((i\*88, 0), (88, 95)), size\[0\]))
    image = pygame.image.load(imagepaths\[1\])
    for i in range(2):
      self.images.append(pygame.transform.scale(image.subsurface((i\*118, 0), (118, 95)), size\[1\]))
    self.image\_idx = 0
    self.image = self.images\[self.image\_idx\]
    self.rect = self.image.get\_rect()
    self.rect.left, self.rect.bottom = position
    self.mask = pygame.mask.from\_surface(self.image)
    # 定义一些必要的变量
    self.init\_position = position
    self.refresh\_rate = 5
    self.refresh\_counter = 0
    self.speed = 11.5
    self.gravity = 0.6
    self.is\_jumping = False
    self.is\_ducking = False
    self.is\_dead = False
    self.movement = \[0, 0\]
  '''跳跃'''
  def jump(self, sounds):
    if self.is\_dead or self.is\_jumping:
      return
    sounds\['jump'\].play()
    self.is\_jumping = True
    self.movement\[1\] = -1 \* self.speed
  '''低头'''
  def duck(self):
    if self.is\_jumping or self.is\_dead:
      return
    self.is\_ducking = True
  '''不低头'''
  def unduck(self):
    self.is\_ducking = False
  '''死掉了'''
  def die(self, sounds):
    if self.is\_dead:
      return
    sounds\['die'\].play()
    self.is\_dead = True
  '''将恐龙画到屏幕'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''载入当前状态的图片'''
  def loadImage(self):
    self.image = self.images\[self.image\_idx\]
    rect = self.image.get\_rect()
    rect.left, rect.top = self.rect.left, self.rect.top
    self.rect = rect
    self.mask = pygame.mask.from\_surface(self.image)
  '''更新小恐龙'''
  def update(self):
    if self.is\_dead:
      self.image\_idx = 4
      self.loadImage()
      return
    if self.is\_jumping:
      self.movement\[1\] += self.gravity
      self.image\_idx = 0
      self.loadImage()
      self.rect = self.rect.move(self.movement)
      if self.rect.bottom >= self.init\_position\[1\]:
        self.rect.bottom = self.init\_position\[1\]
        self.is\_jumping = False
    elif self.is\_ducking:
      if self.refresh\_counter % self.refresh\_rate == 0:
        self.refresh\_counter = 0
        self.image\_idx = 5 if self.image\_idx == 6 else 6
        self.loadImage()
    else:
      if self.refresh\_counter % self.refresh\_rate == 0:
        self.refresh\_counter = 0
        if self.image\_idx == 1:
          self.image\_idx = 2
        elif self.image\_idx == 2:
          self.image\_idx = 3
        else:
          self.image\_idx = 1
        self.loadImage()
    self.refresh\_counter += 1

定义完游戏精灵类,我们就可以实例化他们:

\# 定义一些游戏中必要的元素和变量
score = 0
score\_board = Scoreboard(cfg.IMAGE\_PATHS\['numbers'\], position=(534, 15), bg\_color=cfg.BACKGROUND\_COLOR)
highest\_score = highest\_score
highest\_score\_board = Scoreboard(cfg.IMAGE\_PATHS\['numbers'\], position=(435, 15), bg\_color=cfg.BACKGROUND\_COLOR, is\_highest=True)
dino = Dinosaur(cfg.IMAGE\_PATHS\['dino'\])
ground = Ground(cfg.IMAGE\_PATHS\['ground'\], position=(0, cfg.SCREENSIZE\[1\]))
cloud\_sprites\_group = pygame.sprite.Group()
cactus\_sprites\_group = pygame.sprite.Group()
ptera\_sprites\_group = pygame.sprite.Group()
add\_obstacle\_timer = 0
score\_timer = 0
然后写游戏主循环啦: 
\# 游戏主循环
clock = pygame.time.Clock()
while True:
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      pygame.quit()
      sys.exit()
    elif event.type == pygame.KEYDOWN:
      if event.key == pygame.K\_SPACE or event.key == pygame.K\_UP:
        dino.jump(sounds)
      elif event.key == pygame.K\_DOWN:
        dino.duck()
    elif event.type == pygame.KEYUP and event.key == pygame.K\_DOWN:
      dino.unduck()
  screen.fill(cfg.BACKGROUND\_COLOR)
  # --随机添加云
  if len(cloud\_sprites\_group) < 5 and random.randrange(0, 300) == 10:
    cloud\_sprites\_group.add(Cloud(cfg.IMAGE\_PATHS\['cloud'\], position=(cfg.SCREENSIZE\[0\], random.randrange(30, 75))))
  # --随机添加仙人掌/飞龙
  add\_obstacle\_timer += 1
  if add\_obstacle\_timer > random.randrange(50, 150):
    add\_obstacle\_timer = 0
    random\_value = random.randrange(0, 10)
    if random\_value >= 5 and random\_value <= 7:
      cactus\_sprites\_group.add(Cactus(cfg.IMAGE\_PATHS\['cacti'\]))
    else:
      position\_ys = \[cfg.SCREENSIZE\[1\]\*0.82, cfg.SCREENSIZE\[1\]\*0.75, cfg.SCREENSIZE\[1\]\*0.60, cfg.SCREENSIZE\[1\]\*0.20\]
      ptera\_sprites\_group.add(Ptera(cfg.IMAGE\_PATHS\['ptera'\], position=(600, random.choice(position\_ys))))
  # --更新游戏元素
  dino.update()
  ground.update()
  cloud\_sprites\_group.update()
  cactus\_sprites\_group.update()
  ptera\_sprites\_group.update()
  score\_timer += 1
  if score\_timer > (cfg.FPS//12):
    score\_timer = 0
    score += 1
    score = min(score, 99999)
    if score > highest\_score:
      highest\_score = score
    if score % 100 == 0:
      sounds\['point'\].play()
    if score % 1000 == 0:
      ground.speed -= 1
      for item in cloud\_sprites\_group:
        item.speed -= 1
      for item in cactus\_sprites\_group:
        item.speed -= 1
      for item in ptera\_sprites\_group:
        item.speed -= 1
  # --碰撞检测
  for item in cactus\_sprites\_group:
    if pygame.sprite.collide\_mask(dino, item):
      dino.die(sounds)
  for item in ptera\_sprites\_group:
    if pygame.sprite.collide\_mask(dino, item):
      dino.die(sounds)
  # --将游戏元素画到屏幕上
  dino.draw(screen)
  ground.draw(screen)
  cloud\_sprites\_group.draw(screen)
  cactus\_sprites\_group.draw(screen)
  ptera\_sprites\_group.draw(screen)
  score\_board.set(score)
  highest\_score\_board.set(highest\_score)
  score\_board.draw(screen)
  highest\_score\_board.draw(screen)
  # --更新屏幕
  pygame.display.update()
  clock.tick(cfg.FPS)
  # --游戏是否结束
  if dino.is\_dead:
    break

游戏主循环的逻辑很简单,即每帧游戏画面,我们都需要检测一下玩家的操作,如果玩家按下了空格键或者↑键,则小恐龙跳跃,如果玩家按下了↓键,则小恐龙低头,否则小恐龙正常向前冲。

然后在游戏中,我们随机产生云,飞龙和仙人掌这些游戏场景和障碍物,并且和路面一起以相同的速度向左移动,从而实现小恐龙向右移动的视觉效果。在移动的过程中,我们需要对小恐龙和仙人掌,小恐龙和飞龙进行碰撞检测,当小恐龙碰到这些障碍物时,小恐龙就死掉了,本局游戏也随之结束。

需要注意的是我们应该使用collide_mask函数来进行更为精确的碰撞检测,而不是之前的collide_rect函数:
在这里插入图片描述
即当两个目标的最小外接矩形有重叠时,collide_rect就会判定两个目标有碰撞,这显然是不合理的,会给玩家带来较差的游戏体验。

最后,把当前所有的游戏元素绑定到屏幕上并更新当前的屏幕就ok了。
大概就是这样,大功告成~ 完整源代码可以在下方免费领取
在这里插入图片描述

标签:练手,游戏,Python,self,def,score,pygame,image,rect
From: https://blog.csdn.net/m0_65482549/article/details/140572551

相关文章

  • python 类
    构造方法init方法说明参数self->指的就是实例对象自己,返回值为空,实际是调用了new方法会生成一个实例对象实例化类的时候系统自动调用init方法进行创建(在调用init方法直接系统自动调用new方法创建对象)对象和初始化如果类没有init方法,系统会调用默认的;如果写了就相当于对init......
  • 使用 Elasticsearch Python SDK 查询 Easysearch
    随着数据分析需求的不断增长,高效查询和分析大数据集变得越来越重要。Easysearch作为一种强大的国产化搜索和分析引擎,同时作为Elasticsearch国产替代方案,支持原生DSL查询语法和SQL查询,确保原业务代码无需调整即可无缝迁移。Easysearch兼容ES7.x现有的SDK和索引存储格......
  • 【组合总和】python刷题记录
    目录思路:回溯法框架:本题中(元素不可重复可复选)如果不重复使用重复使用代码:​拓展1:元素无重复不可复选子集问题:组合问题:全排列问题:拓展2:元素可重复不可复选再--子集问题:PS:润到递归了。下面是超级回溯大法!!!!!思路:使用回溯法解决问题----能够穷举所有解回溯法框架:......
  • python3 安装Crypto包 出现No module named ‘Crypto‘和No module named ‘Crypto.Ut
       pycrypto、pycrytodome和crypto是一个东西,crypto在python上面的名字是pycrypto,它是一个第三方库,但是已经停止更新三年了,所以不建议安装这个库;这个时候pycryptodome就来了,它是pycrypto的延伸版本,用法和pycrypto是一模一样的;所以,我现在告诉大家一种解决方法--直接安装:pipin......
  • Python中用来排序的方法sort、sorted
    sort与sorted区别:sort是应用在list上的方法,而sorted可以对所有可迭代的对象(他们可以是list、dict、set、甚至是字符串)进行排序操作。list的sort方法返回的是对已经存在的列表进行操作,无返回值,而内建函数sorted方法返回的是一个新的list,而不是在原来的基础上进行......
  • Python教程:json中load和loads的区别
    一.相同点dump和dumps都实现了序列化load和loads都实现反序列化变量从内存中变成可存储或传输的过程称之为序列化序列化是将对象状态转化为可保存或可传输格式的过程。变量内容从序列化的对象重新读到内存里称之为反序列化反序列化是流转换为对象。二.区别1.load......
  • Python中,如何使用反斜杠 “\“分割字符串?
    Python语言使用反斜杠(\)作为转义符,对一些字符进行转义(escape),例如"\n""\r\n"等。所以当Python字符串中如果出现反斜杠,则会自动转义其后的字符。但这会导致一个问题,就是,如果只是把反斜杠作为字符字面(liberal)意义,应该如何处理?如果不使用re模块(regularexpressionmodule),在Py......
  • Python中4种方法实现 xls 文件转 xlsx
    在Python中,可以采用pandas、pyexcel、win32com和xls2xlsx这四个模块,实现xls转xlsx格式。以Excel示例文件test_Excel.xls为例,具体内容如下图所示:1.pandas安装命令pipinstallpandas-ihttps://mirrors.aliyun.com/pypi/simple具体使用方法importpandasas......
  • 【Python爬虫学习】7个好玩有趣的爬虫教程!(附源码)
    本文介绍了7个Python爬虫小案例,包括爬取豆瓣电影Top250、猫眼电影Top100、全国高校名单、中国天气网、当当网图书、糗事百科段子和新浪微博信息,帮助读者理解并实践Python爬虫基础知识。在文章开始之前先给大家简单介绍一下python爬虫1.什么是爬虫?简单介绍爬虫爬虫的......
  • 用Python将多个excel内容整合成一个excel档
    print("開始!")importosimportpandasaspd设置文件夹路径folder_path='D:\123456'folder_path='D:\1-24714'创建一个空的DataFrame用于存储数据all_data=pd.DataFrame()遍历文件夹中的所有文件forfile_nameinos.listdir(folder_path):iffile_name.ends......