重复执行与屏幕刷新

Scratch中多个指令串的运行规则,它会依照事件积木的启动顺序执行,并在多个指令串的暂停点之间来回切换(绿色表示正在执行,黄色表示等待执行)。

接下来请你继续思考,为什么重复执行积木的最后会有一个暂停点?

要回答这个问题,你首先需要了解一下Scratch中舞台的刷新机制。

我在之前的教程中讲解角色的造型时说过,你在舞台上所看见的动画效果,实际上是利用一张张静止的图片连续播放而成。当舞台从一张图片变为另一种图片时,我们就称舞台完成了一次刷新。为了保证人所看见的动画效果不会出现卡顿,必须对舞台每秒钟的刷新次数做一个限制。

动画中的一张图片也被称为“一帧”,每秒钟刷新的次数也被称为帧率open in new window

通常情况下,要保证动画不会出现明显卡顿,帧率必须至少为30帧/秒,这也正是Scratch中舞台的刷新帧率。也就是说1秒钟内,Scratch会对舞台完成30次刷新。简单的计算一下就知道,舞台每次刷新的时间是,1秒÷30次≈0.033秒/次。

接下来,请你完成一个小实验,测试一下当你使用重复执行积木让角色在舞台上移动时,每一次重复执行需要花费多少时间?

你会发现每一次重复执行的时间正好约等于0.033秒。

也就是说,Scratch会在执行一次重复执行时,完成一次舞台的刷新,并将时间控制在0.033秒。如此一来,就能保证舞台的刷新帧率为30帧/秒。

现在请你注意看我上面这段话中“完成一次舞台的刷新”这几个字,脑子里面有没有冒出什么疑惑。

  1. 舞台什么时候需要刷新?以及舞台什么时候执行刷新?
  2. 如果舞台不需要刷新,完成一次重复执行的时间还是0.033秒吗?

接下来,我们就从第一个疑惑开始解答。舞台什么时候需要刷新?

我想请你先回答一个问题。如果你在舞台上添加了一个角色。但是你没有使用积木块去改变它的位置、方向、造型、大小、特效、层级,或者也没有让它说话、思考,舞台的背景也一直保持不变。这时候舞台会出现变化吗?

根据上面的判断标准,你就可以把Scratch中的积木块分为两类。

第一类,在使用的过程中,不会造成舞台发生变化,例如控制分类中的条件判断、运算积木中的数学运算积木等,这时舞台就不需要被刷新。你可以给这类积木取个名字,叫做“不刷新积木”。

第二类,在使用的过程中,会造成舞台发生改变,例如运动分类中修改坐标和方向的积木,外观分类中修改造型、大小、特效积木等,这时舞台就需要被刷新。你同样可以把这类积木取个名字,叫做“刷新积木”。

接着,请你将Scratch想象成为一个人,它的手中拿着一个小本本和一支笔。在程序执行的过程中,它会观察你使用了哪些积木块。如果你使用的是“不刷新积木”,本子上就会写上“不用刷新”;如果你使用的是“刷新积木”,本子上就会写上“需要刷新”。最后根据本子上所写的内容,Scratch就可以知道是否需要对舞台进行刷新了。

接下来需要考虑的是,Scratch什么时候会去执行刷新?

Scratch什么时候执行刷新,与指令运行的规则相关。

首先来看看下面两个指令串。

如果单独运行左边的指令串,里面使用了“刷新积木”。根据运行规则所有积木会在执行队列中一次性执行完成,这时舞台只会执行一次刷新。你从舞台上看见的效果就是,角色只移动了一次,并且切换了一次造型。

如果单独执行右边的指令串,同样使用了“刷新积木”。每执行一次重复,程序就会到达暂停点,这时程序就会对舞台进行一次刷新。你从舞台上看见的效果就是,角色会同时移动和切换造型,总共重复执行3次。

现在请你思考下面这两个指令串,如果点击舞台上方的绿旗按钮,舞台会如何刷新?

具体的执行过程如下:

点击绿旗按钮后,执行队列会加入两个事件。

  1. 事件一重复执行第1次,运行下一个造型,到达暂停点,事件一暂停。此时舞台不刷新。
  2. 事件二重复执行第1次,运行移动10步,到达暂停点,事件二暂停。

此时执行队列中的所有事件到达暂停点,程序完成一轮操作,Scratch对舞台进行第一次刷新(同时切换角色的造型和移动10步)。

  1. 事件一重复执行第2次,运行下一个造型,达到暂停点,事件一暂停。此时舞台不刷新。
  2. 事件二重复执行第2次,运行移动10步,达到暂停点,事件二暂停。

此时执行队列中的所有事件再次到达暂停点,程序完成第二轮操作,Scratch对舞台进行第二次刷新(同时切换角色的造型和移动10步)。

  1. 事件一重复执行第3次,运行下一个造型,到达暂停点,事件一暂停。此时舞台不刷新。
  2. 事件二重复执行第3次,运行移动10步,到达暂停点,事件二暂停。

此时执行队列中的所有事件再次到达暂停点,程序完成第三轮操作,Scratch对舞台进行第三次刷新(同时切换角色的造型和移动10步)。

最终你从舞台上看见的效果就是,角色同时切换造型和移动10步,总共重复执行了3次。

