news 2026/10/14 1:20:08

Scratch画圆进阶:用坐标与弧度实现圆心半径可控的圆

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Scratch画圆进阶:用坐标与弧度实现圆心半径可控的圆

我见过不少社团里的同学写“Scratch画圆”的脚本,十有八九是这套:重复执行360次,每次移动几步,再左转1度。跑起来确实能出个圆,可一旦要求“圆心放在(30,50),半径80”,整个脚本就崩了。要么画完再靠鼠标手动拖到差不多位置,要么干脆在圆心处画一个辅助点假装对上了。问题就出在:我们一直在“碰运气画圆”,而不是“算出一个圆”。

这次我想把一个非常实用又特别适合教学演示的思路完整拆出来:用多边形逼近法加弧度概念,在Scratch里画一个圆心和半径都完全可控的圆。这个思路不难,只需要用到画笔扩展、几个变量,再加一点点三角函数和弧度换角度的计算。学会之后,你能一步把圆的参数写死,想画哪个位置就画哪个位置,想改半径就直接改半径,顺带还能把圆弧、扇形、圆周运动这些玩法一起拿下。

1. 这个问题的难点,其实卡在“指定”两个字上

1.1 为什么传统的“移动+转向”法不够用

用“重复执行360次,移动2步,左转1度”画圆,思路很好理解:每次走一小段直线,转一个微小的角度,累积360度之后,折线就围成了一个近似圆。这个方法的本质就是“多边形逼近”,只是边数固定在了360条左右,每边很短,肉眼看起来已经很圆。

但问题也很明显。它根本没有定义圆心的概念。脚本运行时,角色从当前位置出发,走完一圈后回到近似原点;如果出发前你没把角色放在指定位置,那这个圆的圆心就和你要的位置毫无关系。更麻烦的是,每次画完后还想调整位置,只能整段脚本推倒重来,或者用“移到某坐标”这种笨方法把图形搬过去。这个做法在“随手画着玩”时无可厚非,但一旦要做一个精确的图形,比如轨道动画、地图坐标点、角色绕圈巡逻,就只能用坐标计算来兜底。

所以,真正要解决的是:我们不能只告诉角色“走多少步、转多少度”,而要告诉它“你去的每个点是什么坐标”。这就是圆心和半径可控的第一层突破。

1.2 多边形逼近法:用有限个顶点去模拟一个圆

圆的几何定义是所有到圆心距离等于半径的点的集合。注意,它要求的是“无数个点”。计算机没法真的画一条完美曲线,它只能用很多小直线段去拼。当直线段足够短、足够多的时候,肉眼就分不清这是直线还是曲线,看起来就是一个圆。

这就引出了“多边形逼近”的核心思想:把一个圆看成顶点均布在圆周上的正多边形。边数越多,离圆越近;边数越少,越容易看出棱角。比如边数只有8,画出来是个正八边形;边数到36,基本能看出圆的轮廓;边数到72甚至120,肉眼几乎无法分辨。用正多边形去逼近圆,本质就是“用线段去逼近曲线”。

这个思想不只在Scratch里有用,很多绘图软件做曲线渲染、3D引擎做圆形和球体建模时,底层用的都是同一招:把光滑曲面拆成有限个三角面或线段。所以这不是一个纯粹的课后作业技巧,而是图形学里最基本的思想之一。

1.3 指定圆心和半径,必须把坐标公式引进来

既然我们要把圆形当成一系列顶点来画,下一步就需要知道每个顶点的坐标。这里就要用到三角函数了。任意一个圆周上的点,如果圆心是( (x_0, y_0) ),半径是( R ),那么这个点和圆心的连线与水平方向的夹角是( \theta ),它的坐标就是:

[ x = x_0 + R \cos \theta ] [ y = y_0 + R \sin \theta ]

这个公式是整个项目的钥匙。只要我们把( \theta )从0度扫到360度,每扫过一个角度,就算出一个顶点坐标,再用画笔把相邻顶点连起来,一个指定圆心、指定半径的圆就画出来了。这里的“扫角度”是关键,而角度到底用什么单位、每次加多少,就需要引入弧度概念了。

2. 弧度概念:先把这个容易绕晕的地方讲透

2.1 弧度是怎么定义的,和角度有什么区别

我们平时常用的是角度制:把一个圆切成360份,每份是1度。这个单位生活里很好用,但在数学公式里不够自然。弧度制的思路完全不同:用弧长和半径的比值来量角度。

