この記事は、griffpatchさんが公開しているYoutube動画「Code a Platformer Game」を見ながら、実際にプラットフォーマーゲームを作ってみた記録です。

今回は2. Next Levelをやってみました。

ここでは、複数のステージを追加しています。また、段差がちょっとズレている場合の対応も解説していますし、便利なScaratchアドオンも紹介しています。

Part2で作成したプロジェクト
Part2で作成したプロジェクト

※ 動画ではゲームの各場面を「レベル」と呼んでいますが、日本では「ステージ」が一般的な感じがするので、記事中では「ステージ」と書いています。ただ、Scratchでは、背景をまとめた場所も「ステージ」という名前なので、ちょっと紛らわしいかも知れません。

準備

前回のプロジェクトを開きます。

最初に、ACCELERATION変数を「1.5」から「2」に上げます。こうすると動作テストするとき、速く移動できます。

ACCELERATION変数を更新
ACCELERATION変数を更新

それから「ファイル」>「コピーを保存」しておきます。動画ではエピソード2(ep2)という名前を付けています。

0:58 The Problem with Multicoloured Levels – 複数の色を使ったステージの問題

前回のプロジェクトでは、ステージを背景に描きました。

この方法は、すばやく結果を得られるし、スクリプトの作成も簡単になります。でも、ステージに複数の色を使おうとすると問題が起こります。床や階段を色で検出しているからです。

この問題を何とかするために、まず階段の色を変えてみます。この状態で動作確認すると、プレイヤーが階段をすり抜けてしまいます。

階段の色を変える
階段の色を変える

判定する色を「または」ブロックでつなげば複数の色に対応できますが、たくさんの色を使うステージでは大変ですし、処理スピードも遅くなります。

そこで、ステージを背景からスプライトに変更します。

3:02 Change from Backdrop to a Level Sprite:背景からスプライトに変更

ステージを背景からスプライトに変更すると、色ではなくスプライトで衝突を検出できます。この方法は、高速で処理できますしステージにたくさんの色をあつかえます。

ステージをスプライトにコピーする

ステージを背景からスプライトに切り替えるには、次のようにしてコピペします。

まず、新しいスプライトを作成して「Level」という名前を付けます。
次に、背景に切り替えて、ステージのコスチュームをスプライトまでドラッグ&ドロップします。
これで、ステージを背景からスプライトにコピーできました。

背景のコスチュームをスプライトまでドラッグ&ドロップ
背景のコスチュームをスプライトまでドラッグ&ドロップ

背景のほうは不要になったのでコスチュームを削除します。「Level」スプライトも、余分なスプライトを削除しておきます。

「Level」スプライトのコードを組み立てる

続いて「Level」スプライトのコードを組み立てていきます。

緑の旗がクリックされたときに、「x座標を「0」y座標を「0」にする」で中央に配置してから、最背面に移動させます。

「Level」スプライトのコードを組み立てる
「Level」スプライトのコードを組み立てる

動作確認すると・・・地面と最初の階段の色を「または」ブロックでつないでいれば、そこは検出できますが、それ以外の色の階段は検出できません。

色の違う階段を検出する

ではPlayerが、Levelスプライトとの衝突を検出できるようにします。

そのために、Playerスプライトの「Move – in steps」ブロックの「()色に触れた」を「”Level”に触れた」に置き換えます。

「"Level"に触れた」に置き換える
「”Level”に触れた」に置き換える

これで、色の違う階段も検出できるようになりました。

5:27 Consolidate Touching Scripts:衝突判定を集約する

これで、少し複雑なデザインのステージでも衝突を判定できます。ただし、移動するプラットフォームやスプライトによる障害物などゲームのなかで衝突判定したい相手が増えてきた場合は、色による判定を引き続き使うことができます。

そこで、多くの場所で同じような判定をしなくてもすむように、衝突判定を一ヶ所にまとめておきましょう。

そのために、「Check Touch Solid」というブロックを定義します。「Check Touch Solid」は、堅いものに触れているかチェックするという意味ですね。このブロックは「画面を再描画せずに実行する」をオンにしてきます。

そして、この「Check Touch Solid」ブロックに「もし、***なら、でなければ」を追加して、条件を「”Level”に触れた」とします。

この条件の結果を持つために「touching」変数を用意して、条件が成立してたら「touchingを”1″にする」として、でなければ「touchingを”0″にする」とします。

「Move – in steps」ブロックのほうは、「”Level”に触れた」の前に「Check Touch Solid」を配置して、「”Level”に触れた」を「touching > 0」ならとします。

