news 2026/7/25 17:14:33

Python Pygame实战:从零开发超级玛丽游戏,掌握游戏循环与碰撞检测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python Pygame实战:从零开发超级玛丽游戏,掌握游戏循环与碰撞检测

1. 项目概述与核心价值

“用Python写一个超级玛丽”,这听起来像是给编程新手画的一个大饼,又像是给老鸟找的一个怀旧乐子。我第一次动这个念头,纯粹是想找个有点挑战但又不太劝退的练手项目,既能巩固Python基础,又能把面向对象、事件处理、碰撞检测这些概念玩明白。结果一上手才发现,这事儿远比想象中有趣,也更有价值。它绝不仅仅是调用几个现成的游戏引擎API那么简单,而是一个从零开始,亲手搭建一个微型游戏世界的完整过程。你需要考虑怎么让马里奥跳起来、怎么让乌龟来回走、怎么让金币被顶到后消失并加分、怎么处理水管和砖块的碰撞……每一个细节,都是对编程逻辑的一次锤炼。

这个项目的核心价值,在我看来有三层。第一层是技术实践价值:你将综合运用Python的pygame库(这是最常用的2D游戏开发库之一)、面向对象编程思想、事件驱动模型、以及基础的物理模拟(比如重力和速度)。第二层是设计思维价值:你需要把一个熟悉的游戏,拆解成可编程的模块(精灵、场景、状态机),这本身就是一种极佳的系统分析训练。第三层是成就感驱动:看着自己写的代码让一个像素小人跑跳顶砖,那种即时反馈的快乐,是很多枯燥教程给不了的。无论你是刚学完Python语法想找点乐子的新手,还是想深入理解游戏循环原理的进阶者,这个项目都能让你满载而归。

2. 开发环境与工具链搭建

工欲善其事,必先利其器。开发一个Python小游戏,环境配置是第一步,也是最容易让新手卡住的地方。下面我会详细拆解每一步,并解释为什么这么做。

2.1 Python解释器安装与版本选择

首先,你需要一个Python解释器。我强烈推荐直接从Python官网下载安装最新稳定版(目前是Python 3.11或3.12)。为什么不推荐使用系统自带的Python或者某些打包工具里的版本?因为官网版本最干净,环境变量配置最清晰,能最大程度避免后续模块依赖的诡异问题。

注意:安装时务必勾选“Add Python to PATH”这个选项。这是为了让系统在任何命令行位置都能识别pythonpip命令。很多新手遇到的“不是内部或外部命令”错误,根源就在这里。

安装完成后,打开命令行(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),输入python --version,如果正确显示版本号,说明安装成功。同时,也检查一下包管理工具pip,输入pip --version,确保它也能正常工作。

2.2 代码编辑器与IDE选型

写代码需要一个顺手的编辑器。这里有几个主流选择:

  • PyCharm:功能强大的专业IDE,对Python支持极好,有智能提示、调试、项目管理等全套功能。社区版免费,对新手非常友好。缺点是启动稍慢,对电脑配置有点要求。
  • VS Code:轻量级但功能强大的编辑器,通过安装Python扩展插件可以获得不输IDE的体验。启动快,插件生态丰富,自定义程度高。是目前非常流行的选择。
  • Sublime Text / Atom:轻量级编辑器的代表,需要自己配置较多插件才能达到较好的开发体验,更适合喜欢折腾的开发者。

对于超级玛丽这个项目,我推荐使用VS Code。它平衡了易用性和灵活性,配置Python环境也很简单。在VS Code中安装官方“Python”扩展后,它就能自动识别Python解释器,并提供代码补全、 linting(代码检查)和调试支持。

2.3 核心依赖库:Pygame详解

游戏开发的核心是pygame库。它不是Python标准库的一部分,所以需要用pip安装。在命令行中输入:

pip install pygame

这条命令会从Python的官方包索引(PyPI)下载并安装pygame及其依赖。

