Python实现吃豆人游戏详解(内附完整代码)

news2024/9/19 10:41:37

在这里插入图片描述

一、吃豆人游戏背景

吃豆人是一款由Namco公司在1980年推出的经典街机游戏。游戏的主角是一个黄色的小圆点,它必须在迷宫中吃掉所有的点数,同时避免被四处游荡的幽灵捉到。如果玩家能够吃掉所有的点数,并且成功避开幽灵,就可以进入下一关,挑战更加复杂的迷宫和更快的幽灵。

二、Python 实现概述

下面我们将使用 Python 编程语言,结合 turtle 模块来实现一个简化版的 Pacman 游戏。turtle 模块是 Python 的标准库之一,它提供了一个绘图板,非常适合用来绘制游戏界面和控制游戏角色的移动。
在这里插入图片描述

三、代码步骤详解

1. 导入必要的模块

from random import choice
from turtle import *
from freegames import floor, vector

这里导入了三个模块:

  • random 用于实现幽灵的随机移动。
  • turtle 用于绘制图形界面。
  • freegames 是一个第三方库,提供了 floor 函数和 vector 类,用于处理向量和地板数。

2. 初始化游戏状态

state = {'score': 0}

定义了一个字典 state 来存储游戏的状态,初始分数为 0。
在这里插入图片描述
创建一个不可见的Turtle对象用于绘制游戏地图:

path = Turtle(visible=False)

创建一个不可见的Turtle对象用于显示分数:

writer = Turtle(visible=False)

设置吃豆人的目标向量,初始为向右移动

aim = vector(5, 0)

3. 设置 Pacman 和幽灵的初始位置及移动方向

pacman = vector(-40, -80)
ghosts = [
    [vector(-180, 160), vector(5, 0)],
    # 其他幽灵的初始位置和移动方向
]

Pacman 的初始位置设置在屏幕的中心偏左上方,幽灵的初始位置和移动方向被存储在一个列表中。

4. 定义迷宫地图

tiles = [
    # 迷宫地图的数据
]

迷宫地图使用一个一维数组表示,数组中的元素为 0 或 1,0 表示通道,1 表示墙壁。
在这里插入图片描述

5. 定义绘制正方形的函数

def square(x, y):
    """在 (x, y) 位置使用 path 绘制正方形。"""
    path.up()
    path.goto(x, y)
    path.down()
    path.begin_fill()
    for count in range(4):
        path.forward(20)
        path.left(90)
    path.end_fill()

使用 path 画笔在指定位置绘制正方形,用于构建迷宫的墙壁。

6. 定义计算偏移量的函数

def offset(point):
    """返回 point 在 tiles 中的偏移量。"""
    x = (floor(point.x, 20) + 200) / 20
    y = (180 - floor(point.y, 20)) / 20
    index = int(x + y * 20)
    return index

将 Pacman 或幽灵的坐标转换为迷宫地图数组中的索引。

7. 定义检查位置有效性的函数

def valid(point):
    """如果 point 在 tiles 中有效,则返回 True。"""
    index = offset(point)
    if tiles[index] == 0:
        return False
    index = offset(point + 19)
    if tiles[index] == 0:
        return False
    return point.x % 20 == 0 or point.y % 20 == 0

确保 Pacman 和幽灵不会移动到墙壁上。

8. 定义绘制迷宫世界的函数

