使用Pygame制作“吃豆人”游戏
创作时间:
作者:
@小白创作中心
使用Pygame制作“吃豆人”游戏
引用
CSDN
1.
https://blog.csdn.net/weixin_41793160/article/details/145409655
本教程将带你使用Python和Pygame库制作一个简易版的"吃豆人"游戏。通过这个项目,你将学习到游戏开发中的核心概念,如网格地图设计、角色移动、敌人AI、碰撞检测和得分系统等。
"吃豆人(Pac-Man)"自 1980 年诞生以来,一直是街机游戏史上的里程碑作品。它的核心玩法是:
- 玩家控制吃豆人(Pac-Man),在迷宫状的地图里收集豆子;
- 幽灵(Ghost)会在地图中巡逻,玩家要设法规避幽灵追击;
- 当豆子吃光或玩家被幽灵抓到,游戏结束。
本篇中,我们编写一个缩小且精简的"吃豆人"原型示例,重点演示:
- 地图使用网格表示:不同数字代表墙壁、豆子、幽灵、可行走区域等;
- 玩家(Pac-Man)的移动:基于方向键上下左右移动,每次移动一格;
- 幽灵的随机行动:每帧可能随机选择一个方向前进(如遇墙则停留或换方向);
- 碰撞和得分:吃完全部豆子即胜利,若与幽灵坐标重叠则游戏失败。
开发环境
- Python 3.x
- Pygame库:若未安装可通过
pip install pygame
- 支持图形界面的操作系统:Windows、macOS 或绝大多数 Linux 均可。
在确保
import pygame
没有报错后,即可开始项目的开发。
游戏设计思路
- 网格地图(MAP)
- 使用一个二维列表来表示地图;
- 为简化,我们定义:
- 1 表示墙壁,不可通过;
- 2 表示幽灵初始位置;
- 3 表示豆子,需要被吃掉;
- 4 表示玩家初始位置;
- 0 表示空地,可以行走。
- 当然,实际 Pac-Man 会有更复杂的地图布局,这里只做示例。
- 玩家
- 存储玩家的网格坐标 (row, col);
- 通过键盘上下左右控制每次移动一格;
- 若目标位置是墙壁则保持不动,否则进入该格;
- 如果在有豆子的格子上,就吃掉该豆子并加分。
- 幽灵
- 地图可含多个幽灵;
- 每帧随机选择一个方向移动,如果该方向是墙壁或出界,则不移动或重新选择;
- 当玩家和幽灵坐标重叠时,游戏失败。
- 得分与胜利
- 每当玩家吃掉一颗豆子(3),得分增加 1,并将该格子改为 0;
- 如果全部豆子都被吃光,游戏胜利。
- 游戏循环
- 处理键盘事件与幽灵的随机移动;
- 更新玩家与幽灵位置;
- 检测玩家是否吃到豆子、是否与幽灵碰撞;
- 如果游戏结束或胜利,则跳转到结束场景。
完整示例代码
将以下代码保存为 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_SIZE
screen = 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 = row
self.col = col
self.score = 0
def move(self, dr, dc):
new_row = self.row + dr
new_col = self.col + dc
if 0 <= new_row < ROWS and 0 <= new_col < COLS:
# 如果不是墙(1),则可移动
if GAME_MAP[new_row][new_col] != 1:
self.row = new_row
self.col = new_col
@property
def x(self):
return self.col * TILE_SIZE
@property
def y(self):
return self.row * TILE_SIZE
# -----------------------
# 幽灵类
# -----------------------
class Ghost:
def __init__(self, row, col):
self.row = row
self.col = col
def update(self):
# 随机尝试一个方向移动(上下左右)
directions = [(-1,0), (1,0), (0,-1), (0,1)]
dr, dc = random.choice(directions)
new_row = self.row + dr
new_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_row
self.col = new_col
@property
def x(self):
return self.col * TILE_SIZE
@property
def y(self):
return self.row * TILE_SIZE
# -----------------------
# 工具函数:初始化游戏对象
# -----------------------
def init_game():
player = None
ghosts = []
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] = 0
elif cell == 2: # 幽灵
ghosts.append(Ghost(r, c))
# 恢复成空地
GAME_MAP[r][c] = 0
return 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] = 0
player.score += 1
# 若吃完所有豆子 -> 胜利
if player.score == total_beans:
running = False
game_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 = False
game_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_SIZE
y = r * TILE_SIZE
if 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。
- 你可以自行扩展地图,或使用文件读取的方式加载更大规模的关卡。
- 玩家与幽灵
- 玩家每帧检测上下左右键,若不是墙就移动。并且若所在格子是豆子就得分、把该格子变成空地;
- 幽灵在 update() 里随机选择一个方向移动,如果遭遇墙壁则保持原地或尝试别的方向。
- 当玩家坐标与幽灵坐标一致,则判定失败。
- 得分与胜利
- 统计地图中豆子的总数 total_beans;
- 如果玩家吃掉的豆子数与 total_beans 相等,则判定胜利;
- 碰到幽灵则立即失败。
- 绘制
- 墙壁用蓝色矩形,豆子用黄色小圆,空地用灰色背景;
- 玩家用绿色圆,幽灵用红色小方块。
- 你可以替换为更精美的贴图或动画帧,让游戏看起来更加逼真。
运行效果
总结
通过本篇示例,你已掌握了一个简化"吃豆人"原型所需的关键实现,你可以在此基础上,结合你对 Pac-Man 的灵感或其他创意,一步步将这个简易示例打磨成更完备、更具乐趣的 2D 游戏。
热门推荐
牛排几分熟最好?米其林大厨:一分熟和七分熟区别,别被人取笑了
牛排的英语怎么说
水蒸蛋和水煮蛋哪个热量高?
科技变革可以帮助医学史的写作吗?——古DNA和全球疾病史写作的新探索
前庭康复训练如何帮眩晕患者找回平衡?
孩子在学校被别的孩子撞伤要怎么处理
直播间有哪些关键指标数据?
怎样知道高速是否堵车更及时?这种了解方式的及时性怎样保证?
汉代高频词:电视剧里经常出现的“未央”,到底什么意思?
补血究竟要吃什么!真正补血的十大食物排名!
车位上安装地锁是否违法?全面解析地下停车设施管理与法律法规
抗日战争中的长沙:为何成为中日争夺焦点?
手掌起红点很痒脱皮怎么回事
日本学前教育:理念、课程与师资培养全解析
幼儿园幼儿请假制度:家长办理请假手续全攻略
直觉vs理性:你以为的理性选择,其实都是直觉在决定
法人代表的职责和责任是什么
杰字取名好不好?杰字取名的寓意全面解读
痱子又痒又疼?防痱攻略来了
婴儿痱子用药指南:5种安全有效的治疗药物
田静回应考研英语一成绩争议,解析评分规则与辅导效果
土地使用权到期了怎么办
C语言全局变量的10种优化方法
无担保小额贷款条件及办理流程详解
机油滤清器有哪些不同类型?
自定义Edge浏览器的右键菜单,简易设置教程
世界陆地最高点珠穆朗玛峰:8848.86米的巅峰挑战
南通市布局6大产业,未来发展不可限量
李勣的一生有哪些贡献与成就?他留下了哪些作品?
松鼠的生活习性与生态角色:可爱动物的勤劳与智慧启示