使用Pygame制作“吃豆人”游戏

news2025/2/2 20:59:05

本篇博客展示如何使用 Python + Pygame 编写一个简易版的“吃豆人(Pac-Man)” 风格游戏。这里我们暂且命名为 Py-Man。玩家需要控制主角在一个网格地图里移动、吃掉散布在各处的豆子,并躲避在地图中巡逻的幽灵。此示例可帮助你理解网格地图角色移动敌人随机移动碰撞与得分等常见2D游戏开发技巧。


1. 前言

吃豆人(Pac-Man) 自 1980 年诞生以来,一直是街机游戏史上的里程碑作品。它的核心玩法是:

  • 玩家控制吃豆人(Pac-Man),在迷宫状的地图里收集豆子;
  • 幽灵(Ghost)会在地图中巡逻,玩家要设法规避幽灵追击;
  • 当豆子吃光或玩家被幽灵抓到,游戏结束。

本篇中,我们编写一个缩小且精简的“吃豆人”原型示例,重点演示:

  • 地图使用网格表示:不同数字代表墙壁、豆子、幽灵、可行走区域等;
  • 玩家(Pac-Man)的移动:基于方向键上下左右移动,每次移动一格;
  • 幽灵的随机行动:每帧可能随机选择一个方向前进(如遇墙则停留或换方向);
  • 碰撞和得分:吃完全部豆子即胜利,若与幽灵坐标重叠则游戏失败。

2. 开发环境

  1. Python 3.x
  2. Pygame 库:若未安装可通过
    pip install pygame
    
  3. 支持图形界面的操作系统:Windows、macOS 或绝大多数 Linux 均可。

在确保 import pygame 没有报错后,即可开始项目的开发。


3. 游戏设计思路

  1. 网格地图(MAP)

    • 使用一个二维列表来表示地图;
    • 为简化,我们定义:
      • 1 表示墙壁,不可通过;
      • 2 表示幽灵初始位置;
      • 3 表示豆子,需要被吃掉;
      • 4 表示玩家初始位置;
      • 0 表示空地,可以行走。
    • 当然,实际 Pac-Man 会有更复杂的地图布局,这里只做示例。
  2. 玩家

    • 存储玩家的网格坐标 (row, col)
    • 通过键盘上下左右控制每次移动一格;
    • 若目标位置是墙壁则保持不动,否则进入该格;
    • 如果在有豆子的格子上,就吃掉该豆子并加分。
  3. 幽灵

    • 地图可含多个幽灵;
    • 每帧随机选择一个方向移动,如果该方向是墙壁或出界,则不移动或重新选择;
    • 当玩家和幽灵坐标重叠时,游戏失败。
  4. 得分与胜利

    • 每当玩家吃掉一颗豆子(3),得分增加 1,并将该格子改为 0;
    • 如果全部豆子都被吃光,游戏胜利。
  5. 游戏循环

    • 处理键盘事件与幽灵的随机移动;
    • 更新玩家与幽灵位置;
    • 检测玩家是否吃到豆子、是否与幽灵碰撞;
    • 如果游戏结束或胜利,则跳转到结束场景。

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_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()

代码说明

  1. 地图 GAME_MAP

    • 用一个二维列表存储地图信息,示例尺寸 10×10:
      • 1(蓝色方块)表示墙壁;
      • 3(黄色小圆)表示豆子;
      • 24 仅用于初始位置记录,随后会被设为 0
    • 你可以自行扩展地图,或使用文件读取的方式加载更大规模的关卡。
  2. 玩家与幽灵

    • 玩家每帧检测上下左右键,若不是墙就移动。并且若所在格子是豆子就得分、把该格子变成空地;
    • 幽灵update() 里随机选择一个方向移动,如果遭遇墙壁则保持原地或尝试别的方向。
    • 玩家坐标幽灵坐标一致,则判定失败。
  3. 得分与胜利

    • 统计地图中豆子的总数 total_beans
    • 如果玩家吃掉的豆子数与 total_beans 相等,则判定胜利;
    • 碰到幽灵则立即失败。
  4. 绘制

    • 墙壁用蓝色矩形,豆子用黄色小圆,空地用灰色背景
    • 玩家用绿色圆,幽灵用红色小方块
    • 你可以替换为更精美的贴图或动画帧,让游戏看起来更加逼真。

5. 运行效果

image.png


6. 总结

通过本篇示例,你已掌握了一个简化“吃豆人” 原型所需的关键实现,你可以在此基础上,结合你对 Pac-Man 的灵感或其他创意,一步步将这个简易示例打磨成更完备、更具乐趣的 2D 游戏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2290942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL入门到精通 理论+实战 -- 在 MySQL 中学习SQL语言

目录 一、环境准备 1、MySQL 8.0 和 Navicat 下载安装 2、准备好的表和数据文件&#xff1a; 二、SQL语言简述 1、数据库基础概念 2、什么是SQL 3、SQL的分类 4、SQL通用语法 三、DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据定义语言 1、操…

x86-64数据传输指令

关于汇编语言一些基础概念的更详细的介绍&#xff0c;可移步MIPS指令集&#xff08;一&#xff09;基本操作_mips指令 sw-CSDN博客 该指令集中一个字2字节。 该架构有16个64位寄存器&#xff0c;名字都以%r开头&#xff0c;每个寄存器的最低位字节&#xff0c;低1~2位字节&…

