コライダー判定のPhysics.Raycastは”コライダー内部にある”という判定はしてくれますが、コライダーから見て外部のどこにあるのか?という判定はしてくれません。なのでコライダー内部にマウスポインターを留めたいというときにはちょっと工夫が必要です。
まず、平面(Plane)とRayとの交差する点を取得するコードを組みます
(これもなぜかUnityに標準で備わっていない)
Planeで交差点を取得するには
//Planeで取得
public static Vector3 CrossPointOnPlane(Plane plane, Ray ray)
{
float enter = 0.0f;
plane.Raycast(ray, out enter);
return ray.GetPoint(enter);
}
平面と交差する点を求められない訳がないとは思いましたが、直接取得する関数がないだけでした
CrossPointOnPlane
//平面上の点PlanePointと法線PlaneNormalで取得
public static Vector3 CrossPointOnPlane(Vector3 PlaneNormal, Vector3 PlanePoint, Ray ray, float distance = 1000.0f)
{
Vector3 result = Vector3.zero;
Vector3 v1 = ray.origin - PlanePoint;
Vector3 v2 = ray.origin + (ray.direction * distance) - PlanePoint;
float d1 = Mathf.Abs(Vector3.Dot(v1, PlaneNormal));
float d2 = Mathf.Abs(Vector3.Dot(v2, PlaneNormal));
float denom = d1 + d2;
if (denom > 0)
{
result = ray.GetPoint(distance / denom * d1);
}
return result;
}
これを利用して
pointerPositionInClosedCollider
public Vector3 pointerPositionInClosedCollider(Camera camera, Collider collider, Vector3 normal)
{
Vector3 pointerPosition = Vector3.zero;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isInCollider = Physics.Raycast(ray, out hit, 100);
if (isInCollider)
{
pointerPosition = hit.point;
}
else
{
Vector3 CrossRayPointOnPlane = Calc.CrossPointOnPlane(normal, collider.transform.position, ray, 100.0f);
pointerPosition = collider.ClosestPoint(CrossRayPointOnPlane);
}
return pointerPosition ;
}
カメラとコライダーとのRayとRaycastHitをとり、コライダー範囲内にカーソルがある場合はRaycastHit.pointを返し、そうでない場合はコライダーを中心とした任意の法線を持つ平面にしたがってコライダー内部にぴったり張り付いて動くポインターが出来上がります。