④窓付きの壁を作る
窓付きの壁を作ってみましょう。今まで習った「繰り返し処理」「条件分岐」を使えば、それほど難しくはありません。
今回の目的は、ただ動けばよいだけでなく、「サブルーチン」をうまく使って、プログラムを短くまとめることです。そうすることで、別の人が読んでもプログラムの意図(何がやりたいか)がわかりやすくなるように工夫してみましょう。うまいプログラマーになるために大切なことです。
10 x10 壁を1面作る
まずY方向に、[setBlock v]を送る
10回繰り返し処理をして、次にZ方向に10回処理を繰り返します。
[mcpiX v] を [0] にする [mcpiY v] を [0] にする [mcpiZ v] を [10] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする (10) 回繰り返す (10) 回繰り返す [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える end
Y方向の繰り返し処理の最後に、[mcpiY v]を[0]にする
を設置して、 Y方向の値をリセットすることを忘れないようにします。ブロックの種類はレンガ(blockTypeId = 45)を選びました。
10 x 10 平行した壁2面を作る
平行した2面の壁を作ります。壁を作る部分のスクリプトを(2)回繰り返す
で囲ってやればよいのですが、2回目の壁作りについて[mcpiX v]を[9]にする
の数値(9)には注意が必要です。
[mcpiX v] を [0] にする [mcpiY v] を [0] にする [mcpiZ v] を [10] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする (2) 回繰り返す (10) 回繰り返す (10) 回繰り返す [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える end [mcpiX v] を [9] にする [mcpiZ v] を [10] にする end
プログラミングの世界では、0 から数えると話しましたが、0, 1, 2, 3, 4, 5, 6, 7, 8, 9 で10番目は「9」になるのです。ちょっと違和感がありますが、慣れるしかありません。
上の「設計図」を見て、2枚目の壁は、mcpiX = 9 mcpiY = 0 mcpiZ = 10 からスタートすることを確認してください。横方向が X方向、上方向が Z方向になっています。最初の壁は (0, 0, 10)からスタートします。2枚目の壁は (9,0,10) からZ 方向に向けて壁が作られていきます。
10 x 10 壁を 4面を作る
残りの2面を作りましょう。1面目と2面目を作るスクリプトを「複製」して、2段重ねにします。それから3面目と4面目を作るスクリプトの変数をスクリーンショットの通りに変更してください。
[mcpiX v] を [0] にする [mcpiY v] を [0] にする [mcpiZ v] を [10] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする (2) 回繰り返す (10) 回繰り返す (10) 回繰り返す [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える end [mcpiX v] を [9] にする [mcpiZ v] を [10] にする end [mcpiX v] を [0] にする [mcpiZ v] を [10] にする (2) 回繰り返す (10) 回繰り返す (10) 回繰り返す [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiX v] を (1) ずつ変える end [mcpiX v] を [0] にする [mcpiZ v] を [19] にする end
改造のポイントは、各面のスタート地点を正しく指定してやることです。そうしないと、四角形になってくれません。先ほどの「設計図」を参照して、スタート地点を指定してください。3面目は1面目と同じ (0, 0, 10)からスタートして、X方向に壁を作ります。最後の面は (0, 0, 19) からスタートして、X 方向に壁が作られていきます。
四隅のブロックが2回ずつ置かれていることに、違和感を持つ人がいるかもしれません。その感覚、素晴らしいです。「なんか変だな」と感じるところに、重要な点が隠されているかもしれませんから。
今回の目的は、繰り返し処理をうまく使ってプログラムを短くすることが目的でした。それで、同じ位置に2回ブロックが置かれることは承知で、こういった形に落ち着きました。もっとうまくやれば、2回置く部分をなくせるかもしれませんが、ここはそんなにこだわるところではない、ということで、そのまま次に進めていくことにします。
窓付きの壁を完成させる
プログラムの途中で、設置するブロックの種類を変えるには、「条件分岐」を使います。
そのためにまずカウンター変数「i」,「j」をセットしなくてはなりません。赤枠の部分がカウンター変数「i」「j」を設置した部分です。カウンター変数の初期値の設定、繰り返しごとに1つずつ増やす部分、リセットする部分を間違えないように設定します。i は Y方向に上がっていくたびにカウントしていきます。j は Z方向(または X方向)に進むたびにカウントしていきます。
青枠の部分が、条件分岐です。地上から高さ1メートルから上の位置(i = 2, i = 3)に、横は中央部分(j = 4, j =5)に「2 x 2 の窓」を設置しました。窓は、ガラス(blockTypeId = 20)を使っています。
[mcpiX v] を [0] にする [mcpiY v] を [0] にする [mcpiZ v] を [10] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする [i v] を [0] にする [j v] を [0] にする (2) 回繰り返す (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end [mcpiX v] を [9] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする end [mcpiX v] を [0] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする (2) 回繰り返す (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiX v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end [mcpiX v] を [0] にする [mcpiZ v] を [19] にする [i v] を [0] にする [j v] を [0] にする end
スクリプトを実行して、窓付きの壁が作られることを確認してください。
なるべく短いプログラムを目指しましたが、 Scratch のスクリプトエリアをはみ出るくらい、長いプログラムになってしまいました。 Scratch の欠点として、複雑なプログラムになってくると、一度に表示できないくらい長くなって、プログラムの可読性が低くなるという問題があります。
長いスクリプトを分離することを考えてみましょう。
サブルーチンを分離して、プログラムを読みやすくする
これからの作業は新しい機能を追加するのではなく、見た目をよくする作業です。「見た目」は大切で、きれいなプログラムが書けると、書いている本人も気持ちがよいものです。またバグを見つけやすくなり、別の人にプログラムの意図を明確に伝えられます。目的の機能が実現できたところでやめないで、プログラムの整形を行いましょう。
サブルーチンとは
プログラムを書いていると、同じような処理を何度も繰り返すことが多くあります。その「同じような処理」を、主のプログラム(メインルーチン)から切り離してたものを「サブルーチン」と言います。今回の例で言うと、4つの平行した壁をプログラムで作成しましたが、この壁を作る部分は、作り始めの位置が違うだけでほぼ同じ処理でした。この部分を切り離すと「サブルーチン」と呼ばれます。
スクリーンショットの赤枠の部分が「Z方向に壁を設置していく処理」です。ここが1枚目の壁と2枚目の壁を建築するスクリプトで、全く同じ処理が2回繰り返されている共通の部分です。
(10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
この部分を切り離して、サブルーチンにしましょう。
切り離したスクリプトの上に[getBlock v]を受け取ったとき
を重ねます。「getBlock▼」をクリックして、「新規/編集」を選びます。Y-Z 平面に平行した壁(= wall)なので、名前は「yz_wall」と名付けました。サブルーチン「yz_wall」の完成です。
[yz_wall v] を受け取ったとき (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiZ v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
コラム 変数の名前の付け方
新しい変数やサブルーチンの名前をつけるときは、そのプログラム言語独自の規則(命名規則)に従わなければなりません。Scratch では初心者が簡単にプログラミングを楽しんでもらうために、細かい規則はありません。日本語で名前をつけることも可能です。例えば、「壁の色を表す変数の名前」を「壁の色」「かべのいろ」「カベノイロ」としてもよいのです。
しかし今後、本格的なプログラム言語に進んでいくことを考えると、少なくとも名前は「英数字」で表すべきです。日本語の名前が許されている言語は少ないからです。
この講義では、英単語を使って、スペース(空白のこと)はアンダーバー( _ )でつなぐ方式を使うことにしました。この規則は「スネークケース」と呼ばれます。例えば、「壁の色を表す変数」は、「wall_color 」と名前を付けることにします。
同じように、青枠で囲った「X方向に壁を設置していく処置」の部分も切り離します。
(10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiX v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
切り離したスクリプトの上に[getBlock v]を受け取ったとき
を重ねます。「getBlock▼」をクリックして、「新規/編集」を選びます。X-Y 平面に平行した壁(= wall)なので、名前は「xy_wall」と名付けました。サブルーチン「xy_wall」の完成です。
[xy_wall v] を受け取ったとき (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を [0] にする [mcpiX v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
メインルーチンの作成
サブルーチンが2つ出来ました。次は、サブルーチンを適切なタイミングで呼び出す側の、主となるプログラム(メインルーチン)を作成します。
メインルーチンは同じ形の繰り返しです。壁を作り始める場所の指定とカウンター変数のリセットのために変数の値を決めます。その次に[サブルーチン名 v]を送って待つ
を重ねて、サブルーチンを呼び出しています。
[mcpiY v] を [0] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする [mcpiX v] を [0] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [yz_wall v] を送って待つ [mcpiX v] を [9] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [yz_wall v] を送って待つ [mcpiX v] を [0] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [xy_wall v] を送って待つ [mcpiX v] を [0] にする [mcpiZ v] を [19] にする [i v] を [0] にする [j v] を [0] にする [xy_wall v] を送って待つ
このスクリプトを実行して、4面の壁が問題なく建築できることを確認できました。
コラム 「送る」と「送って待つ」の違い
サブルーチンを呼び出すには、[サブルーチン名 v]を送って待つ
と[サブルーチン名 v]を送る
の2つの方法があります。「送る」の方は、処理の終了を待たずに、次へ次へと進んでいき、並行処理をするブロックです。今回は、カウンター変数が共通のため、「送る」の方を使うとうまく処理が続きません。
処理が終わるまで待って次の処理の進む「送って待つ」の方を使いましょう。こちらは並列処理をしないので、スクリプトの上から順番に1つずつ建築が進んでいくのです。
今回の目的は「きれいなプログラムを書こう」でした。その解決法の一つは「サブルーチンとして、何度も登場する処理を切り離す」方法です。もっときれいな書き方があるかもしれません。見つけたら是非教えてください。
【追加ミッション】3階建てのビルを建築する
少し手を加えるだけで、3階建てのビルを建築できます。どこに手を加えるか、自分で考えてみてください。
メインルーチンは、(3)回繰り返す
を使って、「4面の壁を作る処理」を3回繰り返します。忘れずに、[mcpiY v]を(10)ずつ変える
を繰り返しの最後に設置して、2階部分、3階部分と、mcpiY の値が上がっていくようにしましょう。
[mcpiY v] を [0] にする [blockTypeId v] を [45] にする [blockData v] を [0] にする (3) 回繰り返す [mcpiX v] を [0] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [yz_wall v] を送って待つ [mcpiX v] を [9] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [yz_wall v] を送って待つ [mcpiX v] を [0] にする [mcpiZ v] を [10] にする [i v] を [0] にする [j v] を [0] にする [xy_wall v] を送って待つ [mcpiX v] を [0] にする [mcpiZ v] を [19] にする [i v] を [0] にする [j v] を [0] にする [xy_wall v] を送って待つ [mcpiY v] を (10) ずつ変える end
サブルーチンは、[mcpiY v]を[0]にする
を[mcpiY v]を(-10)ずつ変える
に入れ替えます。1階部分は問題なく動きますが、2階部分になると、mcpiY = 0 にしてしまうと、Y方向に戻りすぎて、うまく建築ができません。よってレンガを積み上げた分だけ戻る処理に入れ替えました。
[yz_wall v] を受け取ったとき (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を (-10) ずつ変える [mcpiZ v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
[xy_wall v] を受け取ったとき (10) 回繰り返す (10) 回繰り返す もし <<<(i) = [2]> または <(i) = [3]>> かつ <<(j) = [4]> または <(j) = [5]>>> なら [blockTypeId v] を [20] にする でなければ [blockTypeId v] を [45] にする end [setBlock v] を送る (0.1) 秒待つ [mcpiY v] を (1) ずつ変える [i v] を (1) ずつ変える end [mcpiY v] を (-10) ずつ変える [mcpiX v] を (1) ずつ変える [i v] を [0] にする [j v] を (1) ずつ変える end
繰り返し回数を10回にすると「10階建てのビル」が作れますが、高さ63メートル以上は、[reset v]を送る
で消せませんから。止めておいたほうがいいでしょう。
「ファイル」→「名前をつけて保存」から、名前「mcpi_wall_with_window」をつけて保存しておきましょう。
このプロジェクト(mcpi_wall_with_window.sb)はダウンロードして、自分で確かめることがきます。
(サンプルプログラムのページに移動します。ダウンロードしたいファイルのリンクをマウスで右クリックして、「対象をファイルに保存」などのメニューを実行してください。)
次回は、三角屋根に挑戦します。グラフを使って、「なだらかな屋根」や「とがった屋根」「かまぼこ屋根」など、いろいろな形の屋根を作ってみます。
【宿題】高さを24メートルのビルに改造する
先ほど作ったビルは、「横10メートル・縦10メートル・高さ10メートル x 3階」でしたが、
これを「横12メートル・縦14メートル・高さ8メートル x 3階」のビルに改造してください。(ヒント ブロックの構造はそのままで、変数の値を変えればよい)
次に、お好きなブロックを使って、床と天井を作ってみましょう。(ヒント 床と天井を作るサブルーチンを作って、メインルーチンから呼び出す)