def world():
    """使用 path 绘制世界。"""
    bgcolor('black')
    path.color('blue')
    for index in range(len(tiles)):
        tile = tiles[index]
        if tile > 0:
            x = (index % 20) * 20 - 200
            y = 180 - (index // 20) * 20
            square(x, y)
            if tile == 1:
                path.up()
                path.goto(x + 10, y + 10)
                path.dot(2, 'white')

绘制迷宫的墙壁和通道。

9. 定义移动 Pacman 和幽灵的函数

def move():
    """Move pacman and all ghosts."""
    # Pacman 和幽灵的移动逻辑

控制 Pacman 和幽灵的移动,包括 Pacman 的转向和幽灵的随机移动。

10. 定义改变 Pacman 移动方向的函数

def change(x, y):
    """Change pacman aim if valid."""
    # 如果方向改变是有效的,则更新 Pacman 的移动方向

根据玩家的按键输入改变 Pacman 的移动方向。

11. 设置游戏界面

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
# 其他界面设置代码

初始化游戏窗口的大小和画笔的隐藏等。

12. 绑定键盘事件

listen()
onkey(lambda: change(5, 0), 'Right')
# 其他按键绑定代码

绑定键盘的箭头键到 Pacman 的移动方向改变函数。

13. 启动游戏循环

world()
move()
done()

绘制迷宫,启动游戏循环,并在游戏结束后调用 done 函数。

四、完整代码及注释

# 导入random模块中的choice函数,用于随机选择
from random import choice
# 导入turtle模块,用于绘制图形界面
from turtle import *
# 导入freegames模块中的floor和vector函数,用于处理向量和地板函数
from freegames import floor, vector

# 初始化游戏状态字典,包含分数
state = {'score': 0}
# 创建一个不可见的Turtle对象用于绘制游戏地图
path = Turtle(visible=False)
# 创建一个不可见的Turtle对象用于显示分数
writer = Turtle(visible=False)
# 设置吃豆人的目标向量,初始为向右移动
aim = vector(5, 0)
# 设置吃豆人的位置
pacman = vector(-40, -80)
# 设置幽灵的初始位置和移动向量,有四个幽灵
ghosts = [
    [vector(-180, 160), vector(5, 0)],
    [vector(-180, -160), vector(0, 5)],
    [vector(100, 160), vector(0, -5)],
    [vector(100, -160), vector(-5, 0)],
]
# 设置游戏地图的格子,0表示通道,1表示墙壁
tiles = [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
    0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
    0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0,
    0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]

# 定义绘制正方形的函数
def square(x, y):
    """使用path在(x, y)位置绘制正方形。"""
    path.up()  # 提起画笔
    path.goto(x, y)  # 移动到指定位置
    path.down()  # 放下画笔
    path.begin_fill()  # 开始填充
    for count in range(4):  # 绘制四条边
        path.forward(20)
        path.left(90)
    path.end_fill()  # 结束填充

# 定义获取点在地图格子中的偏移量的函数
def offset(point):
    """返回点在tiles中的偏移量。"""
    # 计算x和y的偏移量,并转换为索引
    x = (floor(point.x, 20) + 200) / 20
    y = (180 - floor(point.y, 20)) / 20
    index = int(x + y * 20)
    return index

# 定义检查点是否有效的函数
def valid(point):
    """如果点在tiles中有效,则返回True。"""
    # 检查点是否在通道上
    index = offset(point)
    if tiles[index] == 0:
        return False
    # 检查点移动后的位置是否在通道上
    index = offset(point + 19)
    if tiles[index] == 0:
        return False
    # 如果点在格子的边缘,则有效
    return point.x % 20 == 0 or point.y % 20 == 0

# 定义绘制世界地图的函数
def world():
    """使用path绘制世界地图。"""
    bgcolor('black')  # 设置背景颜色为黑色
    path.color('blue')  # 设置绘制颜色为蓝色
    # 遍历tiles数组,绘制每个格子
    for index in range(len(tiles)):
        tile = tiles[index]
        if tile > 0:
            x = (index % 20) * 20 - 200
            y = 180 - (index // 20) * 20
            square(x, y)  # 绘制正方形
            if tile == 1:
                path.up()  # 提起画笔
                path.goto(x + 10, y + 10)  # 移动到指定位置
                path.dot(2, 'white')  # 绘制小点

# 定义移动吃豆人和所有幽灵的函数
def move():
    """移动吃豆人和所有幽灵。"""
    # 更新分数显示
    writer.undo()
    writer.write(state['score'])
    clear()  # 清除屏幕
    # 检查吃豆人的目标位置是否有效,如果有效则移动
    if valid(pacman + aim):
        pacman.move(aim)
    # 检查吃豆人是否吃到豆子,如果吃到则增加分数并更新地图
    index = offset(pacman)
    if tiles[index] == 1:
        tiles[index] = 2
        state['score'] += 1
        x = (index % 20) * 20 - 200
        y = 180 - (index // 20) * 20
        square(x, y)  # 绘制被吃掉的豆子位置
    # 绘制吃豆人
    up()
    goto(pacman.x + 10, pacman.y + 10)
    dot(20, 'yellow')
    # 移动每个幽灵
    for point, course in ghosts:
        if valid(point + course):
            point.move(course)
        else:
            # 如果幽灵的目标位置无效,则随机选择一个方向移动
            options = [
                vector(5, 0),
                vector(-5, 0),
                vector(0, 5),
                vector(0, -5),
            ]
            plan = choice(options)
            course.x = plan.x
            course.y = plan.y
        up()
        goto(point.x + 10, point.y + 10)
        dot(20, 'red')  # 绘制幽灵
    update()  # 更新屏幕显示
    # 检查吃豆人是否被幽灵抓到,如果是则结束游戏
    for point, course in ghosts:
        if abs(pacman - point) < 20:
            return
    # 每100毫秒调用一次move函数,实现连续移动
    ontimer(move, 100)

# 定义改变吃豆人移动方向的函数
def change(x, y):
    """如果有效,则改变吃豆人的目标方向。"""
    if valid(pacman + vector(x, y)):
        aim.x = x
        aim.y = y

# 初始化游戏窗口
setup(420, 420, 370, 0)
hideturtle()  # 隐藏turtle图形
tracer(False)  # 关闭动画
writer.goto(160, 160)  # 设置分数显示位置
writer.color('white')  # 设置分数颜色为白色
writer.write(state['score'])  # 显示初始分数
listen()  # 开启键盘监听
# 设置键盘按键与改变方向的绑定
onkey(lambda: change(5, 0), 'Right')
onkey(lambda: change(-5, 0), 'Left')
onkey(lambda: change(0, 5), 'Up')
onkey(lambda: change(0, -5), 'Down')
# 绘制游戏地图
world()
# 开始游戏循环
move()
# 游戏结束
done()

以上就是使用 Python 和 turtle 模块实现简化版 Pacman 游戏的详细步骤和代码解析。希望这不仅能够帮助你理解游戏的实现原理,也能够激发你进一步探索和创造属于自己的游戏。如果你有任何问题或想要讨论更多的游戏开发技巧,欢迎随时交流。

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

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

相关文章

小试牛刀--对称矩阵压缩存储

学习贺利坚老师对称矩阵压缩存储 数据结构实践——压缩存储的对称矩阵的运算_计算压缩存储对称矩阵 a 与向量 b 的乘积-CSDN博客 本人解析博客 矩阵存储和特殊矩阵的压缩存储_n阶对称矩阵压缩-CSDN博客 版本更新日志 V1.0: 对老师代码进行模仿 , 我进行名字优化, 思路代码注释 …

VMware Workstation 和 Fusion 现对个人用户宣布免费使用

这周笔记本坏了&#xff0c;重装了系统&#xff0c;部分软件也得重装&#xff0c;其中就有 VMware Workstation&#xff0c; 当我还在上网苦苦找破解包时&#xff08;个人学习使用&#xff09;&#xff0c;发现现在居然免费了…… 原来 VMware 换了新东家&#xff0c;简单回顾下…

DMA方式的知识点笔记

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 目录 1. DMA基本概念 2. DMA传送过程 易错点 DMA控制器操作流程 3. DMA传送方式 这是单总线的结果 &#xff08;CPU说了算 所以不会产生于CPU的冲突&#xff09; 这…

谷歌摸鱼神器来了:推出AI会议替身,一键总结提问发言_会议预约 ai对话

饱受会议折磨的打工人&#xff0c;终于可以解放了&#xff01; 就在刚刚举办的Google Cloud Next’23大会上&#xff0c;谷歌宣布了一系列科技新进展&#xff0c;最瞩目的要属其中的“开会AI替身”了。 只需要一句“帮我参加”&#xff0c;AI就能替你开会&#xff0c;并在合适…

SCI二区TOP|蜘蛛黄蜂优化算法(SWO)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年&#xff0c;M Abdel-Basset受到蜘蛛黄蜂优化社会行为启发&#xff0c;提出了蜘蛛黄蜂优化算法&#xff08;Spider Wasp Optimizer, SWO&#xff09;。 2.算法原理 2.1算法思想 S…

python04——类(基础new)

类其实也是一种封装的思想&#xff0c;类就是把变量、方法等封装在一起&#xff0c;然后可以通过不同的实例化对其进行调用操作。 1.类的定义 class 类名&#xff1a; 变量a def __init__ (self,参数2&#xff0c;参数2...)&#xff1a;初始化函数&#xff01;&#xff01;&…

【LeetCode】生命游戏

目录 一、题目二、解法完整代码 一、题目 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一…

Qt 音频编程实战项目

一Qt 音频基础知识 QT multimediaQMediaPlayer 类&#xff1a;媒体播放器&#xff0c;主要用于播放歌曲、网络收音 机等功能。QMediaPlaylist 类&#xff1a;专用于播放媒体内容的列表。 二 音频项目实战程序 //版本5.12.8 .proQT core gui QT multimedia greate…

C++入门基础(2)

目录 一、引用: 1、定义&#xff1a; 2、特性&#xff1a; 3、引用的使用&#xff1a; 4、const引用&#xff1a;控制权限 const引用定义: const引用可以接收3种对象&#xff1a; 1、正常对象&#xff1a; 2、临时对象&#xff1a; 3、const对象&#xff1a; 总结&…

Sorted Set 类型命令(命令语法、操作演示、命令返回值、时间复杂度、注意事项)

Sorted Set 类型 文章目录 Sorted Set 类型zadd 命令zrange 命令zcard 命令zcount 命令zrevrange 命令zrangebyscore 命令zpopmax 命令bzpopmax 命令zpopmin 命令bzpopmin 命令zrank 命令zscore 命令zrem 命令zremrangebyrank 命令zremrangebyscore 命令zincrby 命令zinterstor…

vue子组件调用父组件方法

父组件 页面<popoverss ref"pop" :goodspop"goodspop"></popoverss>子组件components: {"popoverss": () > import(../comm/popover.vue)},方法goodspop(e){console.log(e"----")return 9999;},子组件 方法props:[go…

【转载】目标检测mAP的含义

转载自三叔家的猫 https://blog.csdn.net/qq_39056987 https://blog.csdn.net/qq_39056987/article/details/104348493 <div id"content_views" class"markdown_views prism-atom-one-light"><svg xmlns"http://www.w3.org/2000/svg" s…

不用服务器 | 我搭建了一个属于自己的GPT聊天应用!!!

原文地址&#xff1a;aiutools.fun/archives/5118 平台限制部分内容未显示&#xff0c;详情请访问原文。 展示 不废话&#xff0c;直接上干货&#xff01; 我这里搭建的Lobe Chat 支持 聊天TTS & STT 语音会话文生图各种优秀的插件 下面搭建好的样子 前期准备 需要…

(2)滑动窗口算法练习:无重复字符的最长子串

无重复字符的最长子串 题目链接&#xff1a;3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长子串的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是"a…

Java字符串(String、字符串拼接、原理)

文章目录 一、String字符串1.1创建方式【直接赋值、new一个对象】1.1.1 使用字符串字面值直接赋值&#xff1a;&#xff08;1&#xff09;字符串字面量创建String对象的转换过程&#xff08;2&#xff09;一些方法&#xff08;3&#xff09;说明 1.1.2 使用new关键字创建字符串…

Java如何自定义注解及在SpringBoot中的应用

注解 注解&#xff08;Annotation&#xff09;&#xff0c;也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性&#xff0c;与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面&#xff0c;用来对这些元素进行说…

最快视频转绘-AnimateDiff-Lightning

最快视频转绘-AnimateDiff-Lightning Video-to-Video Generation AnimateDiff-Lightning 非常适合视频到视频的生成。使用 ControlNet 提供最简单的 comfyui 工作流程。 「ComfyUI-aki-v1.3.7z」链接&#xff1a;https://pan.quark.cn/s/199a753292d8 下载导入工作流 animat…

182440-00SF 同轴连接器

型号简介 182440-00SF是Southwest Microwave的一款连接器。该连接器采用 BeCu UNqS C17300 材料&#xff0c;并进行了镀金处理&#xff0c;以确保良好的导电性和耐腐蚀性&#xff1b;螺纹采用符合 ASTM A2582 标准的钢制合金&#xff0c;并进行磷酸盐钝化处理&#xff0c;以提高…

FunAudioLLM SenseVoice语音转录与CosyVoice语音合成及语音克隆使用案例

参考: https://fun-audio-llm.github.io/ 1、SenseVoice语音转录 在线体验:https://modelscope.cn/studios/iic/CosyVoice-300M 参考:https://github.com/FunAudioLLM/SenseVoice 下载: pip install -U funasr使用: from funasr import AutoModelmodel_dir = "…

如何从 Vue 2 无痛升级到 Vue 3,一文搞定!

大家好,我是CodeQi! 一位热衷于技术分享的码仔。 随着 Vue 3 的发布,许多开发者都面临着从 Vue 2 升级到 Vue 3 的挑战。 本文将详细介绍如何从 Vue 2 无痛升级到 Vue 3,包括每个步骤的详细说明与代码示例。 让我们开始吧! 准备工作 在正式开始升级之前,请确保你已经…