python吃豆人小游戏代码(完整代码+逐步解释)

吃豆人是经典的街机游戏。在本程序中,你需要使用箭头键导航和吃所有的白色食物,并且小心在迷宫里游荡的红色幽灵。

python吃豆人小游戏代码

程序代码

代码来源:grantjenks在github上面的开源项目

同样尝试:python贪吃蛇代码示例(实测可运行)

"""Pacman, classic arcade game.

Exercises

1. Change the board.
2. Change the number of ghosts.
3. Change where pacman starts.
4. Make the ghosts faster/slower.
5. Make the ghosts smarter.
"""

from random import choice
from turtle import *

from freegames import floor, vector

state = {'score': 0}
path = Turtle(visible=False)
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)],
]
# fmt: off
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,
]
# fmt: on


def square(x, y):
    """Draw square using path at (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):
    """Return offset of point in tiles."""
    x = (floor(point.x, 20) + 200) / 20
    y = (180 - floor(point.y, 20)) / 20
    index = int(x + y * 20)
    return index


def valid(point):
    """Return True if point is valid in tiles."""
    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():
    """Draw world using 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')


def move():
    """Move pacman and all ghosts."""
    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

    ontimer(move, 100)


def change(x, y):
    """Change pacman aim if valid."""
    if valid(pacman + vector(x, y)):
        aim.x = x
        aim.y = y


setup(420, 420, 370, 0)
hideturtle()
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()

代码解释

这段代码实现了经典街机游戏Pacman的简化版本。下面是代码的解释:

首先,导入了必要的模块和函数:

from random import choice  # 用于选择随机方向
from turtle import *  # 用于绘制图形
from freegames import floor, vector  # 自定义的辅助函数

然后,定义了一些全局变量和初始状态:

state = {'score': 0}  # 当前游戏状态,初始得分为0
path = Turtle(visible=False)  # 用于绘制游戏场景的路径
writer = Turtle(visible=False)  # 用于显示分数的文字
aim = vector(5, 0)  # Pacman 的移动方向
pacman = vector(-40, -80)  # Pacman 的初始位置
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)],
]

接下来是游戏地图的定义,用一个长串的数字表示墙壁和空地的布局:

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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]

然后定义了一些辅助函数:

  • square(x, y): 在指定位置绘制一个正方形。
  • offset(point): 将坐标转换为地图上的索引。
  • valid(point): 检查坐标是否是合法的游戏区域。

接下来的 world() 函数用于绘制游戏地图:

def world():
    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')  # 在食物位置绘制一个小白点

然后定义了 move() 函数,用于移动 Pacman 和鬼:

def move():
    writer.undo()  # 清除之前的分数显示
    writer.write(state['score'])  # 显示当前分数
    clear()  # 清除之前的绘图

    if valid(pacman + aim):  # 如果 Pacman 可以朝当前方向移动
        pacman.move(aim)  # 移动 Pacman
        index = offset(pacman)
        if tiles[index] == 1:  # 如果 Pacman 吃到了食物
            tiles[index] = 2  # 将食物变成空地
            state['score'] += 1  # 分数加1

    x = (index % 20) * 20 - 200
    y = 180 - (index // 20) * 20
    square(x, y)  # 在 Pacman 移动后的位置接下来是鬼的移动逻辑:

```python
    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')  # 在鬼的位置绘制一个红色点

接下来是 draw() 函数,用于绘制 Pacman 的图形:

def draw():
    clear()  # 清除之前的绘图

    # 绘制 Pacman
    up()
    goto(pacman.x + 10, pacman.y + 10)
    dot(20, 'yellow')  # 在 Pacman 的位置绘制一个黄色点

    # 绘制鬼
    for point, course in ghosts:
        up()
        goto(point.x + 10, point.y + 10)
        dot(20, 'red')  # 在鬼的位置绘制一个红色点

    update()  # 更新绘图

然后是 change(x, y) 函数,用于改变 Pacman 的移动方向:

def change(x, y):
    if valid(pacman + vector(x, y)):
        aim.x = x
        aim.y = y

最后是主循环,负责设置游戏窗口和事件处理:

setup(420, 420, 370, 0)  # 设置游戏窗口的大小和位置
hideturtle()  # 隐藏海龟图标
tracer(False)  # 关闭动画效果
onscreenclick(change)  # 注册鼠标点击事件,用于改变 Pacman 的移动方向

# 设置绘图对象的属性
path.speed(0)
writer.speed(0)
writer.color('white')
path.color('blue')

world()  # 绘制初始游戏地图
tracer(True)  # 打开动画效果

# 游戏主循环
while True:
    move()  # 移动 Pacman 和鬼
    draw()  # 绘制 Pacman 和鬼的图形

done()  # 结束游戏

以上就是这段代码的主要逻辑。它实现了一个简化的 Pacman 游戏,玩家可以通过点击游戏窗口来改变 Pacman 的移动方向,同时要躲避鬼并吃掉食物来获得分数。游戏的界面使用了 Turtle 模块进行绘制,具有简单的图形效果。