圆心角为( \alpha )弧度,就等于这段圆弧的长度( s )除以半径( r ),即:

[ \alpha = \frac{s}{r} ]

一个完整的圆,弧长是( 2\pi r ),所以完整圆的圆心角是( 2\pi )弧度。也就是说,360度=( 2\pi )弧度,180度=( \pi )弧度,90度=( \frac{\pi}{2} )弧度。

举个例子。半径是100的圆,如果圆心角是1弧度,那对应的弧长就是100。这个对应关系非常直观:角度是在“分一块饼”,弧度是在用“饼的半径去量饼的边有多长”。

对于画圆来说,弧度制的优势在于,它和圆周长天然挂钩。我们计算多边形顶点时,每两个相邻顶点之间的圆心角如果写成弧度,就是( \frac{2\pi}{n} ),表达简洁明了,还方便推边长、弧长等数据。实际操作时,我们还是要把弧度换算成Scratch能接受的角度单位。

2.2 Scratch三角运算模块用的是角度制,这是一个大坑

Scratch里有一个运算积木叫“cos”和“sin”,括号里填一个数字就能返回余弦值和正弦值。很多人直接在脚本里写:

cos(当前弧度)

这十有八九会出错。原因是Scratch的三角函数积木默认把输入值当作“度”,而不是“弧度”。你填进“1”的时候,它认为这是1度,返回的是cos(1°)约等于0.99985,而不是cos(1弧度)约等于0.54030。差别虽然不大,但等角度到了几十上百,偏差就非常明显。

所以正确的做法是:先把弧度转成角度,再交给三角函数积木。换算关系很简单:

[ \text{角度} = \text{弧度} \times \frac{180}{\pi} ]

反过来,角度转弧度就是乘以( \frac{\pi}{180} )。这篇项目里,我们会在脚本中定义一个“当前弧度”变量,然后每次在cos()和sin()里面写成“当前弧度 × 180 ÷ 圆周率”。这个转换步骤很多人忽略,但只要漏掉这一步,画出来的图形就可能完全偏离预期。

2.3 先记下这两个换算公式,后面写脚本直接用

既然要在Scratch里用弧度,最好的办法是先把圆周率设置成一个变量,免得每次打一长串小数。

  • 变量“圆周率”设为3.1415926;
  • 弧度转角度:当前弧度 × 180 ÷ 圆周率;
  • 角度转弧度:当前角度 × 圆周率 ÷ 180。

不要在脚本里把3.14159到处乱写,万一有人想提高精度,改一个变量比改成千上万处要方便得多。这个习惯对任何编程项目都一样:把常量做成变量,代码结构才清爽。

3. 脚本实现:直接用顶点坐标法画出可控圆

3.1 准备工作和舞台坐标说明

要画线,先得在Scratch里添加画笔扩展。具体操作是在编辑器左下角的“添加扩展”中选择“画笔”。添加之后,积木区会出现“全部清除”“落笔”“抬笔”“将笔的大小设为”等积木。

舞台的坐标系要提前说清楚。Scratch舞台宽480,高360,正中心是坐标原点(0,0),所以x坐标范围是-240到240,y坐标范围是-180到180。角色移到(0,0)就是舞台正中央,移到(240,180)是右上角。

这个坐标系特别重要,因为我们接下来所有计算都基于它。指定圆心就是指定一个舞台上的位置,比如(30,50),那就是中心往右30、往上50的位置。如果圆心坐标超出舞台范围,圆的一部分就会跑到屏幕外,这个后面会专门讲怎么排查。

3.2 顶点坐标公式和多边形边数的选择

我们画的是内接正多边形。半径为R,边数为n,则任意相邻两个顶点之间的圆心角是:

[ \theta = \frac{2\pi}{n} \text{弧度} ]

如果直接算两点之间的直线距离,也就是正多边形边长,可以用:

[ \text{边长} = 2R \sin\left(\frac{\pi}{n}\right) ]

所以当你用“移动步数”去画圆时,每步走的长度不是随便定的,要按这个公式算。如果用顶点坐标法,就不用管边长,直接让每个顶点的坐标按角度算出来即可。

关于边数,我的建议是取72左右。72条边的正多边形,每条边对应的圆心角是5度,画出来肉眼已经完全看不出棱角,而且计算量也不大,运行流畅。边数取36也行,轮廓略糙一点;取360最精细,但每秒计算量更大。如果是画普通的静态圆,72就已经足够了。

