003-Pacman_FreePythonGames

一 相关知识

1 choice()函数

  • 描述:choice() 方法返回一个列表,元组或字符串的随机项。
  • 语法:choice()是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法。
    • import random
    • random.choice( seq )
  • 参数:seq -- 可以是一个列表,元组或字符串。
  • 返回值:返回随机项。

2 abs()函数

  • 描述:abs() 函数返回数字的绝对值。
  • 语法:abs( x )
  • 参数:x -- 数值表达式。
  • 返回值:函数返回x(数字)的绝对值。

3 turtle.dot(size=None, *color)方法

  • 功能:绘制一个直径为 size,颜色为 color 的圆点。如果 size 未指定,则直径取 pensize+4 和 2*pensize 中的较大值。

4 turtle.undo()方法

  • 撤消 (或连续撤消) 最近的一个 (或多个) 海龟动作。可撤消的次数由撤消缓冲区的大小决定。

5 turtle.write(arg,move=False,align="left",font=("Arial",8,"normal"))

  • 描述:书写文本
  • 参数:
    • arg -- 要书写到 TurtleScreen 的对象, 书写指定的字符串 - 到当前海龟位置
    • move -- True/False,如果 move 为 True,画笔会移动到文本的右下角。默认 move 为 False
    • align -- 指定对齐方式 ("left", "center" 或 right")
    • font -- 一个三元组 (fontname, fontsize, fonttype),表示不同的字体

二 代码结构分析

1 游戏环境的创建:画出背景世界/迷宫,即吃豆人和幽灵的活动范围

主要思路:定义了一个地图列表tiles,通过判断地图列表中元素的值是否为1来画出背景世界的基本框架。代码内容包括两部分:square()函数部分和world函数部分,其中列表索引值和地图坐标之间的转换感觉很奇妙,我想不出来。。。

2 吃豆人和幽灵移动部分

主要思路:

首先需要画出吃豆人和幽灵,调用turtle库中的dot函数就可以了

然后是吃豆人和幽灵的移动过程,都是通过改变他们的坐标来实现的,因为吃豆人和幽灵都是在我们画出的背景世界里面移动的,所以要定义一个函数valid()判断其坐标是否有效,即是否在背景世界范围内;

还有一部分是统计吃豆人在移动过程中吃到了多少个豆子,这部分内容是通过统计改变tiles元素值得次数得到的,然后再利用turtle库的write函数将统计得到的数值写入字典state中。

3 游戏结束判断

吃豆人碰到幽灵即为游戏结束的标志,实际过程中通过判断两者坐标的差值是否小于20个坐标单位即可实现。

三 代码

  1 """Pacman,classic arcade game.
  2 
  3 Exercises
  4 
  5 1. Change the board.
  6 2. Change the number of ghosts.
  7 3. Change where pacman starts.
  8 4. Make the ghosts faster/slower.
  9 5. Make the ghosts smaster.
 10 
 11 """
 12 
 13 from random import choice                  # 导入choice函数,随机返回可迭代对象中任意一个元素值
 14 from turtle import *                       # 导入海龟库
 15 from freegames import floor,vector         # 导入floor函数和vector函数,floor应该表示一种数学运算,类似于取余的逆操作
 16 
 17 state = {'score':0}                        # 定义字典变量用来统计被吃掉的豆子数
 18 path = Turtle(visible=False)               # 用来画出吃豆人活动的世界,游戏中的蓝色部分
 19 writer = Turtle(visible=False)             # 用来将豆子数写入state字典中的画笔
 20 aim = vector(5,0)                          # 用来改变吃豆人的坐标
 21 pacman = vector(-40,-80)                   # 用来表示吃豆人的坐标
 22 ghosts = [
 23     [vector(-180,160),vector(5,0)],
 24     [vector(-180,-160),vector(0,5)],
 25     [vector(100,160),vector(0,-5)],
 26     [vector(100,-160),vector(-5,0)],
 27 ]                                          # 用来表示四个幽灵的初始坐标
 28 tiles = [
 29     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 30     0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
 31     0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
 32     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
 33     0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
 34     0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
 35     0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
 36     0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
 37     0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
 38     0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
 39     0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
 40     0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
 41     0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
 42     0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0,
 43     0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
 44     0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
 45     0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
 46     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
 47     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 48     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 49 ]                                         # 用来表示地图坐标,"1"表是在该位置画出一个蓝色的正方形,"0"表示不画
 50 
 51 # 用来画出背景世界的元素,一个边长为20个坐标单位的正方形
 52 def square(x,y):                                      # 用来画一个大小为20的正方形
 53     "Draw square using path at (x,y)."
 54     path.up()                                         # 提起画笔
 55     path.goto(x,y)                                    # 将画笔移动到坐标为(x,y)处
 56     path.down()                                       # 放下画笔
 57     path.begin_fill()                                 # 开始填充图形
 58     
 59     for count in range(4):                            # 生成一个包含[0,1,2,3]的序列并遍历
 60         path.forward(20)                              # 画笔向前移动20个坐标单位
 61         path.left(90)                                 # 画笔方向逆时针旋转90度
 62     
 63     path.end_fill()                                   # 结束填充
 64 
 65 # 实现坐标和地图列表索引值得切换
 66 def offset(point):
 67     "Return offset of point in titles."
 68     x = (floor(point.x,20) + 200)/20
 69     y = (180 - floor(point.y,20))/20
 70     index = int(x + y * 20)
 71     return index
 72 
 73 # 判断吃豆人和幽灵移动的下一个坐标是否在背景世界内
 74 def valid(point):                                     # 判断坐标是否在地图坐标中
 75     "Return True if point is valid in tiles."
 76     index = offset(point)                             # 调用offset函数,得到index的值,即tiles列表的索引值
 77     
 78     if tiles[index] == 0:                             # 如果索引index对应的列表titles的元素值为0
 79         return False                                  # 返回False
 80     
 81     index = offset(point + 19)                        # 移动到下一个坐标
 82     
 83     if tiles[index] == 0:                             # 再次判断
 84         return False
 85     
 86     return point.x % 20 == 0 or point.y % 20 == 0     # 当坐标在背景世界范围内,返回1
 87 
 88 # 游戏环境的创建
 89 def world():                                            # 画出背景世界
 90     "Draw world using path."
 91     bgcolor('black')                                    # 将背景世界的填充颜色设置为黑色
 92     path.color('blue')                                  # 将画笔的填充颜色设置为蓝色
 93     
 94     for index in range(len(tiles)):                     # 生成一个长度为len(tiles)的列表,并利用index遍历,将index看作列表tiles的索引
 95         tile = tiles[index]                             # 读取index索引对应的列表tiles元素的值
 96         
 97         if tile > 0:                                    # 如果列表元素值为"1"
 98             x = (index % 20) * 20 - 200                 # 通过index得到路径path的横坐标
 99             y = 180 - (index // 20) * 20                # 通过index得到路径path的纵坐标
100             square(x,y)                                 # 调用square函数,画出正方形
101             if tile == 1:                               # 如果列表元素值为1
102                 path.up()                               # 提起画笔
103                 path.goto(x + 10,y + 10)                # 画笔移动到(x+10,y+10)处
104                 path.dot(2,'white')                     # 调用dot函数,画出一个直径为2个坐标单位的点,用来表示豆子
105 
106 # 吃豆人和幽灵移动部分
107 def move():                                             # 控制吃豆人和幽灵的移动
108     "Move pacman and ghosts."
109     writer.undo()                                       # 撤销writer画笔最近的海归/画笔动作(前面画笔颜色是白色)
110     writer.write(state['score'])                        # 设定画笔书写对象是字典state中的关键字'score'对应的值
111     
112     clear()                                             # 从屏幕中删除writer海龟/画笔的绘图。不移动海龟/画笔。海龟的状态和位置以及其他海龟的绘图不受影响。
113     
114     # 吃豆人移动操作
115     if valid(pacman + aim):                             # 调用valid函数,如果函数返回值为真(即该坐标在可移动的范围内)
116         pacman.move(aim)                                # 调用move函数,让吃豆人移动到坐标(pacman.x+aim.x,pacman.y+aim.y)处
117     
118     index = offset(pacman)                              # 计算pacman新坐标对应的索引值
119     
120     # 统计吃到的豆子的部分
121     if tiles[index] == 1:                               # 如果index索引对应的列表元素值为1
122         tiles[index] = 2                                # 将index索引对应的值更新为2,表示该坐标的豆子已经被吃掉了
123         state['score'] += 1                             # 更新分数:每吃一颗豆子,分数加1
124         x = (index % 20) * 20 - 200                     # 将index索引值转化为坐标
125         y = 180 - (index // 20) * 20                    # 将index索引值转化为坐标
126         square(x,y)                                     # 调用square()函数将世界恢复原样
127     
128     # 画出吃豆人
129     up()                                                # 画笔拿起操作
130     goto(pacman.x + 10,pacman.y + 10)                   # 更新吃豆人的位置,移动到(pacman.x + 10,pacman.y + 10)处
131     dot(20,'yellow')                                    # 画出一个黄色的直径为20个坐标单位的点,用来表示吃豆人
132     
133     # 幽灵移动操作
134     for point,course in ghosts:                         # 遍历四个幽灵
135         if valid(point + course):                       # 判断幽灵的坐标是否有效,即在背景世界的活动范围里面
136             point.move(course)                          # 调用move函数,让幽灵移动到坐标(point.x+course.x,point.y+course.y)处
137         else:                                           # 更新course的值,用来控制幽灵的移动
138             options = [
139                 vector(5,0),
140                 vector(-5,0),
141                 vector(0,5),
142                 vector(0,-5),
143             ]                                           # 定义一个列表,用来随机更新course的坐标
144             plan = choice(options)                      # 从options列表中的四个坐标中任意返回一个坐标
145             course.x = plan.x                           # 更新course的横坐标
146             course.y = plan.y                           # 更新course的纵坐标
147         
148         # 画出幽灵
149         up()                                            # 提起画笔操作
150         goto(point.x + 10,point.y + 10)                 # 将画笔移动到坐标(point.x + 10,point.y + 10)处
151         dot(20,'red')                                   # 画出一个直径为20个坐标单位的点,用来表示幽灵
152     
153     update()                                            # 更新画布
154     
155     # 判断游戏是否结束
156     for point,course in ghosts:                         # 判断游戏是否结束
157         if abs(pacman - point) < 20:                    # 如果吃豆人和幽灵碰到一块儿
158             return                                      # 结束游戏
159     
160     ontimer(move,100)                                   # 每隔100ma重新调用move函数
161 
162 # 实现通过键盘控制吃豆人的移动方向
163 def change(x,y):                                        # 用来改变吃豆人的移动方向
164     "Change pacman aim if valid."
165     if valid(pacman + vector(x,y)):                     # 调用valid函数判断新的坐标是否在迷宫内
166         aim.x = x                                       # 更新坐标
167         aim.y = y                                       # 更新坐标
168 
169 setup(420,420,370,0)                                    # 初始化画布尺寸
170 hideturtle()                                            # 隐藏鼠标/海龟
171 tracer(False)                                           # 保证画图操作(食物和贪吃蛇身体)一次性完成
172 writer.goto(160,160)                                    # 将writer画笔移动到坐标(160,160)处
173 writer.color('white')                                   # 将画笔颜色初始化为白色
174 writer.write(state['score'])                            # 设定画笔书写对象是字典state中的关键字'score'对应的值
175 # 按键控制模块
176 listen()                                                # 监听,屏幕聚焦
177 onkey(lambda:change(5,0),'Right')                       # 右转,Right是键盘方向键的右键,不是输入单词Right
178 onkey(lambda:change(-5,0),'Left')                       # 左转
179 onkey(lambda:change(0,5),'Up')                          # 向上走
180 onkey(lambda:change(0,-5),'Down')                       # 向下走
181 world()                                                 # 调用world函数,画出背景世界/迷宫
182 move()                                                  # 调用move函数,开始游戏
183 done()

 

posted @ 2020-07-24 18:36  洛兰123  阅读(259)  评论(0编辑  收藏  举报