使用Pygame制作“吃豆人”游戏
本篇博客展示如何使用 Python + Pygame 编写一个简易版的“吃豆人(Pac-Man)” 风格游戏。这里我们暂且命名为 Py-Man。玩家需要控制主角在一个网格地图里移动、吃掉散布在各处的豆子,并躲避在地图中巡逻的幽灵。此示例可帮助你理解网格地图、角色移动、敌人随机移动、碰撞与得分等常见2D游戏开发技巧。
1. 前言
吃豆人(Pac-Man) 自 1980 年诞生以来,一直是街机游戏史上的里程碑作品。它的核心玩法是:
- 玩家控制吃豆人(Pac-Man),在迷宫状的地图里收集豆子;
- 幽灵(Ghost)会在地图中巡逻,玩家要设法规避幽灵追击;
- 当豆子吃光或玩家被幽灵抓到,游戏结束。
本篇中,我们编写一个缩小且精简的“吃豆人”原型示例,重点演示:
- 地图使用网格表示:不同数字代表墙壁、豆子、幽灵、可行走区域等;
- 玩家(Pac-Man)的移动:基于方向键上下左右移动,每次移动一格;
- 幽灵的随机行动:每帧可能随机选择一个方向前进(如遇墙则停留或换方向);
- 碰撞和得分:吃完全部豆子即胜利,若与幽灵坐标重叠则游戏失败。
2. 开发环境
- Python 3.x
- Pygame 库:若未安装可通过
pip install pygame - 支持图形界面的操作系统:Windows、macOS 或绝大多数 Linux 均可。
在确保 import pygame 没有报错后,即可开始项目的开发。
3. 游戏设计思路
-
网格地图(MAP)
- 使用一个二维列表来表示地图;
- 为简化,我们定义:
1表示墙壁,不可通过;2表示幽灵初始位置;3表示豆子,需要被吃掉;4表示玩家初始位置;0表示空地,可以行走。
- 当然,实际 Pac-Man 会有更复杂的地图布局,这里只做示例。
-
玩家
- 存储玩家的网格坐标
(row, col); - 通过键盘上下左右控制每次移动一格;
- 若目标位置是墙壁则保持不动,否则进入该格;
- 如果在有豆子的格子上,就吃掉该豆子并加分。
- 存储玩家的网格坐标
-
幽灵
- 地图可含多个幽灵;
- 每帧随机选择一个方向移动,如果该方向是墙壁或出界,则不移动或重新选择;
- 当玩家和幽灵坐标重叠时,游戏失败。
-
得分与胜利
- 每当玩家吃掉一颗豆子(3),得分增加 1,并将该格子改为 0;
- 如果全部豆子都被吃光,游戏胜利。
-
游戏循环
- 处理键盘事件与幽灵的随机移动;
- 更新玩家与幽灵位置;
- 检测玩家是否吃到豆子、是否与幽灵碰撞;
- 如果游戏结束或胜利,则跳转到结束场景。
4. 完整示例代码
将以下代码保存为 py_man.py 并执行。你可根据需要自定义地图大小、关卡布局、移动速度、幽灵 AI 等来拓展本示例。
import pygame
import sys
import random# 初始化 Pygame
pygame.init()# -----------------------
# 全局配置
# -----------------------
TILE_SIZE = 40 # 每个格子的像素大小
FPS = 8 # 帧率(适当降低 以便看清幽灵移动)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 200, 0)
RED = (255, 0, 0)
YELLOW= (255, 255, 0)
GRAY = (100, 100, 100)# 地图定义:1-墙,2-幽灵,3-豆子,4-玩家位置,0-空地
GAME_MAP = [[1,1,1,1,1,1,1,1,1,1],[1,4,3,3,0,3,3,3,3,1],[1,3,1,1,3,1,1,1,0,1],[1,3,1,2,3,1,2,3,3,1],[1,3,3,3,3,3,3,1,3,1],[1,0,1,3,1,3,1,3,3,1],[1,3,1,3,1,3,1,3,3,1],[1,3,3,3,3,3,3,3,3,1],[1,3,3,1,3,0,3,3,3,1],[1,1,1,1,1,1,1,1,1,1],
]ROWS = len(GAME_MAP)
COLS = len(GAME_MAP[0])SCREEN_WIDTH = COLS * TILE_SIZE
SCREEN_HEIGHT = ROWS * TILE_SIZEscreen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Py-Man 简易吃豆人")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 32)# -----------------------
# 玩家类
# -----------------------
class Player:def __init__(self, row, col):self.row = rowself.col = colself.score = 0def move(self, dr, dc):new_row = self.row + drnew_col = self.col + dcif 0 <= new_row < ROWS and 0 <= new_col < COLS:# 如果不是墙(1),则可移动if GAME_MAP[new_row][new_col] != 1:self.row = new_rowself.col = new_col@propertydef x(self):return self.col * TILE_SIZE@propertydef y(self):return self.row * TILE_SIZE# -----------------------
# 幽灵类
# -----------------------
class Ghost:def __init__(self, row, col):self.row = rowself.col = coldef update(self):# 随机尝试一个方向移动(上下左右)directions = [(-1,0), (1,0), (0,-1), (0,1)]dr, dc = random.choice(directions)new_row = self.row + drnew_col = self.col + dc# 如果新位置不是墙,且在地图内,则移动if 0 <= new_row < ROWS and 0 <= new_col < COLS:if GAME_MAP[new_row][new_col] != 1:self.row = new_rowself.col = new_col@propertydef x(self):return self.col * TILE_SIZE@propertydef y(self):return self.row * TILE_SIZE# -----------------------
# 工具函数:初始化游戏对象
# -----------------------
def init_game():player = Noneghosts = []for r in range(ROWS):for c in range(COLS):cell = GAME_MAP[r][c]if cell == 4: # 玩家player = Player(r, c)# 恢复成空地GAME_MAP[r][c] = 0elif cell == 2: # 幽灵ghosts.append(Ghost(r, c))# 恢复成空地GAME_MAP[r][c] = 0return player, ghosts# -----------------------
# 主游戏逻辑
# -----------------------
def main():player, ghosts = init_game()running = True# 统计剩余豆子数total_beans = sum(row.count(3) for row in GAME_MAP)while running:clock.tick(FPS)# 1) 事件处理for event in pygame.event.get():if event.type == pygame.QUIT:running = False# 2) 键盘输入(上下左右)keys = pygame.key.get_pressed()if keys[pygame.K_UP]:player.move(-1, 0)elif keys[pygame.K_DOWN]:player.move(1, 0)elif keys[pygame.K_LEFT]:player.move(0, -1)elif keys[pygame.K_RIGHT]:player.move(0, 1)# 玩家若踩到豆子(3),吃掉并得分if GAME_MAP[player.row][player.col] == 3:GAME_MAP[player.row][player.col] = 0player.score += 1# 若吃完所有豆子 -> 胜利if player.score == total_beans:running = Falsegame_over(won=True, score=player.score)continue# 幽灵随机移动for g in ghosts:g.update()# 检测玩家是否与幽灵相碰for g in ghosts:if g.row == player.row and g.col == player.col:# 玩家失败running = Falsegame_over(won=False, score=player.score)break# 3) 绘制场景screen.fill(BLACK)# 绘制地图for r in range(ROWS):for c in range(COLS):tile = GAME_MAP[r][c]x = c * TILE_SIZEy = r * TILE_SIZEif tile == 1: pygame.draw.rect(screen, BLUE, (x, y, TILE_SIZE, TILE_SIZE)) # 墙elif tile == 3: pygame.draw.circle(screen, YELLOW, (x + TILE_SIZE//2, y + TILE_SIZE//2), TILE_SIZE//6) # 豆子else:# 通路 or 空地pygame.draw.rect(screen, GRAY, (x, y, TILE_SIZE, TILE_SIZE))# 绘制玩家 (绿色圆)pygame.draw.circle(screen, GREEN, (player.x + TILE_SIZE//2, player.y + TILE_SIZE//2), TILE_SIZE//2 - 4)# 绘制幽灵 (红色方块)for g in ghosts:pygame.draw.rect(screen, RED, (g.x+5, g.y+5, TILE_SIZE-10, TILE_SIZE-10))# 分数score_surf = font.render(f"Score: {player.score}", True, WHITE)screen.blit(score_surf, (10, 10))pygame.display.flip()pygame.quit()sys.exit()def game_over(won, score):screen.fill(BLACK)if won:msg = f"恭喜,你吃掉所有豆子!得分: {score}"else:msg = f"被幽灵抓住了!得分: {score}"label = font.render(msg, True, WHITE)rect = label.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))screen.blit(label, rect)pygame.display.flip()pygame.time.wait(3000) # 等待3秒再退出if __name__ == "__main__":main()
代码说明
-
地图
GAME_MAP- 用一个二维列表存储地图信息,示例尺寸 10×10:
1(蓝色方块)表示墙壁;3(黄色小圆)表示豆子;2与4仅用于初始位置记录,随后会被设为0。
- 你可以自行扩展地图,或使用文件读取的方式加载更大规模的关卡。
- 用一个二维列表存储地图信息,示例尺寸 10×10:
-
玩家与幽灵
- 玩家每帧检测上下左右键,若不是墙就移动。并且若所在格子是豆子就得分、把该格子变成空地;
- 幽灵在
update()里随机选择一个方向移动,如果遭遇墙壁则保持原地或尝试别的方向。 - 当玩家坐标与幽灵坐标一致,则判定失败。
-
得分与胜利
- 统计地图中豆子的总数
total_beans; - 如果玩家吃掉的豆子数与
total_beans相等,则判定胜利; - 碰到幽灵则立即失败。
- 统计地图中豆子的总数
-
绘制
- 墙壁用蓝色矩形,豆子用黄色小圆,空地用灰色背景;
- 玩家用绿色圆,幽灵用红色小方块。
- 你可以替换为更精美的贴图或动画帧,让游戏看起来更加逼真。
5. 运行效果