3.3 一步步搭出能直接运行的积木脚本

下面这个积木脚本是核心实现。变量方面需要准备:

变量名初始值示例含义
圆心X30圆心横坐标
圆心Y50圆心纵坐标
半径80圆半径
边数72正多边形边数
圆周率3.1415926圆周率常量
当前弧度0当前顶点的圆心角(弧度)

完整逻辑分四步:

  1. 清空舞台,抬笔,设置画笔粗细和颜色;
  2. 把角色移到第一个顶点,也就是角度为0时计算出的坐标,然后落笔;
  3. 重复执行“边数”次,每次把“当前弧度”增加( \frac{2\pi}{n} ),然后移动画笔到新顶点坐标;
  4. 循环结束后,最后一步恰好回到起点,圆自动闭合。

用积木描述大概长这样:

当绿旗被点击 全部清除 抬笔 将笔的大小设为 (2) 将笔的颜色设为 (红色) 将 圆心X 设为 (30) 将 圆心Y 设为 (50) 将 半径 设为 (80) 将 边数 设为 (72) 将 圆周率 设为 (3.1415926) 将 当前弧度 设为 (0) 将x坐标设为 (圆心X + (半径 * (cos ((当前弧度 * 180) / 圆周率)))) 将y坐标设为 (圆心Y + (半径 * (sin ((当前弧度 * 180) / 圆周率)))) 落笔 重复执行 (边数) 次 将 当前弧度 增加 ((2 * 圆周率) / 边数) 将x坐标设为 (圆心X + (半径 * (cos ((当前弧度 * 180) / 圆周率)))) 将y坐标设为 (圆心Y + (半径 * (sin ((当前弧度 * 180) / 圆周率))))

这里有几个容易出错的小地方,先提醒一下。

第一,落笔的位置必须在第一个顶点上。如果你让角色从圆心开始落笔,再进循环,那会从圆心拖出一条线连到圆上,非常难看。所以一定要在抬笔状态下先移动到第一个顶点,确认角标为0,然后再落笔。

第二,循环里是先增加弧度,再移动坐标。这个顺序不能反。如果先移动再增加弧度,第一个顶点会被跳过,画的圆会有缺口。更准确说,我们要保证循环执行“边数”次后,当前弧度刚好累加到( 2\pi ),回到初始点。

第三,cos和sin积木里必须做弧度转角度。上面的脚本写的是“当前弧度 × 180 ÷ 圆周率”,这正是弧度转角度。如果你直接在cos积木里放“当前弧度”,出来的一定是歪的。

3.4 用一组具体参数走一遍计算流程

我建议用这个脚本时,先跑一组好检查的数值,比如圆心(30,50),半径80。

  • 当前弧度=0时,第一个顶点坐标是: ( x = 30 + 80 \times \cos(0) = 110 ) ( y = 50 + 80 \times \sin(0) = 50 ) 这一点在圆心正右方80处,很容易检查。

  • 下一次循环,弧度变成( \frac{2\pi}{72} ),换算成角度是5度。计算得到: ( x \approx 30 + 80 \times \cos(5^\circ) \approx 109.70 ) ( y \approx 50 + 80 \times \sin(5^\circ) \approx 56.97 )

这两个点相当接近,所以画出来是一条约8.7步长的短直线。一直循环下去,最后当前弧度累加到360度,坐标回到(110,50),圆闭合。

如果你想验证脚本是否准确,可以直接检查几个特殊角度:

角度顶点坐标
0度(110,50)
90度(30,130)
180度(-50,50)
270度(30,-30)

这四点分别位于圆心的右、上、左、下,距离都是80,正好是半径。只要这四点对得上,整个圆的数学基础基本没跑偏。

3.5 如果非要用“移动步数”法画,边长该设多少

有些读者可能还是更习惯让角色用“移动+转向”的方式去画圆,这种写法也不是不行,只是圆心位置控制起来更绕。如果你想用这种传统方法,并且初始位置就是圆心,那可以这么做:

  1. 抬笔,移到圆心坐标;
  2. 面向0度方向,移动“半径”步长,到达第一个顶点;
  3. 落笔;
  4. 重复执行“边数”次:移动边长,左转( \frac{360}{边数} )度。

这里的边长要按正多边形内接圆半径计算:

