Scratch编程进阶:从数学原理到算法实现,掌握画圆与几何艺术创作
1. 从数学课到编程课:为什么“画圆”是Scratch进阶的必经之路
如果你问一个刚学Scratch的孩子,画一个圆难不难,他可能会觉得很简单——画笔工具里不是有个画圆的积木吗?点一下,输入半径,一个完美的圆就出现了。这确实是Scratch提供的一种“快捷方式”。但今天,我想从一个数学老师的视角,和你聊聊在Scratch里“画圆”这件事。它远不止是调用一个积木那么简单,而是一个绝佳的、融合了数学思维与编程逻辑的实战项目。
当我们抛开那个现成的“画圆”积木,尝试用最基础的“移动”和“旋转”指令,让角色自己“走”出一个圆时,我们实际上是在复现人类理解圆形几何本质的过程。这个过程,会逼着我们去思考:什么是半径?什么是360度?为什么角色移动一小段、旋转一小度,重复足够多次,就能形成一个圆?这背后是正多边形的极限思想,是角度与弧度的换算,是循环与变量的精妙配合。
对于学习者,尤其是青少年,通过编程来“构造”一个圆,其价值远超“绘制”一个圆。它能将抽象的数学概念(如圆周率π、角度、坐标系)转化为可视的、可交互的动态过程。当孩子调整一个参数,立刻看到图形发生的变化时,那种“原来如此”的顿悟感,是任何静态的教科书插图都无法给予的。所以,这篇内容,就是带你深入这个“构造”的过程。我们会从最朴素的思路开始,一步步推导、优化、扩展,直到你能游刃有余地控制Scratch画出任意大小、任意精度的圆,甚至创作出更复杂的图案。这不仅是学会一个技巧,更是掌握一种用计算思维解决几何问题的通用方法。
2. 核心原理拆解:不用“画圆”积木,圆是怎么“走”出来的?
在深入代码之前,我们必须先建立正确的数学模型。Scratch的舞台是一个480x360的坐标系,角色画笔的“画圆”,本质上是让角色沿着一个圆形路径运动,并沿途留下痕迹。
2.1 正多边形逼近法:理解“圆”的编程本质
最核心的思想叫做“正多边形逼近法”。想象一下,我们要画一个正三十六边形。让角色执行以下步骤:
- 落笔。
- 向前移动一段距离(这将是多边形的边长)。
- 向右旋转10度(因为360度 / 36 = 10度)。
- 将第2、3步重复36次。
- 拾笔。
你会发现,画出来的是一个非常接近圆形的三十六边形。当边数足够多时,比如360条边(每次旋转1度),肉眼就完全看不出棱角,这就是一个完美的“圆”了。这里,移动的“一段距离”并不是圆的半径,而是正多边形的一条边的长度。那么,如何根据我们想要的圆的半径(R)来计算每次移动的距离(即边长,记为步长)呢?
这就涉及到一个简单的数学公式:步长 ≈ 2 * π * R / 边数。
2 * π * R是圆的周长。- 除以
边数,就近似得到了正多边形每条边的长度。 - 当边数很大时,这个近似就非常精确。
在Scratch中,我们可以用(2 * 3.14159 * 半径) / 重复次数来计算步长。但这里有一个更巧妙的、不需要预先计算周长的方法,它直接利用了半径和角度的关系。
2.2 半径旋转法:更直观的“画圆”算法
另一种更符合直觉的方法是“半径旋转法”。我们不让角色沿着圆的切线方向走,而是想象角色始终站在“半径”的末端。
- 角色初始位置在圆心
(x0, y0)。 - 角色向0度方向(正右方)移动半径
R的距离,到达圆上的一点。 - 落笔。
- 角色以圆心为中心,旋转1度。但Scratch角色是围绕自身中心旋转的,如何实现绕圆心旋转?这里需要一点坐标计算。更简单的方法是:角色向圆心方向移动一个微小距离,然后再向外移动回圆周,但方向已经改变了1度。不过这种方法计算复杂。
实际上,在编程中更通用且易于理解的是参数方程法。圆上任意一点的坐标(x, y)可以用参数方程表示:
x = x0 + R * cos(角度)y = y0 + R * sin(角度)
如果我们让“角度”从0度循环增加到360度,并不断计算新的坐标,然后让角色移动到(x, y),就能画出圆。Scratch虽然没有直接的cos和sin函数,但它有相关的运算积木。然而,对于初学者,我们完全可以先用“正多边形逼近法”来构建直观理解,它所需的数学知识更少,更贴近“用动作画图”的思维。
注意:在向初学者讲解时,“正多边形逼近法”因其动作化、流程化的特点,更容易被接受。我们可以先教这个方法,等他们熟练后,再引入坐标和三角函数的概念,讲解更强大的“参数方程法”,这是一个循序渐进的认知过程。
3. 手把手实现:从36边形到光滑的圆
现在,我们进入Scratch实操环节。我将带领你实现一个可调节半径和精度的画圆程序。
3.1 基础版本:画一个固定半径的圆
我们首先实现一个半径为100的圆,用360边形来逼近(这样最光滑)。
初始化与变量创建:
- 新建一个项目,从角色库中添加一个你喜欢的角色(比如箭头
Arrow1),它将成为我们的“画笔”。 - 在“变量”分类中,创建以下变量(适用于所有角色):
半径:用来设置要画的圆的大小。边数:用来控制正多边形的边数,边数越多,圆越光滑。步长:根据半径和边数计算出的每次移动的距离。角度:每次旋转的角度,由360 / 边数计算得出。
- 将
半径设为100,边数设为360(先固定)。
- 新建一个项目,从角色库中添加一个你喜欢的角色(比如箭头
核心脚本编写: 将以下积木组合在一起,通常放在“当绿旗被点击”或“当角色被点击”下面。
当绿旗被点击 全部擦除 将 [半径 v] 设为 [100] 将 [边数 v] 设为 [360] 将 [角度 v] 设为 ((360) / (边数)) 将 [步长 v] 设为 (((2) * (3.14159)) * (半径)) / (边数)) 定位到 x: (0) y: (0) // 回到舞台中心,作为圆心 面向 (90) 度 // 面朝上,起始方向,也可以设为0度(右) 抬笔 移到圆心位置 // 这里假设圆心在(0,0),角色先走到圆周起点 移动 (半径) 步 落笔 重复执行 (边数) 次 移动 (步长) 步 右转 (角度) 度 结束 抬笔关键点解释:
将 [步长 v] 设为 (((2) * (3.14159)) * (半径)) / (边数)):这就是我们前面推导的公式,计算每条边的近似长度。使用3.14159作为π的近似值。重复执行 (边数) 次:循环边数次,每次画一条边并旋转。移动 (步长) 步和右转 (角度) 度:这是画图的核心动作。每次画一条很短很短的边(因为边数多),然后旋转一个很小的角度,最终路径闭合成一个圆。
运行与观察: 点击绿旗,你会看到角色从圆心出发,移动到圆周,然后开始画圆。由于边数设为360,画出的圆非常光滑。你可以尝试将
边数改为36、72等较小的数,观察图形如何从正多边形逐渐逼近圆形。这是一个非常直观的数学演示。
3.2 进阶优化:制作可交互的画圆工具
基础版本只能画固定大小的圆。我们来把它改造成一个可以通过输入框或滑块自由控制参数的工具。
添加输入控件:
- 在舞台上,右键点击
半径和边数这两个变量,选择“滑杆”。这样舞台上会出现对应的滑块,你可以拖动滑块实时改变它们的值。 - 或者,你也可以使用“询问并等待”积木,在运行前让用户输入。
- 在舞台上,右键点击
改造脚本,实现“一键重画”: 我们需要将画圆的逻辑封装成一个自定义积木(函数),这样可以随时调用,并且代码更清晰。
- 在“自制积木”分类中,点击“制作新的积木”,命名为
画圆。 - 在定义
画圆积木时,添加两个输入项:半径参数和边数参数。 - 将之前核心脚本中计算和循环的部分,拖到
定义 画圆 (半径参数) (边数参数)下面。 - 用输入项
半径参数和边数参数替换原来脚本中固定的半径和边数变量。
改造后的主脚本和自定义积木定义如下:
当绿旗被点击 全部擦除 定位到 x: (0) y: (0) 面向 (90) 度 抬笔 画圆 (半径) (边数) // 调用自定义积木,使用当前变量值 定义 画圆 (半径参数) (边数参数) 将 [角度 v] 设为 ((360) / (边数参数)) 将 [步长 v] 设为 (((2) * (3.14159)) * (半径参数)) / (边数参数)) 抬笔 定位到 x: (0) y: (0) 移动 (半径参数) 步 // 走到圆周起点 落笔 重复执行 (边数参数) 次 移动 (步长) 步 右转 (角度) 度 结束 抬笔- 在“自制积木”分类中,点击“制作新的积木”,命名为
实现动态效果: 为了让改变滑块时能实时看到效果,我们可以给变量添加“当变量值改变”的监控。
- 对于
半径和边数变量,在变量上右键,选择“勾选”以在舞台上显示。 - 然后,我们可以创建一个单独的脚本,用来监听变化并重画:
当 [半径 v] 发生变化 // 这是一个事件,需要从“事件”分类中拖出“当变量值改变”并选择“半径” 全部擦除 画圆 (半径) (边数) 当 [边数 v] 发生变化 // 同样处理边数变化 全部擦除 画圆 (半径) (边数)现在,你拖动
半径或边数的滑块,画布会自动擦除并根据新参数重新画圆。你可以清晰地看到,边数很小时是多边形,变大后就成了圆;半径滑块则控制圆的大小。- 对于
实操心得:在封装
画圆自定义积木时,务必处理好“笔的状态”和“角色的初始位置与方向”。一个常见的坑是,连续调用画圆时,因为前一次结束时笔可能落下或角色不在圆心,导致图形错乱。好的习惯是:在自定义积木的开头,明确设置笔的状态(抬笔)和角色的起点(归位到圆心并复位方向),在结尾再将笔抬起。这样能保证每次调用都是独立的、干净的。
4. 问题排查与性能优化:为什么我的圆画得慢或不圆?
在实际操作中,你可能会遇到一些意想不到的情况。我们来逐一分析并解决。
4.1 常见问题一:画出的圆有缺口或不闭合
现象:圆画完后,起点和终点没有完全重合,留下一个小缺口。根因分析:
- 计算精度误差:这是最常见的原因。我们使用的
步长 = 2 * π * R / N和角度 = 360 / N都是除法运算。Scratch(以及所有计算机)处理浮点数时存在微小的精度损失。当N不能被360整除时,角度可能是一个无限循环小数,计算机存储的近似值会导致最后一次旋转后,累计角度不是精确的360度。 - 角色初始位置偏差:如果角色不是从准确的圆周起点开始画,或者
移动 (半径) 步这一步因为方向问题有偏差,也会导致路径不闭合。
解决方案:
- 方法A(治标):增加
边数。边数越多,每条边的步长越短,单次误差的影响越小,缺口就越不明显。通常360边已经足够。 - 方法B(治本):采用“绝对坐标画法”,即参数方程法。这种方法直接计算每个点的坐标,然后让角色“瞬移”过去,不依赖于“移动”和“旋转”的累积,从根本上避免了累积误差。虽然对初学者稍难,但它是更精确的工业标准方法。其伪代码逻辑如下:
定义 用坐标画圆 (圆心x) (圆心y) (半径) (边数) 将 [i v] 设为 [0] 抬笔 重复执行 (边数) 次 将 [当前角度 v] 设为 ((i) * ((360) / (边数))) // 计算当前角度 将 [x坐标 v] 设为 ((圆心x) + ((半径) * ([cos v] 的 (当前角度)))) // 需要“运算符”里的cos 将 [y坐标 v] 设为 ((圆心y) + ((半径) * ([sin v] 的 (当前角度)))) 如果 <(i) = [0]> 那么 // 如果是第一个点,先移动过去再落笔 定位到 x: (x坐标) y: (y坐标) 落笔 否则 定位到 x: (x坐标) y: (y坐标) // 否则直接移动到下一点,笔已落下会自动画线 结束 将 [i v] 增加 (1) 结束 抬笔 - 方法C(实用技巧):在“正多边形逼近法”的循环结束后,强制让角色面向初始方向,并移动一小段距离连接到起点。或者,直接让
重复执行的次数比边数多1次,最后一次只移动不旋转,强行闭合。
4.2 常见问题二:画圆速度太慢,尤其是边数很多时
现象:当边数设置为1000甚至更大时,画圆过程变得非常缓慢,像慢动作。根因分析:Scratch是解释型语言,每个积木块的执行都需要时间。重复执行1000次,意味着要进行1000次“移动”、“右转”以及背后的屏幕刷新。此外,“画笔”功能在绘制大量线段时也会消耗较多资源。
优化方案:
- 减少不必要的屏幕刷新:在画非常复杂的图形前,可以使用“全部擦除”积木,但更重要的是,可以考虑在画图过程中暂时隐藏角色。因为角色的造型渲染也需要时间。使用
隐藏积木,画完后再显示,可以提升速度。 - 使用“自制积木”并选择“运行时不刷新屏幕”:这是Scratch里最重要的性能优化技巧!在“制作新的积木”时,对话框底部有一个复选框“运行时不刷新屏幕”。勾选它之后,Scratch会等这个自定义积木里的所有指令都执行完毕后,才一次性更新舞台画面。对于画圆这种由大量重复步骤组成的操作,速度提升是数量级的。
- 操作:修改我们之前创建的
画圆自定义积木,在定义时勾选“运行时不刷新屏幕”。 - 效果:你会看到圆几乎是瞬间出现的,而不是一段一段地动画绘制。
- 操作:修改我们之前创建的
- 合理设置边数:对于屏幕显示而言,边数达到360-720之间,人眼已经无法区分多边形和圆。盲目追求上万边数只会浪费性能,没有视觉收益。一般设置360或720是一个在效果和性能间很好的平衡点。
4.3 常见问题三:想画一个实心圆或者彩色圆环怎么办?
需求延伸:我们目前画的是空心圆。如何画一个实心的圆,或者一个有粗细、有渐变颜色的圆环?解决方案思路:
- 实心圆:一种方法是画很多个半径递增的同心圆。从半径1开始,到目标半径R结束,每次画一个圆,半径增加1。这样整个圆形区域就被填满了。你可以将这个逻辑放入一个循环中。
定义 画实心圆 (半径) 将 [当前半径 v] 设为 [1] 重复执行 (半径) 次 画圆 (当前半径) (360) // 调用之前定义的画圆积木,边数固定 将 [当前半径 v] 增加 (1) 结束注意:这种方法画大的实心圆会比较慢,因为要画很多个圆。另一种更高效的方法是使用Scratch的“图章”功能,但“图章”画的是角色造型,不是矢量线条。
- 彩色圆环/渐变圆:这需要用到“画笔”分类中的
将画笔颜色设为和将画笔饱和度设为等积木。我们可以在画圆的循环中,动态改变画笔的颜色。例如,让颜色值随着循环变量i的增加而改变。
这样画出来的圆,颜色会平滑地过渡,形成一个彩虹色的圆环。将 [颜色 v] 设为 [0] 重复执行 (边数) 次 将画笔颜色设为 (颜色) 移动 (步长) 步 右转 (角度) 度 将 [颜色 v] 增加 ((100) / (边数)) // 让颜色从0渐变到100 结束
5. 创意拓展:从画圆到构建复杂图案
掌握了精确画圆的能力,你就拥有了一把创作复杂几何艺术品的钥匙。圆的组合、变形、迭代,能产生令人惊叹的视觉效果。
5.1 画一组同心圆
这是最简单的拓展。只需固定圆心,用循环改变半径,反复调用画圆积木。
当绿旗被点击 全部擦除 定位到 x: (0) y: (0) 将 [r v] 设为 [10] // 起始半径 重复执行 (10) 次 // 画10个同心圆 画圆 (r) (360) 将 [r v] 增加 (20) // 半径每次增加20 结束你可以修改每次半径的增量、圆的个数、甚至每个圆的颜色,来创造不同的视觉效果。
5.2 画“繁花曲线”:旋转中的圆
这是一个非常经典的图案。原理是:让角色在画一个大圆的同时,自身也在缓慢旋转。这需要用到两个嵌套的循环。
- 外层循环:控制画多少个“花瓣”或完成多少圈旋转。例如重复360次。
- 内层循环:画一个完整的小圆。但这里的小圆半径不大。
- 关键:每画完一个小圆,角色在外层循环中旋转1度(360/360=1),然后开始画下一个小圆。由于画每个小圆时角色的方向都在变化,这些小圆的圆心分布在一个大圆周上,从而组合成花朵般的图案。
当绿旗被点击 全部擦除 定位到 x: (0) y: (0) 将 [花瓣数 v] 设为 [12] 将 [大半径 v] 设为 [100] // 花朵的“半径” 将 [小半径 v] 设为 [30] // 每个花瓣(小圆)的半径 抬笔 重复执行 (花瓣数) 次 // 1. 移动到当前花瓣的圆心位置 移动 (大半径) 步 // 2. 画一个小圆 落笔 重复执行 (36) 次 // 用36边形画小圆,速度快 移动 (((2) * (3.14159)) * (小半径) / (36)) 步 右转 (10) 度 // 360/36=10 结束 抬笔 // 3. 回到大圆中心 定位到 x: (0) y: (0) // 4. 旋转,准备画下一个花瓣 右转 ((360) / (花瓣数)) 度 结束调整花瓣数、大半径、小半径,你可以得到玫瑰、太阳花、齿轮等各式各样的图案。
5.3 结合随机性:创造动态艺术
让参数随机化,可以产生不可预测的、动态的艺术效果。例如,一个“随机彩色泡泡”生成器:
当绿旗被点击 全部擦除 将画笔粗细设为 (5) 重复执行 (20) 次 // 随机位置 定位到 x: (在 (-200) 到 (200) 间随机选一个数) y: (在 (-150) 到 (150) 间随机选一个数) // 随机颜色 将画笔颜色设为 (在 (1) 到 (100) 间随机选一个数) // 随机半径 将 [r v] 设为 (在 (10) 到 (50) 间随机选一个数) // 画圆 落笔 重复执行 (360) 次 移动 (((2) * (3.14159)) * (r) / (360)) 步 右转 (1) 度 结束 抬笔 结束每次运行,都会在舞台上随机位置生成20个大小、颜色各异的圆,像彩色的泡泡。
从理解一个简单的数学原理开始,到实现一个可交互的工具,再到排查问题、优化性能,最后进行创意发散——这就是“跟数学老师学Scratch”系列想传递的学习路径。画圆,只是一个起点。当你理解了如何用指令“构造”一个圆,你就掌握了用程序描述几何形状的基本能力。这种能力可以迁移到画正方形、画螺旋线、画任何由数学方程定义的曲线。更重要的是,你学会了将复杂问题分解(画圆 -> 画很多短边)、模式识别(重复移动旋转)、抽象(用变量表示半径和精度)和算法设计(正多边形逼近)这一套计算思维的核心流程。下次,当你再看到Scratch里那个简单的“画圆”积木时,你看到的将不再是一个黑箱工具,而是一个可以被你拆解、理解和再造的思维过程。这才是编程学习中最宝贵的部分。