【概要】
通常、UE4でBoxとSphereの重なりを判定したい場合、BoxComponentとSphereComponentのCollisionを作成しOverlapにより判定を行う。
しかし、諸々の都合上Component無しで重なりを判定したい状況があった為、判定を独自に実装してみた。
主に自分で忘れない為の備忘録用。
【解説】
基本的な考え方は、以下のサイト様を参考にさせて頂いた。
実際の手順に沿って解説していく。
1.相対座標に変換
上記サイトの方法では、Sphereの座標をBox内にクランプすることで、Sphereから最も近いBoxの範囲内の座標を求めている。
しかし、Boxが斜めになっていた場合そのままクランプするのは難しい為、最初にSphereの座標をBoxから見た相対座標に変換する。
計算式は以下のようになる。(W = ワールド原点、B = Boxの座標、S = Sphereの座標)
^{B}\boldsymbol{T}_{S} = (^{W}\boldsymbol{T}_{B})^{-1} \, \times \, ^{W}\boldsymbol{T}_{S}
Transformの変換については以下で解説しています。
2.Sphereから最も近いBoxの範囲内の座標を求める
1で得たBoxから見たSphereの相対座標を、Box内にクランプする。
FVector::ClampVector
という便利そうな関数がエンジンにあったので、今回はこれを使ってみた。
3.求めた座標をワールド座標系に変換
2でSphereから最も近いBoxの範囲内の座標を求められたが、これはBoxから見た相対座標になっているので、ワールド座標系に再変換する。
計算式は以下のようになる。(P = Sphereから最も近いBoxの範囲内の座標)
^{W}\boldsymbol{T}_{P}= \, ^{W}\boldsymbol{T}_{B} \, \times \, ^{B}\boldsymbol{T}_{P}
4.Sphereと重なっているか判定
3までの手順で、Sphereから最も近いBoxの範囲内のワールド座標を求めることが出来た。
あとはこの座標からSphereの座標までの距離が、Sphereの半径以下だった場合、BoxとSphereが重なっていると判定出来る。
この距離の計算をする場合、実際の距離で計算すると平方根の計算が走る為、二乗された状態で計算した方が計算コストが安く済む。
【結果】
以上を踏まえ出来上がったのがこちら。
Transformの計算は使わないと忘れていきそうなので、積極的に使っていきたい。
bool MyLibrary::IsSphereInBox(const FVector& SphereLocation, float SphereRadius, const FVector& BoxLocation, const FRotator& BoxRotation, const FVector& BoxExtent)
{
const FVector SphereLocationInBoxSpace = (FTransform(BoxRotation, SphereLocation) * FTransform(BoxRotation,BoxLocation).Inverse()).GetLocation();
const FVector ClosestPointToBoxInBoxSpace = ClampVector(SphereLocationInBoxSpace, -BoxExtent, BoxExtent);
const FVector ClosestPointToBoxInWorldSpace = (FTransform(BoxRotation, ClosestPointToBoxInBoxSpace) * FTransform(BoxRotation,BoxLocation)).GetLocation();
return FVector::DistSquared(SphereLocation, ClosestPointToBoxInWorldSpace) <= FMath::Square(SphereRadius);
}