为了便于看清上面描述的效果,你还可以在两个重复执行(3)次积木中各增加一个等待(1)秒积木。

以上我们就解答了第一个疑惑。你如果在一轮操作中使用了“刷新积木”,Scratch就会在该轮操作完成时,对舞台进行一次刷新。

接下来,我们继续来解答第二个疑惑。如果舞台不需要刷新,完成一次重复执行的时间还是0.033秒吗?

你可以把最开始用来测试重复执行时间的指令串修改一下,删除会造成舞台刷新的运动积木,然后运行指令重新测试。

你会发现列表中的数据全部都是0,也就是说每一次重复执行都没有花费时间。

类似的测试,你还可以换成下图的指令来做对比。增加一个计数器变量显示在舞台上,然后单独执行图中的其中一个指令串,观察计数器变量的增加速度有什么区别?

你会看见单独执行左边的指令串时,计数器的数值增加的比较慢

这是因为左边的指令串中使用了“刷新积木”,每完成一次重复,就要对舞台进行一次刷新。每次刷新的时间是0.033秒,所以1秒内计数器的变量值只会增加30。

单独执行右边的指令串时,计数器的数字增加的非常快

这是因为右边的指令串中使用的是“不刷新积木”,每次重复执行不再受到0.033秒的时间限制,所以计数器就会以1秒几万次的运行速度增加。

以上,也就解答了上面的第二个疑惑,如果舞台不需要刷新时,完成一次重复执行的时间会远远小于0.033秒。

现在两个疑惑已经解答,我想请你继续来思考一个问题。

还是利用上面的两个指令串,再增加一个变量计数器2,用来替换右边指令串中的变量。现在当你点击舞台上方的绿旗按钮时,两个计数器增加速度会是下面三个选项中的哪一个?

  1. 计数器1增加慢,计数器2增加快
  2. 计数器1和2都慢速增加
  3. 计数器1和2都快速增加

测试以后你会发现,答案是第2个选项,计数器1和2都慢速增加。

具体的执行过程类似于下面这样:

  1. 事件一重复执行第1次,运行移动10步,检测碰到边缘就反弹,计数器1增加1,数值变为1,达到暂停点,事件一暂停。此时舞台不刷新。
  2. 事件二重复执行第1次,计数器2增加1,数值变为1,达到暂停点,事件二暂停。

此时执行队列中的所有事件到达暂停点,完成第1轮操作。因为该轮事件一中使用了“刷新积木”,Scratch对舞台进行第1轮刷新。

  1. 事件一重复执行第2次,运行移动10步,检测碰到边缘就反弹,计数器增加1,数值变为2,达到暂停点,事件一暂停。此时舞台不刷新。
  2. 事件二重复执行第2次,计数器2增加1,数值变为2,达到暂停点,事件二暂停。

此时执行队列中的所有事件到达暂停点,完成第2轮操作。因为该轮事件一中使用了“刷新积木”,Scratch对舞台进行第2轮刷新。

  1. 不停的重复以上两步。

你可以将上述操作想象为,Scratch在开始执行一轮操作时,会先在小本本中写上“不用刷新”,然后开始监测这一轮中所有需要运行的积木块。如果其中包含了一个“刷新积木”(即使该积木并没有真正让舞台发生改变),Scratch就会将小本本中的“不用刷新”改为“需要刷新”。当这轮操作结束时,Scratch会根据小本本上记录的内容,决定是否对舞台执行刷新。

在上面这段描述中,有两点需要你注意:

  1. Scratch在判断是否需要刷新时,会对执行队列中的所有指令串,在某一轮中所有需要运行的积木块进行判断,只要其中包含一个“刷新积木”,所有指令串在本轮都会按照0.033秒的时间运行。

  2. 使用的“刷新积木”即使没有造成舞台发生改变,Scratch也会在小本本中写成“需要刷新”,从而在该轮结束时执行刷新操作。

关于第1点,在刚刚的实验中已经验证。关于第2点,你可以使用下图的指令进行实验。

点击绿旗按钮后,角色的坐标并没有发生改变,所以舞台不会发生任何改变。但是计数器的数值依然是缓慢增加。

最后,关于指令对屏幕刷新的影响,还有一个特殊的积木,隐藏积木。

在介绍隐藏积木和虚像特效的区别时,我打过一个比方。

使用虚像积木让角色消失时,就像是给角色穿上了一件隐身衣,虽然我们看不见它,但是角色还是会停留在舞台上。这时指令串中如果使用了“刷新积木”,Scratch依然会执行刷新操作。

使用隐藏积木让角色消失时,就像是把角色从舞台中删除。既然角色都被删除了,该角色所使用的“刷新积木”也就不再生效,Scratch也就不再执行刷新操作。

你可以单独执行下图中的两个指令串进行验证。

到此为止,我们就回答了为什么重复执行积木的最后会有一个暂停点?

Scratch为了保证动画的流程性,将舞台的帧率设置为30帧/秒。具体执行的过程中,它会对程序中使用的积木进行判断。如果执行队列在一轮操作中使用了“刷新积木”,它就会对舞台完成一次刷新操作。

Last Updated:
Contributors: lanheixingkong