为什么是Pygame?因为它是一个专为2D游戏开发设计的库,提供了绘制图形、播放声音、处理键盘鼠标输入、检测碰撞等游戏开发所需的几乎所有基础功能。它底层基于SDL库,效率足够我们开发这种量级的小游戏。相比起更复杂的游戏引擎(如Unity, Godot),Pygame更轻量,让你能接触到更底层的游戏循环逻辑,学习价值更大。

安装完成后,可以写一个简单的测试脚本来验证:

import pygame pygame.init() screen = pygame.display.set_mode((800, 600)) print(“Pygame初始化成功!”) pygame.quit()

运行这个脚本,如果弹出一个黑色窗口并打印成功信息,说明pygame安装无误。

2.4 项目结构与资源管理

在开始写代码前,先规划好项目目录结构。一个清晰的结构能让开发过程井井有条。建议创建如下目录:

super_mario_python/ ├── main.py # 游戏主程序入口 ├── settings.py # 游戏全局配置(如屏幕尺寸、颜色、重力常数) ├── sprites/ # 存放所有精灵类 │ ├── __init__.py │ ├── mario.py # 马里奥角色类 │ ├── enemy.py # 敌人(蘑菇怪、乌龟)类 │ └── block.py # 砖块、水管、地面等静态物体类 ├── levels/ # 关卡数据文件(可以用列表或字典定义关卡布局) │ └── level_01.py ├── assets/ # 资源文件 │ ├── images/ # 图片(马里奥、敌人、砖块、背景) │ ├── sounds/ # 音效(跳跃、吃金币、死亡) │ └── fonts/ # 字体文件 └── README.md # 项目说明文档

把图片、声音等资源文件放在独立的assets目录下,是为了与代码分离,方便管理和替换。在代码中,使用相对路径来加载这些资源,例如pygame.image.load(‘assets/images/mario.png’)

3. 游戏核心架构与主循环设计

游戏程序的核心是一个无限循环,称为游戏主循环。每一轮循环,我们做四件事:处理用户输入、更新游戏状态、绘制当前画面、控制循环速度。Pygame为我们管理这个循环提供了框架。

3.1 游戏初始化与窗口创建

一切从初始化开始。在main.py中,我们首先导入必要的模块,并初始化pygame。

import pygame import sys from settings import SCREEN_WIDTH, SCREEN_HEIGHT, FPS from sprites.mario import Mario # 导入其他需要的模块… pygame.init() # 初始化pygame所有模块 pygame.mixer.init() # 初始化声音模块 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # 创建游戏窗口 pygame.display.set_caption(“Python Super Mario”) # 设置窗口标题 clock = pygame.time.Clock() # 创建时钟对象,用于控制帧率

SCREEN_WIDTHSCREEN_HEIGHT我定义在settings.py里,比如(800, 600)。使用clock对象是控制游戏速度的关键,它能确保游戏在不同性能的电脑上以基本相同的速度运行。

3.2 理解游戏状态与场景管理

一个完整的游戏通常有多个状态,比如开始菜单、游戏进行中、暂停、游戏结束等。对于初版超级玛丽,我们可以先实现两个状态:PLAYINGGAME_OVER。用一个状态变量来管理当前场景。

# 在settings.py中定义状态常量 PLAYING = 0 GAME_OVER = 1 # 在main.py主循环中 game_state = PLAYING while True: # 游戏主循环 if game_state == PLAYING: # 处理游戏进行中的逻辑 pass elif game_state == GAME_OVER: # 处理游戏结束的逻辑(显示分数,等待重启) pass

这种简单的状态机模式,让代码逻辑清晰,便于后续扩展更多状态(如关卡选择、胜利画面)。

3.3 事件处理:让游戏响应你的操作

在主循环中,我们需要处理各种事件,比如退出事件和键盘事件。

for event in pygame.event.get(): # 遍历当前帧的所有事件 if event.type == pygame.QUIT: # 如果点击了窗口关闭按钮 pygame.quit() sys.exit() # 彻底退出程序 if game_state == PLAYING: if event.type == pygame.KEYDOWN: # 按键按下事件 if event.key == pygame.K_SPACE: # 让马里奥跳跃 player.jump() elif event.key == pygame.K_RIGHT: player.move_right = True elif event.type == pygame.KEYUP: # 按键松开事件 if event.key == pygame.K_RIGHT: player.move_right = False

