複雑な形状の3Dオブジェクト同士を高速で当たり判定する方法 | ザッパの開発日誌

ザッパの開発日誌

iOSアプリ開発を中心とした日常のこと

KENKIいっぱい

「KENKIいっぱい」のあたり判定

以下は、プレイステーションでゲーム開発をしていた時に書いたもので現在の処理速度とは比較にならない程低速な時代でしたが、今でも通用する技術だと思っています。

多関節の複雑な動きをするオブジェクト同士の当たり判定をする場合、かなりの計算処理が入り、非常に重たくなりゲーム性を著しく失う結果になります。
その為、仮想の単純形状を配してあたり判定をすることになりますが、オブジェクトによってはどうしてもある程度細かな当たり判定が必要になる場合があります。
そのような場合に有用な方法について説明します。

処理速度を失わないと言うことは、プログラムを簡単にすると言うことです。
使用する数式は、垂直な円筒形(シリンダー)同士の領域チェックです。
色々試した結果、これが一番単純で高速でした。
垂直なシリンダーオブジェクト(NULLオブジェクト)を形状全体に配置して、全てのシリンダーに対して領域チェックを行います。
プログラムを単純にする代償として、オブジェクトデータの構築が面倒になりますが、非常に効果的です。

それでは、具体的に説明して行きます。

ポリゴンデータの構築:(ライトウェーブの場合)

使用するポリゴンデータにNULLオブジェクトを配置します。
NULLオブジェクトとは、見えない点(位置情報)のことで、シリンダーの中心の座標です。
シリンダーの高さと半径は、別途テーブルを用意してそこに記述します。
出来るだけ少数で隙間のないような配置を考えます。

以下にポリゴンデータのイメージ図を示します。


プログラムの説明:

シリンダー間の領域チェックプログラムは以下の通りで、円同士の領域チェックに高さを加えたものです。

//--------------------------------------------------------------
//  円筒形同士の当り判定    両者共垂直の時
//
//  パラメータ:
//      円筒のデータは、底面(下)の中心座標と高さと半径を
//      すべて short で。
//          short   cylinder_data[6];
//              0 - 2   x, y, z          シリンダーの中心座標
//              3       height           シリンダーの高さ
//              4       radius           シリンダーの半径
//              5       1:Not active     あたり判定の対象なら0
//
//      関数値:
//              当たっていれば1、そうでなければ0を返す。
//
//--------------------------------------------------------------
int HitCylinderCylinder(short *s1, short *s2)
{
    long            s;
    unsigned long   h;
    long long       d, e;


    if(s1[5]==1 || s2[5]==1) return(0);


    s= (s1[1] - s2[1]) + s2[3];
    h= s1[3] + s2[3];


    if ( (u_long)s > h ) return(0);


    d= s1[0] - s2[0];
    e= s1[2] - s2[2];
    d= d*d + e*e;


    e= s1[4] + s2[4];
    e= e*e;


    if (d > e) return(0);


    return(1);
}