news 2026/9/3 4:20:47

Pygame实战:开发一个皮卡丘主题桌面小游戏(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pygame实战:开发一个皮卡丘主题桌面小游戏(附完整代码)

Python 学到能写爬虫、能调 Pandas 之后,很多人会陷入一种奇怪的尴尬:语法都认识,示例代码能跑,但让自己从零做一个“拿得出手”的东西,却完全不知道从哪里下手。如果你正处在这个阶段,我建议你花一个下午做一个皮卡丘主题的桌面小游戏。项目名就叫《皮卡丘,我们来咯》。

我的判断很直接:Pygame 不是生产级游戏引擎,但它是 Python 初学者完成“从语法到项目”跨越时性价比最高的工具之一。它把函数封装、面向对象、事件机制、坐标运算、碰撞检测这些平时散落的知识点,全部缝进了一个有角色、有反馈、有分数的程序里。改一个数值,屏幕上立刻有视觉变化;写错一个坐标,角色直接飞出窗口。这种强反馈循环,是刷一百道语法题都换不来的。

这篇文章的落点很具体:先讲清楚 Pygame 的核心概念,再带你从零写完一个可玩的“皮卡丘接精灵球”小游戏,包含完整代码、运行验证、常见排错和工程化建议。读完你不仅能跑通这个项目,还能顺着扩展思路把它改成自己的作品。

1. 为什么写一个皮卡丘小游戏,而不是继续刷语法题

很多 Python 教程的节奏是:字符串、列表、字典、函数、类、文件操作……每一步都有例子,但例子之间没有关联。学完后面忘掉前面,最大的原因不是你记忆力差,而是知识没有形成一个可以运行的整体。游戏开发恰好能把大部分 Python 核心语法串起来:类的继承用来管理角色和道具,函数用来封装绘制逻辑,字典和列表用来保存游戏状态,文件读写可以用来做存档,异常处理用来兜底崩溃场景。

选择皮卡丘主题也有现实考量。做项目最怕中途放弃,而一个你有情感认同的主题,会显著拉高你把它做完的概率。看到黄色小怪兽在屏幕上移动、碰到精灵球时闪出电光,这种正反馈比“成功打印一行 Hello World”强太多了。而且皮卡丘的辨识度高,拿去给同学演示,对方一眼就知道你在做什么。

不过要说清楚一点:Pygame 适合入门和快速原型,但不适合做复杂物理、3D 渲染或大型商业游戏。它的定位是“让你理解游戏底层是怎么运转的”。你用 Pygame 写完一个游戏,再去学 Unity、Godot 或者 Cocos,会发现很多概念是相通的:场景循环、精灵渲染、碰撞检测、输入响应。先用 Pygame 把游戏主循环刻进脑子里,后面的路会顺很多。

还有一个容易被忽略的点:皮卡丘是受版权保护的角色形象。本文不会提供任何官方素材文件,而是用 Pygame 绘图函数生成一个“程序化占位版皮卡丘”。这个占位角色足以承载全部游戏逻辑教学,如果你之后想发布或商用,记得替换成自己原创的美术素材,这是开发者应该有的版权意识。

2. Pygame 核心概念:窗口、事件循环、精灵与碰撞

写 Pygame 之前,必须先建立几个核心概念。不需要背下来,但至少要理解它们在游戏循环中的角色。

2.1 坐标系:左上角是原点

Pygame 的窗口坐标系和我们上学时学的笛卡尔坐标系不一样。原点在窗口左上角,x 轴向右增大,y 轴向下增大。也就是说,角色的 y 坐标越大,它在屏幕上越靠下。很多新手一上来就搞反方向,结果按“上箭头”时角色往下跑。

游戏中的位置和碰撞不处理像素坐标,而是处理矩形。每个可见对象都能用一个矩形包起来,这个矩形就是 Rect 对象,它由xywidthheight四个值描述。Pygame 的碰撞检测也基于 Rect 之间的相交判断,这比逐像素检测快得多。

2.2 Surface 与 blit

一个 Surface 就是一块内存中的画布。整个窗口是一个 Surface,每个贴到屏幕上的图片、文字,也都可以是一个 Surface。绘制游戏对象的标准流程是:先在独立 Surface 上画好内容,再调用screen.blit(surface, 坐标)把它“贴”到窗口上。blit 这个名字来自 Bit Block Transfer,本质是一块像素数据到另一块的复制。

2.3 事件循环:游戏不是从上往下执行完就结束

普通 Python 脚本从上往下执行完就退出,游戏程序不一样。它需要持续运行,每秒钟重新绘制几十次画面,同时不断处理玩家的按键、鼠标和窗口关闭请求。这个持续运行的循环叫主循环,也叫事件循环。

游戏每一帧大概要做四件事:处理输入事件、更新游戏状态、绘制画面、控制帧率。Pygame 中pygame.event.get()负责取出当前帧发生的所有事件,比如点击了关闭按钮、按下了某个键。而像方向键这种需要持续按住才能移动的操作,更适合用pygame.key.get_pressed()获取按键状态。

2.4 帧率与 Clock

游戏画面是快速切换的静态帧。帧率 FPS 表示每秒刷新多少次,通常设置为 60。Pygame 的pygame.time.Clock()可以控制循环速度,clock.tick(60)表示这一帧最多执行 1/60 秒,剩余时间让程序休眠。没有这一步,游戏会在高配电脑上快得没法玩。

2.5 Sprite 与 Group

Pygame 提供了一个轻量级的精灵基类pygame.sprite.Sprite。把游戏对象写成 Sprite 子类后,每个对象都有imagerect两个属性,一个管绘制内容,一个管位置和碰撞。再用Group把多个精灵放在一起,就能一次性完成更新和绘制。

传统控制台程序和 Pygame 游戏程序的执行方式有本质区别,可以参考下面的对比:

维度传统控制台程序Pygame 游戏程序
执行方式顺序执行,跑完退出主循环驱动,每帧重复执行
用户输入input() 阻塞等待事件队列,非阻塞轮询
状态变化变量直接打印修改对象属性,重绘画面
时间控制Clock 控制帧率
程序退出执行到最后一行收到 QUIT 事件或 running=False

3. 环境准备与项目结构

本文示例代码面向 Python 3.10 及以上版本,Pygame 2.x。具体版本以你本机环境为准,核心思路不变。

3.1 安装 Pygame

建议先创建虚拟环境,再安装依赖。Windows 命令行或 macOS/Linux 终端执行:

python -m venv venv

激活虚拟环境。Windows:

venv\Scripts\activate

macOS / Linux:

source venv/bin/activate

然后安装 Pygame:

pip install pygame

如果网速较慢,可以换国内镜像源:

pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后验证:

python -c "import pygame; print(pygame.version.ver)"

能输出版本号说明安装成功。

3.2 项目目录结构

我建议从一开始就按可分文件的方式组织代码,而不是把所有逻辑塞进一个文件。本文的项目结构如下:

pikachu-game/ ├── config.py # 游戏配置常量 ├── sprites.py # 角色与道具精灵类 ├── main.py # 主程序入口 ├── requirements.txt # 依赖清单 └── README.md # 项目说明

config.py集中管理窗口尺寸、颜色、速度等常量,这样改配置时不需要翻游戏逻辑代码。sprites.py只放角色类。main.py里放游戏主类和启动入口。项目规模不大时,这种分层看起来“多此一举”,但等你想加音效、加关卡、加开始画面时,会发现它非常好扩展。

4. 核心流程拆解:从零搭建“皮卡丘,我们来咯”

正式写代码之前,先把游戏流程拆成几个清晰的步骤。这一步很重要,如果一开始就埋头写代码,很容易写着写着把自己绕晕。

4.1 初始化窗口与游戏配置

Pygame 使用的第一步是调用pygame.init(),它会初始化显示器、音频、事件等所有模块。然后用pygame.display.set_mode((宽, 高))创建窗口,用set_caption设置标题。窗口创建完成后,游戏就有了一个可以绘制的画布。

4.2 设计精灵类

整个游戏有两个核心精灵:玩家控制的皮卡丘,以及需要去碰触的精灵球。我把它们都设计为pygame.sprite.Sprite的子类。

皮卡丘这个类需要实现:

  • __init__中生成自己的imagerect
  • 提供一个update方法,根据按键状态更新坐标
  • clamp_ip把角色限制在窗口范围内,防止跑出屏幕

精灵球类相对简单,只需要:

  • 随机出现在屏幕内部
  • 在更新时检查是否存活过久,过期后自动消失

这样设计的好处是:主循环只需要调用all_sprites.update()all_sprites.draw(screen),所有类型的对象都会自动更新和绘制,完全不用在主循环里写一大堆针对单个对象的判断。

4.3 玩家移动

玩家移动使用pygame.key.get_pressed(),它返回一个元组,记录每个键盘按键当前是否被按下。在update方法里,每一帧都检查方向键和 WASD 键的按下状态,然后累加位置偏移。

为什么不用事件循环里的KEYDOWN来处理移动?因为KEYDOWN只在按键刚按下去的瞬间触发一次。如果你想实现“按住方向键持续移动”,用事件循环就需要自己维护按键状态集合,非常麻烦。key.get_pressed()直接返回每一帧的实时按键快照,处理持续移动最合适。

4.4 精灵球生成与碰撞

精灵球的生成需要一个节流器。最简单的做法是记录上一次生成精灵球的时间,当前时间减去上次时间大于设定间隔时,生成一个新的精灵球。

碰撞检测直接用pygame.sprite.spritecollide(player, pokeballs, True)。第三个参数为 True,表示碰撞后自动删除被碰到的精灵球。这个函数会返回所有被碰撞到的精灵球列表,我们遍历列表累加分数即可。

4.5 文字提示与游戏反馈

为了让游戏更有“皮卡丘,我们来咯”的主题感,每次碰到精灵球时,在窗口顶部显示一句“皮卡丘,我们来咯!”。实现方式是在碰撞发生时记录当前时间,绘制画面时判断这句提示文字是否还在显示时间内。这比直接打印到终端更有游戏反馈感。

4.6 主循环组装

最后在主循环里把以上所有模块串起来。每一帧执行:

  1. 处理退出事件
  2. 按节流规则生成精灵球
  3. 更新玩家和精灵球状态
  4. 检测碰撞并更新分数
  5. 绘制背景、精灵、文字
  6. 调用clock.tick(60)控制帧率

主循环是一个游戏程序的心脏,所有逻辑都挂在这条时间轴上执行。

5. 完整可运行代码实现

下面直接给出完整代码。按顺序创建文件即可,不需要额外安装其他库。

5.1 依赖清单requirements.txt

在项目根目录创建:

pygame>=2.0

5.2 配置文件config.py

所有可调整的常量都集中在这里:

# 文件路径:config.py # 窗口尺寸 WIDTH = 900 HEIGHT = 640 # 帧率 FPS = 60 # 游戏标题 GAME_TITLE = "皮卡丘,我们来咯" # 精灵球生成间隔(毫秒) SPAWN_INTERVAL = 1200 # 皮卡丘移动速度 PLAYER_SPEED = 5 PLAYER_BOOST_SPEED = 8 # 颜色定义 YELLOW = (255, 215, 0) DARK = (45, 45, 45) RED = (220, 50, 50) WHITE = (245, 245, 245) BLUE = (70, 130, 240) BG_COLOR = (120, 190, 90) TEXT_COLOR = (35, 35, 35)

5.3 精灵与绘制函数sprites.py

这个文件包含皮卡丘和精灵球的绘制函数、精灵类。

# 文件路径:sprites.py import random import pygame from config import ( BLUE, DARK, HEIGHT, PLAYER_BOOST_SPEED, PLAYER_SPEED, RED, WIDTH, YELLOW, WHITE, ) def create_pikachu_surface(size: int = 100) -> pygame.Surface: """用绘图函数生成一个简化的皮卡丘占位图。 这里不使用官方素材,而是用几何图形拼出一个 黄色为主、有耳朵和腮红的角色,用于演示精灵逻辑。 """ surf = pygame.Surface((size, size), pygame.SRCALPHA) # 身体:黄色椭圆 body_rect = pygame.Rect(int(size * 0.2), int(size * 0.3), int(size * 0.6), int(size * 0.62)) pygame.draw.ellipse(surf, YELLOW, body_rect) pygame.draw.ellipse(surf, DARK, body_rect, 2) # 左耳 left_ear = [(int(size * 0.22), int(size * 0.34)), (int(size * 0.06), int(size * 0.05)), (int(size * 0.18), int(size * 0.20))] pygame.draw.polygon(surf, YELLOW, left_ear) pygame.draw.polygon(surf, DARK, left_ear, 2) # 左耳黑尖 left_tip = [(int(size * 0.08), int(size * 0.10)), (int(size * 0.06), int(size * 0.05)), (int(size * 0.14), int(size * 0.10))] pygame.draw.polygon(surf, DARK, left_tip) # 右耳 right_ear = [(int(size * 0.78), int(size * 0.34)), (int(size * 0.94), int(size * 0.05)), (int(size * 0.82), int(size * 0.20))] pygame.draw.polygon(surf, YELLOW, right_ear) pygame.draw.polygon(surf, DARK, right_ear, 2) # 右耳黑尖 right_tip = [(int(size * 0.92), int(size * 0.10)), (int(size * 0.94), int(size * 0.05)), (int(size * 0.86), int(size * 0.10))] pygame.draw.polygon(surf, DARK, right_tip) # 眼睛 pygame.draw.circle(surf, DARK, (int(size * 0.34), int(size * 0.52)), int(size * 0.04)) pygame.draw.circle(surf, DARK, (int(size * 0.66), int(size * 0.52)), int(size * 0.04)) # 腮红 pygame.draw.circle(surf, RED, (int(size * 0.18), int(size * 0.66)), int(size * 0.06)) pygame.draw.circle(surf, RED, (int(size * 0.82), int(size * 0.66)), int(size * 0.06)) # 嘴巴 mouth_rect = pygame.Rect(int(size * 0.40), int(size * 0.60), int(size * 0.20), int(size * 0.12)) pygame.draw.arc(surf, DARK, mouth_rect, 0, 3.14, 2) return surf def create_pokeball_surface(radius: int = 24) -> pygame.Surface: """绘制一个精灵球图标。""" surf = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA) # 上半部分红色,下半部分白色 pygame.draw.circle(surf, RED, (radius, radius), radius) pygame.draw.rect(surf, WHITE, (0, radius - 4, radius * 2, radius + 4)) # 黑色中线 pygame.draw.rect(surf, DARK, (0, radius - 4, radius * 2, 6)) # 中心圆 pygame.draw.circle(surf, WHITE, (radius, radius), radius // 2) pygame.draw.circle(surf, DARK, (radius, radius), radius // 2, 3) # 外圈描边 pygame.draw.circle(surf, DARK, (radius, radius), radius, 3) return surf class Pikachu(pygame.sprite.Sprite): """玩家角色:皮卡丘""" def __init__(self, x: int, y: int): super().__init__() self.image = create_pikachu_surface() self.rect = self.image.get_rect(center=(x, y)) self.speed = PLAYER_SPEED self.boost_speed = PLAYER_BOOST_SPEED def update(self, keys) -> None: dx, dy = 0, 0 speed = self.boost_speed if keys[pygame.K_SPACE] else self.speed if keys[pygame.K_LEFT] or keys[pygame.K_a]: dx -= speed if keys[pygame.K_RIGHT] or keys[pygame.K_d]: dx += speed if keys[pygame.K_UP] or keys[pygame.K_w]: dy -= speed if keys[pygame.K_DOWN] or keys[pygame.K_s]: dy += speed self.rect.x += dx self.rect.y += dy # 防止角色跑出窗口 self.rect.clamp_ip(pygame.Rect(0, 0, WIDTH, HEIGHT)) class Pokeball(pygame.sprite.Sprite): """可捕获的精灵球道具""" def __init__(self): super().__init__() self.image = create_pokeball_surface() x = random.randint(60, WIDTH - 60) y = random.randint(100, HEIGHT - 60) self.rect = self.image.get_rect(center=(x, y)) self.born_time = pygame.time.get_ticks() self.lifetime = 120 # 存活帧数 def update(self) -> None: # 存活超过寿命的精灵球自动消失 frame_alive = max(1, self.lifetime) if pygame.time.get_ticks() - self.born_time > frame_alive * 16: self.kill()

说明一下,create_pikachu_surface用几何图形画了一个简化版角色,这不是官方形象,只是用来承载移动、碰撞、绘制这些游戏教学逻辑。如果你有自己画的像素图或购买的素材,可以直接加载图片替换掉绘制函数。

5.4 主程序main.py

# 文件路径:main.py import pygame from config import ( BG_COLOR, FPS, GAME_TITLE, HEIGHT, SPAWN_INTERVAL, TEXT_COLOR, WIDTH, ) from sprites import Pikachu, Pokeball class Game: def __init__(self): pygame.init() self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(GAME_TITLE) self.clock = pygame.time.Clock() self.running = True self.score = 0 self.combo = 0 self.spawn_timer = 0 self.hit_text = "" self.hit_text_time = 0 self.player = Pikachu(WIDTH // 2, HEIGHT - 80) self.pokeballs = pygame.sprite.Group() self.all_sprites = pygame.sprite.Group(self.player) # 字体:优先使用系统中文字体 self.font = pygame.font.SysFont("simhei", 28, bold=True) self.small_font = pygame.font.SysFont("simhei", 20) def handle_events(self) -> None: for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.running = False def spawn_pokeball(self) -> None: now = pygame.time.get_ticks() if now - self.spawn_timer > SPAWN_INTERVAL: self.spawn_timer = now ball = Pokeball() self.pokeballs.add(ball) self.all_sprites.add(ball) def update(self) -> None: keys = pygame.key.get_pressed() self.player.update(keys) self.pokeballs.update() # 碰撞检测,碰到精灵球则加分 hits = pygame.sprite.spritecollide(self.player, self.pokeballs, True) for _ in hits: self.score += 10 self.combo += 1 self.hit_text = "皮卡丘,我们来咯!" self.hit_text_time = pygame.time.get_ticks() def draw(self) -> None: self.screen.fill(BG_COLOR) # 绘制草地点缀 for y in range(0, HEIGHT, 40): for x in range(0, WIDTH, 40): pygame.draw.circle(self.screen, (100, 165, 75), (x + 10, y + 20), 4) self.all_sprites.draw(self.screen) score_surface = self.font.render(f"得分: {self.score}", True, TEXT_COLOR) self.screen.blit(score_surface, (20, 20)) combo_surface = self.small_font.render(f"连击: {self.combo}", True, TEXT_COLOR) self.screen.blit(combo_surface, (20, 58)) # 显示捕获成功提示 if self.hit_text and pygame.time.get_ticks() - self.hit_text_time < 800: tip = self.font.render(self.hit_text, True, (255, 255, 255)) tip_rect = tip.get_rect(center=(WIDTH // 2, 80)) self.screen.blit(tip, tip_rect) pygame.display.flip() def run(self) -> None: while self.running: self.handle_events() self.spawn_pokeball() self.update() self.draw() self.clock.tick(FPS) pygame.quit() def main(): game = Game() game.run() if __name__ == "__main__": main()

主循环的顺序是有讲究的:先处理事件,再生成新精灵球,再更新所有精灵,最后绘制画面。如果你把绘制放在更新之前,这一帧的画面就会比状态慢一帧,虽然在小游戏里影响不明显,但进入复杂项目后会成为很难排查的 bug。

5.5 运行方法

在项目根目录执行:

python main.py

然后就可以用方向键或 WASD 控制皮卡丘移动。碰到红色精灵球会加分,屏幕顶部显示“皮卡丘,我们来咯!”提示。按 ESC 退出游戏。

6. 运行结果与效果验证

启动后在窗口中你应该能看到:

  • 一个绿色草地风格的背景,上面有草点装饰
  • 一只黄色为主、带耳朵和腮红的角色位于窗口下方中央
  • 每隔 1.2 秒左右,屏幕随机位置出现一个红白配色的精灵球
  • 移动角色碰触精灵球后,分数增加 10 分,顶部出现提示文字

判断游戏是否成功,不只看画面,还要确认几个核心逻辑是否正常:

验证点预期表现
窗口创建标题为“皮卡丘,我们来咯”,尺寸 900x640
移动控制方向键和 WASD 均可控制角色移动
边界限制角色不能移出窗口
加速移动按住空格时角色移动明显变快
精灵球生成间隔约 1.2 秒生成一个新精灵球
碰撞计分碰到精灵球后分数 +10,精灵球消失
提示文字碰撞后 0.8 秒内显示“皮卡丘,我们来咯!”
退出机制ESC 或关闭窗口均能正常退出

如果发现精灵球生成过快或过慢,在config.py里调整SPAWN_INTERVAL,例如 1800 表示 1.8 秒生成一个,数值越大越困难。如果角色移动太飘,调整PLAYER_SPEED

调试时可以临时在update方法里加print输出,例如每次碰撞时打印分数,这能帮助你确认碰撞逻辑是否真的执行:

for _ in hits: self.score += 10 self.combo += 1 print(f"[DEBUG] catch pokeball, score={self.score}")

看到输出再关掉,比盲目猜测效率高得多。

7. 常见问题与排查思路

新手运行 Pygame 项目时,最容易在环境、路径、字体和坐标系上出问题。我把高频问题整理成一张表,方便你直接对照排查。

问题现象可能原因排查方式解决方案
ModuleNotFoundError: pygamePygame 未安装或未激活虚拟环境检查终端提示符前缀是否显示 venv运行pip install pygame并激活虚拟环境
窗口一闪而过立刻退出主函数书写错误或代码中有未捕获异常用命令行运行查看完整报错确认main()被调用,检查异常堆栈
窗口出现但看不到角色精灵坐标超出窗口范围,或被背景覆盖在 update 中打印 rect 坐标检查clamp_ip是否生效,调整初始坐标
中文显示为方框系统缺少指定中文字体打印pygame.font.get_fonts()查看可用字体改用pygame.font.match_font("pingfang")等系统字体
游戏运行太卡或太快未调用clock.tick(FPS)或 FPS 设置异常查看是否每帧都执行了 tickself.clock.tick(FPS)放入主循环末尾
Linux 下窗口显示不出来SDL 视频驱动不匹配查看终端报错信息设置SDL_VIDEODRIVER=x11后重试
精灵球一瞬间生成太多生成间隔判断写在更新前且未更新 timer检查spawn_timer赋值位置在生成操作后立即更新self.spawn_timer
按住方向键没有反应使用了事件循环的 KEYDOWN 而不是按键状态添加打印确认事件是否触发update中改用pygame.key.get_pressed()

这里重点说下中文显示问题。pygame.font.SysFont("simhei", ...)在 Windows 下通常能直接显示中文,但在 macOS 上不一定能找到 simhei。更稳妥的做法是先用pygame.font.get_fonts()查看当前系统可用字体,再选择一个中文字体名称。即使没有中文文字,也不影响游戏玩法,因为计分数字的渲染不受字体影响。

还有一个常见误区是:把self.spawn_timer = now写在判断条件外面。这会导致计时器每一帧都刷新,永远满足不了“间隔大于 SPAWN_INTERVAL”的条件,结果精灵球永远不生成。我刚学 Pygame 时在这个问题上卡了半小时。

8. 最佳实践与工程建议

这个小游戏虽然只有三个文件,但已经足够用来验证一些工程化习惯。如果你打算把它扩展成更大的项目,下面这些建议越早养成越好。

第一,配置和逻辑分离。所有窗口尺寸、颜色、速度、间隔都放config.py。你想调整游戏难度时,只改配置文件,不需要深入游戏逻辑。等游戏复杂到配置项超过几十个时,可以考虑用dataclass或配置文件统一管理。

第二,使用 Sprite 和 Group 组织对象,而不是用列表散装管理。Pygame 的 Group 不仅能统一更新和绘制,还能结合spritecollide做高效碰撞检测。一旦你开始同时管理敌人、子弹、道具、特效,这种设计带来的收益会非常明显。

第三,注意清理生命周期结束的对象。我在Pokeball.update里做了超时自动销毁,避免精灵球积压导致性能下降。实际开发中,子弹飞出屏幕、敌人死亡后的特效粒子,都要在合适的时机调用kill()释放。

第四,素材版权是真实存在的问题。皮卡丘的图像、名称都受版权保护,个人学习演示问题不大,但发布到公共平台或商用需要谨慎。建议学习时使用程序绘制或原创像素画,等作品成熟后再替换素材,这是一个技术博主应该承担的传播责任。

第五,考虑打包分发。想把这个小游戏发给朋友玩,可以用 PyInstaller 打包成 exe 或可执行文件:

pip install pyinstaller pyinstaller -F -w main.py

-F表示打包成单文件,-w表示不显示控制台窗口。注意打包后需要验证字体和素材路径是否正确,因为打包会改变资源文件的相对路径。更推荐的做法是在项目内创建一个assets目录,并使用os.path.join(Path(__file__).parent, "assets")来定位资源。

第六,调试信息要能快速开关。可以在config.py里加一个DEBUG_MODE = True,然后在代码里用条件控制打印输出。这样开发时打开调试,发布前统一关闭,比频繁删改 print 语句干净得多。

9. 扩展方向:如何把它改造成真正的个人作品

游戏跑通只是第一步。要把它变成能写进简历或者展示给朋友的作品,可以从下面几个方向依次升级。

游戏机制方面,目前的设计是“碰到即得分”。你可以增加负面障碍,碰到后扣生命值;可以记录最高分并存到本地 JSON 文件;可以让精灵球生成间隔随分数提高而缩短,形成难度曲线;还可以增加道具种类,例如金色精灵球得 50 分、会逃跑的精灵球。

画面表现方面,可以给皮卡丘做多帧动画。准备几张不同步伐的帧图,在移动时按帧率切换,角色跑动时会有动画效果。碰撞时生成闪电特效,用一片短暂的明黄色闪电形状叠加显示。背景可以换成有渐变层次的地图,用地砖数组绘制。

声音交互方面,Pygame 的pygame.mixer支持播放 wav 或 ogg。碰撞时播放一个短音效,移动时播放环境音,能极大增强游戏反馈。开发时可以把音效总开关做成配置项,避免办公环境突然出声吓到同事。

技术深度方面,如果你想继续提升,可以尝试把游戏改成状态机模式,显式区分开始画面、游戏中、暂停、结算等状态。每一关的状态变化用独立的类管理,这就开始接近商业游戏引擎里的场景管理思路了。再往后学 Unity 或 Godot 时,你会发现这套思维方式可以直接迁移。

我自己写这个小游戏最大的感触是:代码量不大,但它逼着你去思考“程序为什么需要主循环”“对象之间怎么协作”“性能问题会藏在哪里”。这些问题的答案,不是靠背语法能悟出来的,是真的写完一个能跑、能玩、能被朋友评价“有点意思”的程序之后才懂的。

建议你先按文章代码把最小版本跑起来,然后从改一个数值开始,比如把精灵球生成间隔改成 500 试试游戏难度变化。等你想清楚要加什么功能,再动手改代码。每加一个功能前,先记下“这个功能需要哪些类、哪些状态、哪些新文件”,把设计写下来再动手,你会比直接改代码顺利得多。

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

Python实现眼动追踪数据可视化:从注视点轨迹到注意力热图

简介&#xff1a;本资源是一套面向心理学研究者、人机交互工程师及UI/UX设计师的Python眼动数据分析工具包&#xff0c;聚焦注视点轨迹绘制与热图生成两大核心任务&#xff0c;解决眼动数据难以直观呈现、行为模式难量化的问题。压缩包共5个文件&#xff08;3个核心Python模块、…

作者头像 李华
网站建设 2026/9/3 4:16:45

IAI电缸驱动器上位机软件安装调试与故障排查全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 4:16:21

《穿越半径2》四级TOP任务开荒攻略:数据核心回收全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 4:14:22

Ansible 2.9.27实战:文件批量分发与权限设置避坑指南

简介&#xff1a;Ansible 2.9.27 是面向 CentOS7/RHEL7 系统的自动化运维工具安装包&#xff0c;专为需要批量配置管理、应用部署与日常维护的系统管理员设计。资源以 gz 压缩包形式提供&#xff0c;共 29 个文件&#xff0c;主体为 rpm 软件包&#xff0c;并包含 gz、bz2、xml…

作者头像 李华
网站建设 2026/9/3 4:14:20

单相逆变器原理与SPWM控制:从基础到电赛实战设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 4:12:52

CTP穿透式监管测试全流程:从SIMNOW仿真到宏源环境适配详解

简介&#xff1a;这是面向期货CTP量化交易者的穿透式监管下单测试程序包&#xff0c;针对2019年6月上期所CTP接口升级后申请交易权限需完成自动化测试的场景&#xff0c;提供自动开仓、撤单和平仓的完整实现。压缩包内共89个文件、15.64MB&#xff0c;包含C源码&#xff08;cpp…

作者头像 李华