スクラッチに挑戦している皆さん、どうも!スクラッチコーチです。
準備
このレッスンは3番目の「マリオゲームの作り方」です。前回までのレッスンがまだの人はこちらからどうぞ。
スクラッチでマリオのゲームを作る特大レッスン#02 ジャンプせよ
マリオのジャンプの作り方が分かります。「スクラッチでマリオ」シリーズの最終ゴールをチェック
最後までレッスンを進めるとこのようなマリオゲームの土台となるプロジェクトが完成する予定です。
「レッスン#03 壁・坂」のゴール確認
壁と坂を攻略するとこのような動きが実現できる予定です。
プロジェクトをコピーする
スクラッチのエディタを開いて、ファイル>コピーを保存を選び「マリオプロジェクト003」に変えておきましょう。何を作るのか分かりやすく「マリオプロジェクト003 壁・坂」としておいてもいいでしょう。
壁の判定を作る
壁に触れたらプレイヤーを前に進めなくするところを作ります。
ステージのコスチュームで壁を作る
ステージスプライトのシーン1に、適当な四角い壁を作ってください。
プレイヤーに条件ブロックを追加する
プレイヤーのブロック定義「X軸に移動する」で「もしステージに触れたら」という条件を追加します。
変数「直前のX座標」を追加する
前回レッスンでY軸の移動時に1座標ずつ「未来を予測」する判定を行いました。X軸でも似たようなことをしていきます。そこで、変数「直前のX座標」を作っておきます。
変数「★カメラのX座標」を使う
第1回目のレッスンで作成したように、横移動には変数「★カメラのX座標」を使っています。
壁判定でも使います。
もし壁に当たったら、プレイヤーが壁に食い込まないように、変数「★カメラのX座標」を元に戻したいと思います。
また、「もし〜ならブロック」の前に変数「直前のX座標」を初期化することも忘れずに。
動きを試してみると、これだと少し食い込んでしまうと思います。
元よりも少し後ろに下がった状態に戻したい
食い込んだ状態を直すには、単純に少し後ろに下げてあげればOKです。あまりスマートではありませんが、ひとまずこのシンプルな方法を試してみます。
動作確認する
おっと!おそらくヘンテコな動きになってしまったのではないですか?
理由は、「ステージに触れたら」という条件が壁だけではなく地面のステージにも当てはまっているからです。地面に触れているのですから、変数「★カメラのX座標」は常に「直前のX座標 - 10」されることになります。そのため、予期せぬ動きになってしまうのです。
これでは駄目ですね。
当たり判定エリアを実装する
そこで、地面には触れずに壁に触れたかどうかを判定するため専用のコスチュームを作成する作戦で解決したいと思います。当たり判定エリアというコスチュームを作っていきます。
まず「ステージに触れたかどうか調べる」判定をまとめておく
当たり判定用のコスチュームを作る前に、少し最適化しておきます。
ステージに触れているかどうかは、前回のジャンプを作るレッスンでも行った判定ですので、今回で2回目です。今後も何かと使用頻度が高そうな判定なので、2回目だなと気づいたこのタイミングでブロック定義化しておきたいと思います。
ブロック定義を作る
変数を作る
ロジックを作る
もしステージに触れたら変数を1に、触れてないときは0にします。
置き換える
既存の処理を置き換えます。「もし〜なら」の条件ブロックの前にブロック定義「ステージに触れているかどうか調べる」を実行して、直後で変数「ステージに触れているかどうか」が0より大きい(つまり当たった)かどうかを判定します。
ここの演算は必ずしも「大なり」ではなくてもOKです。「イコール1かどうか」としてもいいです。
「ステージに触れているかどうか > 0」ではなく、「ステージに触れているか = 1」でもOKです。イコールの方が分かりやすいかもしれません。
動きを確認する
既存の動きをブロックに置き換えただけでも、動作確認はします。動けばOKです。
コスチューム「当たり判定エリア」を作る
プレイヤーのスプライトのコスチュームに、当たり判定専用のコスチュームを追加します。
歩く1を複製して名前を変える
歩く姿を参考に使うので、歩く1を複製して名前を「当たり判定エリア」と変えておきます。
四角を描く
何色でもいいので、足がちょこっと見えるようにして、四角を作ります。
足を消します
最後に足を消せば、とりあえず完成です。
これで宙に浮いているかのようなコスチュームが完成しました。
コーディングに組み込みます
動作確認する
これで動きを確認してみます。おそらく歩けるようになったのではないでしょうか。
X軸に移動する際に、宙に浮いてる(地面に足がついてない)コスチュームに着替えてから「ステージに触れたかどうか」を調べているので、地面は無視して壁だけを検知できるようになりました。
いい調子です。まだまだぎこちない動きなので、もっと改善していきましょう。
とくに壁に当たるとブルブルする点が気になりますね。止まるのはいいのですが、ブルブルせずにピタッと止まってほしいものです。この問題にもタックルします。
壁を両サイドに作る
もっとチェックしやすくなるように、壁を両サイドに配置します。ステージのスプライトのコスチューム「シーン1」に描けばOKです。
動作確認する
この段階では、右側の壁には当たって止まりますが、左側の壁はすり抜けてしまいます。
それどころか、左の壁に当たると加速してしまいます。
原因は、ステージに触れた際に「★カメラのX座標 + 10」としているところです。右の壁に当たった際はプラス10するとちょうど動きが止まるように見えるのですが、左の場合はマイナス10したいのです。
右の壁に当たったのか、左の壁に当たったのか、どのように判定すればいいでしょうか。
レッスン1で「スピードX」という横に歩く速度を管理する変数を作りました。この変数には、歩く方向によってプラスかマイナスの値が入っています。
この変数「スピードX」は今回の壁判定にも都合がいいですね。
変数「直前のX座標」に変数「スピードX」の値を加算しました。しかし、今度はどちらの壁に当たっても加速してすり抜けるようになってしまいました。
すこし修正しないとダメそうです。
後ろに下がる処理に補正値を設ける
壁に当たって加速する原因は、変数「スピードX」の値がここで使いたい値とは反対側に向かう力だからです。つまり、欲しい値は右のときはプラスで左のときはマイナスなのに、スピードXの値は右のときはマイナスで左のときはプラスになっているということです。
これを直すのはカンタンです。マイナスを掛ければOKですね。
何度か試したところ、マイナス1.4か1.5くらいがちょうどよさそうです。
しかし、まだ完璧ではありません。もう少し修正を続けます。
変数「スピードX」をゼロにする
壁に当たったタイミングで、それ以上前に進む必要がなくなります。なので、スピードXの値は不要になります。利用した直後にゼロにしてしまいましょう。
動作確認する
できた!と思ったのも一瞬。壁に当たったタイミングで素早く方向転換すると、ズブズブと壁に埋もれていってしまいます。
この理由は、壁に当たった瞬間に向きを変えるとコスチューム「当たり判定エリア」の一部が壁に触れた状態であるにも関わらず、向きが変わっているせいでスピードXが期待通りに作用しないためです。
つまり、右の壁に当たった瞬間に左を向くとコスチュームが一部触れているけど、スピードXは左に進む用の値になっているため、左の壁に当たっていると勘違いしてしまうのです。実際は右の壁に当たっているので、後ろに下がる=左に下がるはずですが、左の壁に当たっているとか勘違いすることで、後ろに下がる=右に下がるとなり、結果として壁にズブズブ埋まってしまうのです。
当たり判定エリアを修正する
最大の原因は、当たり判定エリアが大きすぎることです。そこで、小さくしたいと思います。どのように小さくすれば良いでしょうか。
今回は壁に当たったことを検知できればいいので、プレイヤーの「前」にだけ当たり判定エリアがあればOKです。
このようにスリム化しましょう。
動作確認する
分かりやすくするために、「こんにちは!」と言うブロックをやめて、当たったら「当たった」と、当たってなかったら「当たってない」と言うブロックを配置したいと思います。
そのため、「もし〜なら」ブロックではなく「もし〜でなければ〜」ブロックに変更します。
もっと分かりやすく動作確認する
これで壁判定している感が分かりやすくなったと思いますが、更にわかりやすくしたい場合は、いったんブロック定義「アニメーションする」をゴッソリ中身を外してみてください。そうするとコスチューム「当たり判定エリア」の四角がウロチョロして壁に触れると「当たった」と言う様が見れて、何が起きているのか分かります。
戻し忘れにご注意
かなり良くなってきましたが、まだ致命的なバグがあります。ジャンプしながら壁に当たってみてください。
壁に吸着してしまいます。これは意図した動きではありませんね。
ジャンプしたときも壁判定を動作させる
この問題にタックルする前に、原因を考察します。
そもそもナゼ壁にピタッとくっつけてしまえるのか。
壁に当たったらスピードXがゼロになるので、横に動かないというのは仕様通りです。ここで考えるべきは、どうして下に落ちないのか、という点ですね。
下に落ちるロジックはブロック定義「Y軸に移動する」で実装しています。これは前回のレッスンで作ったものです。
そこでは、ステージに触れるまで変数「スピードY」を変数「★重力」でマイナスしつづけ、ステージに触れたら変数「スピードY」をゼロにするとしています。
つまり、ステージに触れたらそれ以上は下に落とさない、というロジックになっているのです。
これを作ったときは壁を考慮していなかったので問題ありませんでした。しかし、いまとなっては壁判定との相性は良くないですね。ブロック定義「Y軸に移動する」と壁判定のどちらか、または両方を修正する必要があります。
今回は、両方を修正する作戦で行きたいと思います。
ブロック定義「X軸に移動する」を改善する
まずはX軸に移動するロジックを改善していきます。
いまのロジックでは、壁に当たったらスピードXにマイナス1.5を掛けた分だけ後ろに下がるような仕掛けで壁判定を行っていますが、あまり緻密なロジックとは言えません。「まぁだいたいこのくらい下げておけばパッと見は止まったように見えるかな」というレベルです。
それが駄目ということではないです!普段ならロジックにこだわりまくるより、クリエイティブな発想や面白い仕掛けにフォーカスしてスクラッチに取り組むほうが吉だと思います。今回はレッスンなので、もうちょっとロジックにこだわっていきたいという意味です。
進行方向に壁がないか、1座標ずつ確認する
Y軸でやったときと同じです。まず変数「スピードX」の絶対値の数だけ判定を繰り返すようにループさせます。
コスチュームを変えるタイミングを移動する
ループの前にコスチュームを変えるようにします。
変数「★カメラのX座標」を1座標ずつ変える
変数「スピードX」を自分の絶対値で割ります。こうすることで変数「スピードX」が10なら「10 ÷ 10 = 1」となり、マイナス10なら「-10 ÷ 10 = -1」となるので、左右への移動が担保されます。
また、これを追加することで一番上の「★カメラのX座標をスピードXずつ変える」ブロックが不要になるので消します。
補正値を修正する
スピードXにマイナス1.5掛けて止まっているように見せていた箇所は、もういらなくなります。なぜなら、1座標ずつチェックしているので、単純に直前のX座標に戻せばいいだけだからです。
しかし、動きがカクカクしてしまいますね。こんなことはY軸のときでもありましたね。こんなときどう直したでしょうか。
画面を再描画せずに実行する
はい、ということでブロック定義「X軸に移動する」を右クリックして、オプションにチェックを入れてください。
これで描画する前に一瞬でロジックを実行してくれるので、なんちゃって未来予測処理が実現できます。
動作確認する
1座標ずつ壁判定を行っているので、X軸の移動が緻密になりました。
ブロック定義「ステージに触れているか調べる」を改善する
問題の本質に迫る前に、もう1つ改善しておきます。
当たった・当たってないと言う場所を変える
まずブロック定義「X軸に移動する」内の「〜と言う」ブロックを外してください。そして「もし〜でなければ」ブロックの代わりに「もし〜なら」ブロックを採用します。スッキリします。
そして、これは必須ではないですが、「当たった」「当たってない」というブロックは、ブロック定義「ステージに触れているか調べる」に移します。
後でもっと便利なデバッグ技で置き換えますが、とりあえずはこれでOKでしょう。
デバッグとは、開発中のプログラミングを修正する作業のことです。こういう吹き出しとかを条件ブロック内に配置して、処理が本当に動いているかどうかを調べる作業は、代表的なデバッグ作業です。
ブロック定義「Y軸に移動する」を改善する
さて、いよいよ本題です。ジャンプして壁に当たると下に落ちずにくっついてしまう問題にタックルします。
問題の本質は、Y軸に移動する際に判定している「ステージに触れたら落下を止める」という処理が、壁に当たったときにも動いてしまうことでした。
処理が動くと上手くいかないなら、壁に当たったときはこの処理を動かないようにすればいいのです。問題をひっくり返せば解決策になるパターンです。
ステージに触れているかどうか調べる部分を改善する
まずはステージに触れいているかどうか調べるロジックが古いので、これをブロック定義を使う方法に変えておきます。
ジャンプ用の当たり判定エリアを作る
いろいろ改善が続きましたが、ここからが本質的な修正作業です。X軸の移動と同じように、Y軸においても当たり判定エリアを利用します。
そこで、X軸で作った当たり判定エリアのコスチューム名を「当たり判定エリア右」と修正しておきます。
このコスチュームを複製して、今度は当たり判定エリア下を作ります。
- コスチューム「当たり判定エリア右」を複製する
- コスチューム名を「当たり判定エリア下」に改名する
- 目安として、コスチューム「歩く1」をコピーして、「当たり判定エリア下」内でペーストします
- 足元あたりに四角を作ります。
- 目安として使っていたスクラッチキャットの画像は消してOKです
当たり判定前にコスチュームを変えます
Y軸の当たり判定を行う前に、今作ったコスチュームに着替えます。
動作確認する
どうでしょうか、うまくいきました?上手くいってない場合は、コスチューム「当たり判定エリア下」の四角が右に寄りすぎて、壁に当たってしまっている可能性があります。四角を小さくするか、少し左にズラして試してください。
デバッグ技「ログ出力」をマスターする
この機能はマリオを作る上で絶対必要というわけではないので、お急ぎの方は飛ばしてもOKです。ただ、とても便利な技なので時間があればチェックしておいてください。また、今後この機能がある前提でスクショをお届けするので、その点は把握しておいてください。
上手くいったとしたら、それはどうなっているのか、改めて考えてみたいと思います。さきほども述べましたが、Y軸の移動時には壁に当たっても落下が止まらないようにしたわけですが、プログラム上ではどういうことが起きているのか、じっくり観察したいと思います。
というか観察を口実にして、1つ有意義なデバッグ技をご紹介します。
これはマリオのみならず、ほとんどすべてのスクラッチプロジェクトで有効な技です。ここでマスターしてしまいましょう。
ブロック定義「ログ」を作る
リスト「★ログコンソール」を作る
リストは使い慣れない人、苦手意識を持っている人、いると思います。リストの練習にもこのログ機能は役立ちます。接頭語に★をつけるのをお忘れなく。すべてのスプライト用のリストです。
1番目になにかを挿入する
1番目にどんどんログを追加していきます。
りんごとばななを2つ設置する
「りんごとばなな」ブロックを2つつなげて「ラベル:ログ」という情報を挿入するようにします。
いったんこれでOKです。
ブロック定義「X軸に移動する」内に設置する
ブロック定義「X軸に移動する」内で、壁判定が通ったタイミングでログを出力します。
ブロック定義「Y軸に移動する」内にも設置する
ジャンプのステージ判定処理が通ったタイミングにも1つ設置しておきます。
こうするとダーッと同じログが追加されてしまうと思います。
いったん赤いボタンを押して処理を止めると安心です。
連続して同じログは追加しない
条件ブロックを追加して、無駄に同じログが登録されないようにします。
これで安心です。
壁にジャンプしてからログを確認する
これで何が起きているのか順番も考慮して把握することが出来ました。さっそく壁にジャンプして何がログコンソールに出力されるか見てみます。
まず壁にジャンプして当たったタイミングでは「右がステージに触れました」が出力されます。このタイミングではX軸のみの判定が行われています。ブロック定義「X軸に移動する」内で、壁に当たったら変数「スピードX」をゼロにしているため、壁がコスチューム「当たり判定エリア下」に触れる前に横移動がストップするのです。このため、ブロック定義「Y軸に移動する」のステージに触れたかどうかの判定は行われません。つまり、壁に当たった直後に横移動は止まり、今度は落下が始まります。
そして、着地するとリスト「★ログコンソール」には「下がステージに触れました」というログが表示されます。
ロジック的にも直感的にも、正しい動きといえるでしょう。
「○と言う」ブロックでは処理の順番を追いかけるのが困難ですが、このブロック定義「ログ」を使うことで繊細なデバッグ作業を実現することが出来ます。
「○と言う」ブロックのほうが分かりやすい局面はたくさんあるので、併用していきましょう。
バックパックに入れる
このログ機能は様々なスプライトで使い回せるので、バックパックにドラッグ・アンド・ドロップしておくと便利です。
このあとログ機能自体も改善する場面がありますが、バックパックに登録し直すのも面倒なので、最終形態をこちらに掲載しておきます。マリオのプロジェクトに必須な機能ではないので、もし余裕があればこの画像を見て同じように作ってからバックパックにしまっておいてください。
ステージにもログ機能を付けてみる
バックパック経由でステージのスプライトにもログを作ってみます。そうするとプレイヤーのログもステージのログもすべてリスト「★ログコンソール」で管理できるようになり、デバッグ作業に便利です。メッセージなどを駆使して複雑なプロジェクトになればなるほど、処理がどういう順番で実行されたのか、内部の動きを追いかけるのに有効です。
坂に挑む
坂もマリオにはよく出てきます。坂を登ったり降ったり、よくある動きですね。しかし、今の壁判定のままでは少しの坂でも一歩も進めなくなってしまいます。コスチューム「当たり判定エリア右」が坂に触れた途端に変数「スピードX」はゼロになり、ピタッと止まるでしょう。これではいけません。
そこで、ここからはプレイヤーが当たったのが坂なのか壁なのかを判定する処理を加えていきます。
作戦としては、ステージに触れたらY座標を1つ上にズラしてみます。それでもまだ触れていたら、もう1つ上にズラしてみます。これを数回繰り返して、「どうやら所定の回数Y座標を上に1ずらしてもステージに触れたままなので、これは壁だろう」と判断したり、「Y座標を3つ上にずらしたらステージに触れなくなったぞ、きっとこれは坂だ」と判断していきます。一緒にやってみましょう!
坂を作る
ステージのコスチュームをいじって坂を作ります。あまり急な坂ではなく、なだらかな坂にしておきます。
動作確認する
この坂を登れるか、念の為試してみます。みなさんがどの程度の傾斜の坂を作ったか分かりませんが、おそらく少し坂に足を踏み入れたところでピタッと止まったのではないでしょうか。
今はこれでOKです。ここから修正を始めていきましょう。
Y座標を1つ上にズラす
作戦通り、Y座標をズラしていきます。
挿入場所は、ステージに触れていることが確定した直後です。
ステージに触れているか調べる
Y座標を1つズラしたところで、またステージに触れているかチェックします。
条件ブロックも追加します
壁だと判断したらズラした分を戻す
Y座標をズラしたのに、まだステージに触れていたなら(壁だったと判断して)Y座標を元に戻します。坂ではない以上、登るモーションは不要だからです。
Y座標をズラす作業を繰り返す
坂の傾斜によってはY座標を1つズラしただけでは坂を上手く登れないでしょう。どのくらいの傾斜を登れるようにするかはゲーム次第ですが、ここではもう少しキツイ坂でも登れるようにしていきます。
壁だったなら-2にする
今回は結局Y座標を3つ分は坂であると判定しました。なので、これと同じ処理をもう1つ追加して、壁のときは-3します。
プレイヤーの大きさを変える
この段階でプレイヤーの大きさを変えていきます。今の状態では大きすぎるので、大きさ40くらいに指定します。サイズはお好みでOKです。
動作確認する
プレイヤーを小さくしたことで坂を登ったり降ったりする動作を、長く確認できるようになりました。
しかし、下り坂ではピョコピョコと浮いてしまうような動きになっていませんか?これもカンタンに直せるなら直したいですね。
落下時の処理を変える
いまは落下時にステージに触れたら、変数「スピードY」をゼロに変えてピタッと落下を止めています。これにゆとりをもたせることで、坂の下り動作を改善します。
変数「スピードY」に0.8を掛けるだけです。これでスムーズに坂を下ることが出来ます。
デバッグ技「メモ」をマスターする
いろんな動作をカバーしてきたので、コーディングブロックが混雑してきていると思います。
そこで、ブロックが入り組んでいても少し可読性を高めるべく、新しいデバッグ技を紹介します。
それはメモというブロック定義です。
ブロック定義「↓」を作る
ブロック定義の名前はとくになく、単に下矢印を記入します。引数にメモと書いておきます。
中身は何もしない
これはただメモを残したいだけなので、中には何もブロックを置きません。
メモを残す
このブロック定義の直後に何をしているのかをメモしていきます。
リファクタリングする
ここに至るまでも実はリファクタリング(コードを美しくする作業)をしてきましたが、改めて立ち止まって時間を取ります。
ブロック定義「坂や壁を判定する」を作る
単純に今作った処理をブロック定義に移動します。
スクショではブロックが被ってしまっていますが、ブロック定義「X軸に移動する」の最後の部分を移動し、代わりに「坂や壁を判定する」ブロックをループ内に置いています。
「このスクリプトを止める」ブロックを置く
壁と判定したら、このスクリプトを止めるブロックを実行して処理を止めます。これは次にやる処理の布石です。
坂の傾斜によってスピードを遅くする
きつい坂を登れば登るほどスピードが落ちるのは自然です。むしろ今のままでは坂を平地のように駆け登るので、やや不自然です。
これはY座標を1つ上にあげるたびにスピードXを減らせばいいだけです。
各「ステージに触れているかどうか」判定の最後に、スピードXを減らす処理を配置すればOKです。
スクショでは傾斜の判定を5回行っているので5箇所に減衰処理を追加しています。ここはどの程度の坂を登れるかによって数が異なるので、必ず5箇所に置く必要はありません。
動作確認する
しかし、動きを見るとアニメーションが上手く機能していない人もいるのではないでしょうか。
登っている姿が、コスチューム「歩く1」で固定されてしまっているのではないでしょうか。
これはバグですね!修正しなければなりません。
ブロック定義「アニメーションする」を改善する
レッスン1で作成したブロック定義「アニメーションする」で、スピードXの値が小さいときはコスチュームを歩く1に固定する処理を書きました。今回のバグはこれが原因です。平地であればノロノロと歩くことはなかったのですが、坂道の処理を作ったことでプレイヤーがゆっくり動くケースが出てきました。そのためにバグ化してしまったのです。
これを修正するには、現在は「スピードXが1より小さいならコスチューム歩く1に固定する」というスピードXの1という値を、もっと小さくすれば解決します。
ここでは0.2から0.25くらいが最適だと判断しました。
ゴールを作ろう
次はゴールを作ります。プレイヤーがゴールに触れたらステージクリアです。そして次の第2ステージに進むという処理を作りたいと思います。
マリオではステージをクリアすればするほど難易度が上がったり、色んなステージを楽しめたりする仕様があるので、このスクラッチプロジェクトでも実現したいと思います。
ステージごとにシーンを管理する方法なども必要になります。今回作ったステージのスプライトに手を加える必要も出てくるでしょう。
こちらも数日以内に更新予定です。こちらの記事にもリンクを貼るので、またブックマークなどしておいてもらえたら幸いです! 更新しました!
おまけ
坂と壁の処理をスマートに書く方法も知りたいという声をいただいたので、こちらに模範解答としてスクショを載せておきますね!これでも上述した処理と同じ動きになり、コードもスッキリです。
ただ坂を登るときにスピードXを減衰するところは緻密ではなくなっています。まだまだ改善する余地はありそうですね。
- ① 横スクロールを制覇せよ
- ② ジャンプせよ
- ③ 壁・坂を攻略せよ (いまここ)
- ④ ゴールして次へ
- ⑤ ブロックを壊せ
- ⑥ スタックを回避せよ (バグ修正の回)
- ⑦ コインをゲットせよ
- ⑧ 敵モブを召喚せよ1 (クローン化、スポーン、横移動など)
- ⑨ 敵モブを召喚せよ2 (方向転換や重力)
- ⑩ 敵モブを召喚せよ3 (アニメーション)
- ⑪ バトル開始1 (ダメージを受ける・与える)
- ⑫ バトル開始2 (ダメージのアニメーション、フィードバック)
- ⑬ 敵モブをリスポーンさせる
- ⑭ iPadに対応する
- ⑮ 迫りくるパタパタ (敵モブに新しい動きを追加する手順が分かる)
- ⑯ パタパタのバグ修正 (ジャンプのバグを直す)
- ⑰ 壁に埋もれるバグ修正 (バグがバグを呼ぶ、でも諦めない)