綠旗不同步

綠旗不同步 1 -Scratch內部執行機制

在2020年年中(大概6月)以前,綠旗不同步的問題很容易發生,雖然後來 Scratch 更新版本後此問題不再出現,但這問題讓我們注意到在 Scratch 中也要有良好的程式架構,必須對變數先進行初始化,再開始執行各角色的程式。

Scratch 的初學者會在相同或不同的角色中用到很多『當綠旗被點擊』積木,如果再與變數搭配起來,就會產生一些莫名的 bug,傑夫老師就先來說說問題是什麼!

當時要做一個貓咪與鴨子跳繩跳 10 秒的小程式,貓咪的程式如下。左邊的程式是一個自製的倒數計時器,主要有個「時間」這個變數,而貓咪在綠旗點擊後,利用『重複直到』開始跳躍(其實是滑行)直到「時間」為0。

貓咪的程式,主要是變數「時間」

貓咪的程式,主要是「時間」這個變數

鴨子的程式除了沒有自製計時器的部分,跳躍的程式與貓咪幾乎一樣。跳繩的程式也是利用了『重複直到』直到「時間」為0,就不貼程式了。

鴨子的程式與貓咪跳躍的程式幾乎相同

鴨子的程式與貓咪跳躍的程式幾乎相同

在2020年年中改版前,只要先完整執行一次讓時間變數為 0,再按綠旗執行會發現鴨子與跳繩不動了,但貓咪還會跳。

執行的結果

當時執行的結果,現在此問題已經解決

因為綠旗按下時,無法掌握哪個角色的哪一段程式會被先執行,這就是所謂的綠旗不同步的問題。這個跳繩專案中,主要在於「時間」這個變數被初始化設為 10 的時機比鴨子與跳繩程式開始的時間晚,導致再次執行時,鴨子與跳繩不動了。

這問題讓我們注意到在 Scratch 中也要有良好的程式架構,必須確保綠旗被按下後變數都能先完成初始化,再開始執行各個角色的程式。解決的方式很簡單,利用一個廣播就能做的到。

首先,程式修改成當綠旗按下時只進行變數「時間」的初始化,接著建立一個新的廣播訊息「開始計時」,並發出這個訊息。而倒數計時的程式,改由收到「開始計時」這個訊息後才開始。

綠旗按下時只進行變數「時間」的初始化,接著發出廣播訊息

綠旗按下時只進行變數「時間」的初始化,接著發出廣播訊息

而貓咪、鴨子與跳繩也都是改成收到「開始計時」這個廣播訊息後,才開始動起來,不再是直接從『當綠旗被點擊』就開始。下面是貓咪的程式,鴨子與跳繩的程式幾乎相同。

貓咪的程式

貓咪的程式

雖然原本的作法現在(2020年8月)不會再發生問題,但是如果 Scratch 專案中用的角色與變數越來越多,就更應該注意到變數初始化的問題,這也是從新手邁向高手的第一步。

下一篇,傑夫老師會繼續介紹 Scratch 內部是如何運行的,說明綠旗不同步發生的原因到底是什麼。

綠旗不同步 2 -Scratch內部執行機制

前篇介紹了綠旗被按下時,程式積木串沒有同步執行的問題,這一篇就來說明其原理。這篇內容主要翻譯自 Scratch 專家 Cliff Davies 的 "Scratch and its inner workings"

Cliff Davies "Scratch and its inner workings"

Scratch 程式如何同步執行?

其實並沒有同步執行!Scratch 其實並沒有真的讓各個程式積木串同時執行起來,而是輪流執行,Scratch 源碼裡面稱之為執行緒 "threads"。當綠旗按下前當然沒有任何執行緒要被執行,一旦綠旗被按下時接在『當綠旗被點擊』下的程式積木串,就形成了一個有效 (active) 執行緒並且被加入執行緒列表中準備被執行。

假設有個小專案就只有下面兩個以『當綠旗被點擊』為啟始的程式積木串。

兩串程式積木先後被執行,但無法知道其先後

兩串程式積木先後被執行,但無法知道其先後

當綠旗被按下時,上面的積木串形成了兩個有效的執行緒,但重點是無法控制哪一個會被先加入執行緒列表中(或說雖然可以研究出加入的順序但還不如就當它是隨機的要簡單許多)。所以,不管是左邊的還是右邊的程式積木先被執行,一邊的程式積木串被執行完畢之後就會接著執行另一邊的。就Scratch內部而言,就是有兩個有效又簡短的有效執行緒要被執行,而當這兩個執行緒都被執行完畢後,執行緒列表也就跟著清空了。

起始型積木(或Scratch正式稱為帽子型積木)為首的程式積木串都能成為有效執行緒被加到執行緒列表中,主要是因為它們要負責接收包含綠旗被按下、音量達到某個程度或收到廣播訊息等各式各樣的事件。下面這些就是在事件類型中常見到的起始型積木。

事件類型中常見到的起始型積木

