SCRATCHでPONGを作る企画の4回目。前回の記事で作成したRev1.0をベースに、いくつか機能を拡張してRev2.0にしてみましょう。
Rev2.0機能拡張内容

Rev2.0ではRev1.0から以下のような機能拡張を実施したいと思っています。
- 得点カウント機能追加。それに付随してゲーム終了画面の仕様も追加
- ボールが壁やパドルにあたったとき、得点が入ったときに音を鳴らす
- パドルにボールがあたった際に、パドルとボールの位置関係に応じて跳ね返り角度を変更する
得点機能の追加について

まず得点についてですが、得点が入ったかどうかの判定に相当する部分はすでにRev1.0のボールのコード部分に記載があって、ボールのX座標が-200~200の範囲を超えたときとなります。Rev1.0ではボールを消すだけでしたが、ここで得点を加算していきましょう。下図はボールスプライトの最後の方のコードを一部抜粋したものですが、左側のRev1.0のコードに対してスコアを1加算するコードを追加します。
-1.png)
なお、SCRATCHにコメント機能があることを知ったので、今回Rev2.0からはコメントを追記しておきました。
そのためにスコア左とスコア右という二つの変数を「すべてのスプライト用」で作ってここで得点を覚えておきます。「すべてのスプライト用」にする理由は、得点結果によってゲーム終了判定をする部分を背景部分にコードとして書こうと考えており、背景からもこれら二つの変数の値を参照するためです。
左のキャプチャ画像のところ、「スコア右」と「スコア左」にチェックが入っていますが、ここでチェックをするとゲーム画面上に変数値が表示されるようになります。チェックを入れてから画面上の適当な位置に移動させてスコア表示としましょう。



さて、得点が入ったら次はゲーム終了条件についてです。先に11点を取ったら勝ちとしていましたので、その部分を以下のようなコードで実装しました。Rev2.0からゲームの流れ(ゲーム開始→ゲーム中→ゲーム終了)のコントロールは背景部分に集約しようと思ったので、このコードは背景部分に書いてみました。そして背景からメッセージの送信機能を使ってパドルとボールとの同期をとります。
SCRATCHでは背景やスプライトにそれぞれコードを書くことができます。また背景、スプライトの中でも、複数のコードを書くことができます(SCRATCHではそれぞれのコードをスクリプトと呼びます)。これらのコードは同時並行的に実行されることがあるので、各コード間の同期をとりたい場合に有効なのがメッセージ機能です。

ゲーム終了時の画面

第1回目の記事に動画を貼りましたが、オリジナルのPONGではゲーム終了時には両パドルが消え、ボールは上下左右の壁にあたって跳ね返り続けるという演出になっていました。Rev2.0ではこれを再現したいと思います。下記はパドルのコードをRev2.0用に更新したものです。
背景で「ゲーム開始メッセージ」を送るようにしたのでそれを受けてメインのコードが動くように変えたところと、「ゲーム終了メッセージ」を受けたらそれまで動いていたパドル操作のスクリプトを止め、そしてパドルを隠しています。
.png)
そして次にボールが「ゲーム終了メッセージ」を受けた場合ですが、以下のようなコードを追加しました。
-1.png)

まずはこれまでボールを動かしていたスクリプトを止め、ゲーム終了になった時点ではボールは隠されているかと思いますのでそれを表示し、そして後は壁で永久に跳ね返り続けるようにしています。これにてゲーム終了画面の出来上がりです。ゲーム終了時はこのような画面になります。
音を鳴らす

さて、次は音を鳴らしてみたいと思います。きっちりと音を作りこみたいところではありますが、例によってここで時間を使うとモチベーションが下がりかねないので、一旦SCRATCHが用意してくれている音でイメージに近いものを持ってきて、ちょっと修正して使っちゃいましょう。第1回目の記事で書きましたが、以下の3種類の音を鳴らしてみます。
- ボールが画面上下に当たる音(低めの短い音)
- ボールがパドルに当たる音(少し高めの「PONG」という短い音)
- 得点が入ったときの音(少し低めの「BOO」という長い音)

いずれもボール部分のコードに書いていきますが、実は一番上の画面上下に当たったときの音を出すのが一番面倒でした。下図はRev1.0のコードですが、画面上下に当たったとき以外は音を出すコードを一文追加するだけでいいです。一方、画面上下に当たったときはその判定自体SCRATCH側でやってくれているため、画面上下に当たったのかどうかがこのままでは判別できません。
-645x1024.png)

そこで、画面の上下端に当たったときのボールのY座標がどういった値になるのかを調べて、どうやら下端がー173ぐらい、上端が173ぐらいのようだったので、ボールのY座標を見て音を鳴らすことにしました。ちょっとやっつけ仕事ですが、まぁこれで不都合は出てないのでヨシとします。
.png)
ボールの跳ね返り角度を変える

