大西Scratch日記

Scratch初心者です。勉強するためにブログを始めてみます

配列を疑似的に使ってスネークゲームを作る

古典的なスネークゲームを作りましょう。

f:id:onishi:20170514115905j:plain

スネークゲームとは

  • 自分はヘビ。えさを食べると胴が伸びる
  • 壁や自分自身の胴に当たったらゲームオーバー

という簡単なゲームです。誰しも小学生くらいで作りますよね。

スネークゲームの実装も色々ありますが、胴体の座標の配列を持っていて、配列の操作でお尻を消していく、と言うのが一般的だと思います。

Scratchの変数は、スカラと配列というシンプルなものしかありません。今回は、配列に要素を加えていき、奇数要素はX座標、偶数要素はY座標と決め、配列操作は「必ず2つずつ行う」ということで実現してみました。

遊べるものはこちら。


変数

  • 体の長さ
    • 1から始まり、えさを食べると増えます(サンプルでは2つずつ増えます)
  • 移動量X,Y
    • スネークは進み続けます。常にどちらに向いているかを変数に格納しておき、カーソルキーで方向を変えます
  • 速度
    • スネークの一回の移動距離を調整できるように変数にしました。調整しながら作っていくときは、一箇所変えるといろんな場所が書き換わるように変数を活用すると便利ですね
  • スネーク(配列)
    • この配列に、1回移動するごとに前回の座標を格納するようにしました

変数の操作

f:id:onishi:20170514121050j:plain:w300
移動時はこのように、配列の末尾に現在のX,Y座標を格納してから移動します。

f:id:onishi:20170514121102j:plain:w300
尻尾を消す際は、このように配列の先頭(つまりスネークからすると尻尾)からX,Y座標を取り出してから削除します。1つずつやらないとずれるので注意ですね。

f:id:onishi:20170514122031j:plain:w400
配列の長さ、は配列の要素数です。今回は、X,Y座標を格納しているので「配列の長さ/2」が今の胴の実際の長さですね。これと「体の長さ」という変数を比較することで、尻尾を消すかどうかを判定しています。逆にえさを食べた時の処理は「体の長さ」変数に数字を足すだけです。シンプルですね。

f:id:onishi:20170514122243j:plain:w200
スネーク自身に対する処理は「体の長さ」変数に伸ばしたい数字を足すだけ。あとは食べたえさを消す処理と新しいえさを作る処理です。

その他の工夫

カーソル取得だけのループ

f:id:onishi:20170514121336j:plain:w300
メインループとは別にカーソルを取得するだけのループを作りました。
メインループは速度調整のために、大きなループの中で「0.1秒待つ」と入れています。操作を取得する処理をメインループに入れてしまうと、0.1秒待ってる間は操作できず、入力取得漏れが発生するので、こういう際は別ループにすると良いです。
同じようなことは、キャラクターのアニメーションや、BGMを再生する、などの非同期な処理に便利に使えますので、是非使いこなしたいテクニックですね。

スタンプ

蛇の胴体をたくさん置いていく際に、いちいちクローンを作る必要はありません。
処理が不要なものは「スタンプ」機能を作ると、効率の良いプログラムになります。

f:id:onishi:20170514121844j:plain:w200

Scratchはこのように「色」との当たり判定が使えるので、今回は蛇の胴体を描くのも、尻尾を消すのも、コスチュームを変えてスタンプを置くことで実装しました。

何もない空間にえさを出す

f:id:onishi:20170514122458j:plain:w400
否定の演算ブロックと「まで繰り返す」を組み合わせて、何もないところに新しいえさが置かれるようにしています。Scratchにはループを脱出する構文がないので、この「まで繰り返す」のテクニックは必須です。

その他細かく色々やってるので是非中身を見てみてください!enjoy!!
scratch.mit.edu

キーアップイベントで波動砲を打つ

キーアップ(keyup)イベント、JS書いててもよく使いますが、Scratch でどうやればいいんでしょう。

f:id:onishi:20170513174049j:plain:w200

このように「イベント」と「調査」に「キーが押された」ことを取得することはできるのですが、「キーが離された」を取得はできないように思えます。

そんなことはなく実は簡単。否定の演算ブロックを使えばいいだけです。

f:id:onishi:20170513174315j:plain:w250

それでは、これを利用してR-TYPE波動砲のように(例えが古い)、「キーを押されている」間は力を溜めて、「キーが離されたら」発射する例を作ってみましょう。


スペースキーを押している間、力を溜めて、離すと発射します。それだけのサンプルです。

自機のスクリプト

f:id:onishi:20170513174658j:plain:w300
ループとキーダウン・キーアップを組み合わせています。
自機と弾との間は3種類のメッセージでやりとりしています。

  1. 溜め始める
  2. 溜めている間。弾が大きくなる
  3. 発射

弾のスクリプト

f:id:onishi:20170513174912j:plain:w200

