今日のlibGDX目次
http://snoopopo.hatenablog.com/entry/2015/04/27/220545
今日のテーマ:E. 開発記 6 衝突応答とゲームオーバー
衝突応答とゲームオーバーを作っていきます。
ゲームオーバー
ボールを落としてしまったらゲームオーバーというのを作っておきます。
private boolean isGameOver() { return boll.getY() + boll.getImage().getHeight() < 0; }
今後は、画面下部のあたり判定はなしにし、おっこちちゃったらゲームオーバーです。
壁のきわまで移動する前に跳ね返ってる or めりこむ問題をなんとかする。
これはひらしょー本*1にもある現象とおなじ。
壁とバーを例にすると、今は、
10移動したい→6移動した先に壁があるから4分めりこんでしまう(衝突検出=TRUE)→ぶつかってるのでうごかない
という処理になってるため、6分は移動できるのに動いていないため、壁にぶつかる前にぶつかったようにみえる。という現象。
今回は、
10移動したい→6移動した先に壁があるから4分めりこんでしまう(衝突検出=TRUE)→でも移動する。(4分めりこむ)→4分だけもとに戻る
の「めりこんだら戻す」方式をやってみることにする。
めりこんだ量の計算
左壁にバーがぶつかる場合は、バーが左向きに移動している場合になる.
バーの左 - 左側の壁 をひけばよい.
move = ber.getX() - 0; //左壁にぶつかった場合 move = move * -1; //戻すから符号を反転
おなじように右壁にバーがぶつかる場合は、バーが右向きに移動している場合.
move = ber.getX() + ber.getImage().getWidth() - WINDOW_WIDTH; //右壁にぶつかった場合 move = move * -1;
バーが今どちらの壁にぶつかっているかをみてもいいけど、移動方向によって判断できるし、 移動方向は入力キー「→」「←」を毎フレームみていて判断できるので、 今回はそこで判断するようにしよう。
//プレイヤーバーの移動処理 boolean berDirectionRight = false; if (Gdx.input.isKeyPressed(Keys.LEFT)) { berDirectionRight = false; ber.updatePosition(ber.getX() - PLAYER_BER_MOVEMENT, ber.getY()); } else if (Gdx.input.isKeyPressed(Keys.RIGHT)) { berDirectionRight = true; ber.updatePosition(ber.getX() + PLAYER_BER_MOVEMENT, ber.getY()); } //プレイヤーバーと壁のコリジョン if (collision(ber.getX() + ber.getImage().getWidth(), ber.getX(), 0, WINDOW_WIDTH)) { float move = 0; if (berDirectionRight) { //右壁にぶつかった場合 move = ber.getX() + ber.getImage().getWidth() - WINDOW_WIDTH } else { move = ber.getX() - 0; //左壁にぶつかった場合 } move = move * -1; ber.updatePosition(ber.getX() + move, ber.getY()); //バーはX座標のみ動く }
というわけでこういう感じになりました。
これは一番簡単なプレイヤーバーと壁をやったけど、
プレイヤーバーとボールについては、
どちらも動いていて期待通りの動きをしないので、ほかの考え方で考える必要がありそうだ.
XとY両方うごくものをみるとき
おお…,ようやく思い通りの動きになったので書いておきます。
上記はX座標があたったときの応答処理をしていて、Y座標についてもおなじことをすればいいと思っていたのですが、 これは期待していない動きでした。
if (collision(berRight, berLeft, bollRight, bollLeft)) { if (collision(berUp, berDown, bollUp, bollDown)) { float moveX = 0; if (0 < boll.getDirectionX()) { moveX = (bollRight - berLeft) * -1; } else if (0 > boll.getDirectionX()) { moveX = (bollLeft - berRight) * -1; } float moveY = 0; if (0 < boll.getDirectionY()) { moveY = (bollUp - berDown) * -1; } else if (0 > boll.getDirectionY()) { moveY = (bollDown - berUp) * -1; } //めり込み分を元に戻す boll.updatePosition(boll.getX() + moveX, boll.getY() + moveY); } }
(期待してなかった動きをしたコード↑)
X、Y座標両方ともめりこんだ分をめりこまない位置に戻して調整していますが、
これだと調整した移動があまりに大きすぎる場合がありました。
たとえば、左右に5画素めり込んでいて、上下に30画素めりこんだ場合なんかがそうなのですが、 これだと上下に30画素分戻すことになり、微調整のつもりがすごく大きく移動してしまいました。ちょっとぶつかった瞬間ふっとんじゃうかんじになります。
で、改善したコード↓
if (collision(berRight, berLeft, bollRight, bollLeft)) { if (collision(berUp, berDown, bollUp, bollDown)) { float moveX = 0; if (0 < boll.getDirectionX()) { moveX = (bollRight - berLeft) * -1; } else if (0 > boll.getDirectionX()) { moveX = (bollLeft - berRight) * -1; } float moveY = 0; if (0 < boll.getDirectionY()) { moveY = (bollUp - berDown) * -1; } else if (0 > boll.getDirectionY()) { moveY = (bollDown - berUp) * -1; } //XY座標どちらも動かすと動きすぎるので、移動量が少ない座標のみ移動 float x = (moveX < 0) ? moveX * -1 : moveX; float y = (moveY < 0) ? moveY * -1 : moveY; if (y >= x) { //めり込み分を元に戻す boll.updatePosition(boll.getX() + moveX, boll.getY()); } else if (x > y) { //めり込み分を元に戻す boll.updatePosition(boll.getX(), boll.getY() + moveY); } boll.chengeDirection(); //移動方向方向反転 } }
移動量であるmoveXとmoveYをそのまま計算しても、マイナス値である場合が当然あるので、 それぞれの絶対値をもとめてから、絶対値が小さい方の座標のみ動かすようにします。 (移動量のxとyが同じ場合は、xが動くようにしてしまっていますが)
移動量の少ない座標のみずらしてあげれば、あたっていることにはならないので、ちょっとの移動で済みます。
これでバーがボールにめり込んでいたのですが、めりこまなくなりました!
ひらしょー本にも書いてあったけど、衝突応答は共通的な方法はない…というかゲームによると思うのでこういう調整は今後も必要だ。
このあと
次回はゲームの演出(音とか、画像とか)の強化をします。
*1:8章あたりです