【ESP32】ESP-IDF开发 | WiFi开发 | TCP传输控制协议 + TCP服务器和客户端例程

1. 简介 TCP&#xff08;Transmission Control Protocol&#xff09;&#xff0c;全称传输控制协议。它的特点有以下几点&#xff1a;面向连接&#xff0c;每一个TCP连接只能是点对点的&#xff08;一对一&#xff09;&#xff1b;提供可靠交付服务&#xff1b;提供全双工通信&…

算法基础学习——快排与归并(附带java模版)

快速排序和归并排序是两种速度较快的排序方式&#xff0c;是最应该掌握的两种排序算法&#xff0c; &#xff08;一&#xff09;快速排序&#xff08;不稳定的&#xff09; 基本思想&#xff1a;分治 平均时间复杂度&#xff1a;O(nlogn) / 最慢O(n^2) / 最快O(n) 步骤&…

指针的进化—sizeof和strlen对比(字符串和字符数组的区分)

1.前言 如果你对各个数组的内容存放是什么没有个清晰的概念&#xff0c;对指针偏移之后的数量算不出来或者模棱两可&#xff0c;那么本篇就来详细介绍sizeof和strlen来具象化的显示数组的内容存放了多少内容&#xff0c;偏移量变化后的变化&#xff0c;这个数组进行运算后会不会…

TensorFlow简单的线性回归任务

如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题&#xff0c;其中输入特征 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引用传递

先看一段代码&#xff1a; public static void add(String a) {a "new";System.out.println("add: " a); // 输出内容&#xff1a;add: new}public static void main(String[] args) {String a null;add(a);System.out.println("main: " a);…

925.长按键入

目录 一、题目二、思路三、解法四、收获 一、题目 你的朋友正在使用键盘输入他的名字 name。偶尔&#xff0c;在键入字符 c 时&#xff0c;按键可能会被长按&#xff0c;而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&am…

【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.2.1. 什么是Deref trait Deref的全写是Dereference&#xff0c;就是引用的英文reference加上"de"这个反义前缀&#xff0c…

吴恩达深度学习——超参数调试

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 超参数调试调试选择范围 Batch归一化公式整合 Softmax 超参数调试 调试 目前学习的一些超参数有学习率 α \alpha α&#xff08;最重要&#xff09;、动量梯度下降法 β \bet…

【赵渝强老师】K8s中Pod探针的ExecAction

在K8s集群中&#xff0c;当Pod处于运行状态时&#xff0c;kubelet通过使用探针&#xff08;Probe&#xff09;对容器的健康状态执行检查和诊断。K8s支持三种不同类型的探针&#xff0c;分别是&#xff1a;livenessProbe&#xff08;存活探针&#xff09;、readinessProbe&#…

如何对系统调用进行扩展?

扩展系统调用是操作系统开发中的一个重要任务。系统调用是用户程序与操作系统内核之间的接口,允许用户程序执行内核级操作(如文件操作、进程管理、内存管理等)。扩展系统调用通常包括以下几个步骤: 一、定义新系统调用 扩展系统调用首先需要定义新的系统调用的功能。系统…

安卓(android)订餐菜单【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.掌握Activity生命周的每个方法。 2.掌握Activity的创建、配置、启动和关闭。 3.掌握Intent和IntentFilter的使用。 4.掌握Activity之间的跳转方式、任务栈和四种启动模式。 5.掌握在Activity中添加…

Python安居客二手小区数据爬取(2025年)

目录 2025年安居客二手小区数据爬取观察目标网页观察详情页数据准备工作&#xff1a;安装装备就像打游戏代码详解&#xff1a;每行代码都是你的小兵完整代码大放送爬取结果 2025年安居客二手小区数据爬取 这段时间需要爬取安居客二手小区数据&#xff0c;看了一下相关教程基本…

happytime

happytime 一、查壳 无壳&#xff0c;64位 二、IDA分析 1.main 2.cry函数 总体&#xff1a;是魔改的XXTEA加密 在main中可以看到被加密且分段的flag在最后的循环中与V6进行比较&#xff0c;刚好和上面v6数组相同。 所以毫无疑问密文是v6. 而与flag一起进入加密函数的v5就…

深度学习 DAY3:NLP发展史

NLP发展史 NLP发展脉络简要梳理如下&#xff1a; (远古模型&#xff0c;上图没有但也可以算NLP&#xff09; 1940 - BOW&#xff08;无序统计模型&#xff09; 1950 - n-gram&#xff08;基于词序的模型&#xff09; (近代模型&#xff09; 2001 - Neural language models&am…

家居EDI:Hom Furniture EDI需求分析

HOM Furniture 是一家成立于1977年的美国家具零售商&#xff0c;总部位于明尼苏达州。公司致力于提供高品质、时尚的家具和家居用品&#xff0c;满足各种家庭和办公需求。HOM Furniture 以广泛的产品线和优质的客户服务在市场上赢得了良好的口碑。公司经营的产品包括卧室、客厅…

【08-飞线和布线与输出文件】

导入网表后 1.复制结构图(带板宽的) 在机械一层画好外围线 2.重新定义板子形状(根据选则对象取定义) 选中对象生成板子线条形状 3.PCB和原理图交叉选择模式 过滤器选择原理图里的元器件 过滤器"OFF",只开启Componnets,只是显示元器件 4. 模块化布局 PCB高亮元…