はい、簡単ですね。Scratchは不自由なようで、制御と演算のブロックの組み合わせで意外といろんなことができます。組み合わせを考えるのもパズルのようで面白いですね。enjoy!!

scratch.mit.edu

変数のスコープ

Scratchの変数のスコープは「すべてのスプライト」と「このスプライトのみ」の2種類があります。
「すべてのスプライト」は読んだとおり、グローバル変数でしょう。
スプライトのオリジナルをクラス、クローンをインスタンスと考えた場合、「このスプライトのみ」はクラス変数なのか、インスタンス変数なのか調べてみました。

こんなプログラムです。
f:id:onishi:20170422154556j:plain

スタートすると、3匹の猫がクローンされます。クリックされるごとに「すべてのスプライト」変数と「このスプライトのみ」変数をそれぞれインクリメントします。

f:id:onishi:20170422154816j:plain:w430

するとこのように、クローンされたスプライトごとに「このスプライトのみ」変数が保持されていることがわかります。インスタンス変数ということですね。
複数のクローンされたスプライトで共用する、クラス変数のようなものは持てないことがわかります。それはクローンのオリジナルの「このスプライトのみ」変数でもグローバル変数でも代用はできそうですね。

作ったプログラムはこちら。
scratch.mit.edu

ゼルダが楽しいので草を燃やすプログラムを書いた

久しぶりの投稿になります。いやー、ゼルダ楽しいですね。Scratch と Switch 似てますね。
自分もそうですが、子どもの可処分時間がゼルダに奪われていてヤバいです。ハイラルに旅立った子どもが、Scratchの世界に戻ってくるために気を引くプログラムを書きました。

草に火を付けて燃え広がるのが面白いので、それを簡単に再現してみました。マウスで松明を動かして、クリックで火を付けます。密集した草に火が燃え広がる、というだけの簡単なプログラムです。
f:id:onishi:20170316123805j:plain

