絶対値と2乗の平方根と当たり判定【絵が動くゲームを作ってみよう】
2020/07/05

本記事の説明に使う画像を悪戦苦闘しながら作っていてだいぶできたところでアプリケーションが落ちるという事故に遭遇しました。。。 1時間くらいの作業が無駄に。 同じことをやりなおすのはさすがに早くて30分もかからずに済んだけれど、こまめにセーブしよう。。。
さて本日は平成31年度 センター試験 数学I 第1問に出てくる以下の数式がちょっと面白いなと思ったので、これを使ってお話してみようと思います。
\( | a+2 | + \sqrt{ (2a-3)^2 } \)
※問題の解答を含んでしまうので若干変えてあります。
プログラミングブログ記事一覧
絶対値
| と | に囲まれた部分は絶対値(ぜったいち)と言って、同じ大きさのプラスの値を表します。 つまり \( | 3 | \) は \( 3 \)、\( | -3 | \) も \( 3 \)、\( | -256 | \) は \( 256 \)です。 数式を使うと \( |x| \) は、\(x\)がプラスならそのまま、マイナスなら \(-x\)ですね。 ややこしいのですがマイナスのマイナスはプラスで、\( x = -3 \) のときの \( -x \) は(プラスの) \( 3 \) です。
平方根
次の√はルートと読みます。 日本語で平方根(へいほうこん)と言い、2乗するとその中の数になる数値です。 例えば \( \sqrt{9} \) は \(3\)、\( \sqrt{16} \) は \(4\)です。 なので \( \sqrt{(2a-3)^2} = (2a-3) \) …と思ってしまいますがひっかかってはダメです!
というのは例えば \( a = 0 \) のとき\( \sqrt{(2a-3)^2} \) は \( \sqrt{(-3)^2} \) になりますが、それは \(3\) すなわち \( -(2a-3) \) になるからです。 2乗する前の値がマイナスなら前にマイナスをつけてプラスにしないとですね。ややこしい。 一般化すると \( \sqrt{x^2} \) は、\(x\)がプラスならそのまま、マイナスなら \(-x\)ということになります。
絶対値と2乗の平方根
私はこれが面白いと思うのですが、そうなんですよね。絶対値と2乗の平方根って同じなのです。 だから問題の数式は \( |a+2| + |2a-3| \) と置き換えてしまった方がわかりやすいでしょう。
- \( a \ge \frac{3}{2} \) のときはどちらの中もプラス
- \( a \lt -2 \) ならどちらも中がマイナス
- \( -2 \le a \lt \frac{3}{2} \) のときは \( |2a-3| \) の中だけがマイナス
そして \( -2 \le a \lt \frac{3}{2} | \) のときの \( | a+2 | + \sqrt{ (2a-3)^2 } \) を求めよと言われたら、 右の中だけプラスマイナスを反転する形で \( (a+2)+(-(2a-3)) = -a + 5 \) と計算できます。
ゲームにおける当たり判定
そしてこの問題に触発されて、ゲーム開発で絶対値を使うところを考えてみました。その中では2Dゲームの当たり判定がわかりやすそうかなと思いますので紹介します。 図のボールは半径が16、キャラクターは横幅32、縦幅64くらいです。 ですので横については\(|iBallX-iPlayerX|\)が32以下なら、縦は\(|iBallY-iPlayerY|\)が48以下なら当たっていると判定することができます。 これが当たり判定ですね。
そしてそれは、横なら\(\sqrt{(iBallX-iPlayerX)^2}\)が32以下かどうかで判定するのと同じです。
絶対値で当たり判定
それではまず、絶対値での当たり判定をしてみましょう。
絶対値で横の当たり判定
まずは横の座標を考えてみます。
// 距離を求める(横) iDiffX = iBallX - iPlayerX; if ( iDiffX < 0 ) { iDiffX = -iDiffX; } // 当たり判定 if ( iDiffX < 32 ) { hitPlayerWithBall(); }
=は代入
最初の行には「=」(イコール)がありますが、これは数学と異なり、右辺を左辺に代入することを意味します。
iDiffX = iBallX - iPlayerX;
この行はすなわち、 iDiffX に iBallX-iPlayerX の計算結果を入れる処理です。 このiDiffXのような、値を入れておく場所を変数と呼びます。
ifで条件分岐
次の3行が横の座標の差分を絶対値を取得するプログラムです。
if ( iDiffX < 0 ) {
iDiffX = -iDiffX;
}
if文は条件分岐で、()内が成立すれば、続く{}内を実施します。成立しなければ実行しません。 つまりこのプログラムは、変数iDiffXの値がマイナスなら、iDiffXの符号を反転する処理になります。
hitPlayerWithBall()で当たった処理を実施
本記事では hitPlayerWithBall() にて、当たり処理を実施するものとします。
つまりこのプログラムで、iDiffXが32未満なら、すなわち \(|iBallX - iPlayerX|\)が32未満なら、 つまり横の位置が重なっていたら、当たり処理を実施されます。
絶対値で縦の当たり判定
次に縦の当たり判定も加えてみます。
// 距離を求める(横) iDiffX = iBallX - iPlayerX; if ( iDiffX < 0 ) { iDiffX = -iDiffX; } // 距離を求める(縦) iDiffY = iBallY - iPlayerY; if ( iDiffY < 0 ) { iDiffY = -iDiffY; } // 当たり判定 if ( iDiffX < 32 && iDiffY < 48 ) { hitPlayerWithBall(); }
&&で「且つ」
ifの()内にある「&&」(この読みは…普段はアンドアンドと読んでいますがたぶん正式にはアンパサンドアンパサンド)は論理積を表します。 「且つ」ですね。両方とも成立して初めて成立することを意味します。
if ( iDiffX < 32 && iDiffY < 48 ) {
hitPlayerWithBall();
}
すなわちこのプログラムは、iDiffXが32未満で、iDiffYが48未満、その両方の条件を満たしたときに次の{}内を処理します。 これにより、縦にも横にも当たっているときに限り当たり処理を実施します。
2乗で当たり判定
では今度は、2乗の平方根でやってみましょう。
ただし平方根は現在のコンピューターではまだ処理が遅いので実際には行わず、 比較する値を2乗することで同じ意味になるようにします。
iDiffX = iBallX - iPlayerX;
iDiffY = iBallY - iPlayerY;
if ( iDiffX*iDiffX < 32*32 && iDiffY*iDiffY < 48*48 ) {
hitPlayerWithBall();
}
*は掛け算
式に登場した「*」(アスタリスク)は乗算、掛け算を表します。 つまり以下は、iDiffX の2乗です。
iDiffX*iDiffX
2乗の方がスッキリ?
こうしてプログラムを比較すると、2乗を使用した方が行数が減りました。 そのぶんスッキリ読みやすいプログラムに見えると思います。
2Dゲームが主流の時代は掛け算はかなり遅い処理でしたので、掛け算を使用しないプログラミングが普通だったと思います。 要は絶対値の方がベターでした。
今のコンピューターは掛け算が高速になりました。 またifのような条件分岐の処理が遅いマシンもあります。 まあ筆者の感覚では、分岐が遅いというより、分岐が無ければチョッパヤということなのですが。 いずれにしても2乗を使った方が高速になる場合があります。
円を使用した当たり判定
もうひとつ関連して、円を使用した当たり判定も紹介します。図のような形で円に見立て、円が重なるかどうかで判定します。 円が重なるかどうかは、中心同士の距離が一定数より近いかどうかで判定できます。 一定数というのはそれぞれの円の半径の和ですね。
実際のプログラムを以下に記しました。 プログラムはさらに短くなり、とてもスッキリしていますよね。 あ、こちらも平方根は省略して、比較する値の方を2乗しています。
iDiffX = iBallX - iPlayerX;
iDiffY = iBallY - iPlayerY;
iDistance2 = iDiffX*iDiffX + iDiffY*iDiffY;
if ( iDistance2 < 40*40 ) {
hitPlayerWithBall();
}
この考え方は、3次元の球でも、Zの要素を加えるだけで、同様にできます。 しかも変わらずスッキリしています。
ゲームの主流が3Dになり、掛け算が高速になって以降は、球でできる当たり判定は球で行うことが多いと思います。
まとめ
絶対値と2乗の平方根および当たり判定について解説しました。
数学における絶対値の、この場合はこう、この場合はこう、という条件分岐の考え方は、プログラムに通じるものがあると思います。
また筆者は、2乗すれば条件分岐せずに処理できるという、プログラミングテクニックを考えるのが楽しいです。 少しでもその楽しさが伝わりましたら幸いです。
補足
- 本記事に記載のプログラムソースコードは、悪意のない範囲で自由に使用・改変していただいて問題ありません。ただし、ご自身の判断と責任でお願いします。
- センター試験の問題は、独立学校法人 大学入試センター さんのサイトで確認しました。
- \( |x| \) も \( \sqrt{x^2} \) も \( x = 0 \) なら\( 0 \)です。
- 本記事は\(a\)や\(x\)が実数であること前提にしています。センター試験の問題でも実数と明記されています。
- 数式表現にMathJaxを使用しております。
- 画像内のラスタライズ文字フォントにOpen Font LicenseのZen Antiqueを使用しております。
- ※各社の登録商標または商標について「®」「™」等の表記はしておりません。
- (本記事公開後)初期に提示していたプログラム記述ギミックを2025年5月にセキュリティー上の都合で閉じました。
カテゴリー:絵が動くゲームを作ってみよう,当たり判定
[PR]