这里有一个关键细节:对于移动(如持续按住右键),我们通常不在KEYDOWN事件里直接改变位置,而是设置一个标志位(如move_right = True)。然后在游戏状态更新的部分,根据这个标志位来持续移动角色。这样做的原因是,KEYDOWN事件在按键按住时可能只触发一次或按固定间隔触发,不够平滑。而通过标志位在每帧更新,移动会更流畅。

3.4 游戏逻辑更新与画面渲染

处理完输入后,就该更新游戏世界了。这包括更新所有精灵的位置、状态,检查碰撞等。

# 更新部分 all_sprites.update() # 调用精灵组中所有精灵的update方法 # 碰撞检测(示例:马里奥与敌人的碰撞) hits = pygame.sprite.spritecollide(player, enemies_group, False) if hits: # 如果马里奥从上方踩中敌人 if player.velocity_y > 0 and player.rect.bottom < hits[0].rect.centery: player.bounce() # 马里奥小跳一下 hits[0].kill() # 敌人消失 score += 100 else: # 否则,马里奥受伤或死亡 game_state = GAME_OVER

更新完状态,就该把一切都画出来了。渲染的顺序很重要,先画的在底层,后画的在顶层。

# 渲染部分 screen.fill((135, 206, 235)) # 用天蓝色填充背景,模拟天空 background_group.draw(screen) # 绘制背景(如云朵、山) platforms_group.draw(screen) # 绘制地面和砖块 all_sprites.draw(screen) # 绘制所有活动精灵(马里奥、敌人、金币) # 绘制UI(如分数、生命值) font = pygame.font.Font(None, 36) score_text = font.render(f‘Score: {score}’, True, (255, 255, 255)) screen.blit(score_text, (10, 10)) pygame.display.flip() # 将绘制好的画面更新到屏幕上

最后,用clock.tick(FPS)来控制游戏循环的速度。FPS(Frames Per Second)我设置为60,意味着程序会努力每秒循环60次。clock.tick(60)会在每次循环末尾计算时间,如果本次循环用时少于1/60秒,它会暂停一下,确保帧率稳定。

4. 精灵系统与核心角色实现

在Pygame中,一切可以移动、可以交互的物体,通常都继承自pygame.sprite.Sprite类,我们称之为“精灵”。精灵类封装了图像(image)、位置矩形(rect)和更新逻辑(update)。

4.1 马里奥角色类深度解析

马里奥是我们的主角,他的行为最复杂。我们来创建一个Mario类。

