基于Python制作小恐龙游戏

基于 Python 设计的小恐龙小游戏 本来想带大家用遗传算法再训一波龙的,于是把自己之前写的仿谷歌浏览器小恐龙代码找了出来,就是这个: Python 制作小游戏(七) 想在它基础上加 AI 控制的代码的

本文包含相关资料包-----> 点击直达获取<-------

基于 Python 设计的小恐龙小游戏

本来想带大家用遗传算法再训一波龙的,于是把自己之前写的仿谷歌浏览器小恐龙代码找了出来,就是这个:

Python 制作小游戏(七)

想在它基础上加 AI 控制的代码的,结果发现之前写的那个版本和原版还是有出入的,而且玩起来的体验也很差,于是就打算先重构一版这个游戏分享给大家,然后再带大家训龙。毕竟磨刀不误砍柴工嘛。

废话不多说,让我们愉快地开始吧

开发工具

Python 版本:**3.6.4

相关模块:

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 标识,所以占的空间更大:

飞龙的定义就稍微复杂一些了,因为它不仅需要向左移动,还需要做出不停扇动翅膀的效果。具体而言,飞龙有两张图:

你需要做的就是每隔一段时间就切换一次当前的飞龙图片,以实现飞龙扇动翅膀的效果:

python '''飞龙''' 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)

最后,我们需要定义一下小恐龙类,也就是最复杂的一个游戏精灵类。它有低头,跳跃,普通前进三种状态。对于低头来说:

你只需要和飞龙扇动翅膀一样,不断切换两张低头的图片以实现小恐龙跑动的效果就可以了。

对于普通状态也是类似的:

对于跳跃状态,我们则可以通过初中学的上抛和自由落体运动公式来建模,从而计算小恐龙在竖直方向上的位置。具体而言,代码实现如下:

python '''小恐龙''' 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 ```

然后写游戏主循环啦

```python

游戏主循环

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 就会判定两个目标有碰撞,这显然是不合理的,会给玩家带来较差的游戏体验。

另外,当分数每提高一千分,我们就和原版的游戏一样增加一点场景和障碍物向左移动的速度(也就是增加小恐龙向右移动的速度)。

参考文献

  • 网页游戏平台的管理与设计(哈尔滨师范大学·苏润泽)
  • 豆玩手机游戏平台的设计与实现(吉林大学·李天明)
  • 基于SSH架构的个人空间交友网站的设计与实现(北京邮电大学·隋昕航)
  • 游戏化学习在培养小学生专利保护意识中的应用(福建师范大学·马承慧)
  • 基于SSH架构的个人空间交友网站的设计与实现(北京邮电大学·隋昕航)
  • 面向中小学教育资源的网络爬虫的研究与设计(中央民族大学·郑名达)
  • 项目管理中测试管理软件系统设计(电子科技大学·金亚敏)
  • 面向计算思维发展的游戏创作学习活动设计与应用研究——以小学编程教学为例(华东师范大学·程亮)
  • 基于SSH框架的电子宠物系统设计与实现(吉林大学·王丽丽)
  • 基于J2EE的协同益智游戏的研究(华北电力大学(北京)·郭晶晶)
  • 面向中小学教育资源的网络爬虫的研究与设计(中央民族大学·郑名达)
  • 基于Cocos2d-JS引擎的手机网页游戏设计与实现(武汉邮电科学研究院·赵甜)
  • 基于Cocos2d-JS引擎的手机网页游戏设计与实现(武汉邮电科学研究院·赵甜)
  • 豆玩手机游戏平台的设计与实现(吉林大学·李天明)
  • 面向计算思维发展的游戏创作学习活动设计与应用研究——以小学编程教学为例(华东师范大学·程亮)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:毕设向导 ,原文地址:https://m.bishedaima.com/yuanma/36046.html

相关推荐

发表回复

登录后才能评论