SmalltalkからScratchをいじる(3) スプライトの位置を表示する


プログラムが期待した通りに動かないときに、内部状態を見たいことがあります。
今回はその一例としてプログラム実行中のスプライトの位置を表示するようにScratchを改造します。（Smalltalkを使えるようにする方法やハロの出し方等は[こちら](http://qiita.com/maeda_/items/7a525cae6b3c1ca45773)を参照）

※今回はScratchのコードをいじります。必ずバックアップをとっておきましょう。（バックアップ手順は[こちら](http://qiita.com/maeda_/items/7076dc32816259a0117a)）

# やりたいこと
以下のスクリプトがあるとします。（[ここ](https://dl.dropboxusercontent.com/u/55482425/move.sb)からダウンロードできます。）
![0_script.png](https://qiita-image-store.s3.amazonaws.com/0/12251/69351873-640a-374c-c40f-316c81788c38.png)

旗をクリックして実行した時のスプライトの位置を文字情報として、逐次表示するようにします。
![result.png](https://qiita-image-store.s3.amazonaws.com/0/12251/c74ef92c-0252-7967-b7c5-a10b8a80862a.png)
※残念ながらスプライト名が日本語の場合は文字化けしてしまいます


# 手順
1. スプライトを動かす方の「旗がクリックされたとき」のブロックにハロを表示します。ハロの下側に"EventHat"と表示されます。レンチのアイコンをクリックして「inspect morph」でインスペクタを開きます。
![halo.png](https://qiita-image-store.s3.amazonaws.com/0/12251/f92e1fee-70e0-838f-68f0-4e36c0258fc7.png)


2. 画面上部左側の枠を少しスクロールさせて、"scratchProc"をクリックします。scratchProcの中身を表示したまま、旗をクリックしてScratchを動かしたり、止めたりしてください。このブロックが実行中の場合は"a Scratch Process"と、止まっている場合は"nil"と表示されます。
![inspector.png](https://qiita-image-store.s3.amazonaws.com/0/12251/327aceac-35a3-7d74-5e7e-21a98d32e1a0.png)
このオブジェクトはScratchのブロックの実行を管理するためのオブジェクトです。
3. ブロックが実行されている時に"scratchProc"のところでメニューを表示（Windowsの場合は右クリック、macの場合はoption+クリック）し、"browse full"を選択します。(Alt/Command + b でも可)
![menu.png](https://qiita-image-store.s3.amazonaws.com/0/12251/be2e2590-5c45-2d32-1ac1-89a29f0ea400.png)

4. 緑のウィンドウが開きます。これはシステムブラウザ（またはクラスブラウザ）と呼ばれ、ScratchやScratchを動かしているSmalltalk自体のコードを閲覧、変更することができます。
![browser.png](https://qiita-image-store.s3.amazonaws.com/0/12251/790c6672-dba9-a5e6-756a-6e6d56965395.png)

5. ウィンドウ上部の右から2番目の枠から"entry point"をクリックし、その後、一番右の枠から"runStepFor:"をクリックします。
 
6. ウィンドウ下部に"runStepFor:"メソッドのコードが表示されます。3行目（| t2 |と書かれている下）に```self halt.```という行を挿入します。（最後のドットを忘れずに）
![browser2.png](https://qiita-image-store.s3.amazonaws.com/0/12251/789cc5f1-7216-9242-5296-580d1dc32d99.png)
```self halt.```は実行を一時停止させるための命令です。
7. コードの変更が終わったら、変更を反映させるために、メニューを表示させて、"accept"をクリックします。(Alt/Command + s でも可)
![accept.png](https://qiita-image-store.s3.amazonaws.com/0/12251/e5071b0e-6455-98e7-017d-0005de56708b.png)

8. 初めてacceptを実行した場合、変更した人の名前（イニシャル）を聞かれます。Smalltalkはコードを変更した場合、だれがいつ、どのような変更をしたのか記録されます。あなたのイニシャルを入力して、Acceptをクリックしてください。
![name.png](https://qiita-image-store.s3.amazonaws.com/0/12251/6f624aaa-4c0f-c780-2994-29fcbbe013f9.png)
9. Scratchが実行状態になると、下記の赤いウィンドウが表示されます。先ほど挿入した```self halt```が実行されると、このウィンドウが表示されます。
![halt.png](https://qiita-image-store.s3.amazonaws.com/0/12251/842c1799-2a19-683e-cb4d-7c52f6ab6510.png)
この時Scratchの世界は停止しています。Scratchのウィンドウを見ると、スプライトはマウスカーソルを追いかけず停止したままになっています。
10. ウィンドウ上部の一番右の"Debug"をクリックしてください。別のウィンドウが表示されます。このウィンドウをデバッガ(Debugger)といいます。画面上部の上から3番目の"ScratchProcess>>runStepFor:"をクリックしてください。
![debugger.png](https://qiita-image-store.s3.amazonaws.com/0/12251/59862fa2-efab-518a-760f-1f67fdbd6b7d.png)
先ほど編集した"runStepFor:"のコードが表示され、haltのところがハイライトされています。haltのところで止まっていることを表します。
11. インスペクタからコードを実行できたように、デバッガからもコードを実行できます。先ほどの```self halt```を削除して、```topBlock addHalo: nil```と入力してください。入力した文字だけを選択し、"do it"(Alt/Command + d)をしてみてください。
![doit.png](https://qiita-image-store.s3.amazonaws.com/0/12251/c7ceb063-bcf6-2dbd-3b08-96d28e3f90a5.png)
ScratchProcessオブジェクトが持っているtopBlockは最初に処理されるブロックを表します。「旗がクリックされたとき」のブロックにハロが表示されます。
12. 今度はデバッガ上で、```topBlock scriptOwner xpos```と入力してください。入力した文字だけを選択し、"print it"(Alt/Command + p)をしてみてください。
![printit.png](https://qiita-image-store.s3.amazonaws.com/0/12251/1d9f4c5c-3c39-e40b-6209-70ab0fb94c57.png)
```topBlock scriptOwner```でスプライトを取得し、そのx座標を表示しました。
13. 次にスプライトの位置情報を表示するためのウィンドウを起動します。文字表示用のウィンドウをトランスクリプト(Transcript)と呼びます。デバッガはそのままにして、背景をクリックしてメニューを開き、"open"→"transcript"でトランスクリプトが表示されます。
![transcript.png](https://qiita-image-store.s3.amazonaws.com/0/12251/6f39aaa6-a15f-1446-f552-291175aa321c.png)

14. トランスクリプトを表示したまま、またデバッガに戻り、最初に```self halt```を入力した場所に、下記のコードを入力してください。(これまでprint it やdo itしたコードは消してください。)

    ```lang:Debuggerの中で実行.st
Transcript show: Time now;
      show: topBlock scriptOwner objName;
      show: ' x='; show: topBlock scriptOwner xpos;
      show: ' y='; show: topBlock scriptOwner ypos;
      cr.
```
15. 入力した文字を選択して、"do it"すると、トランスクリプトに時刻、スプライトの名前、座標が表示されます。
![transcript2.png](https://qiita-image-store.s3.amazonaws.com/0/12251/658d1d98-15d7-8d60-d791-89388ebe7361.png)
     ※スクリプト名が日本語の場合は文字化けしてしまいます。
16. 無事に動きそうなら、Alt/Command + sを押して、コードの変更を反映します。
17. 反映すると画面上部の枠の一番上が"ScratchProcess>>runStepFor:"になっています。そのあたりでメニューを表示（windowsなら右クリック、macはopt+クリック）して、"restart"を選択すると変更後のコードで再実行されます。
![restart.png](https://qiita-image-store.s3.amazonaws.com/0/12251/1ea4db8c-fe3c-0412-d641-0ecbd0ff26e1.png)
18. ```self halt```を消したので、デバッガはもう起動しなくなります。かわりにトランスクリプトに情報が表示されるようになります。
(早すぎて分かりにくい場合はScratchのメニューから編集→ステップ実行を設定→ブロックを点滅させる(遅く)でゆっくりにできます)


# 元に戻す
元に戻す場合はクラスブラウザから先の挿入したコードを削除してもよいですが、オブジェクトの状態がおかしなことになっている可能性もあることに注意してください。デバッガで止めたり、コードを変更したり、インスペクタからオブジェクトにメッセージを送ったりしていると、コードは元と同じでもオブジェクトが本来あるべき状態になっておらず、通常のScratchと異なる動作をする可能性があります。
また、Smalltalkが持っているバージョン管理システムを使う方法もあるのですが、配布されているScratchはコードがついていないためこの機能は使えません。また、バージョン管理しているのはコードだけで、オブジェクトの状態を戻すことはできません。

# 解説
これまで様々なオブジェクトをインスペクタで見てきました。見ているオブジェクトの種類によってインスペクタのタイトルは異なっています。例えば、スプライトをインスペクタで開いたら、タイトルはScratchSpriteMorphとなり, ステージならScratchStageMorph、処理の実行を表すオブジェクトならScratchProcessとなります。
このタイトルに表示されているのは**クラス**と呼ばれるものを表します。オブジェクトがメッセージを受け取った時、どのように振る舞うかはクラスによって決まります。
システムブラウザはクラスの閲覧/編集のためのツールです。クラスを見て、どのようなメッセージを受け取れるか、どのように処理を行っているのか、を知ることができます。デバッガで処理を止めながら動作を確認/修正することもできます。

# 補足
普通に配布されているScratchはコードが付属していません。今回システムブラウザで見たコードは逆コンパイルされて生成されたものです。コンパイルとはコードを実行可能な形式に変換すること、逆コンパイルは実行可能な形式からコードを復元することをいいます。コードにはコードコメントや変数名など理解の手助けとなる情報がありますが、コンパイルすると失われます。そのため、逆コンパイルで生成したコードはそれらが失われたコードになります。
ソースコードつきイメージはこちらからダウンロード可能です。
http://wiki.scratch.mit.edu/wiki/Scratch_1.4_Source_Code

そもそもScratchを動かしているSmalltalkは非常に古いバージョン(少なくとも10年以上前のバージョン)のため、使いにくい部分もあります。コードの理解をしたい場合などは最新のSmalltalk上で動作するPhratchを検討するのもよいかもしれません。
http://qiita.com/search?utf8=%E2%9C%93&sort=rel&q=phratch