# sprites/mario.py import pygame from settings import GRAVITY, PLAYER_ACC, PLAYER_FRICTION, JUMP_POWER class Mario(pygame.sprite.Sprite): def __init__(self, game, x, y): super().__init__() self.game = game # 传入游戏主对象,方便访问其他精灵组 self.load_images() # 加载各种状态下的图片 self.current_frame = 0 self.last_update = 0 self.image = self.standing_frames[0] # 初始图像 self.rect = self.image.get_rect() self.rect.center = (x, y) # 物理属性 self.pos = pygame.math.Vector2(x, y) # 使用浮点数向量记录精确位置 self.vel = pygame.math.Vector2(0, 0) # 速度向量 self.acc = pygame.math.Vector2(0, 0) # 加速度向量 # 状态标志 self.jumping = False self.facing_right = True self.on_ground = False def load_images(self): # 这里加载马里奥站立、跑动、跳跃的精灵图(spritesheet) # 通常需要从一张大图上截取多个小图,形成一个图片列表 self.standing_frames = […] self.walk_frames_r = […] self.walk_frames_l = […] # 向左走的图片是向右走的水平翻转 self.jump_frame = […] def update(self): self.animate() # 处理动画帧切换 self.apply_gravity() # 应用重力 self.calc_movement() # 计算基于加速度和摩擦力的移动 # 位置更新与碰撞检测(与平台) self.pos.x += self.vel.x self.rect.x = self.pos.x self.collide_with_platforms(‘x’) # 检测水平方向碰撞 self.pos.y += self.vel.y self.rect.y = self.pos.y self.collide_with_platforms(‘y’) # 检测垂直方向碰撞 # 如果掉出屏幕底部,游戏结束 if self.rect.top > self.game.screen_height: self.game.game_over() def calc_movement(self): # 根据按键状态设置水平加速度 self.acc = pygame.math.Vector2(0, GRAVITY) # 垂直方向始终受重力 keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: self.acc.x = -PLAYER_ACC self.facing_right = False if keys[pygame.K_RIGHT]: self.acc.x = PLAYER_ACC self.facing_right = True # 应用摩擦力(仅当水平方向没有输入时) self.acc.x += self.vel.x * PLAYER_FRICTION # 运动学公式:v = v0 + a*t (这里t=1帧) self.vel += self.acc # 限制最大速度,防止因加速度累积过快 self.vel.x = max(-PLAYER_MAX_SPEED, min(PLAYER_MAX_SPEED, self.vel.x)) def jump(self): # 只有在站在地面上时才能跳 if self.on_ground: self.vel.y = -JUMP_POWER # 给一个向上的初速度 self.jumping = True self.on_ground = False # 可以在这里播放跳跃音效 # self.game.jump_sound.play()

这里有几个关键点

  1. 使用浮点数向量(Vector2)记录位置rect的坐标是整数,直接用它来做物理计算(尤其是速度、加速度)会导致移动不流畅,有“卡顿”感。用浮点数pos记录精确位置,每帧更新后再赋值给rect,移动会平滑很多。
  2. 分离轴碰撞检测:在update中,我们先更新x轴位置并检测水平碰撞,再更新y轴位置并检测垂直碰撞。这样做可以避免在斜角碰撞时角色被“卡住”的问题。
  3. 动画状态机animate()方法里要根据vel.x,on_ground,jumping等状态,决定当前显示哪一组动画的哪一帧,并控制帧与帧之间的切换速度。

4.2 敌人与NPC行为逻辑

敌人(比如蘑菇怪Goomba)的行为相对简单:沿着地面水平移动,碰到障碍物或悬崖就回头。

# sprites/enemy.py class Goomba(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = pygame.image.load(‘assets/images/goomba.png’).convert_alpha() self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.direction = 1 # 1表示向右,-1表示向左 self.speed = 2 self.walk_count = 0 # 用于简单的悬崖检测 def update(self): # 水平移动 self.rect.x += self.direction * self.speed # 简单的悬崖和墙壁检测(这是一个简化版) # 在实际项目中,可能需要用射线检测或碰撞检测来实现更精确的逻辑 self.walk_count += 1 if self.walk_count > 30: # 每走30帧检查一次前方是否为空 # 假设前方一个身位没有平台,就回头 if not self.check_platform_ahead(): self.direction *= -1 self.walk_count = 0 def check_platform_ahead(self): # 创建一个检测点,在敌人前方、脚下位置 check_x = self.rect.centerx + (self.rect.width // 2 + 5) * self.direction check_y = self.rect.bottom + 5 # 这里需要访问游戏主类中的平台精灵组,判断该点是否接触平台 # 简化起见,返回True或False return True

对于更复杂的敌人(比如会飞的或者会发射子弹的),就需要更复杂的状态机和行为树,但基本框架是一样的:在update()里根据自身状态和周围环境,决定下一步行动。

4.3 静态物体与场景搭建

砖块、水管、地面这些是静态精灵,它们通常没有复杂的update逻辑,主要作用就是提供碰撞体积和视觉表现。

