スクラッチに挑戦している皆さん、どうも!スクラッチコーチです。
- ① マップチップ(タイル)をグリッド状に並べる
- ② タイルをスクロールさせる
- ③ リストでグリッドを管理する
- ④ ステージを洗練させる
- ⑤ 衝突処理を作る
- ⑥ マリオを動かす! (いまここ)
- ⑦ キー長押しでハイジャンプ
- ⑧ 走るアニメーションをヌルヌル動かす
- ⑨ キュキュッとターンで土ケムリぶわぁ!
- ⑩ もはやマリオメーカー!自由自在にステージを改造する仕組み
- ⑪ 落ちる挙動を作ろう & 品質向上
- ⑫ ステージコードをエンコードする(セーブ)
- ⑬ ステージコードをデコードする(ロード)
- ⑭ コインをゲットしてがっぽり♪
前回は衝突判定を作ってプレイヤーがブロックを通り抜けられなくした!
衝突判定にはグリッドのインデックスをXとY座標から算出する特殊な式を使ったね。このあたりは複雑で乗り越えるのが大変だったんじゃないかなと思ってる。
ここからはカンタン!って言えれば良かったのだけど、険しい道程は続くぜ。でも一緒に見ていけば大丈夫、伴走するから乗り越えていこう!
今回の目標「マリオ、華麗にジャンプを決める」
今回は衝突判定を改善して、マリオが歩いたりジャンプしたりできるようにしていく。なおアニメーションは含まれない予定。
上キーを押したらジャンプして、左右キーを押したらマリオが歩く。今でも動くけど、重力がないから歩いてる感はないよね。そのあたりを洗練していくよ。
とくにジャンプは着地判定とかけっこう考えること多い。でも1つ1つはカンタンだから安心してね。
作戦会議
今回の作戦を説明する前に、そもそも現状の何が問題なのかを共有したい。
今ってコスチュームを点にしてるよね。
これを本格的にマリオ(スクラッチキャット)のコスチュームに変えたいと思ってる。
このスクラッチキャットはデフォルトと少し違うの分かる?ポーズもだけど、線の色とか太さとか口が違う。個人的にはデフォルトよりかわいいと思ってるw
問題発生!
で、そうすると問題が起きる。名付けて「大きいコスチューム問題」だw
下の図を見てほしい。
点のときは現在地の取得がカンタンだった。点のある位置の座標を使えば該当するグリッドのインデックスを算出することができた。
でもマリオのコスチュームに変えると、おいおいおい!大きくなったら複数のタイルをまたいでるじゃないか!?こりゃいったいどういうこってぇ!?(江戸っ子w)
上の図で言うならインデックス10と11のどっちが正しい現在地なのか、一言では言えない、っていう問題が生じるのだ。
マリオがキノコでも食べてもっと大きくなったらもっと多くのタイルをまたいでしまうことにもなる。そう思うと、何らかの数学的な解決策が求められるよね。
何らかの、数学的な、解決策が、求められる。(なんかカッコいいから2回言いましたw)
現状を把握する
だいたいいつも問題を解決するときの最初の一歩目は現状把握だ。
ということでとりあえずマリオのコスチュームにして動かしてみようか。
うん、上の動画をみると、ヨコはまぁまぁいい感じにストップしてるけど、タテの衝突が上手くいってない感じだね。アゴのあたりまでズブっと沈んでいるように見える。
この原因を追求するために、点のコスチュームとマリオのコスチュームを重ねて見たいと思う。
すると点の位置がちょうどマリオの真ん中あたりに来ることが分かる↓
現在の座標計算ではスプライトのセンターの座標を使って計算しているので、結果的に上の動画みたいにタテの衝突が効いてないように見える。実際はセンターで衝突判定が成功しているためコレ以上は沈まないけど、直感的に変だよね。ちゃんと足で止まってほしい。
これは上にブロックがある場合も同じで、このままだと頭がブロックにめり込んでしまうw
対策を練る
現状はタテの衝突がとくにおかしくて、その原因はセンターで衝突判定をしているから。だから足が沈んだり頭がめり込んだりしてしまうわけだ。
ではどうすればいいか……ってカンタンだ!頭と足の座標でも衝突判定を行えばいいだけ。
現状把握で原因まで見出すことができたから対策も的確に練ることができる。
バグが起きたら闇雲に対策を打ちまくるのではなくて、なぜバグってるのかを見つけることが大事なんだ。そうして原因を突き止めれば、自ずと対策も見つかるものだよ。
そう、人生もじゃ。ふぉっふぉっふぉっ(仙人w)
作戦が決まったぞ!
ということで作戦としては、頭とセンターと足の3箇所で衝突判定を行おうと思う。タテもヨコも3箇所全てを衝突判定していく。
ざっくりとした手順は以下の通り。このあともっと細かく解説していくから流し読みでOK!いつもみたいに一緒にやろう。
- 衝突値という変数を用意する
- そして衝突判定を行う
- 衝突してたら衝突値に1以上の値ずつ加える
- 3回の衝突判定を行ったあとに衝突値がゼロのままなら衝突箇所はないから何もしない
- 衝突値が1以上なら、衝突処理(座標をもとに戻す)を行ってスピードもゼロにする
これをタテ方向にもヨコ方向にも、再描画無しで行えば埋もれることなくマリオを動かす土台が整う。
うん、文字での説明はこのくらいにしておこう。だいたい文字での要約っていうのは後から振り返ると役に立つけど、事前に書いても「へぇ」くらいにしか思わないもんだ。でもその「へぇ」がこのあとの理解を助けてくれるから書いておいた。
よし、次は手を動かすぞ!
衝突処理を改善する
衝突処理をヨコで3回、タテで3回、合計6回も繰り返すことになるから、変数やブロック定義などを用意して改善していきたいと思う。
変数「身長のハーフサイズ」を作る
頭と足の位置を正確に知るには、身長の半分のサイズを中心座標に足したり引いたりすればOKだ。その値を保持する器として変数を作っておこう。
では身長のサイズはどのように知ることができるか。そこは決め打ちだからカンタンだ。
コスチュームの右側の数字が身長になるぞ。
で、いまって大きさを200%にしてるじゃん?
だから実際の身長は27 * 2 = 54になってる。それの半分でOKだからまぁ27という数字を変数に入れておきたいんだ。
動画ではガチのマリオのコスチュームを使っているから、身長は24として設定しているよ。僕はスクラッチキャットをマリオとして使っているから身長27にしてる。みんなももし違うコスチュームを使う場合は、そのコスチュームの身長を見て数字を変えてね。
初期化する
値は初期化時に入れるようにしておこうか。
ブロック定義「衝突を処理する」を作る
再描画なしでブロック定義を作ろう。引数名が変数名と全く同じになるとややこしいので、引数名の接頭語にアンダーバーを付けておこうか。
処理を移す
すでに作った処理を一部このブロック定義に移動するよ。
変更前
移動と削除を行うよ。ガチャガチャ動かす必要あり。
変更後
ブロック定義「_衝突を処理する」
2つの変数を引数で置き換えることを忘れずに。
メッセージ「プレイヤーが動きます」を受け取ったとき
コードを移動した跡地でブロック定義を実行するよ。1回目はスピードX、2回目はスピードYを引数に渡しているところに注目。
1つ目はスピードYがゼロ、2つ目はスピードXがゼロ、という感じにしておいてね。理由はあとから明らかになるよ。
動作に変わりがないか確認しておこう!
今まで通りに動くか、壊れてないか、緑の旗を押して確認しておこう。
ブロック定義「_衝突値を算出する」を作る
よし、動作に異常はないね。相変わらず埋もれてしまうから何も解決してないけど、先に進めよう。
ここでもう1つブロック定義を作る。衝突値を算出する処理をまとめていこうと思う。
長すぎて引数が映らないw こちら↓
テキストで書くとこちら↓
_衝突値を算出する)X座標:[_X座標] Y座標:[_Y座標] スピードX:[_スピードX] スピードY:[_スピードY]
うーん、引数が4つもあるのは正直微妙なんだけど、まぁ仕方ない。
衝突処理をする際に実行する
ブロック定義「_衝突を処理する」内で使うよ。名前が似てるから混乱しそうだよね、注意。
ブロック定義「_タイルを調べる」はここからは消してOK。
タイルを調べる
で、こっち(_衝突値を算出する)でタイルを調べるようにしよう。
引数には、ブロック定義「衝突値を算出する」の引数をそのまま渡そう。
足元とセンターと頭で衝突値を算出する
ではこの衝突値を算出する処理を3箇所で呼び出すよ。
- 足元は仮想Y座標から身長のハーフサイズをマイナスすれば算出できる
- 頭は仮想Y座標に身長のハーフサイズをプラスすれば算出できる
これを使ってブロック定義「_衝突値を算出する」を実行しよう。
見た目のインパクトすげぇw
実はここ、処理が足りてない。もっとあとの動画で判明してバグ修正することになる。でも動きに致命的なバグではないので、記事でもこのままにしておくつもり。次の解説記事で修正が入る予定。ちなみにバグ内容としてはX方向の衝突判定が甘い状態になってる。身体の幅も踏まえた当たり判定にすべき。
変数「衝突値」を作る
ここで新たに変数を1つ作るよ。
よしよし。
初期化する
この変数はブロック定義「_衝突を処理する」で空白を割り当てておくよ。
衝突値を算出する
ではこの衝突値を算出していこう。ブロック定義「_衝突値を算出する」の中に条件ブロックを置くよ。
タイルが空白かどうか
条件式はタイルが空白かどうかを見ればいいから、コスチューム番号が2より大きいかどうかっていう演算でOKだ。
衝突値をセットする
衝突してたら値に10をセットするよ。
10っていう数字には今のところ意味はない!
衝突の処理を変える
今度はブロック定義「_衝突を処理する」の方の条件ブロックを変えていこうか。行ったり来たりで分かりにくいよね(T_T)
変更前
変更後
衝突値がゼロよりも大きかったら、という条件式に変えよう。
「_衝突値を算出する」を3回実行して、1箇所でも何かに触れてたら衝突値は10になっているはずだね。
動かしてみるぅ!
じゃあテストだ。
OK!まだまだスキマが空いてたり微妙だけど、衝突値をつかったストップ処理が機能していることは確認できるね。
歩く!
ここでいったん衝突処理から離れて、プレイヤーの歩く動作について洗練させていくぞ。
ブロック定義「_スピードXを計算する」を作る
ブロック定義を作ってキー操作を管理していく。
スクショでは分かりやすさのためにカッコで歩くって文字を入れてある。けっこうよくやる。
変数「★横移動キーの入力値」を作る
入力値をセットする
すでに別の場所で作った処理だけど、キーの入力値をこの変数にセットするよ。
例の「右キーが押された - 左キーが押された」という演算結果をセットする。
スピードXを算出する
スピードXの値を、この入力値を使って計算していくよ。演算に使うブロックはこちら↓
完成形はこう↓
これをスピードXにセットする。
メッセージ「プレイヤーが動きます」を受け取ったとき
で、ココ↓からは処理を消すよ。
その代わりこのブロック定義を実行するように置く。
スムーズな横移動が実現したぞ!
これで緑の旗を押してみようか。
いいねいいね!タイル空間でプレイヤーがスムーズに横移動するようになったぞ!
ジャンプ!
つぎはタテ方向の動きを洗練して、ジャンプができるようにしていこう。
いまは単に上下に移動するだけだから全然それっぽくないもんね。
ブロック定義「_スピードYを計算する」を作る
実行する
スピードYのところをこのブロック定義で置き換えよう。
変更前
変更後
上キーを押したらジャンプする仕組みを作る
ブロック定義の中を作っていくよ。とりま条件ブロックを置こう。
条件式を作る
スピードYに14をセットする
条件ブロック内ではスピードYの値をセットしよう。14くらいにしておこう。
重力っぽい減算をする
このままだとスピードYが下がらずに落下が始まらないので、徐々に下げるような力を加えよう。
「スピードYを-2ずつ変える」を置こう。
制御をかける
このままだとスピードYがずっとマイナス値をぶっちぎるので、ある程度のマイナスになったらストップするようにしておこうか。
-22より小さくなったら、という条件ブロックを追加して↓
それ以上は下がらないようにしよう。
ジャンプしてみるぅ!
はい、なんか変なスキマが空いてるね。け、計算通りなんだからね!
衝突処理を改善する
このスキマをなくしていくために、再び衝突処理を改善していくよ!
条件ブロックを追加する
条件式を作る
さぁ、ここでやっとさっき渡したゼロという値が役に立ってくる。
_スピードXがゼロなのか_スピードYがゼロなのかを調べる。それによってタテ方向の衝突処理なのかヨコ方向なのかを確認する。
スピードをゼロにする
もし_スピードXがゼロならタテ方向の衝突処理だから、変数「スピードYをゼロにする」ブロックを置く。逆もまた然りだ。
飛んでみるぅ!
どうだぁ!
あ、スキマはなくなったけど、なんか、カクってなる……け、け、け、計算通りなんだからね!!
埋もれを修正する
さて、ここからは数学タイムだ。
大丈夫、ここまで来れた君たちなら何も恐れることはない。ただ1つだけ不安材料があるとすれば、それはこれから数学タイムを解説をする僕が数学オンチということだけだw
現状把握
問題が起きたとき何から始めればいいか、そう、現状把握からだ。
今どうしてスキマが埋もれてしまっているのか、それは現在のロジックが「埋もれたら移動を取り消す」ようにできているから。
でもこれだと単純に最後の移動をそのまま取り消すだけだから、直前の「地面に触れてない」状態に戻るのだ。結果的にスキマが空いてしまう可能性が高い。
コレが現状だね。
対策プランA
こういう埋もれを修正する際にもっともよく使われるのが、地面から離れるまで1座標ずつ上に戻す方法だ。
しかし、この方法はタイルで作られた世界では微妙に使いづらい。
ナゼかというと、例えば「地面から離れるまで」というのは「タイルから離れるまで」ということだけど、壁と地面の両方に接している状態だと困ると思わない?
壁もタイルだし、地面もタイルだから、タイルから離れるまでという条件では地面の埋もれが解消されても壁には触れているから、きっと壁の上の方までプレイヤーが瞬間移動するはずw
対策プランB
そこで今回は数学的な解決策を施そうと思う。
作戦としてはどのくらい埋もれているかを計算から導き出そうと思う。
僕らが使える材料としては、次の2つ。
- まずタイルの高さである32
- そしてセンター座標から身長のハーフサイズを引いて出した足元の座標
実はこの2つだけで、どのくらい埋もれているかが計算できる。
演算の手順は次の通り。
- まずどのくらい埋もれて「いないか」を算出する
- それを32から引く
これで埋もれている高さを知ることが出来るのだ!
ステップ1:埋もれてない高さを算出する
埋もれてない高さは、剰余演算で算出できる。
足元のY座標 % 32
ステップ2:埋もれている高さを算出する
埋もれている高さは、シンプルに全体(32)から埋もれていない高さを引けば算出できる。
32 - (足元のY座標 % 32)
この結果を仮想Y座標に加えることで、埋もれてしまった高さの分だけ戻すことが出来るのだ。
足元の埋もれを修正するコーディング
ではこれを実現するためのコーディングを作っていこう。
このあたりは複雑になりやすいから、少し処理を整理しながら進める。そのためさっきの演算をストレートに作るのではなく、いくつかの処理の移動も含めて実装していくね。1つずつ一緒に見ていこう。
仮想座標の修正箇所を変える
現在はブロック定義「_衝突を処理する」で仮想座標の修正を行っているけど、これをいったんブロック定義「_衝突値を算出する」に移したいので、削除してしまおう。
足元の埋もれを修正する
今度はブロック定義「_衝突値を算出する」にコードを追加していくよ。
まずは足元の埋もれが見つかったら、という条件ブロックを作るよ。
ここに処理が来るということはタイルが空白以外の何らかのブロックだったということだよね。
そしてスピードYがマイナス値であるということは、落下している途中ということだ。落下中に埋もれるということは、埋もれている箇所は頭ではなくて足元であるということが導き出せるよね。
そこでさきほどの演算を適用して仮想Y座標を修正したいと思う。
使うブロックはこちら↓
完成形はこう↓
実行してみる
これだけでも足元の埋もれが直っているはずだから実行してみようぞ。
うぇーーーーい!
ピタッと止まってるぜピタッとぉ!最高かよっ!
左ヨコ移動の埋もれを修正する
では次はX軸のマイナスである左方向の埋もれを修正しよう。
今作った条件ブロックを複製してね。
そして直後に置いて3箇所をXに変えよう↓
これで左の埋もれ修正もOK。
顔が埋まってるように見えるけど、計算通りなのだ。Yと違ってXは頭もセンターも足元も3つとも体の正中線(中心)の座標だからね。
どうしてもヨコもタテみたいに厳密に埋もれを修正したい、という場合は体の幅(コスチュームサイズの左側)を利用しよう。ただヨコ3箇所 x タテ3箇所の合計9回の衝突処理を実行することになるので、もっと複雑にはなる。
右ヨコの埋もれを修正する
今度は右にいってみようか。複製でいけるか、と思いきやちょっと変更が必要なんだ。
下の図を見てみて。
埋もれている幅を算出する方法はさっきと同じ考えで剰余演算を使えばいいんだけど、右の埋もれを修正する場合は右側に動かしたいから32からマイナスするのではなくて0からマイナスする。
でもそれだと数字上は微妙に埋もれたままになってしまう。
そこで、本当に少しだけ余分に埋もれを修正するため、ゼロではなくて-0.000001からマイナスすることで埋もれを修正する。
分かりづらいところだから、ちょいやってみよう。
最小値として定数を作る
まずこの-0.000001という数字は如何にも謎めいているので、変数化しておきたい。そしてこの数字はゲーム開始後に変わらないことを明示するために接頭語を付けて定数であることを宣言しておこう。
初期化する
ステージを選択して初期化しておこう。
マイナス0.000001を入れよう。小数点以下はゼロ5個と1だよ。
コーディングする
よし、マリオに戻ろう。
では「スピードXがプラスだったら」という条件ブロックを作ってみよう。
この中で埋もれを修正して仮想X座標に加えよう。
上の埋もれを修正する
上は右と同じ。最小値からマイナスすればOK。複製して作っちゃおう。
ぶつかりまくろう!
さぁテストだ!
ひゃっほー!すごくいい感じだ!
左右上下どこもブロックを通り抜けないようになってる。
アニメーションを行う!
タイルで構築された空間、そしてカメラを使ったスクロール機構、さらにそこを自然に飛び回るプレイヤーの実装……スクロールゲームの最低限の基盤が整ったね!
これだけでもあとは少しの追加でアスレチックゲームとか作れるレベルで仕上がってる。
このあとはジャンプをもっとマリオっぽく改善して、そのあとアニメーションを実装し、より本格的なゲームとして動くエンジンに仕上げていくぞ!
このチュートリアルではみんなが作った作品をスタジオに掲載していこうと思ってるので、もし自分のアレンジを見てほしい!という人がいたらスタジオのコメントで教えてね。
あと挑戦したけど上手くいかなくて、自分ではもう無理・挫折寸前、みたいな人もコメントで状況を教えてくれたら、時間が割けるときにチェックしてみるよ。
- ① マップチップ(タイル)をグリッド状に並べる
- ② タイルをスクロールさせる
- ③ リストでグリッドを管理する
- ④ ステージを洗練させる
- ⑤ 衝突処理を作る
- ⑥ マリオを動かす! (いまここ)
- ⑦ キー長押しでハイジャンプ
- ⑧ 走るアニメーションをヌルヌル動かす
- ⑨ キュキュッとターンで土ケムリぶわぁ!
- ⑩ もはやマリオメーカー!自由自在にステージを改造する仕組み
- ⑪ 落ちる挙動を作ろう & 品質向上
- ⑫ ステージコードをエンコードする(セーブ)
- ⑬ ステージコードをデコードする(ロード)
- ⑭ コインをゲットしてがっぽり♪