事件類型中常見到的起始型積木

例如你的專案有這麼樣的一個程式積木串

專案有一個這樣的程式積木串

專案有一個這樣的程式積木串

因為執行緒列表會一直被監測並執行,按下空白鍵時觸發了這個事件,並將此程式積木串變成了執行緒並加到執行緒列表中並被執行,即便綠旗並沒有被按下,整個專案並沒有被執行起來,按下空白鍵也能讓角色說話。

單獨點擊執行一個程式積木

單獨點擊執行一個程式積木

所以,綠旗不同步並不是問題,而是 Scratch 內部執行的機制。Cliff 的文章還有更多說明,傑夫老師後續會繼續翻譯與說明下去。

本篇文章主要翻譯自 "Scratch and its inner workings",作者 Cliff Davies 是一位 Scratch 專家,也是 Facebook "Teaching with Scratch" 群組管理員,他的 Scratch 帳號 Youtube 頻道皆為 "RokCoder",是要成為 Scratch 高手學習的對象。

綠旗不同步 3 -Scratch內部執行機制

前篇介紹了 Scratch 內部利用了有效執行緒與 執行緒列表 來執行每一個程式積木串,這一篇就來看看 Scratch 專家 Cliff Davies 在 "Scratch and its inner workings" 中所下的結論。

例如你的專案有這三個程式積木串,你希望按下綠旗後角色從舞台中心開始朝45度方向移動並碰到邊緣就反彈,還有在按下空白鍵時角色停止移動。

專案有這三個程式積木串

專案有這三個程式積木串

由於不知道兩個以『當綠旗被點擊』起始的兩個程式積木串哪一個會被先加到執行續列表中。如果你執行過一次專案並按了空白鍵來停止,當綠再被按下時有可能發生以下兩種結果:

  1. 第一種結果,最左邊的程式積木串先被執行,接著再執行中間的程式積木串。那這樣結果就會如所預期的角色再次動了起來,直到空白鍵被按下。

  2. 另一種結果,中間的程式積木串先被執行了,然而當時變數「game over?」還處於前一次結束時的狀態,也就是"true",就造成了中間的程式積木立刻結束重複直到迴圈停止執行,再輪到執行最左邊的程式積木串,此時才把變數「game over?」初始化設為"false"。如果這時候再(第三次)按下綠旗,角色才會如預期的動起來。這也就是在2020年上半年常遇到的問題。

瞭解了 Scratch 內部執行的機制,就能更清楚的知道所謂「綠旗不同步」的問題發生的原因。然而,這篇文章當然不只有提到這些,傑夫老師之後會藉由其他問題,再將此文章內容翻譯出來。

下一篇,會再回到第一篇的專案,看看現在 (2020 年 8 月) 還是不是有一樣的問題,也再驗證一下執行緒列表的說法是否正確。

本篇文章主要翻譯自 "Scratch and its inner workings",作者 Cliff Davies 是一位 Scratch 專家,也是 Facebook "Teaching with Scratch" 群組管理員,他的 Scratch 帳號 Youtube 頻道皆為 "RokCoder",是要成為 Scratch 高手學習的對象。

綠旗不同步 4 -Scratch內部執行機制

在第一篇中提出了「綠旗不同步」這個問題,但可能有人會想問:傑夫老師發現問題是在2020年5月,但又說這問題到了8月就沒再出現了,那現在這個問題算是有解了嗎?

傑夫老師與 Cliff 討論過後,他說這個問題一定還會存在,因為執行緒列表的機制沒變,儘管 Scratch 在 2020 年中做了一些改變,但問題應該只是換了個形式出現。

如果現在 (2020 年 8 月) 重新製作一個一模一樣的專案,結果很有可能是不管按下幾次綠旗,三個角色都能如預期的動起來。

下面是貓咪與鴨子的程式:

貓咪的程式積木串

貓咪的程式積木串

鴨子的程式積木串

鴨子的程式積木串

問題看似解決了,但其實不然,如果把貓咪的兩個程式串『當綠旗被點擊』下的積木"互換",那麼問題就又發生了。下面是互換的方法:

"互換" 是將綠旗下面的積木互換

"互換" 是將綠旗下面的積木互換

互換之後結果如下:(注意變數「時間」已經是 0 )

積木互換之後的結果

積木互換之後的結果

結果是貓咪不動,但鴨子與跳繩依然跳得好好的。如果能理解前面兩篇內部執行機制的人對這個結果應該不會感到意外,貓咪的兩個程式積木被互換了內容,但位在左邊的程式積木依然會被先加到執行緒列表中,所以原本先執行的『變數時間設為 0』延後了執行,導致貓咪不會跳了。

說了那麼多,要教給大家的還是一樣,綠旗被按下時要先做好變數的初始化,再利用廣播訊息讓各個角色動起來,這樣就不會因為不同步的問題產生一些莫名的 bug 了。

希望對大家有幫助。Bye now~