# sprites/block.py class Brick(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = pygame.image.load(‘assets/images/brick.png’).convert() self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class QuestionBox(pygame.sprite.Sprite): def __init__(self, x, y, item_type=‘coin’): super().__init__() self.frames = […] # 加载问号砖的动画帧 self.image = self.frames[0] self.rect = self.image.get_rect(topleft=(x, y)) self.item_type = item_type # 里面藏的是金币、蘑菇还是星星 self.is_hit = False # 是否被顶过 self.animation_timer = 0 def update(self): if self.is_hit: # 被顶后,播放变成空砖的动画,并生成道具 self.animate_hit() else: # 未被顶时,播放闪烁的问号动画 self.animate_blink() def hit(self): if not self.is_hit: self.is_hit = True # 生成道具的逻辑 if self.item_type == ‘coin’: self.game.spawn_coin(self.rect.centerx, self.rect.top) self.game.add_score(100)

在关卡设计文件(如levels/level_01.py)中,我们可以用一个列表或字典来定义这些静态物体的位置和类型,然后在游戏初始化时批量创建它们。这种方式将关卡数据与代码逻辑分离,方便后期修改和设计新关卡。

5. 碰撞检测与物理模拟的实现细节

碰撞检测是游戏开发中的核心难题,处理不好就会让角色“穿墙”、“抖动”或者“卡住”。我们的目标是实现既准确又高效的碰撞响应。

5.1 基于矩形与像素的碰撞检测

Pygame提供了spritecollide,groupcollide,spritecollideany等函数进行矩形碰撞检测,非常方便。对于马里奥和平台的碰撞,我们通常使用矩形检测。

def collide_with_platforms(self, direction): # 假设self.game.platforms是所有平台的精灵组 hits = pygame.sprite.spritecollide(self, self.game.platforms, False) if hits: if direction == ‘x’: # 水平方向碰撞 if self.vel.x > 0: # 向右移动时撞到 self.rect.right = hits[0].rect.left self.pos.x = self.rect.x # 同步浮点位置 elif self.vel.x < 0: # 向左移动时撞到 self.rect.left = hits[0].rect.right self.pos.x = self.rect.x self.vel.x = 0 # 水平速度归零 if direction == ‘y’: # 垂直方向碰撞 if self.vel.y > 0: # 向下移动时撞到(即落到平台上) self.rect.bottom = hits[0].rect.top self.pos.y = self.rect.y self.vel.y = 0 self.on_ground = True # 标记为在地面上 self.jumping = False elif self.vel.y < 0: # 向上移动时撞到(即顶到头) self.rect.top = hits[0].rect.bottom self.pos.y = self.rect.y self.vel.y = 0

这里有个非常重要的技巧:在垂直碰撞处理中,我们通过判断self.vel.y的正负来区分是“落地”还是“顶头”。只有vel.y > 0(即正在下落)时撞到平台上方,才将on_ground设为True,这确保了马里奥从侧面或下方接触平台时不会错误地被判定为“站在地上”。

对于敌人与马里奥的碰撞,有时需要更精确的判断(比如踩头)。我们可以结合矩形检测和位置判断:

# 在Mario的update或专门的碰撞检测函数中 hits = pygame.sprite.spritecollide(self, self.game.enemies, False) for enemy in hits: # 判断马里奥的脚底是否在敌人的上半部分 if self.vel.y > 0 and self.rect.bottom < enemy.rect.centery: # 踩中敌人 self.bounce() # 马里奥反弹一下 enemy.die() # 敌人死亡 else: # 侧面或头顶碰到敌人,马里奥受伤 self.get_hurt()

5.2 重力、跳跃与运动手感调优