燃え広がる部分をどうやったらシンプルに書けるかな、と考えて、まず、松明と草のスプライトはこんな感じのコスチュームに。
f:id:onishi:20170316123640j:plain:w100 f:id:onishi:20170316124007j:plain:w100
どちらも炎を真っ赤(#F00)にしています。
f:id:onishi:20170316124117j:plain:w220
このように、草が赤色に触れたら燃える(赤くなって大きくなる)、自分自身も赤いので、連鎖的に燃えていく、という風にしてみました。
わりとシンプルなのにそれっぽい感じになって満足しました。子どものゼルダが一段落したらこれを見せて、また一緒にプログラミングしようと思います。
ソースコードはこちらから。enjoy!!
草を燃やす Burn grass on Scratch

Scratchでタイピング練習ソフト

子どもにScratchを教えていたのですが、最近躓いてしまいました。
何かというと「文字入力」です。まだ学校でもローマ字も習っていないので、本やウェブサイトを見ながらプログラムを書いていても変数名を入力しましょうってなると「おとうさんこれ書いて」ってなってしまいます。
というわけでタイピングを子どもに教えたい!せっかくだからScratchで教えたい!と思って簡単なゲームを作ってみました。

やっていることは簡単なのですが、いくつか躓いて

  • キー入力は取れるけど、「今何のキーが押されているか」がとれない?
  • スプライトに A-Z の26種類のコスチュームを用意しても、「n番目のコスチュームにする」とはできない?

などなど、幾多の困難があり、ご覧の通りのプログラムになっていますのでご笑覧ください。

f:id:onishi:20170513180043j:plain:w200
すごいことになっている様子です(これでも一部)

1スプライトだけでスクリプトも全部一箇所で読めます。関数(ブロック)の返り値があれば楽だったんですけどね…。
しかしこういうプログラムを書いていると Scratch の非同期処理の簡単さと、ちょっと複雑な処理を書こうとすると同期処理と非同期処理の使い分けを考えなければならない、というところが、Scratchの良いところだなーと思いました。
「こうすればもっとうまくいくよ!」というのがあれば是非ご教示ください!

scratch.mit.edu

複数のクローンされたスプライト同士のあたり判定

Scratchでシューティングゲームを作る時に、あたり判定が難しいな、といつも思いますので自分なりのベストプラクティスを考えてみました。

クローンを使わずに「単発の弾」と「単機の敵」の場合は問題ないのですが、クローンを使って複数複数を考えると難しいんですよね。

複数の弾をクローンで扱う

f:id:onishi:20160523180321p:plain:w230
こんな感じで「自機」「弾」「敵」の3種類のスプライトがいる簡単なシューティングゲームを作ってみましょう。

自機のスクリプトはこんな感じですね。(PCで見ている方は画像をクリックすると拡大します)
f:id:onishi:20160523180508p:plain:w205
カーソルで左右に動き、スペースで弾を撃ち、敵に当たったら終わりです。
複数の弾を撃つために、このように「弾のクローンを作る」とすれば、スペースを押す度に弾が発射されます。

弾のスクリプトはこのようにします。
f:id:onishi:20160523180912p:plain:w173
「旗がクリックされたとき」の処理は初期化に留め、「隠す」しておきます。
「クローンされたとき」に、自機の位置に表示して上に移動する処理を書きます。

より高度な弾の処理(同時発射数と連射防止)

これだけでも複数の弾を扱えるのですが、このままでは、スペースキーを押しっぱなしにすると連射されてしまうため、シューティングゲームとしては不自然です。そこで、

  • 同時発射数を制限する
  • 連射を防止する

という2つの処理を入れてみましょう。
f:id:onishi:20160523181609p:plain:w267
同時発射数を防止するために、「弾の数」という変数を用意し、画面上に表示されている弾の数を監視します。弾が消える時に変数の数字を減らすのを忘れないように。
連射を防止するために、タイマーを用いてみました。弾を撃つ際にタイマーをリセットし、一定時間以内には弾が撃てないようにします。
弾が消える処理が増えたとき用に「消える」ブロックを用意することで、「弾の数」変数の減らし漏れが無いように工夫しています。
これで、同時発射数は5発まで、0.2秒以内には連射できない、となりました。

複数の敵

複数の敵も複数の弾と同様に書きます。
f:id:onishi:20160523182051p:plain:w290
「旗がクリックされたとき」の処理は初期化と「隠す」しておき、2秒ごとにクローンを発生させるようにしました。
「クローンされたとき」に、ランダムな位置と向きで表示させています。

複数の弾と複数の敵のあたり判定

さていよいよ本題です。弾がレーザーのように敵を貫通するならよいのですが、弾が敵に当たったら両方消える、とする場合の処理はなかなか難しいです。

素朴に書くとこのようになりますが、これではうまくいきません。
f:id:onishi:20160523182351p:plain:w185 f:id:onishi:20160523182407p:plain:w199
お互いが「相手に触れたなら」という判定を使っています。これで動きそうに思えますがダメです。
なぜならプログラムは常に複数の動作を同時にこなしているように思えますが、実は高速に割り込み処理を入れているだけで順番に一つ一つの処理を行っているだけなのです。
つまりこのプログラムでは「たまたま先に実行されたどちらか」の処理だけが行われ、「弾は消えるが敵は消えない」もしくは「敵は消えるが弾は消えない」のどちらかになってしまいます。

クローンに対して個別にメッセージが送れれば良さそうなものですが、Scratchにおけるメッセージはブロードキャスト(全部のスプライトに対して一様にメッセージが送信される)のみ。なので「この弾がこの敵にあたった」という判定は難しいです。


そこで考えてみたのがこういうやり方です。

f:id:onishi:20160523183235p:plain:w205
弾の処理では、ループ内であたり判定をしますが、「メッセージを送って待つ」を利用してみます。

f:id:onishi:20160523183255p:plain:w194
敵の処理としては、「メッセージを受け取ったとき」だけあたり判定をしています。向こう(弾)はメッセージを送って待っているはずなので、この瞬間ならどちらも「相手に触れたなら」の条件が揃うはずです。

これで、どちらか片方だけ消えるという問題が解決できました。

完成

というわけで、これで複数のクローンされたスプライト同士のあたり判定が思ったようにできました。実際に動く物はこちらです。

ソースコードはこちらで見ることができます。
クローン同士のあたり判定 on Scratch

Scratch初心者なので、これがベストな方法なのか自信がないです。もっといい方法があれば是非教えてください!

キー入力取得における「イベント」と「調べる」を比較

onishiです。Scratch初心者ですので、勉強するためにブログを始めてみます。

さて、Scratchで最初にやることと言えばまずはスプライトをカーソルで動かすプログラムではないでしょうか。
カーソルキー入力を取得する方法も幾つかありますが、どれがいいのでしょう。比べてみます。

「イベント」で「キーが押されたとき」を取得

f:id:onishi:20160508123106p:plain:w211

「調べる」で「キーが押された」を取得

f:id:onishi:20160508123411p:plain:w285

こんな感じですね。

実際に動く物を用意してみました。



それぞれ、下の青いペンギンは「イベント」で、上の赤いペンギンは「調べる」でキー取得しています。
左右のカーソルキーで動かせるので比べてみましょう。

結果

f:id:onishi:20160508123834p:plain:w479
2匹のペンギンの速度は同じなのにこのように差がついています。
実際に動かしてみるとわかりますが、「イベント」でキー取得している青ペンギンは動き出しが一瞬遅いようです。イベントの開始をチェックするタイミングが無限ループの中での「調べる」でのキー取得をチェックするよりも遅いんでしょうか。

とにかく、アクションゲームでキー取得する場合は「調べる」を使ってキー取得するのが良さそうです。

ソースコード

今回比較用に作ったプロジェクトは以下で公開しています。
「イベント」と「調べる」を比較 on Scratch