[ \text{边长} = 2R \sin\left(\frac{180}{边数}\right) ]

注意Scratch的sin积木输入的是角度,所以在脚本里写“半径 × 2 × sin(180 ÷ 边数)”。这个式子就是前面弧度公式( 2R\sin(\pi/n) )经过角度转换后的形式。用这种方式画圆,虽然落笔点头一次给对了圆心,但因为角色是一圈圈绕回去的,累计误差比坐标法大,一旦边数多或者步长带小数,闭合处容易出现裂缝。我个人更推荐坐标法,排错更方便。

4. 实操过程记录:三版脚本的对比与踩坑

4.1 第一版:用移动加转向,圆心根本对不齐

我让社团里一个同学先用自己的老方法写,脚本大概是:重复执行360次,移动2步,左转1度。最后画出一个半径大约115的圆。我问他圆心在哪,他愣了半天,说大概是舞台中央偏右上。

这个版本的失败点在于:角色一开始移到哪个位置,这个位置就是整个圆的出发点,和真正的圆心没有明确关系。就像一个人原地转圈,不管他站在哪里都能转出一个圈,但你很难说这个圈的圆心是他脚底。代码里没有一个变量保存圆心坐标,自然无法满足“指定圆心”。

后来我把脚本改成:先移到(30,50),然后面向0度移动80步,再重复执行360次“移动约1.4步,左转1度”。出来的圆确实经过目标点附近,但如果我要求圆心在(30,50),它实际上是以(30,50)为起点绕出来的,圆心偏了不是一点点。这也是我下定决心切换到坐标法的原因。

4.2 第二版:坐标法画出来有缺口,问题出在循环次数

刚改成坐标法的时候,我用的是“重复执行72次,每次先更新坐标,再增加当前弧度”。结果画出来的圆从起点出发,最后一个点落在355度附近,和起点之间少了一道边,看上去有一个小缺口。

原因很简单:循环结束后,当前弧度还没走到360度,图形自然不会闭合。解决办法有两个,要么把循环次数改成73次,让它多走一步回到0度;要么像我前面的脚本一样,在每次循环开头就增加弧度,循环次数保持72次,最后一次增加后刚好是360度,自动闭合。这里没有标准答案,你只要确保“最后一个顶点等于第一个顶点”就行。

提示:检查图形是否闭合,最简单的方式是看最后一个点和第一个点坐标是否一致。可以用一个全局变量“起始x坐标”“起始y坐标”,在开笔前记下来,循环后用广播或判断比较一下。不需要真跑到舞台上量。

4.3 第三版:边数和性能的平衡怎么选

画圆时边数太少会看出多边形痕迹,边数太多脚本计算量大。我实测下来,静态画圆72条边是最舒服的选择。30条边会明显看到角,尤其大半径时尤其明显。120条边更平滑但提升空间有限,360条边跑起来也没问题,只是没太大必要。

如果这个脚本将来要用在“循环动画”里,比如每秒刷新30次,每次都要计算上百个点的坐标,那就要注意性能了。我建议动画场景下用36条边就够了,尤其在贴图上,其实玩家根本看不出区别。静态场景里追求圆润度,用72到120。

4.4 一个容易忽略的问题:坐标会超出舞台范围

Scratch舞台x坐标最大是240,y坐标最大是180。如果你把圆心放在(200, 100),半径又是150,那圆的右边会到350,远远超出画面,只能看到一半圆。很多人一开始就懵了:明明半径是150,怎么只显示一小段弧?

要提前检查,只需要确保圆心坐标加上半径后不越界:

[ \text{圆心X} - \text{半径} \ge -240,\text{圆心X} + \text{半径} \le 240 ] [ \text{圆心Y} - \text{半径} \ge -180,\text{圆心Y} + \text{半径} \le 180 ]

例如圆心(30,50)、半径80完全在范围内,因为左右范围是-50到110,上下范围是-30到130,非常安全。如果圆心(150,100)、半径100,圆的上方到200,超出舞台顶部180,那就会出现截断。设计脚本时,要确保圆心和半径的搭配不会超过这个边界,否则画出来的图就像被剪刀剪过一样。

4.5 常见问题速查表