物理手感是游戏好不好玩的关键。超级玛丽中的跳跃感觉非常独特,它并不是简单的匀加速运动。

  • 重力加速度:在settings.py中定义一个GRAVITY常量,比如0.8。这个值会影响下落速度。值越大,下落越快,手感越“重”。
  • 跳跃初速度JUMP_POWER决定了按下跳跃键时,给予马里奥向上的初始速度,比如-15(因为屏幕坐标系y轴向下为正,所以向上是负值)。
  • 跳跃手感:原版超级玛丽中,按住跳跃键的时间长短会影响跳跃高度。我们可以通过一个技巧来模拟:在jump方法中启动一个计时器,如果按键一直按住且计时器未超时,则持续给一个较小的向上加速度;一旦松开按键或超时,就取消这个加速度,让重力主导。这比单纯给一个固定初速度要复杂,但手感更接近原版。
  • 空中控制:马里奥在空中的水平操控性应该比在地面差。可以在calc_movement中根据on_ground状态来调整水平加速度PLAYER_ACC和摩擦力PLAYER_FRICTION的值。

调优这些参数是一个反复测试的过程。我的经验是,先设定一组看起来合理的值,然后边玩边调,直到跳跃的弧线、滞空时间、移动的惯性都让你觉得“舒服”为止。

5.3 摄像机视角跟随与场景滚动

当关卡宽度大于屏幕宽度时,我们需要一个“摄像机”来跟随马里奥移动。实现原理不是真的移动所有游戏物体,而是改变我们绘制这些物体时的偏移量

# 在游戏主类中 class Game: def __init__(self): # … self.camera = pygame.math.Vector2(0, 0) self.camera_smoothness = 0.1 # 摄像机跟随的平滑系数 def update_camera(self): # 目标是将马里奥置于屏幕中央偏左的位置(让玩家看到前方) target_x = self.player.rect.centerx - SCREEN_WIDTH // 2 # 使用线性插值让摄像机移动更平滑,避免生硬跳动 self.camera.x += (target_x - self.camera.x) * self.camera_smoothness # 限制摄像机范围,不让它看到关卡边界之外 self.camera.x = max(0, min(self.camera.x, LEVEL_WIDTH - SCREEN_WIDTH)) def draw(self, screen): # 绘制所有精灵时,减去摄像机偏移量 for sprite in self.all_sprites: screen.blit(sprite.image, (sprite.rect.x - self.camera.x, sprite.rect.y))

这样,在绘制每个精灵时,其rect.x减去camera.x,就实现了场景的滚动。camera_smoothness参数控制跟随的紧密度,值越小(如0.05),跟随越平滑但有延迟;值越大(如0.5),跟随越紧但可能抖动。

6. 资源管理、音效与游戏状态完善

一个完整的游戏体验离不开图像、声音和流畅的状态切换。

6.1 图像资源加载与优化

对于像素风格的游戏,图像资源通常不大,但加载和管理也有讲究。

  • 转换(Convert):使用pygame.image.load(‘path.png’).convert()可以显著提高绘制速度。如果图片有透明背景,则使用.convert_alpha()
  • 精灵图(Spritesheet):将角色所有动画帧放在一张大图上,能减少文件数量,也符合传统游戏开发习惯。你需要写一个工具函数来从大图上截取小图。
def load_spritesheet(self, filename, frame_width, frame_height): spritesheet = pygame.image.load(filename).convert_alpha() sheet_rect = spritesheet.get_rect() frames = [] for y in range(0, sheet_rect.height, frame_height): for x in range(0, sheet_rect.width, frame_width): rect = pygame.Rect(x, y, frame_width, frame_height) frame_image = pygame.Surface(rect.size, pygame.SRCALPHA) frame_image.blit(spritesheet, (0, 0), rect) frames.append(frame_image) return frames
  • 图像缩放:如果原图尺寸和游戏内显示尺寸不一致,可以在加载后使用pygame.transform.scale()进行一次性缩放,避免每帧都缩放消耗性能。

6.2 音效与背景音乐的集成

音效能极大增强游戏的沉浸感。Pygame的mixer模块处理声音很简单。

# 在游戏初始化时加载音效 self.jump_sound = pygame.mixer.Sound(‘assets/sounds/jump.wav’) self.coin_sound = pygame.mixer.Sound(‘assets/sounds/coin.wav’) self.death_sound = pygame.mixer.Sound(‘assets/sounds/death.wav’) # 播放背景音乐(音乐文件通常较大,用Music模块) pygame.mixer.music.load(‘assets/music/overworld.mp3’) pygame.mixer.music.set_volume(0.5) # 设置音量 pygame.mixer.music.play(-1) # -1表示循环播放 # 在需要时播放音效 def collect_coin(self): self.score += 100 self.coin_sound.play() # 播放吃金币音效

