用 Python 实现一个简易版的 Pong 游戏 (二)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"回顾"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在第一部分,对 Pong 游戏进行了简单的介绍,以及演示了 Turtle 模块的使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了Turtle 的初步体验后,接下来将开始实现 Pong 游戏。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:由于篇幅所限,正文的代码区域不一定显示完整代码,\"......\" 代表省略的代码。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"游戏窗口配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Visual Studio Code 里新建一个 名为 "},{"type":"text","marks":[{"type":"italic"}],"text":"pong.py"},{"type":"text","text":" 的Python 文件,然后对它进行编辑。"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 引入turtle模块\nimport turtle\n# 创建一个 Screen 对象,并赋给变量 wn\nwn = turtle.Screen()\n# 设置程序窗口的标题为 Pong by @MatrixTech\nwn.title(\"Pong by @MatrixTech\")\n# 设置程序窗体的背景颜色为黑色\nwn.bgcolor(\"black\")\n# 设置绘图屏幕宽为 800 像素大小,高为 600 像素大小\nwn.setup(width=800, height=600)\n# tracer方法调用禁止动画显示\nwn.tracer(0)\n\n# 主循环\nwhile True:\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里特别注意的是,"},{"type":"text","marks":[{"type":"italic"}],"text":"wn.tracer(0)"},{"type":"text","text":" 使用tracer方法禁止动画显示,然后通过"},{"type":"text","marks":[{"type":"italic"}],"text":"wn.update()"},{"type":"text","text":" 对屏幕进行刷新,让绘制的图形一次性显示在窗口,无需漫长地等待绘制过程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行 "},{"type":"text","marks":[{"type":"italic"}],"text":"pong.py"},{"type":"text","text":":Visual Studio Code -> 右键-> Run Python File Terminal "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0f/0f727ae4091efec08f68c1b361a01959.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行成功后,会出现游戏应用的窗体:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4e/4e18adf78e523563866b4485f48b6f5d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"添加球拍和乒乓球"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"添加球拍 A"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 引入turtle模块\nimport turtle\n# 创建一个 Screen 对象,并赋给变量 wn\nwn = turtle.Screen()\n# 设置程序窗口的标题为 Pong by @MatrixTech\nwn.title(\"Pong by @MatrixTech\")\n# 设置程序窗体的背景颜色为黑色\nwn.bgcolor(\"black\")\n# 设置游戏屏幕宽为 800 像素大小,高为 600 像素大小\nwn.setup(width=800, height=600)\n# tracer方法调用禁止动画显示\nwn.tracer(0)\n\n# 球拍 A (左边球拍)\n# 通过 turtle 模块创建一个 Turtle 对象,并赋给变量 paddle_a\npaddle_a = turtle.Turtle()\n# 设置速度为0\npaddle_a.speed(0)\n# 设置形状为矩形\npaddle_a.shape(\"square\")\n# 设置颜色为白色\npaddle_a.color(\"white\")\n# 画笔擡起,移动时不画线\npaddle_a.penup()\n# 前往 x 座标为 -350, y 座标为 0 的位置\npaddle_a.goto(-350,0)\n\n# 球拍 B\n\n# 乒乓球\n\n\n# 游戏主循环\nwhile True:\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4f3b381f4df2edbd31d5a1b6f7a9ed23.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序运行后,球拍A是一个长和高都是20像素的方块,因此我们需要调整它形状大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 turtle.shapesize 方法可以调整球拍的形状大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"turtle.shapesize(stretch_wid=None, stretch_len=None, outline=None)"}]}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stretch_wid : 接受正数值,垂直方向拉伸;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stretch_len : 接受正数值,水平方向拉伸;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"outline :接受正数值,形状轮廓描边的宽度;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回或设置画笔的属性 x/y-拉伸因子和/或轮廓。海龟基于拉伸因子调整外观: "},{"type":"text","marks":[{"type":"italic"}],"text":"stretch_wid"},{"type":"text","text":" 为垂直于其朝向的宽度拉伸因子,"},{"type":"text","marks":[{"type":"italic"}],"text":"stretch_len"},{"type":"text","text":" 为水平于其朝向的长度拉伸因子,决定形状轮廓线的粗细。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"turtle.shapesize 方法的详细介绍可参阅手册 : "},{"type":"link","attrs":{"href":"https://docs.python.org/zh-cn/3/library/turtle.html#turtle.shapesize","title":null},"content":[{"type":"text","text":"https://docs.python.org/zh-cn/3/library/turtle.html#turtle.shapesize"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍A 方块原来是20*20 像素,现在垂直方向拉伸5,水平拉伸1,最终长水平方向为20像素,垂直方向为100像素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 球拍 A (左边球拍)\n# 通过 turtle 模块创建一个 Turtle 对象,并赋给变量 paddle_a\npaddle_a = turtle.Turtle()\n# 设置速度为0\npaddle_a.speed(0)\n# 设置形状为矩形\npaddle_a.shape(\"square\")\n# 设置颜色为白色\npaddle_a.color(\"white\")\n# 球拍A 在垂直方向拉伸5(100 像素),水平方向拉伸1(20 像素)\npaddle_a.shapesize(stretch_wid=5,stretch_len=1)\n# 画笔擡起 -- 移动时不画线\npaddle_a.penup()\n# 前往 x 座标为 -350, y 座标为 0 的位置\npaddle_a.goto(-350,0)\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/64/644c7ba17c9f2b1b7b37346fa465b235.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"添加球拍 B"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍B的代码与球拍A的代码相似,因此复制球拍A的代码块,做些许修改即可。"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 引入turtle模块\nimport turtle\n# 创建一个 Screen 对象,并赋给变量 wn\nwn = turtle.Screen()\n# 设置程序窗口的标题为 Pong by @MatrixTech\nwn.title(\"Pong by @MatrixTech\")\n# 设置程序窗体的背景颜色为黑色\nwn.bgcolor(\"black\")\n# 设置程序屏幕宽为 800 像素,高为 600 像素\nwn.setup(width=800, height=600)\n# tracer方法调用禁止动画显示\nwn.tracer(0)\n\n# 球拍 A (左边球拍)\n# 通过 turtle 模块创建一个 Turtle 对象,并赋给变量 paddle_a\npaddle_a = turtle.Turtle()\n# 设置速度为0\npaddle_a.speed(0)\n# 设置形状为矩形\npaddle_a.shape(\"square\")\n# 设置颜色为白色\npaddle_a.color(\"white\")\npaddle_a.shapesize(stretch_wid=5,stretch_len=1)\n# 画笔擡起 -- 移动时不画线\npaddle_a.penup()\n# 前往 x 座标为 -350, y 座标为 0 的位置\npaddle_a.goto(-350,0)\n\n# 球拍 B (右边球拍)\n# 通过 turtle 模块创建一个 Turtle 对象,并赋给变量 paddle_b\npaddle_b = turtle.Turtle()\n# 设置速度为0\npaddle_b.speed(0)\n# 设置形状为矩形\npaddle_b.shape(\"square\")\n# 设置颜色为白色\npaddle_b.color(\"white\")\npaddle_b.shapesize(stretch_wid=5,stretch_len=1)\n# 画笔擡起 -- 移动时不画线\npaddle_b.penup()\n# 前往 x 座标为 350, y 座标为 0 的位置\npaddle_b.goto(350,0)\n\n# 乒乓球\n\n\n# 游戏主循环\nwhile True:\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/53/5302e029c6859d7d7cf6989087226db8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"添加乒乓球"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在原点添加一个方块作乒乓求,添加的方法和上面的球拍添加一样, 代码如下:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 球拍 B (右边球拍)\n# 通过 turtle 模块创建一个 Turtle 对象,并赋给变量 paddle_b\npaddle_b = turtle.Turtle()\n# 设置速度为0\npaddle_b.speed(0)\n# 设置形状为矩形\npaddle_b.shape(\"square\")\n# 设置颜色为白色\npaddle_b.color(\"white\")\npaddle_b.shapesize(stretch_wid=5,stretch_len=1)\n# 画笔擡起 -- 移动时不画线\npaddle_b.penup()\n# 前往 x 座标为 350, y 座标为 0 的位置\npaddle_b.goto(350,0)\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\nball.penup()\nball.goto(0,0)\n\n# 游戏主循环\nwhile True:\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"实现球拍的移动"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"步骤"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建控制球拍垂直方向上下移动的函数;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把移动函数与键盘按键事件绑定,程序监听键盘按键事件;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用户按下被绑定的按键,触发移动的函数;"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"移动球拍 A"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍A 向上移动:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\nball.penup()\nball.goto(0,0)\n\n# 向上移动球拍 A\ndef paddle_a_up():\n \t# 获得 paddle_a 的y座标数值,并赋给变量 y\n y = paddle_a.ycor()\n # 在原来的数值基础上,增加20个像素\n y += 20\n # 重新设置 paddle_a 的y座标,使其向上移动20个像素的距离\n paddle_a.sety(y)\n\n# 键盘事件绑定\n# 事件监听\nwn.listen()\n# 键盘 w 键的按下事件与方法 paddle_a_up 绑定\nwn.onkeypress(paddle_a_up,'w')"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af4028c4da78729a09c7ce7fc9c744c7.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍A 向下移动:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 函数\n# 向上移动球拍 A\ndef paddle_a_up():\n \t# 获得 paddle_a 的 y 座标数值,并赋给变量 y\n y = paddle_a.ycor()\n # 在原来的数值基础上,增加20个像素\n y += 20\n # 重新设置 paddle_a 的y座标,使其向上移动20个像素的距离\n paddle_a.sety(y)\n\n# 向下移动球拍 A\ndef paddle_a_down():\n # 获得 paddle_a 的y座标数值,并赋给变量 y\n y = paddle_a.ycor()\n # 在原来的数值基础上,减少20个像素\n y -= 20\n # 重新设置 paddle_a 的y座标,使其向下移动20个像素的距离\n paddle_a.sety(y)\n\n# 键盘事件绑定\n# 事件监听\nwn.listen()\n# 键盘 w 键的按下事件与方法 paddle_a_up 绑定\nwn.onkeypress(paddle_a_up,'w')\n# 键盘 s 键的按下事件与方法 paddle_a_down 绑定\nwn.onkeypress(paddle_a_down,'s')\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f5b6de46621dad13a708294927800698.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"移动球拍 B"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍B的移动实现与球拍A的一样,可以参考球拍A移动的代码。"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 函数\n......\n......\n......\n\n# 向上移动球拍 B\ndef paddle_b_up():\n \t# 获得 paddle_b 的y座标数值,并赋给变量 y\n y = paddle_b.ycor()\n # 在原来的数值基础上,增加20个像素\n y += 20\n # 重新设置 paddle_b 的y座标,使其向上移动20个像素的距离\n paddle_b.sety(y)\n\n# 向下移动球拍 B\ndef paddle_b_down():\n # 获得 paddle_b 的y座标数值,并赋给变量 y\n y = paddle_b.ycor()\n # 在原来的数值基础上,减少20个像素\n y -= 20\n # 重新设置 paddle_b 的y座标,使其向下移动20个像素的距离\n paddle_b.sety(y) \n\n# 键盘事件绑定\n# 事件监听\nwn.listen()\n# 键盘 w 键的按下事件与方法 paddle_a_up 绑定\nwn.onkeypress(paddle_a_up,'w')\n# 键盘 s 键的按下事件与方法 paddle_a_down 绑定\nwn.onkeypress(paddle_a_down,'s')\n# 键盘 ↑ 键的按下事件与方法 paddle_b_up 绑定\nwn.onkeypress(paddle_b_up,'Up')\n# 键盘 ↑ 键的按下事件与方法 paddle_b_down 绑定\nwn.onkeypress(paddle_b_down,'Down')\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/67/67226f279ae74b83402916b1ea3e9e50.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"实现乒乓球的移动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序运行后,在游戏的主循环里不断地按一个座标增量去更新原先的乒乓球座标,从而实现乒乓球的移动。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"乒乓球的移动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为乒乓球添加座标的增量:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\nball.penup()\nball.goto(0,0)\n# 乒乓球的 x 座标增量\nball.dx = 2\n# 乒乓球的 y 座标增量\nball.dy = 2\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在游戏主循环中更新乒乓球座标:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 游戏主循环\nwhile True:\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()\n # 移动乒乓球\n ball.setx(ball.xcor() + ball.dx)\n ball.sety(ball.ycor() + ball.dy)\n \n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在添加上述代码后运行程序,就会看到乒乓球向右上角移动然后消失。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以为乒乓球添加边界检测解决其消失的问题,实现当乒乓球碰撞到窗口边界后反弹。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先把乒乓球对象的 ball.penup() 注释,让“钢笔”放下,这样海龟移动时候就出现轨迹,方便我们看看它碰撞边界后反弹的轨迹。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"上边界检测"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注释 ball.penup() :"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\n# ball.penup()\nball.goto(0,0)\n# 乒乓球的 x 座标增量\nball.dx = 2\n# 乒乓球的 y 座标增量\nball.dy = 2\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在游戏主循环添加上边界检测代码:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n......\n......\n\n# 游戏主循环\nwhile True:\n print(\"x: \",ball.xcor(),\" y: \",ball.ycor() )\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()\n # 移动乒乓球\n ball.setx(ball.xcor() + ball.dx)\n ball.sety(ball.ycor() + ball.dy)\n\n # 上边界检测\n if ball.ycor() > 290:# 当乒乓球的 y 座标大于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序后,看到效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4c85853eeceb3b59a3b608706314bfb5.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序启动后,乒乓球从座标原点(0,0)以增量 (dx=2,dy=2) 往右上角移动。当乒乓球的 y 座标大于 290 时,乒乓球被被反弹,以增量(dx=2,dy=-2)移动。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"下边界检测"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n......\n......\n\n# 游戏主循环\nwhile True:\n print(\"x: \",ball.xcor(),\" y: \",ball.ycor() )\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()\n # 移动乒乓球\n ball.setx(ball.xcor() + ball.dx)\n ball.sety(ball.ycor() + ball.dy)\n\n # 上边界检测\n if ball.ycor() > 290:# 当乒乓球的 y 座标大于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n # 下边界检测 \n if ball.ycor() < -290:# 当乒乓球的 y 座标小于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(-290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了查看效果,在运行程序之前,还需要修改乒乓球的座标增量如下:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\n# ball.penup()\nball.goto(0,0)\n# 乒乓球的 x 座标增量\nball.dx = 2\n# 乒乓球的 y 座标增量\nball.dy = -2"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把 "},{"type":"text","marks":[{"type":"italic"}],"text":"ball.dy = 2"},{"type":"text","text":" 改成 "},{"type":"text","marks":[{"type":"italic"}],"text":"ball.dy = -2"},{"type":"text","text":" 后,乒乓球在程序运行后会向右下方移动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看结果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/9476beefda84edd588410ec10a0d49a0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,已经完成了上下边界的检测,可以把 "},{"type":"text","marks":[{"type":"italic"}],"text":"ball.penup()"},{"type":"text","text":" 的注释取消:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\nball.penup()\nball.goto(0,0)\n# 乒乓球的 x 座标增量\nball.dx = 2\n# 乒乓球的 y 座标增量\nball.dy = 2\n\n......\n......\n......"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"左右边界检测"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 游戏主循环\nwhile True:\n\t\t......\n\t\t......\n\t\t......\n\n # 下边界检测\n if ball.ycor() < -290:# 当乒乓球的 y 座标小于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(-290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n\n # 右边界检测\n if ball.xcor() > 390: #当乒乓球的 y 座标大于 390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n\n # 左边界检测\n if ball.xcor() < -390: #当乒乓球的 y 座标小于 -390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"球拍 A 的 y 座标为 -350,球拍 B 的 y 座标为 350。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当乒乓球的 y 座标大于350 时候,说明球拍 B 没有接住乒乓球,球拍 B 失一分,球拍 A 得一分;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反之,当乒乓球的 y 座标小于 - 350 时候,说明球拍 A 没有接住乒乓球,球拍 A 失一分,球拍 B 得一分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看结果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/77af4fd599437b43e9accd55f1c9aa3f.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"实现球拍和球的碰撞检测"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 游戏主循环\nwhile True:\n\t......\n\t......\n\t......\n\n # 右边界检测\n if ball.xcor() < -390: #当乒乓球的 y 座标小于 -390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n\n # 乒乓球与球拍 B 的碰撞检测\n if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50 ):\n ball.setx(340)\n ball.dx *= -1\n\n # 乒乓球与球拍 A 的碰撞检测\n if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() - 50 ):\n ball.setx(-340)\n ball.dx *= -1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2e/2e898d85faa6e28d06aac85fbb25c23a.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分数计算"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"创建 Pen 绘制得分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 乒乓球\nball = turtle.Turtle()\nball.speed(0)\nball.shape(\"square\")\nball.color(\"white\")\nball.penup()\nball.goto(0,0)\n# 乒乓球的 x 座标增量\nball.dx = 2\n# 乒乓球的 y 座标增量\nball.dy = -2\n\n# Pen\npen = turtle.Turtle()\npen.speed(0)\npen.color(\"white\")\npen.penup()\n# 隐藏海龟,加快绘制速度\npen.hideturtle()\npen.goto(0,260)\npen.write(\"玩家 A: 0 玩家 B: 0\", align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/77d6537fc4282f7beee566e32806f9ad.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"计算得分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分别为玩家 A 和玩家 B 添加分数变量:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# Pen\npen = turtle.Turtle()\npen.speed(0)\npen.color(\"white\")\npen.penup()\n# 隐藏海龟,加快绘制速度\npen.hideturtle()\npen.goto(0,260)\npen.write(\"玩家 A: 0 玩家 B: 0\", align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n# 得分\nscore_a = 0\nscore_b = 0\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加计分逻辑代码:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n\n# 游戏主循环\nwhile True:\n \t......\n\t\t......\n\t\t......\n\n # 左边界检测\n if ball.xcor() > 390: #当乒乓球的 y 座标大于 390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # score_a 得 1 分\n score_a += 1 \n # 清除画笔 pen 的绘图\n pen.clear()\n # 重新绘制得分结果\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n # 右边界检测\n if ball.xcor() < -390: #当乒乓球的 y 座标小于 -390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # 玩家 B 得 1 分\n score_b += 1\n # 清除画笔 pen 的绘图\n pen.clear()\n # 重新绘制得分结果\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n\t\t......\n\t\t......\n\t\t......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"},"content":[{"type":"text","text":"运行程序,查看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f55d47fbfa998682666ce934fd42242f.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"添加音效"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"音效文件可在此下载: "},{"type":"link","attrs":{"href":"https://github.com/matrixtechxyz/Pong/blob/master/bounce.wav","title":""},"content":[{"type":"text","text":"https://github.com/matrixtechxyz/Pong/blob/master/bounce.wav"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"MacOS 系统"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在 MacOS 上开发 Pong, 可以通过引入 os 模块通过sytem 方法调用 afplay 命令播放音效文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在顶部引入 os 模块"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 引入turtle模块\nimport turtle\n# 引入 os 模块\nimport os\n\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在碰撞检测的地方添加音效:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 游戏主循环\nwhile True:\n # print(\"x: \",ball.xcor(),\" y: \",ball.ycor() )\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()\n # 移动乒乓球\n ball.setx(ball.xcor() + ball.dx)\n ball.sety(ball.ycor() + ball.dy)\n\n # 上边界检测\n if ball.ycor() > 290:# 当乒乓球的 y 座标大于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n os.system(\"afplay bounce.wav&\")\n\n\n # 下边界检测\n if ball.ycor() < -290:# 当乒乓球的 y 座标小于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(-290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n os.system(\"afplay bounce.wav&\")\n\n # 左边界检测\n if ball.xcor() > 390: #当乒乓球的 y 座标大于 390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # score_a 得 1 分\n score_a += 1 \n # 清除画笔的绘图\n pen.clear()\n # 重新生成新的绘图\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n # 右边界检测\n if ball.xcor() < -390: #当乒乓球的 y 座标小于 -390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # 玩家 B 得 1 分\n score_b += 1\n # 清除画笔的绘图\n pen.clear()\n # 重新生成新的绘图\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n\n # 乒乓球与球拍 B 的碰撞检测\n if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50 ):\n ball.setx(340)\n ball.dx *= -1\n os.system(\"afplay bounce.wav&\")\n\n # 乒乓球与球拍 A 的碰撞检测\n if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() - 50 ):\n ball.setx(-340)\n ball.dx *= -1\n os.system(\"afplay bounce.wav&\")"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Windows 系统"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在 Windows 系统下开发 Pong ,可以使用 winsound 模块播放 音效文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在源码文件顶部引入 winsound 模块:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 引入turtle模块\nimport turtle\n# Windows 系统发声模块\n#import winsound\n\n......\n......\n......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在碰撞检测的地方添加音效:"}]},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"......\n......\n......\n# 游戏主循环\nwhile True:\n # print(\"x: \",ball.xcor(),\" y: \",ball.ycor() )\n # 执行 TurtleScreen 刷新。在每帧绘制结束后调用update方法进行屏幕刷新,让绘制的图形一次性显示在窗口里。\n wn.update()\n # 移动乒乓球\n ball.setx(ball.xcor() + ball.dx)\n ball.sety(ball.ycor() + ball.dy)\n\n # 上边界检测\n if ball.ycor() > 290:# 当乒乓球的 y 座标大于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n # Windows 系统下播放 bounce.wav 音频文件\n #winsound.PlaySound(\"bounce.wav\", winsound.SND_ASYNC)\n\n\n # 下边界检测\n if ball.ycor() < -290:# 当乒乓球的 y 座标小于 290 像素时候\n # 设置乒乓球的 y 座标设置为290\n ball.sety(-290)\n # 把乒乓球 y 座标的增量变成 -2\n ball.dy *= -1\n # Windows 系统下播放 bounce.wav 音频文件\n #winsound.PlaySound(\"bounce.wav\", winsound.SND_ASYNC)\n\n # 左边界检测\n if ball.xcor() > 390: #当乒乓球的 y 座标大于 390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # score_a 得 1 分\n score_a += 1 \n # 清除画笔的绘图\n pen.clear()\n # 重新生成新的绘图\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n # 右边界检测\n if ball.xcor() < -390: #当乒乓球的 y 座标小于 -390 像素时候 \n # 乒乓球恢复到原点座标\n ball.goto(0,0)\n # 改变开球方向\n ball.dx *= -1\n # 玩家 B 得 1 分\n score_b += 1\n # 清除画笔的绘图\n pen.clear()\n # 重新生成新的绘图\n pen.write(\"玩家 A: {} 玩家 B: {}\".format(score_a,score_b), align=\"center\", font=(\"Courier\",24,\"normal\"))\n\n\n # 乒乓球与球拍 B 的碰撞检测\n if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50 ):\n ball.setx(340)\n ball.dx *= -1\n # Windows 系统下播放 bounce.wav 音频文件\n #winsound.PlaySound(\"bounce.wav\", winsound.SND_ASYNC)\n\n # 乒乓球与球拍 A 的碰撞检测\n if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() - 50 ):\n ball.setx(-340)\n ball.dx *= -1\n # Windows 系统下播放 bounce.wav 音频文件\n #winsound.PlaySound(\"bounce.wav\", winsound.SND_ASYNC)"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"源代码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源代码地址 : "},{"type":"link","attrs":{"href":"https://github.com/matrixtechxyz/Pong","title":null},"content":[{"type":"text","text":"https://github.com/matrixtechxyz/Pong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"参考资源"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.youtube.com/watch?v=C6jJg9Zan7w&t=380s","title":""},"content":[{"type":"text","text":"《Python Game Tutorial: Pong》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.python.org/zh-cn/3/library/turtle.html#turtle.hideturtle","title":""},"content":[{"type":"text","text":"《海龟绘图》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://yltang.net/tutorial/python/5/","title":""},"content":[{"type":"text","text":"小乌龟绘图模组"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://sites.google.com/site/ezpythoncolorcourse/useturtle","title":""},"content":[{"type":"text","text":"使用Turtle模组绘图"}]},{"type":"zerowidth"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://blog.csdn.net/weixin_41137248/article/details/81188801","title":""},"content":[{"type":"text","text":"Python学习(一):turtle库介绍与简单案例"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.python.org/zh-cn/3.7/library/winsound.html?highlight=winsound#module-winsound","title":""},"content":[{"type":"text","text":"windsound -- Suond-playing interface for Windows"}]},{"type":"zerowidth"},{"type":"zerowidth"},{"type":"zerowidth"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.matrixtech.xyz/2020/08/04/cs-python-turtle-pong/[https://zh.wikipedia.org/wiki/%E4%B9%93](https://zh.wikipedia.org/wiki/%E4%B9%93)","title":null},"content":[{"type":"text","text":"维基百科 Pong"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"zerowidth"}]},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"用 Python 实现一个简易版的 Pong 游戏 (一)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/273f9ff6dc0cc667bf80d5d93","title":""},"content":[{"type":"text","text":"https://xie.infoq.cn/article/273f9ff6dc0cc667bf80d5d93"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章