现象可能原因解决办法
圆心到圆弧有多余线条在圆心位置落笔后才开始移坐标先在抬笔状态下移到一个顶点,再落笔
圆不闭合,差一个缺口循环次数不够,最后一个顶点没回到起点检查循环次数和弧度累加是否到2π
圆扭曲变形把弧度直接填进cos和sin积木用“当前弧度 × 180 ÷ 圆周率”转换
图形边缘有棱角边数太少把边数调到72以上
圆的一半跑出屏幕圆心或半径超出舞台范围重新调整圆心坐标或缩小半径
画笔轨迹太粗,看不清圆润感画笔大小设置过大画笔大小调到1或2,方便观察顶点和连线

5. 画完这个圆之后,还能顺手做出哪些东西

5.1 画扇形和圆弧

如果你只要画一段圆弧而不是完整的圆,只需把循环次数从“边数”改成“目标角度对应的边数”。比如半圆,边数取72的话,半圆就是36个间隔,循环36次即可。如果想画从30度到120度的任意弧段,只要把“当前弧度”的初始值设成30度对应的弧度,把循环次数设成90度对应的间隔数,而不是每次都从0开始,图形就会非常精准。

这种方法特别适合画统计图里的饼图、时钟、仪表盘,或者各种弧形装饰。唯一要留心的是,画完圆弧后如果需要封闭扇形,记得再画两条从圆心到弧两端点的线段。

5.2 做圆周运动:让角色围绕一个点转圈

既然我们已经有了圆心坐标和半径,只要把“落笔”去掉,把“重复执行n次”改成“重复执行”,角色就可以沿着圆形轨道连续移动。比如让一个小球模拟行星绕中心转,每帧让“当前弧度”增加一点,然后更新坐标:

重复执行 将 当前弧度 增加 (0.05) 将x坐标设为 (圆心X + (半径 * cos(当前弧度 * 180 / 圆周率))) 将y坐标设为 (圆心Y + (半径 * sin(当前弧度 * 180 / 圆周率)))

这个改动几乎不费吹灰之力,就从一个静态图形变成了一个动画效果。做巡逻的敌人、围绕主角转圈的护卫、摆钟摆锤,都是同一套数学。这也是我为什么特别推荐掌握这个画圆思路的原因:它不是只能画一条线,而是把“一个点如何围绕另一个点运动”的基础模型装进了脚本里。

5.3 改变半径,顺手画出螺旋

如果把“半径”从常量改成变量,并在循环里让它逐渐增加,那画出来的就不再是圆,而是阿基米德螺旋。比如每走一步,半径增加0.5:

将 半径 增加 (0.5) 将x坐标设为 (圆心X + (半径 * cos(当前弧度 * 180 / 圆周率))) 将y坐标设为 (圆心Y + (半径 * sin(当前弧度 * 180 / 圆周率)))

半径逐渐变大,图形一圈圈向外扩大。这个效果很多特效里会用到,比如旋涡、雷达扫描线、装饰性花纹。核心改动就这么一点点,但能延伸出来的玩法非常多。

最后说点我个人的体会。我一开始觉得“画个圆还要扯到弧度,太小题大做”,但真正做完这个项目后,才理解为什么老师总强调“先把数学公式写出来,再动手拼积木”。坐标公式放在那里,圆心、半径、角度三个变量一改,整个图形就完全变样;而同样的道理放在移动转向法里,改一个参数就得重新调试半天。

如果你也是第一次接触这种“用坐标算图形”的思路,建议先用我给的参数把圆画出来,再改成自己的圆心和半径。等你跑通了第一版,试着把边数从10、36、72分别调一遍,看一眼图形的变化,你会对“多边形逼近”这四个字有更直观的感受。再往后,把圆变成圆弧,把圆弧变成圆周运动,就是水到渠成的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 1:19:46

Python入门练手:逢七拍腿游戏实现与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:19:33

ICME23 知识蒸馏复现 · SLDNet阶段性沉淀

ICME23 知识蒸馏复现 阶段性沉淀项目:ICME 2023《Collaborative Spatial-Temporal Distillation for Efficient Video Deraining》 数据:NTURain(合成测试集 8 序列 真实雨 7 序列) 阶段:链路打通并验证,…

作者头像 李华
网站建设 2026/10/14 1:19:28

Python爬虫实战:requests+BeautifulSoup采集豆瓣电影TOP250

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:19:14

OpenSSL 3.0迁移实战:提供者机制与废弃API避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:19:07

新媒体增长全链路:从引流到裂变的完整实操流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:19:01

STM32 IAP跳转失败原因全解析:从向量表到中断残留的排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华