さて、最後にRev2.0で一番難しいこの仕様を実装してみたいと思います。ボールがパドルの中央付近以外に当たったときにどういう挙動をすべきかは何が正解か良く分かりませんが、ある程度論理的にそれっぽくて実装も難しくない方法として以下のような案を考えてみました。
.png)
普通なら点線矢印の角度に跳ね返るべきところを、実線矢印の方向に跳ね返らせようと思います。イメージとしてはパドルのボールに当たる面が若干丸みを帯びていて、端にいくにしたがって細くなっていくような感じ。
このためパドルのY座標をボールのスプライト側に教えてやり、パドルのY座標とボールのY座標を比較することで、パドルのどの位置にボールがあたったのかを判定することにしました。中央付近なら今まで通り、中央より上側や下側にボールがあたった場合は少し傾けて反射させるようにします。

さらにもう一点。Rev1.0で遊んでいるとたまにボールがパドルにめり込んでパドル表面でボールが連続的に跳ね返るような動きをすることがありました。具体的には以下の動画のような動きですが、これも一緒に修正したいと思います。

ボールがパドルにめり込む原因は主に、パドルの上端 or 下端にボールがぶつかって、そこからパドルにめり込んでいくことであると推測しています。つまり以下の左図のような挙動をしているということです。

これは本来は右図のような挙動になってほしいです。ということで、パドルの上端や下端付近にボールがあたったと判定された場合はボールの跳ね返り方向を変えるという対策が考えられます。パドルのY座標をボールのスプライト側で知ることができるようになったので、パドルの上端や下端にボールがあたったと考えられる場合はボールの向きを「180-向き」度とすることで上の右の図のようにボールが跳ねるようにしました。
なおここで一旦お詫びを。私はSCRATCHではスプライトの向きを左図のように0°~360°で保持しているのだと思ってましたが、どうやら右図のようにー180°~180°でデータ保持しているようです。なので、これに合わせてボールの向きを判定していきます。

これからこの判定部分のコーディングをしていきますが、ちょっとコードが長くなって見難いので、ブロック定義という機能を使うことにしました。本来は何度も現れる同じ機能をひとつのブロックにまとめるために使用する機能だと思いますが、今回は単にコードが長いので分割するために使ってます。
下図はパドル(左)のコードだけ掲載しています。すべてのコードを見たい場合はこの記事の後ろの方にSCRATCHプロジェクトへのリンクを貼ってありますのでそちらからアクセスしてください。
ボール).png)
ボールとパドル(右)との衝突判定、及びパドル(左)との衝突判定をひとつのブロック定義にまとめた方がコードとしてはエレガントなんですが、プログラミングに慣れてない人には見難いコードになるかなと思ったのであえて分けて書いてます。

これまでの説明に書いてませんでしたが、上記コードには「パドルにボールがめり込む問題の回避として」と「入射角が小さい場合の対策として」という部分があります。最後に簡単にそれらの説明をしておきます。
ボールがパドルの上端、下端にあたった場合の跳ね返り方法は見直しましたが、パドルY座標とボールY座標の引き算の絶対値で判定しているだけで厳密なものではありませんでした。そのためやっぱりたまにボールのめり込みは発生します。もしめり込んだ場合でもヘンな挙動に見えないように、パドル(左)とボールがぶつかった場合はボールの向きが0未満(つまり画面の左方向にボールが進んでいる状態)のみボールの向きを変えるようにしています。こうするとパドルに何度もあたって跳ね返るという挙動をしなくなります。
パドルの中央より上側下側にボールがあたったときの反射角度を20度ほど傾けた結果、どんどんボールの反射角が浅くなる(向きで言うと0度や180度に近くなる)場合があります。こうなってしまうと相手側にボールが飛んでいくのに非常に時間がかかるので、ある程度以上浅い入射角でパドルにあたった場合は反射角度を傾けないように調整しました。

このあたりは、対策の仕方は色々あると思いますので上記のやり方はあくまで一例だと思ってください。こういった課題に対してどういう対策を施すか、またそれをどれだけエレガントなコードで実現するかといったところは結構センスが要求されるところだと思いますので、是非皆さん自身が悩んで試してみてほしいところだと思います。
遊んでみましょう
解説が長くなりましたが、この状態でRev2.0としてプロジェクトを保存し遊んでみましょう。例によってSCRATCHのプロジェクトを公開しておきます。操作方法はRev1.0と同じです。
(あ、書くのを忘れてましたが、ボールの反射角が変わることで事前の予想が難しくなりましたので、パドルの移動スピードをRev1.0から少しアップさせてます。)

さて、これでPONGの開発については終わりです。「パドルが上下だけじゃなく左右にも動かせたら面白いかも」とかゲームを楽しくするためのアイディアはいくつかありますので、そのうちRev3.0を作るかもしれませんが一旦はここまでということで。
こんなゲームを作ってほしいとかこんな機能を追加してほしいといったご意見ございましたらコメント欄で教えてくださいね。
コメント