衝突判定を一ヶ所にまとめる
衝突判定を一ヶ所にまとめる

これで、衝突判定を一ヶ所にまとめることができました。

※これだけでは同じ動作ですが、この後のパートで障害物などを追加していくと、プログラムをすっきりわかりやすくできるはずです。

7:28 Organise our code:コードを整理する

ここで、これまで作ったコードを振り返ってみます。内容は簡潔ですが、長いスクリプトがあって、どこで何をしているか理解するのが面倒です。

そこで、イベントと名前付きの定義ブロックに分割します。このあとイベントやブロックの名前を伝えられたら、それを見つけやすくなります。

「ずっと」ループで「Tick – Player」を繰り返す

最初に「ずっと」ループの中身をひとつにまとめます。これは、このゲームのメインループであり、ゲームの1コマごとに1回実行されます。そこで「Tick – Player」というメッセージ名を付けます。「Tick」(ティック)は、時計がチクタクと動作するという意味です。

ずっとループで「Tick - Player」を繰り返す
ずっとループで「Tick – Player」を繰り返す

「ずっと」ループが毎秒30回動作を繰り返すと、その1回ごとに「Tick – Player」メッセージを受け取って、ゲームのアニメーションを生成します。

「Tick – Player」を分解

次に「Tick – Player」を分解します。

まずは上矢印キーでジャンプするところ。

「Controls – Up and Down」というブロックを定義して、「画面を更新せずに実行する」をオンにします。この「Controls – Up and Down」とジャンプ処理のブロックを組み合わせておきます。

それから左右の矢印キーで移動するところ。同じように「Controls – Left and Right」というブロックを定義して、「画面を更新せずに実行する」をオンにします。そして「Controls – Left and Right」と左右の移動処理を組み合わせます。

「Move – in steps」は、「Tick – Player」の下に戻しておきます。

あと「Controls – Left and Right」の一番下に「Speed yを”GRAVITY”ずつ変える」があります。左右の移動処理のなかにこれがあるのはへんなので、「Controls – Up and Down」の一番下に移動しておきましょう。

「Tick - Player」を分解
「Tick – Player」を分解

これで動作確認すると、今までと同じようにプレイできます。

ゲーム開始時の処理を分解する

さらに、緑の旗のブロックのところを見てみましょう。

最初の4つはプロジェクトの設定で、その下はステージをリセットしゲームループを開始するための処理です。そこで、この後半部分を分解します。

「Rest and Begin Level」というブロックを定義して、「画面を更新せずに実行する」をオフにするよう注意すること。そして「Rest and Begin Level」とステージをリセットしゲームループと組み合わせます。

さらに「ずっと」ループ自体を分離します。こうすると、他のスプライトにも「ゲームループ」を組み込みできます。そのため「Game Loop」というメッセージブロックを付けます。

あと、「Tick – Player」を2列目の一番上にしています。

ゲーム開始時の処理を分解する
ゲーム開始時の処理を分解する

リセットされているか確認する

最後に「Rest and Begin Level」が本当にリセットできているか確認します。

そのため「Rest and Begin Level」に「speed xを”0″にする」と「fallingを”99″にする」を追加します。「fallingを”99″にする」は、最初の着地までジャンプできなくするためです。

試しに、同じように動作するか確認しておきましょう。

スクラッチアドオンでブロックを整列

ちなみにコードブロックを見やすく整列するには、Scratchアドオンの「きれいにする+」機能を使うと簡単です。Scratchアドオンは、ブラウザに組み込んでScratchのプロジェクト開発を便利にする拡張プログラムです。Scratchアドオンをインストールしておくと、右クリック > 「きれいにする+」で、ブロックを自動的に整列してくれます。

便利なので、ぜひ入れておきましょう。

11:50 Change of Scene – A Multi-screen level:複数のステージに切り替える

いよいよ新しいステージを作っていきます。プレイヤーが画面の右端に来たら次のステージに移動させますが、まずは1面目を左右反転させたステージを作ります。

ステージを追加する

Levelスプライトのコスチュームエディタを表示して、現在のステージを右クリック > 複製をクリックします。

これで、次のステージができました。

このステージを左右反転するには、コスチュームエディタの「左右反転」ボタンを使います。でも、地面と階段をまとめて選択して左右を反転しても、中心がずれてしまいます。「左右反転」ボタンは、キャンバスの中央ではなく選択した図形の中央で反転するからです。

