SmalltalkからScratchをいじる(2) スプライトの一括削除


# やりたいこと
前回( http://qiita.com/maeda_/items/7a525cae6b3c1ca45773 )はスプライトをコピーして大量生成しました。
![0_copied.png](https://qiita-image-store.s3.amazonaws.com/0/12251/5664c6ed-0caa-1fb1-a8ff-5bf60788bd30.png)


今回は逆に一括で削除する方法を説明します。しかし、ステージ上のスプライトすべてを削除するのではなく、適切に対象を選択/除外する必要が有ります。今回の例で言えば、コピーして作成したスプライトは削除したいですが、コピー元のスプライトを残したいとします。

# 手順

## 方法1. すべてのスプライトの名前が'copy-'で始まるスプライトを選んで、削除
1. まず前回の手順に従い、ステージのモーフにハロを表示します。3回Altクリック(macの場合はoptionクリック)すると、見づらいですがモーフの下に"ScratchStage"と表示された状態になります。(ImageFrameと表示されている状態からもう一度ハロを表示してください)その状態でレンチのアイコンをクリックします。
![3_halo.png](https://qiita-image-store.s3.amazonaws.com/0/12251/15ba0f49-5a7c-93f7-06fa-a8709c2f0fe3.png)


2. インスペクタの下部の枠に下記のコードをコピペして、コードを全選択して、実行(do it)します。(Alt/Command + d でも可)

     ```ScratchStageMorphのインスペクタで実行.st
| selected |
selected _ self sprites select: [:each|
      each objName beginsWith: 'copy-'].
selected do: [:each | each delete]
```

3. 名前が'copy-'で始まるスプライトが削除されます。

## 方法2. 残したいスプライトをインスペクタで開き、開いたスプライト以外を削除
1. 残したいスプライトのモーフにハロを表示して、レンチのアイコンからインスペクタを開きます。
![1_halo.png](https://qiita-image-store.s3.amazonaws.com/0/12251/51e2a53b-b707-90ce-e42e-1f0cd63a8c0c.png)

2. インスペクタの下部の枠に下記のコードをコピペして、コードを全選択して、実行(do it)します。(Alt/Command + d でも可)

    ```ScratchSpriteMorphのインスペクタで実行.st
| selected |
selected _ self owner sprites reject: [:each| each = self].
selected do: [:each | each delete].
```
![2_doit.png](https://qiita-image-store.s3.amazonaws.com/0/12251/ffb0a81c-dda6-0bcc-1197-546a6780aea1.png)

3. インスペクタを開いていたスプライト以外が削除されます。

# 解説
動作を確認するために、コードを少しづつ実行してみます。
インスペクタの中で下記のコードのどちらかを入力し、右クリック(macの場合はCommandを押しながらクリック)してメニューを表示し、inspect itをクリックしてください。(Alt/Command + i でも可)

```インスペクタの中で実行.st
self owner sprites. "スプライトのモーフ(ScratchSpriteMorph)を開いている場合"
self sprites. "ステージのモーフ(ScratchStageMorph)を開いている場合"
```
![4_inspect.png](https://qiita-image-store.s3.amazonaws.com/0/12251/7ee5109f-54b9-1bab-c642-d774ccb48e2a.png)
"OrderedCollection"と表示されたインスペクタが新しく表示されます。今インスペクタで覗いているオブジェクトは、ステージ上の全スプライトを集めたオブジェクトです。画面上部左側に1〜11の数字が表示されており、コピー元のスプライト1つとコピーしたスプライトの10個の計11個のオブジェクトがあることが分かります。このような複数のオブジェクトをあつめたオブジェクトをコレクションと呼びます。
例えば、画面上部の"3"をクリックすると11個のうちの3番目のスプライトを選択したことになります。適当な番号を選択して、それをinspectしてみましょう。スプライトのモーフに対するインスペクタが開きます。画面上のどのスプライトを開いたかわかるようにスプライトのインスペクタで下記のコードを実行(do it)してください。

```スプライト(ScratchSpriteMorph)のインスペクタで実行.st
self addHalo:nil.
```
![5_add_halo.png](https://qiita-image-store.s3.amazonaws.com/0/12251/e5b23fa3-f61d-0275-690a-a87566e2ccf8.png)
ハロが表示されたスプライトが今インスペクタで見ているオブジェクトです。下記のコードを入力して、今度はメニューから"print it"を選びましょう。(Alt/Command + p でも可)

```スプライト(ScratchSpriteMorph)のインスペクタで実行.st
self objName
```
![6_print_it.png](https://qiita-image-store.s3.amazonaws.com/0/12251/26ee0a8a-0ac8-f69d-43ae-033a7491501f.png)
スプライトの名前が表示されます。コードの実行結果をみたいけどインスペクタを開くのは仰々しい場合は"print it"がお手軽です。逆にモーフのような複雑なオブジェクトは"print it"では内容がわからないので、インスペクタで確認します。命令を送るだけで結果がいらない場合は"do it"を使います。

スクリプトの名前が'copy'で始まるかどうかを確認するにはbeginsWith:を使います。下記のコードを"print it"してください。

```スプライト(ScratchSpriteMorph)のインスペクタで実行.st
self objName beginsWith: 'copy'
```
![7_print_it_2.png](https://qiita-image-store.s3.amazonaws.com/0/12251/be5ec940-936b-c9eb-09ee-2780da7c9fd5.png)

"true"(真)と表示された場合は条件を満たす、つまりスクリプトの名前が'copy'で始まることを表します。'copy'を別の文字に変えて条件を満たさないようにした場合、"false"(偽)と表示されます。これを使って複数のスプライトのうち、条件を満たすもの(true)、または満たさないもの(false)だけを集めることが可能です。

```コレクション select: [ 条件 ]```でコレクションの内の条件を満たすもの、```コレクション reject: [ 条件 ]```で条件を満たすものを除外したコレクションを取得します。下記のコードをコレクションの要素数を表示するコードを**一行づつ**実行してください。(ここではステージのモーフをインスペクタで開いているとします。)

``` ステージ(ScratchStageMorph)のインスペクタで実行.st
(self sprites) size. "→ 11 と表示される"
(self sprites select: [:each | each objName beginsWith: 'copy-' ]) size. "→ 10 と表示される"
(self sprites reject: [:each | each objName beginsWith: 'copy-' ]) size. "→ 1 と表示される"
```
スプライトをインスペクタで開いた場合は```self```にスプライトのオブジェクトが入っていましたが、上のコードでは、```self sprites```の各オブジェクトが```each```という変数に入っており、eachについて条件を満たす/満たさないの判断をしています。

コレクションに含まれるオブジェクトをまとめて"do it"する場合は、```コレクション do: [処理内容]```というコードを書きます。前回```(1 to: 10) do: [ ... ]``` というコードを実行しました。これは1〜10の値のコレクションについて"do it"していたことになります。
下記のコードを一行づつ"do it"すると、複数スプライトについてハロを表示できます。

``` ステージ(ScratchStageMorph)のインスペクタで実行.st
self sprites do: [:each | each addHalo: nil].
(self sprites reject: [:each | each objName beginsWith: 'copy-' ]) do: [:each| each addHalo: nil]
```

このようにselect:やreject:でコレクションから必要な要素を選択し、do:を使ってひとつひとつの処理を実行します。