注意:音效文件不宜过长,最好是几秒钟的短音频。格式推荐使用.wav.ogg,它们兼容性好,加载快。背景音乐可以用.mp3.ogg

6.3 游戏状态、分数与生命值管理

我们需要一个中心化的游戏状态管理器。可以在主游戏类里维护这些信息。

class Game: def __init__(self): self.score = 0 self.lives = 3 self.coins = 0 self.time_remaining = 400 # 关卡倒计时 self.game_state = ‘PLAYING’ # 也可以是 ‘MENU’, ‘PAUSED’, ‘GAME_OVER’ def update(self): if self.game_state == ‘PLAYING’: self.time_remaining -= 1 / FPS # 假设FPS=60,每秒减1 if self.time_remaining <= 0: self.time_out() def draw_ui(self, screen): # 绘制分数、生命、金币数、时间 font = pygame.font.Font(‘assets/fonts/prstart.ttf’, 24) # 使用点阵字体更有感觉 score_text = font.render(f‘MARIO {self.score:06d}’, True, (255, 255, 255)) screen.blit(score_text, (50, 20)) # … 绘制其他UI def game_over(self): if self.lives > 0: self.lives -= 1 # 重置角色位置,重新开始当前关卡或检查点 self.reset_level() else: self.game_state = ‘GAME_OVER’ # 显示“GAME OVER”画面,等待玩家按键重启

UI的绘制要放在所有游戏精灵绘制之后,确保UI在最上层。字体文件可以放在assets/fonts/目录下。

7. 性能优化、打包与扩展思路

当游戏功能基本完成后,我们还需要考虑性能、分发和未来扩展。

7.1 渲染优化与脏矩形更新

默认情况下,pygame.display.flip()pygame.display.update()会更新整个屏幕。如果游戏画面复杂,这可能会成为性能瓶颈。一种优化方法是使用脏矩形更新,即只更新屏幕上发生变化的部分。

# 在绘制循环中 changed_rects = [] # 记录所有需要更新的矩形区域 # 绘制背景(可能只需要更新一部分) changed_rects.append(screen.blit(background, (0, 0))) # 绘制每个移动的精灵,并记录其新旧位置 for sprite in moving_sprites: old_rect = sprite.old_rect # 需要你在精灵类中记录上一帧的位置 new_rect = sprite.rect # 将精灵旧位置和新位置所在的区域都加入更新列表 changed_rects.append(old_rect) changed_rects.append(screen.blit(sprite.image, new_rect.rect)) sprite.old_rect = new_rect.copy() # 更新旧位置记录 # 只更新这些变化的区域 pygame.display.update(changed_rects)

对于像超级玛丽这样的横向卷轴游戏,如果摄像机在移动,几乎整个屏幕都在变,脏矩形优化的收益可能不大。但对于静态背景居多的场景,优化效果明显。

7.2 使用PyInstaller打包为可执行文件