そこで、グリフパッチさんは次のテクニックを紹介しています。

  1. ステージ全体にかさなる四角形を描く
  2. 四角形を中央に配置する
  3. 四角形といっしょに、地面と階段を選択する
  4. 左右反転する
  5. 四角形を削除する

この方法なら、キャンバスの中央で左右反転できます。

それから、各ステージコスチュームの名前を変えておきます。動画では「Scene1」「Scene2」としています。先頭は大文字で、数字の前にスペースを付けていません。

2つ目のステージを左右反転する
2つ目のステージを左右反転する

ステージを切り替える

では、ステージを切り替えるコードを組み立てます。

「Level」スプライトのコードエディタで、ステージを切り替える「Change Scene」メッセージを作成します。そして、「Change Sceneを受け取ったとき」の下に「コスチュームを”Scene1″にする」を配置して、ステージを切り替えます。

ステージの指定は、文字列を連結する「”りんご”と”バナナ”」ブロックを使います。「”Scene”と”SCENE #”変数」とすることで、”Scene”という文字列と”SCENE #”変数を連結できます。

このとき”SCENE #”変数は、すべてのスプライト用としておきます。

※「#」は、英語で番号を表すときに使う記号です。

「SCENE #を”2″にする」ブロックを用意して、このブロックと「Change Sceneを受け取ったとき」ブロックをクリックすると、ステージを切り替えるテストができます。

ステージを切り替える
ステージを切り替える

※ 動作確認が終わったら、「SCENE #を”2″にする」ブロックは削除します。

ステージを指定する

次は、プレイヤーの動作に合わせてステージを指定しましょう。

playerスプライトのコードエディタに切り替えて、「Reset and Begin Level」ブロックを見てください。ここに「SCENE #を”1″にする」ブロックを追加します。

さらに「Game loopを送る」の前に「Change Sceneを送る」を配置します。

ステージを指定する
ステージを指定する

では動作確認です。「SCENE #を”*”にする」ブロックで「1」を指定して、緑の旗をクリックすると、1番目のステージが表示されます。

「SCENE #を”*”にする」ブロックで手動で「2」を指定して、緑の旗をクリックすると、2番目のステージが表示されます。

15:40 Moving off the Edge of the Screen:画面の端で次のステージに進む

では、画面の端までたどりついたら、次のステージに切り替えしょう。

画面右端に来たことを検出する

ステージの切り替えは「Game Loop」のなかで「Tick – Last」メッセージを実行して処理します。こうすると、すべての処理がTickごとに確実に処理できます。

そこで、「Game Loop」のなかに「Tick – Lastを送る」を追加します。

それから「Tick – Lastを受け取ったとき」の下に、「もし」ブロックを追加して、条件を「x座標 > 235」とします。

画面の右端の座標は「240」ですが、そこまでたどり着けない場合もあるので5ピクセル少ない「235」としています。

あとは「もし」ブロックの中に、「SCENE #を”1″ずつ変える」と「Change Sceneを送る」を追加します。

画面の端で次のステージに切り替える
画面の端で次のステージに切り替える

ステージを切り替えたらプレイヤーも移動する

動作確認すると・・・画面の右端までたどり着いたときに次のステージに切り替わりますが、プレイヤーはそのまま右端にいます。

修正するには、「Change Sceneを送る」のあとで「x座標を”-235″にする」だけです。

動作確認すると・・・今度は、画面が切り替わると同時にプレイヤーは左端に戻りました。

ステージを切り替えたらプレイヤーも移動する
ステージを切り替えたらプレイヤーも移動する

画面の左端に来たら前のステージに戻る

続いて、画面の左端に来たら前のステージに戻るようにしてみましょう。

「Tick – Lastを受け取ったとき」の下にある「もし」ブロックを複製して、元のブロックの下に配置します。上が左端の検出なので、下を右端の検出に書き換えます。

「もし」ブロックの条件を「x座標 < -235」とします。

それから「もし」ブロックの中にで「SCENE #を”-1″ずつ変える」とします。

あとは、「Change Sceneを送る」のあとで「x座標を”235″にする」とします。

画面の左端に来たら前のステージに戻る
画面の左端に来たら前のステージに戻る

動作確認すると、1番目と2番目のステージを行ったり来たりできるようになりました。

3番目のステージを追加する

では、2番目のステージで右端に到着するとどうなるでしょうか。「SCENE #」変数は「3」になりますが、まだ3番目のステージを用意していないのでステージはそのままです。

そこで、3番目のステージを追加します。名前は「Scene3」としておきます。レイアウトも少し変えましょう。

これで、3つのステージを行き来できるようになりました。

3つのステージを行き来する
3つのステージを行き来する

