32ドット×32ドットのキャラは、4隅の座標が上記のようになっています。これを元に、それぞれの座標を確認して行きます。, キャラの座標と、なにを比較して衝突判定をするのか。上でも書きましたが、画面の見た目ではなく、マップデータの配列と比較します。この処理は頻出するのでメソッド化しておきましょう。, メソッドと言ってもたった一行ですが。キャラの座標を受け取って、その座標にある配置パーツの番号を返します。, yとxを32で割る事により、キャラの座標とマップ配列のスケールを合わせています(割り算では商だけが返ります)。これをスケーリングと言います(厳密な用法ではないかも)。このようにして、複雑なデータと単純なデータを比較できるようにします。, ちなみに、ここでは分かりやすく除算を使っていますが、プログラムとしては「y>>5」とビットシフトを使った方が速いし分かりやすいでしょう(コード可読性は落ちるかな……)。, また、マップ配列の構造上、"arr[y/32][x/32]"と、xとyが入れ替わっているのに注意して下さい。これはまあ、こういう物かなという気がします。, キャラの右下端と左下端の座標(先の図を参照)を指定して配置パーツを取得し、そのどちらかが障害物(1)であれば、床に接地した物として、Y座標を補正します。"y/32*32"は同数を割って掛けてで意味がないと思われるかもしれませんが、これは32で割った時に余りが切り捨てられており、再び掛けた時にYが32の倍数(つまり、ぴったりブロックに乗っている状態)になるようにしています。, これも、プログラミング的には行儀が悪く、「(y>>5)<<5」とか、あるいはビット演算で下位4ビットをクリア(「y & ~31」かな? 間違ってたらすみません。NOTした値をANDする意味についてはこちらの記事も参照>http://d.hatena.ne.jp/mihael2/20050312)させた方がいいかもしれません(適切な方法を知りません)。, あるいは、衝突した障害物のY座標を正しく求めて、そこからキャラの高さを減算する方が、実装として正しいかもです。あ、最初からこうすべきだった気がしてきたな。反省……。, また、地面に接地している場合のみ、ジャンプを許可するフラグを立てています。これで、床との衝突判定は完了です。, Input.xは右を押されていれば+1、左を押されていれば-1を返すので、それを2倍して、xに加算すれば左右移動が実現されます(このTIPSはアドベントカレンダ6日目のあおたくさんの記事から頂きました)。, 本当は、徐々に移動速度が上がったり、慣性が効いたりした方が面白いと思います。実装方法を考えてみて下さい。, 理屈は床の衝突判定と同じです。右方向と左方向でそれぞれ判定しています。左右それぞれの上下2隅と重なっている配置パーツを確認し、どちらか片方でも障害物にめり込んでいれば座標を補正します。左側にめり込んでいる場合、そのまま補正するとそのめり込んだ障害物と同座標になってしまうので、32ドット右側にずらしています。, スペースキーを押したらジャンプできるようにしましょう。ジャンプ時のY座標更新ロジックですが、実はジャンプの実装は既に終わっています。先に書いた落下処理に、ジャンプが行われる最初のフレだけ、f値に-20を設定するのです(次フレからは元の+2に戻ります)。, すると、数値増分は以下のようになります。放物線を描いてまた戻ってくるのが分かります(また、元の座標(0)に戻った時に、強い移動量(+20)がかかっています)。, ジャンプ処理その物はこう書きます。jump_okフラグが立っている時(=地面に接地している時)しかジャンプを認めない事に注意して下さい。, 最後は天井との衝突判定です。これまでの応用なので、特記する事はありません。座標補正時に+32しているのは、左方向への衝突判定と同じ処理になります。, 以上で、マップ内をキャラが動き回れるようになりました。全てのコードが入ったサンプルを一番下に置いておきます(初期化処理や、穴に落ちたら復帰する処理が入っています)。おつかれさまでした!, 2Dアクションゲームにおける簡易衝突判定入門は以上です。以下は、発展を含めた補講になります。, 自キャラと敵キャラの衝突判定をする場合は、どちらもspriteで実装して、spriteの衝突判定ロジックを使うのが楽で早いです。, 「敵キャラをspriteにするなら、障害物のパーツも全部spriteにすればいいんじゃない?」と思われる方もいるかもしれません。これはアプローチの差なので、そのやり方もアリだと思います。マップをスクロールさせた場合に、画面外に消えたspriteを消すのがちょっと面倒かもなので、それを上手く処理する必要があります。, また、敵キャラに限らず、「移動する床」などは無理にマップデータの配列では持たず、「当たってもダメージを受けない敵キャラ」として実装するのが良いと思います。ちなみに、移動する床の上にキャラが乗るようにした場合、その床の上で座標を合わせつつ通常と同じ移動を行わせるのは結構大変かもしれません。, 今回は「通行可のパーツ」と「通行不可のパーツ」しかありませんでしたが、複雑なゲームを作る場合、これでは全く不足しています。例えば「下からぶつかると壊れる(スーマリのブロック)」「上から乗ると跳ね返る(同音符ブロック)」「乗ると強制的に横に移動する(同……あったっけ?)」などが考えられます。配置パーツの切り替えや、アニメーションなども考えたいところです。, また、変則的な物として「下からはすり抜けるが、上からは障害物扱いされる」というパーツも考えられます(すなわち、天井との衝突判定時には通行可と判定され、床との衝突判定時には通行不可と判定されるわけです)。パーツのバリエーションが増えると、IDごとの判定をハードコードするのは大変なので、上手いデータの持ち方が必要になるかも。, 更に、斜面の配置パーツを作りたい場合、どう実装するかはなかなかに難しいでしょう。斜面に立たせる/歩かせる/滑らせるなどは、その斜面とキャラとのドット単位での判定が必要になります。ここまでくると、見た目との判定の方が速いのではないか? とも思えてきます。そうかもしれないし、そうでないかもしれません。, 今回は固定画面での実装となりましたが、どうせならマップをスクロールさせたいですよね。横方向へのスクロールは、マップデータが伸びるだけです。マップ全体の座標(ワールド座標と言います)と画面上の座標(同スクリーン座標)がずれることになりますが、DXRubyのオフセット座標機能を使えば大丈夫じゃないかな(良く知らない)。, 横方向だけでなく、全方向スクロールさせたいとなった場合、またちょっと工夫が必要かもしれません(DXrubyのマップタイル機能だと、透過的に扱えるかも)。それ以前に、マップが大きくなったり増えたりすると、そのデータをどう管理するかの方が重要になってきます。, 今回は障害物(パーツ番号1)しか判定しませんでしたが、実際にアクションゲームを作る場合、障害物のパーツだけで何種類も用意する事になるでしょう。その場合、どのように判定処理を行えば良いでしょうか。, 1つのアプローチとして、通行可・不可を特定ビットで指定するというのがあります。例えば、配置パーツを256個(8bit)とする場合、最上位ビットが0なら通行可、1なら通行不可とすれば、以下のように判定できます(rubyでは、if式がfalseになるのは、演算結果がfalseかnilの場合のみである事に注意して下さい), このように、各ビットに障害物の属性を与えて、それに応じて処理を分岐させる事ができます。ただし、上記のやり方はメモリや演算速度に余裕が無かった頃の手法なので、今は使わないかも。配置パーツIDごとに別途属性情報を用意して、そこで通行可/不可の設定を持っても構わないかと思います。, 文字通り前世紀のゲームではありますが、「スーパマリオブラザーズ」の挙動は当時の段階で2Dアクションゲームとしてほぼ完成の域に達していました(個人的には「スーパマリオブラザーズ3」が真の完成だと思っています)。プレイヤーをより気持ちよく遊ばせる為に、以下のような挙動がありました(書いていて思いついた分だけです)。, 他にもダッシュ中の急停止や、ジャンプ中移動の際に「向き」が考慮されているなど、学びたい点は無数にあります。これらをどう実装するか考えてみるのも面白いかもです。, 今回は説明を楽するために冗長なコードになっています。例えば、キャラの4隅の配置パーツを取得すればいいのに、collision_tileは1フレ中に計8回呼ばれています。また、collision_tileは呼ばれる度に、xとyを32で割っています。これは効率が悪いのではないでしょうか。もっと短く、分かりやすいコードが書けるのではないでしょうか。, もし、これらの処理を一度に済ませるとした場合、ちょっと考えて欲しいのは、座標補正をどのタイミングで行うかという点です。今回はそこまでの考察はしません。練習問題にどうぞ。, 繰り返しになりますが、今回紹介したのは古典的な2Dアクションの衝突判定ロジックです。現代的なゲームプログラミングでは、もうマップ全部を3Dで組み、ポリゴン同士の衝突判定ロジックを使った方が楽かもしれません。あるいは、物理演算エンジンを使って床との接地を判定したほうがシンプルかもしれません。, けれど、スケーリングを初めとした多くのTIPSは普遍的な武器になりますし、古典的なロジックの方が軽いゲームでは実装しやすいだろうと、今回一通りの実装を行ってみました。現代的ゲームプログラミングに移行する際にも理解の助けになるかと思います。, また、今回、当初考えていたロジックが上手く機能せず、バグに悩んだ時間もあったのですが、それも含めて、コードを書き始めてから2時間程度でほぼ動作するサンプルを作ることができました。これはRuby+DXRubyの物作りの容易さから来ている物だと思っています。.

つまり敵の左右のX座標の範囲内にあるときの当たり判定となります。 なので、他の当たり判定も是非試してください! その方が、よりプログラミングを理解できるからです! これでも当たり判定はいけます。 ゲームはアイディアが大事ですので、思いついたら、すぐにやってみることです! DXRuby Advent Calender 2013 12日目です。 11日目はfourxzさんのRuby使ったことない人がDXRubyでゲームを作りたいのでIDE3つインストールしましたでした。RubyにはWindows用のまともなIDEは無い物と思い込んでいたので、こんなに選択肢があるとは意外でした。 弾の左X座標が敵の右X座標より左にあるという意味です。