你肯定希望把你的成果分享给不会安装Python的朋友。PyInstaller可以将Python脚本打包成独立的可执行文件(.exe, .app等)。

  1. 安装:pip install pyinstaller
  2. 基础打包:在项目根目录下执行pyinstaller --onefile --windowed main.py
    • --onefile: 打包成单个exe文件。
    • --windowed: 运行时不显示命令行窗口(对于游戏是必须的)。
  3. 处理资源文件:默认情况下,PyInstaller不会自动包含你的assets文件夹。你需要创建一个.spec文件,或者在命令中通过--add-data参数指定。
    pyinstaller --onefile --windowed --add-data “assets;assets” main.py
    (在Windows上用分号;分隔源路径和目标路径,在Mac/Linux上用冒号:
  4. 在代码中,你需要修改资源加载路径,使其在打包后也能正确找到文件。可以使用sys._MEIPASS属性。
    import sys import os def resource_path(relative_path): “”” 获取资源的绝对路径。在开发环境和打包后都能工作 “”” try: # PyInstaller创建的临时文件夹 base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(“.”) return os.path.join(base_path, relative_path) # 加载资源时 image_path = resource_path(‘assets/images/mario.png’)

打包后的exe文件会比较大(因为包含了Python解释器和依赖库),但可以独立运行。

7.3 项目扩展与进阶方向

完成基础版本后,你可以尝试以下方向来深化这个项目:

  • 更多关卡与地图编辑器:设计多个不同主题的关卡(地下、水下、城堡)。甚至可以开发一个简单的图形化地图编辑器,用鼠标摆放砖块和敌人,然后导出为关卡数据文件。
  • 更多敌人与道具:实现会飞的乌龟、会发射子弹的食人花、吃了会变大的蘑菇、无敌星星等。
  • 粒子效果:当砖块被顶碎、敌人被踩扁时,添加一些飞溅的碎片粒子,增强表现力。
  • 状态保存与读取:实现游戏进度的保存,比如使用picklejson模块将当前关卡、分数、生命值保存到文件。
  • 网络联机(高级):使用socket库尝试实现双人同屏游戏,或者简单的分数排行榜功能。

这个项目就像一颗种子,你实现的基础框架(游戏循环、精灵系统、碰撞检测、状态管理)是它的根。在此基础上,你可以尽情生长出各种有趣的枝丫,每一次扩展都是对编程和游戏设计更深的理解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/25 17:14:04

独家披露:某独角兽公司竞品监控平台日均处理2.4亿条非结构化数据的技术栈选型逻辑(含LLM微调参数清单)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI自动化 竞品监控 在高度动态的数字市场中&#xff0c;实时、精准、可扩展的竞品监控能力已成为企业战略决策的核心基础设施。传统人工爬取与Excel比对方式已无法应对每日数万条价格变动、促销策略更新、评论…

作者头像 李华
网站建设 2026/7/25 17:06:47

Loopweave:智能音频无缝循环提取算法原理与实践指南

如果你曾经尝试从音频文件中提取循环片段&#xff0c;可能会遇到这样的困扰&#xff1a;无论怎么调整起点和终点&#xff0c;循环播放时总会出现明显的卡顿或跳变。这种不自然的过渡不仅影响听觉体验&#xff0c;也让音乐制作、游戏音效设计和多媒体项目显得不够专业。传统解决…

作者头像 李华
网站建设 2026/7/25 17:06:18

吸塑机远程监控运维管理平台方案

吸塑机作为塑料加工行业的核心设备&#xff0c;广泛应用于包装、电子、汽车、医疗等多个领域的吸塑成型工艺中。然而&#xff0c;随着生产规模的扩大和工厂布局的分散&#xff0c;传统的人工巡检、现场维护模式逐渐暴露出响应滞后、运维成本高、设备利用率低等问题。尤其是在多…

作者头像 李华
网站建设 2026/7/25 17:04:24

Pearcleaner:彻底释放Mac磁盘空间的智能清理解决方案

Pearcleaner&#xff1a;彻底释放Mac磁盘空间的智能清理解决方案 【免费下载链接】Pearcleaner A free, source-available and fair-code licensed mac app cleaner 项目地址: https://gitcode.com/gh_mirrors/pe/Pearcleaner 你是否曾卸载macOS应用后发现磁盘空间并没有…

作者头像 李华
网站建设 2026/7/25 17:02:06

Java程序员转战AI大模型开发:收藏这份实战指南,小白也能轻松入门!

本文分享了Java程序员从传统后端开发转向AI应用开发的实战经验&#xff0c;揭示了AI应用开发并非简单的API调用&#xff0c;而是涉及系统工程&#xff0c;包括文档解析、切分策略、检索方案、精排调优、Agent设计、MCP集成等复杂环节。文章强调Java程序员的工程化能力在这一领域…

作者头像 李华