AOYAMA KOJI's PROGRAMMING BLOG

絶対値と2乗の平方根と当たり判定【絵が動くゲームを作ってみよう】初中級者向けプログラミング解説

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

 本記事の説明に使う画像を悪戦苦闘しながら作っていてだいぶできたところでアプリケーションがちるという事故に遭遇しました。。。 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| \) の中だけがマイナス
と3パターンにけられるというのがわかりやすくなるかなと。
 そして \( -2 \le a \lt \frac{3}{2} | \) のときの \( | a+2 | + \sqrt{ (2a-3)^2 } \) を求めよと言われたら、 右の中だけプラスマイナスを反転する形で \( (a+2)+(-(2a-3)) = -a + 5 \) と計算できます。

ゲームにおける当たり判定


絶対値と2乗の平方根と当たり判定【絵が動くゲームを作ってみよう】  そしてこの問題に触発されて、ゲーム開発で絶対値を使うところを考えてみました。
 その中では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乗の平方根と当たり判定【絵が動くゲームを作ってみよう】 fig.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 LicenseZen Antiqueを使用しております。
・(本記事公開後)初期に提示していたプログラム記述ギミックを2025年5月にセキュリティー上の都合で閉じました。

カテゴリー:絵が動くゲームを作ってみよう,当たり判定
[PR]