6. 总结
通过本篇示例,你已掌握了一个简化“吃豆人” 原型所需的关键实现,你可以在此基础上,结合你对 Pac-Man 的灵感或其他创意,一步步将这个简易示例打磨成更完备、更具乐趣的 2D 游戏。
相关文章:
使用Pygame制作“吃豆人”游戏
本篇博客展示如何使用 Python Pygame 编写一个简易版的“吃豆人(Pac-Man)” 风格游戏。这里我们暂且命名为 Py-Man。玩家需要控制主角在一个网格地图里移动、吃掉散布在各处的豆子,并躲避在地图中巡逻的幽灵。此示例可帮助你理解网格地图、角…...
使用 Docker(Podman) 部署 MongoDB 数据库及使用详解
在现代开发环境中,容器化技术(如 Docker 和 Podman)已成为部署和管理应用程序的标准方式。本文将详细介绍如何使用 Podman/Docker 部署 MongoDB 数据库,并确保其他应用程序容器能够通过 Docker 网络成功连接到 MongoDB。我们将逐步…...
SQL入门到精通 理论+实战 -- 在 MySQL 中学习SQL语言
目录 一、环境准备 1、MySQL 8.0 和 Navicat 下载安装 2、准备好的表和数据文件: 二、SQL语言简述 1、数据库基础概念 2、什么是SQL 3、SQL的分类 4、SQL通用语法 三、DDL(Data Definition Language):数据定义语言 1、操…...
Electricity Market Optimization 探索系列(一)
本文参考链接:Linear Programming Mini Example 先从一个线性规划的例子说起: 问题背景: 现在需要使用两台发电机满足用户的用电需求,发电机一的发电功率上限是 6MW,发电机二的发电功率上限是 4MW,发电…...
x86-64数据传输指令
关于汇编语言一些基础概念的更详细的介绍,可移步MIPS指令集(一)基本操作_mips指令 sw-CSDN博客 该指令集中一个字2字节。 该架构有16个64位寄存器,名字都以%r开头,每个寄存器的最低位字节,低1~2位字节&…...
【ESP32】ESP-IDF开发 | WiFi开发 | TCP传输控制协议 + TCP服务器和客户端例程
1. 简介 TCP(Transmission Control Protocol),全称传输控制协议。它的特点有以下几点:面向连接,每一个TCP连接只能是点对点的(一对一);提供可靠交付服务;提供全双工通信&…...
算法基础学习——快排与归并(附带java模版)
快速排序和归并排序是两种速度较快的排序方式,是最应该掌握的两种排序算法, (一)快速排序(不稳定的) 基本思想:分治 平均时间复杂度:O(nlogn) / 最慢O(n^2) / 最快O(n) 步骤&…...
指针的进化—sizeof和strlen对比(字符串和字符数组的区分)
1.前言 如果你对各个数组的内容存放是什么没有个清晰的概念,对指针偏移之后的数量算不出来或者模棱两可,那么本篇就来详细介绍sizeof和strlen来具象化的显示数组的内容存放了多少内容,偏移量变化后的变化,这个数组进行运算后会不会…...
TensorFlow简单的线性回归任务
如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题,其中输入特征 x 和标…...
【memgpt】letta 课程1/2:从头实现一个自我编辑、记忆和多步骤推理的代理
llms-as-operating-systems-agent-memory llms-as-operating-systems-agent-memory内存 操作系统的内存管理...
6-图像金字塔与轮廓检测
文章目录 6.图像金字塔与轮廓检测(1)图像金字塔定义(2)金字塔制作方法(3)轮廓检测方法(4)轮廓特征与近似(5)模板匹配方法6.图像金字塔与轮廓检测 (1)图像金字塔定义 高斯金字塔拉普拉斯金字塔 高斯金字塔:向下采样方法(缩小) 高斯金字塔:向上采样方法(放大)…...
深入理解Java引用传递
先看一段代码: public static void add(String a) {a "new";System.out.println("add: " a); // 输出内容:add: new}public static void main(String[] args) {String a null;add(a);System.out.println("main: " a);…...
925.长按键入
目录 一、题目二、思路三、解法四、收获 一、题目 你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&am…...
【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 15.2.1. 什么是Deref trait Deref的全写是Dereference,就是引用的英文reference加上"de"这个反义前缀,…...
图书管理系统 Axios 源码__新增图书
目录 功能介绍 核心代码解析 源码:新增图书功能 总结 本项目基于 HTML、Bootstrap、JavaScript 和 Axios 开发,实现了图书的增删改查功能。以下是新增图书的功能实现,适合前端开发学习和项目实践。 功能介绍 用户可以通过 模态框…...
吴恩达深度学习——超参数调试
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习所用。 文章目录 超参数调试调试选择范围 Batch归一化公式整合 Softmax 超参数调试 调试 目前学习的一些超参数有学习率 α \alpha α(最重要)、动量梯度下降法 β \bet…...
【赵渝强老师】K8s中Pod探针的ExecAction
在K8s集群中,当Pod处于运行状态时,kubelet通过使用探针(Probe)对容器的健康状态执行检查和诊断。K8s支持三种不同类型的探针,分别是:livenessProbe(存活探针)、readinessProbe&#…...
如何对系统调用进行扩展?
扩展系统调用是操作系统开发中的一个重要任务。系统调用是用户程序与操作系统内核之间的接口,允许用户程序执行内核级操作(如文件操作、进程管理、内存管理等)。扩展系统调用通常包括以下几个步骤: 一、定义新系统调用 扩展系统调用首先需要定义新的系统调用的功能。系统…...
ChatGPT与GPT的区别与联系
ChatGPT 和 GPT 都是基于 Transformer 架构的语言模型,但它们有不同的侧重点和应用。下面我们来探讨一下它们的区别与联系。 1. GPT(Generative Pre-trained Transformer) GPT 是一类由 OpenAI 开发的语言模型,基于 Transformer…...
安卓(android)订餐菜单【Android移动开发基础案例教程(第2版)黑马程序员】
一、实验目的(如果代码有错漏,可查看源码) 1.掌握Activity生命周的每个方法。 2.掌握Activity的创建、配置、启动和关闭。 3.掌握Intent和IntentFilter的使用。 4.掌握Activity之间的跳转方式、任务栈和四种启动模式。 5.掌握在Activity中添加…...
Python安居客二手小区数据爬取(2025年)
目录 2025年安居客二手小区数据爬取观察目标网页观察详情页数据准备工作:安装装备就像打游戏代码详解:每行代码都是你的小兵完整代码大放送爬取结果 2025年安居客二手小区数据爬取 这段时间需要爬取安居客二手小区数据,看了一下相关教程基本…...
happytime
happytime 一、查壳 无壳,64位 二、IDA分析 1.main 2.cry函数 总体:是魔改的XXTEA加密 在main中可以看到被加密且分段的flag在最后的循环中与V6进行比较,刚好和上面v6数组相同。 所以毫无疑问密文是v6. 而与flag一起进入加密函数的v5就…...
深度学习 DAY3:NLP发展史
NLP发展史 NLP发展脉络简要梳理如下: (远古模型,上图没有但也可以算NLP) 1940 - BOW(无序统计模型) 1950 - n-gram(基于词序的模型) (近代模型) 2001 - Neural language models&am…...
前端知识速记:节流与防抖
前端知识速记:节流与防抖 什么是防抖? 防抖是一种控制事件触发频率的方法,通常用于处理用户频繁触发事件的场景。防抖的核心思想是将多个连续触发事件合并为一个事件,以减少执行次数。它在以下场景中特别有效: 输入…...
家居EDI:Hom Furniture EDI需求分析
HOM Furniture 是一家成立于1977年的美国家具零售商,总部位于明尼苏达州。公司致力于提供高品质、时尚的家具和家居用品,满足各种家庭和办公需求。HOM Furniture 以广泛的产品线和优质的客户服务在市场上赢得了良好的口碑。公司经营的产品包括卧室、客厅…...
【3】阿里面试题整理
[1]. ES架构,如何进行路由以及选主 路由:在Elasticsearch(ES)中,默认的路由算法是基于文档的_id。具体来说,Elasticsearch会对文档的_id进行哈希计算,然后对分片数量取模,以确定该文…...
【08-飞线和布线与输出文件】
导入网表后 1.复制结构图(带板宽的) 在机械一层画好外围线 2.重新定义板子形状(根据选则对象取定义) 选中对象生成板子线条形状 3.PCB和原理图交叉选择模式 过滤器选择原理图里的元器件 过滤器"OFF",只开启Componnets,只是显示元器件 4. 模块化布局 PCB高亮元…...
python 从知网的期刊导航页面抓取与农业科技相关的数据
要从知网的期刊导航页面抓取与农业科技相关的数据,并提取《土壤学报》2016年06期的结果,可以使用requests库来获取网页内容,BeautifulSoup库来解析HTML。由于知网页面结构可能会发生变化,在实际使用中,需要根据页面结构…...
【单细胞第二节:单细胞示例数据分析-GSE218208】
GSE218208 1.创建Seurat对象 #untar(“GSE218208_RAW.tar”) rm(list ls()) a data.table::fread("GSM6736629_10x-PBMC-1_ds0.1974_CountMatrix.tsv.gz",data.table F) a[1:4,1:4] library(tidyverse) a$alias:gene str_split(a$alias:gene,":",si…...
机器学习优化算法:从梯度下降到Adam及其变种
机器学习优化算法:从梯度下降到Adam及其变种 引言 最近deepseek的爆火已然说明,在机器学习领域,优化算法是模型训练的核心驱动力。无论是简单的线性回归还是复杂的深度神经网络,优化算法的选择直接影响模型的收敛速度、泛化性能…...