18:57 Getting Stuck in the Level:ステージ間の移動で引っ掛かる!?

先ほど、1番目のステージを左右反転して2番目のステージを作りました。そのため、1番目と2番目で階段の位置がそろっていました。でも、実際のステージはズレがあったり、障害物を置くかもしれません。

ステージがズレていると、そこでプレイヤーが引っ掛って動かなくなくなります。

ステージがズレていると引っ掛かる!?
ステージがズレていると引っ掛かる!?

これは、階段の位置を少しずらすと、実際に試すことができます。

そこで、ステージ間にズレがあっても、自動的に上下の位置を調整するようプログラムを改良します。

20:40 Preventing collision bugs:修正の準備

では、プレイヤーが引っ掛かるバグ修正の準備をしましょう。

「Tick – Last」の処理をまとめる

まずズレの調整を追加する前に「Tick – Last」の処理をまとめます。「Tick – Last」は、ステージの右端と左端のふたつの処理があるので、ブロック定義でひとつにします。

そのために、「Begin Scene # “scene #” go to x “x”」というブロックを定義します。「Begin Scene # go to x」は、「”scene #”のステージを開始して、”x”に移動する」という意味合いになります。「scene #」と最後の「x」は引数です。間にある”go to x”はラベルです。「画面を再描画せずに実行する」はオフにしておきます。

ブロックを定義する
ブロックを定義する

定義したブロックの下には、「Tick – Last」の「もし」ブロックの中身を移動しておきます。そして「SCENE #」を”1″ずつ変える」の代わりに「SCENE #」を引数”scene #”にする」ブロックを配置して、「x座標を引数”x”にする」とします。

「Tick – Last」の「もし」ブロックの中身は、右端では「Begin Scene # “SCENE # + 1” go to x “-235″」、左端では「Begin Scene # “SCENE # – 1” go to x “235”」とします。

定義したブロックで置き換える
定義したブロックで置き換える

これで「Tick – Last」の「もし」ブロックの中身を、定義したブロックで置き換えできました。

動作確認すると、先ほどと同じように動くはずです。

プレイヤーの引っ掛りを検出する場所

次は、ステージを切り替えた時、引っ掛かっていないか検出します。それはどこに追加すればいいでしょう。

動画では、「Begin Scene # “scene #” go to x “x”」ブロックで、「Change Sceneを送る」の後に「スプライトの他のスクリプトを止める」を入れて試しています。

こうすると、ステージを切り替えた後に、スプライトの他のスクリプト、つまりGame Loopを止めます。そのおかげで、キー操作などは受け付けなくなります。

そして「スプライトの他のスクリプトを止める」の下にある「x座標を”x”にする」を実行するので、プレイヤーが左端に表示されます。

プレイヤーの引っ掛りを検出する
プレイヤーの引っ掛りを検出する

なので、この「Begin Scene # “scene #” go to x “x”」ブロックでGame Loopを止めたあと、プレイヤーの引っ掛りを検出して衝突を解消すれば良さそうです。

ステージ切り替え処理の順番を変更する

その前に、ステージ切り替え処理の順番を変更しておきましょう。

「x座標を”x”にする」を「SCENE #を”scene #”にする」の下に移動します。それから、「スプライトの他のスクリプトを止める」の下に、「0秒待つ」を配置します。

プレイヤーの引っ掛りを検出する
プレイヤーの引っ掛りを検出する

実は、「***を送る」ブロックは、すぐに実行されず後で実行されるようキュー(待ち行列)に入れられます。しかし私たちは、衝突チェックをおこなう前にステージを切り替えてもらう必要があります。

こうすると、ステージを更新して引っ掛かりを検出する準備ができると、動画では述べています。

※意味がよく分かっていません。

衝突を検出・解消するブロックを定義する

では、引っ掛かりを検出して衝突を解消する処理を作っていきます。

そのために「Fix Collisions in Direction “dir”」というブロックを定義します。これは、Dir方向の衝突を解消するという意味合いですね。「dir」は引数にします。今回は「画面を再描画せずに実行する」をオンにします。

定義したブロックを「0秒待つ」の下に配置します。

このとき、引数dirに「0」を設定すると、上下方向の引っ掛かりを調べて衝突を解消します。「90」と設定すると、左右方向の衝突を調べて衝突を解消することにします。

そして、衝突を解消したら、止めていたGame loopを再開するため「Game loopを送る」を実行します。

ブロックを定義する
ブロックを定義する

あらためて動作を確認すると、ステージが切り替わっても、Game loopが再開されるのでキー操作でプレイヤーを動かすことができます。でも、引っ掛かる現象はまだ解消されていません。

25:22 Getting Un-Stuck:引っ掛かりを解消する

いよいよ、引っ掛かりを実際に解消するコードをいよいよ作っていきます。そのために、移動・回転・移動・回転を繰り返します!

初期設定

まず、このスプライトのみで使う変数として「temp」変数を作ります。「temp」(テンプ)は「temporary」(テンポラリー)の略で一時的という意味ですね。

「Fix Collisions in Direction “dir”」ブロックに「tempを”向き”にする」ブロックを配置します。temp変数で、playerスプライトの「向き」を一時的に保存して、あとで再現するために使います。

続いて「distance」という変数を作成します。「distance」(ディスタンス)は距離という意味です。この変数で、衝突していない位置を探してどこまで移動する必要があるか記録します。初期値を「1」としておきます。

次は「”dir”に向ける」ブロックを追加します。この引数dirが、引っ掛かりを解消するために移動する方向になります。

さらに「”**”回繰り返す」ブロックを配置します。とりあえず、繰り返し回数は「64」としておきます。

衝突が解消された場合

そして、この繰り返しのなかで、衝突が解消されたか調べます。そのために、以前に作った「Check Touch Solid」ブロックを使います。これは、地面や壁に衝突していないか調べるブロックでした。「Check Touch Solid」ブロックを呼び出すと、「touching」変数が衝突していれば「1」になり、衝突していなければ「0」になります。

なので、「もし」ブロックを使って、衝突していないか調べます。条件は「touching < 1」とします。そして、衝突していなければ「”temp”度に向ける」で、元の向きに戻します。あとは「このスクリプトを止める」ブロックを追加します。

衝突している場合

衝突している場合は、まず「”distance”歩動かす」で移動します。それから「”180″度回す」で反対方向を向いて、「distanceを”1″ずつ変える」で調べる距離を増やします。

引っ掛かりを解消する
引っ掛かりを解消する

衝突を解消する仕組み

「Fix Collisions in Direction “dir”」ブロックがどのように動くか、頭の中で動かしてみるといいでしょう。このブロックは大きく3つの部分に分かれています。

衝突を解消する仕組み
衝突を解消する仕組み

まずtemp変数とdistance変数を用意しています。それから向きを変えています。これが、衝突解消の初期設定です。

※ 現在のところプレイヤーの向きは変えていないので、これは将来の改良に備えているんだと思います。

それから64回繰り返すなかに、2つの処理があります。前半は衝突していなかった場合で、後半は衝突していた場合です。

衝突していなかった場合は、向きを元に戻して、衝突の解消処理を停止します。衝突していた場合は、少しずつ距離を伸ばしながら上下の移動を衝突が解消するか64回まで繰り返します。

繰り返し回数を64回と指定しているのは、無限ループに陥らないようにするためだそうです。

動作を確認する

動作を確認するのは簡単です。

プレイヤーのスプライトを地面に重ねたら、「Fix Collisions in Direction “dir”」ブロックをクリックします。すると、プレイヤーが素早く上下に動いて、衝突が解消されるまで繰り返します。

重なっているので、上側でも下側でも構いません。

動作を確認する
動作を確認する

実際にステージ間を移動して引っ掛かった時は、この上下の移動は見えません。「Fix Collisions in Direction “dir”」ブロックの「画面を描画せずに実行する」をオンにしてあるからです。

28:50 Bad Level Design:悪いステージデザイン

ステージを切り替えたとき、空中のブロックに引っ掛かった場合も、この処理は自動的に引っ掛かりを抜け出します。ただし、ステージのデザインが悪くて調べる距離が大きすぎる場合、抜け出せずに立往生してしまうことがあります。

そのため、慎重にステージをデザインする必要があります。

ステージの境界に大きなブロックをどうしても配置したい場合は、衝突解消の繰り返し回数を「64」から「128」のように大きな数に変えるといいでしょう。

あと、ステージ1の左端までたどり着くと、ステージ0に進んでしまいます。ステージ1の左端に高い壁を配置するといいでしょう。遊んでくれる人にも、この先に進めないことが一目でわかります。


これで、プラットフォーマゲームでステージ間を移動できるようになりました。

よく分からないところもあると思いますが、実際に作ってみたり、いろいろ改良してみるといいと思います。


これまでの記事 – Youtube:グリフパッチのプラットフォーマー開発チュートリアルをやってみた

  1. 基本
  2. ネクストレベル
  3. 当たり判定とアニメーション