OOBCollisionDetection

本文详细介绍了一种在Unity中实现轴对齐包围盒(OBB)碰撞检测的方法,包括如何计算两个3D对象的相对位置和方向,以及使用分离轴定理来判断两个OBB是否相交。通过具体的代码实现,展示了如何处理不同场景下物体的碰撞检测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

using UnityEngine;
using System.Collections;


class OOBCollisionDetection //: MonoBehaviour
{	
	// test code
	void Update()
	{
		GameObject box1 = GameObject.Find("Cube1");	
		GameObject box2 = GameObject.Find("Cube2");
		
		Vector3 a = new Vector3(box1.transform.localScale.x / 2.0f, box1.transform.localScale.y / 2.0f, box1.transform.localScale.z / 2.0f);
		Vector3 b = new Vector3(box2.transform.localScale.x / 2.0f, box2.transform.localScale.y / 2.0f, box2.transform.localScale.z / 2.0f);
		
		Vector3 Pa = box1.transform.position;
		Vector3 Pb = box2.transform.position;
		
		Vector3[] A = new Vector3[3];
		
		A[0] = box1.transform.forward;
		A[1] = box1.transform.up;
		A[2] = box1.transform.right;
		
		Vector3[] B = new Vector3[3];
		
		B[0] = box2.transform.forward;
		B[1] = box2.transform.up;
		B[2] = box2.transform.right;
		
		if (OBBOverlap(a, Pa, A, b, Pb, B))
		{
			Debug.Log ("Collision");	
		}
		else
		{
			Debug.Log ("Not Collision");	
		}
	}
	
	public static Vector3 GetLineIntersection(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4){
		var denom = ((p4.z - p3.z) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.z - p1.z));
		
		var nume_a = ((p4.x - p3.x) * (p1.z - p3.z)) - ((p4.z - p3.z) * (p1.x - p3.x));
		
		var nume_b = ((p2.x - p1.x) * (p1.z - p3.z)) - ((p2.z - p1.z) * (p1.x - p3.x));
		
		var result = Vector3.zero;
		
		if (denom == 0.0) {
			result.x = -1;
			result.z = -1;
			
			return result;
		}
		
		var ua = nume_a / denom;
		var ub = nume_b / denom;
		
		if (ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) {
			var x = p1.x + ua * (p2.x - p1.x);
			var y = p1.y + ua * (p2.y - p1.y);
			
			result.x = x;
			result.z = y;
			
			return result;
		}
		else {
			result.x = -1;
			result.z = -1;
			
			return result;
		}
	}
	
	/*public static bool IsRayIntersectingBox(Vector3 boxPosition, Vector3 boxSize, Vector3 start, Vector3 end)
	{
		Vector3 leftFront 	= new Vector3(boxPosition.x - boxSize.x/2.0f, boxPosition.y, boxPosition.z - boxSize.z/2.0f);
		Vector3 rightFront 	= new Vector3(boxPosition.x + boxSize.x/2.0f, boxPosition.y, boxPosition.z - boxSize.z/2.0f);
		
		Vector3 leftBack 	= new Vector3(boxPosition.x - boxSize.x/2.0f, boxPosition.y, boxPosition.z + boxSize.z/2.0f);
		Vector3 rightBack 	= new Vector3(boxPosition.x + boxSize.x/2.0f, boxPosition.y, boxPosition.z + boxSize.z/2.0f);	
		
		bool result = false;
		
		Vector3 v1 = GetLineIntersection(leftFront, rightFront, start, end);
		Vector3 v2 = GetLineIntersection(rightFront, rightBack, start, end);
		Vector3 v3 = GetLineIntersection(rightBack, leftBack, start, end);
		Vector3 v4 = GetLineIntersection(leftBack, leftFront, start, end);
		
		result = (v1.x != -1 && v1.y != -1) || (v2.x != -1 && v2.y != -1) || (v3.x != -1 && v3.y != -1) || (v4.x != -1 && v4.y != -1);
		
		return result;
	}*/
	
	/*public static bool IsRayIntersectingBox(Vector3 boxPosition, Vector3 boxSize, Vector3 lineDir, Vector3 midpoint, double segmentHalflength)
	{
		
		Vector3 T = boxPosition - midpoint;
		Vector3 v = Vector3.zero;
		double r = 0;
		
		//do any of the principal axes
		//form a separating axis?
		
		if( Mathf.Abs(T.x) > boxSize.x + segmentHalflength*Mathf.Abs(lineDir.x) )
			return false;
		
		if( Mathf.Abs(T.y) > boxSize.y + segmentHalflength*Mathf.Abs(lineDir.y) )
			return false;
		
		if( Mathf.Abs(T.z) > boxSize.z + segmentHalflength*Mathf.Abs(lineDir.z) )
			return false;
		
		//l.cross(x-axis)?
		
		r = boxSize.y*Mathf.Abs(lineDir.z) + boxSize.z*Mathf.Abs(lineDir.y);
		
		if( Mathf.Abs(T.y*lineDir.z - T.z*lineDir.y) > r )
			return false;
		
		//l.cross(y-axis)?
		
		r = boxSize.x*Mathf.Abs(lineDir.z) + boxSize.z*Mathf.Abs(lineDir.x);
		
		if( Mathf.Abs(T.z*lineDir.x - T.x*lineDir.z) > r )
			return false;
		
		//l.cross(z-axis)?
		
		r = boxSize.x*Mathf.Abs(lineDir.y) + boxSize.y*Mathf.Abs(lineDir.x);
		
		if( Mathf.Abs(T.x*lineDir.y - T.y*lineDir.x) > r )
			return false;
		
		return true;
	}*/
	
	/*public static bool IsRayIntersectingBox(Vector3 lineDest, Vector3 midPoint, Vector3 boxPosition, Vector3 boxSize) 
	{ 
	    float s, ss, xhit, yhit, zhit; 
	
	    ss = float.MaxValue; 
	
		int sideHit = 0;
		
	    // translate ray origin to objects space 
	    Vector3 relativePos = new Vector3(midPoint.x, midPoint.y, midPoint.z); 
	    relativePos -= boxPosition; 
	
	    // check x faces 
	    if (lineDest.x != 0) 
	    { 
	        s = (boxSize.x - relativePos.x) / lineDest.x; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            yhit = Mathf.Abs(relativePos.y + s * lineDest.y); 
	            zhit = Mathf.Abs(relativePos.z + s * lineDest.z); 
	
	            if ((yhit < boxSize.y) && (zhit < boxSize.z)) 
	            { 
	                sideHit = 0; 
	                ss = s; 
	            } 
	        } 
	
	        s = (-boxSize.x - relativePos.x) / lineDest.x; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            yhit = Mathf.Abs(relativePos.y + s * lineDest.y); 
	            zhit = Mathf.Abs(relativePos.z + s * lineDest.z); 
	
	            if ((yhit < boxSize.y) && (zhit < boxSize.z)) 
	            { 
	                sideHit = 1; 
	                ss = s; 
	            } 
	        } 
	    } 
	
	    // check y faces 
	    if (lineDest.y != 0) 
	    { 
	        s = (boxSize.y - relativePos.y) / lineDest.y; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            xhit = Mathf.Abs(relativePos.x + s * lineDest.x); 
	            zhit = Mathf.Abs(relativePos.z + s * lineDest.z); 
	
	            if ((xhit < boxSize.x) && (zhit < boxSize.z)) 
	            { 
	                sideHit = 2; 
	                ss = s; 
	            } 
	        } 
	
	        s = (-boxSize.y - relativePos.y) / lineDest.y; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            xhit = Mathf.Abs(relativePos.x + s * lineDest.x); 
	            zhit = Mathf.Abs(relativePos.z + s * lineDest.z); 
	
	            if ((xhit < boxSize.x) && (zhit < boxSize.z)) 
	            { 
	                sideHit = 3; 
	                ss = s; 
	            } 
	        } 
	    } 
	
	    // check z faces 
	    if (lineDest.z != 0) 
	    { 
	        s = (boxSize.z - relativePos.z) / lineDest.z; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            xhit = Mathf.Abs(relativePos.x + s * lineDest.x); 
	            yhit = Mathf.Abs(relativePos.y + s * lineDest.y); 
	
	            if ((xhit < boxSize.x) && (yhit < boxSize.y)) 
	            { 
	                sideHit = 4; 
	                ss = s; 
	            } 
	        } 
	
	        s = (-boxSize.z - relativePos.z) / lineDest.z; 
	
	        if ((s > 0.0) && (s < ss)) 
	        { 
	            xhit = Mathf.Abs(relativePos.x + s * lineDest.x); 
	            yhit = Mathf.Abs(relativePos.y + s * lineDest.y); 
	
	            if ((xhit < boxSize.x) && (yhit < boxSize.y)) 
	            { 
	                sideHit = 5; 
	                ss = s; 
	            } 
	        } 
	    } 
	
	    if (ss == float.MaxValue) 
	        return false; 
	
	    // ss is the distance the ray travelled before hitting the box.
	    hit.m_isect_t = ss; 
	    // Project the ray ss units to find the intersection point.
	    hit.m_Point = ray.Project(ss); 
	
	    hit.m_fEnter = ((ray.m_Position.x > m_Min.x || ray.m_Position.x < m_Max.x) || 
	                    (ray.m_Position.y > m_Min.y || ray.m_Position.y < m_Max.y) || 
	                    (ray.m_Position.z > m_Min.z || ray.m_Position.z < m_Max.z)); 
	
	    hit.m_pPrim = this; 
	    hit.m_pSurf = m_pSurf; 
	
	    return true; 
	}*/
	
	static Vector3  operatorOr (Vector3 _c, Vector3 _d) 
    {
        return new Vector3(_c.y * _d.z - _c.z * _d.y,
                _c.z * _d.x - _c.x * _d.z,
                _c.x * _d.y - _c.y * _d.x);
    }
	
	public static  bool IsRayIntersectingBox(Vector3 segment_pt0, Vector3 segment_pt1, Vector3 boxPosition, Vector3 boxSize)
	{
		/*if (box.isInside(segment_pt0) && box.isInside(segment_pt1))
			return true;*/
	
		float[] fAWdU = new float[3];
		float[] fADdU = new float[3];
		float[] fAWxDdU = new float[3];
		float fRhs;
		Vector3 kSDir = 0.5f * (segment_pt1 - segment_pt0);
		Vector3 kSCen = segment_pt0 + kSDir;
	
		Vector3 kDiff = kSCen - boxPosition;
	
		fAWdU[0] = Mathf.Abs(kSDir.x);
		fADdU[0] = Mathf.Abs(kDiff.x);
		fRhs = boxSize.x + fAWdU[0];
		if (fADdU[0] > fRhs)
			return false;
	
		fAWdU[1] = Mathf.Abs(kSDir.y);
		fADdU[1] = Mathf.Abs(kDiff.y);
		fRhs = boxSize.y + fAWdU[1];
		if (fADdU[1] > fRhs)
			return false;
	
		fAWdU[2] = Mathf.Abs(kSDir.z);
		fADdU[2] = Mathf.Abs(kDiff.z);
		fRhs = boxSize.z + fAWdU[2];
		if (fADdU[2] > fRhs)
			return false;
	
		Vector3 kWxD = operatorOr(kSDir, kDiff);
	
		fAWxDdU[0] = Mathf.Abs(kWxD.x);
		fRhs = boxSize.y * fAWdU[2] + boxSize.z * fAWdU[1];
		if (fAWxDdU[0] > fRhs)
			return false;
	
		fAWxDdU[1] = Mathf.Abs(kWxD.y);
		fRhs = boxSize.x * fAWdU[2] + boxSize.z * fAWdU[0];
		if (fAWxDdU[1] > fRhs)
			return false;
	
		fAWxDdU[2] = Mathf.Abs(kWxD.z);
		fRhs = boxSize.x * fAWdU[1] + boxSize.y * fAWdU[0];
		if (fAWxDdU[2] > fRhs)
			return false;
	
	    return true;
	}
	
	public static bool AreBoxexOverlapping(GameObject box1, Vector3 box2Size, Vector3 box2Position)
	{	
		Vector3 a = new Vector3(box1.transform.localScale.x / 2.0f, box1.transform.localScale.y / 2.0f, box1.transform.localScale.z / 2.0f);
		Vector3 b = new Vector3(box2Size.x / 2.0f, box2Size.y / 2.0f, box2Size.z / 2.0f);	
		
		Vector3 Pa = box1.transform.position;
		Vector3 Pb = box2Position;
		
		Vector3[] A = new Vector3[3];
		
		A[0] = box1.transform.forward;
		A[1] = box1.transform.up;
		A[2] = box1.transform.right;
		
		Vector3[] B = new Vector3[3];
		
		B[0] = new Vector3(1f, 0f, 0f);
		B[1] = new Vector3(0f, 1f, 0f);
		B[2] = new Vector3(0f, 0f, 1f);
		
		return OBBOverlap(a, Pa, A, b, Pb, B);
	}
	
	public static bool AreBoxexOverlapping(Vector3 box1Size, Vector3 box1Position, Vector3 box2Size, Vector3 box2Position)
	{	
		Vector3 a = new Vector3(box1Size.x / 2.0f, box1Size.y / 2.0f, box1Size.z / 2.0f);
		Vector3 b = new Vector3(box2Size.x / 2.0f, box2Size.y / 2.0f, box2Size.z / 2.0f);	
		
		Vector3 Pa = box1Position;
		Vector3 Pb = box2Position;
		
		Vector3[] A = new Vector3[3];
		
		A[0] = new Vector3(1f, 0f, 0f);
		A[1] = new Vector3(0f, 1f, 0f);
		A[2] = new Vector3(0f, 0f, 1f);
		
		Vector3[] B = new Vector3[3];
		
		B[0] = new Vector3(1f, 0f, 0f);
		B[1] = new Vector3(0f, 1f, 0f);
		B[2] = new Vector3(0f, 0f, 1f);
		
		return OBBOverlap(a, Pa, A, b, Pb, B);
	}
	
	public static bool AreBoxesOverlapping(GameObject box1, GameObject box2)
	{
		if (box1 == null || box2 == null)
			return false;
		
		Vector3 a = new Vector3(box1.transform.localScale.x / 2.0f, box1.transform.localScale.y / 2.0f, box1.transform.localScale.z / 2.0f);
		Vector3 b = new Vector3(box2.transform.localScale.x / 2.0f, box2.transform.localScale.y / 2.0f, box2.transform.localScale.z / 2.0f);	
		
		Vector3 Pa = box1.transform.position;
		Vector3 Pb = box2.transform.position;
		
		Vector3[] A = new Vector3[3];
		
		A[0] = box1.transform.forward;
		A[1] = box1.transform.up;
		A[2] = box1.transform.right;
		
		Vector3[] B = new Vector3[3];
		
		B[0] = box2.transform.forward;
		B[1] = box2.transform.up;
		B[2] = box2.transform.right;
		
		return OBBOverlap(a, Pa, A, b, Pb, B);
	}
	
	static bool OBBOverlap(Vector3 a, Vector3 Pa, Vector3[] A, Vector3 b, Vector3 Pb, Vector3[] B)
	{
		//translation, in parent frame
		Vector3 v = Pb - Pa;
		//translation, in A's frame
		Vector3 T = new Vector3(Vector3.Dot(v, A[0]), Vector3.Dot(v, A[1]), Vector3.Dot(v, A[2]));  
		
		//B's basis with respect to A's local frame
		float[][] R = new float[3][];
		
		for (int m = 0; m < 3; m++)
		{
			R[m] = new float[3];
			for (int l = 0; l < 3; l++)
			{
				R[m][l] = 0.0f;	
			}
		}
		
		float ra, rb, t;
		long i, k;
		
		//calculate rotation matrix
		for( i=0 ; i<3 ; i++ )
		{
			for( k=0 ; k<3 ; k++ )
			{	
				R[i][k] = Vector3.Dot(A[i], B[k]);
			}
		}
				
		/*ALGORITHM: Use the separating axis test for all 15 potential 
		separating axes. If a separating axis could not be found, the two 
		boxes overlap. */

		//A's basis vectors
		for( int x=0 ; x<3 ; x++ )
		{		
			ra = a[x];

			rb = b[0]*Mathf.Abs(R[x][0]) + b[1]*Mathf.Abs(R[x][1]) + b[2]*Mathf.Abs(R[x][2]);

			t = Mathf.Abs( T[x] );

			if( t > ra + rb ) 
				return false;		
		}

		//B's basis vectors
		for( int y=0 ; y<3 ; y++ )
		{
			ra = a[0] * Mathf.Abs(R[0][y]) + a[1]*Mathf.Abs(R[1][y]) + a[2]*Mathf.Abs(R[2][y]);
			rb = b[y];
		
			t = Mathf.Abs(T[0]*R[0][y] + T[1]*R[1][y] + T[2]*R[2][y] );
		
			if( t > ra + rb )
				return false;				
		}

		//9 cross products
		
		//L = A0 x B0
		ra = a[1]*Mathf.Abs(R[2][0]) + a[2]*Mathf.Abs(R[1][0]);
		
		rb = 
		b[1]*Mathf.Abs(R[0][2]) + b[2]*Mathf.Abs(R[0][1]);
		
		t = 
		Mathf.Abs( T[2]*R[1][0] - T[1]*R[2][0] );
		
		if( t > ra + rb )
			return false;
		
		//L = A0 x B1
		ra = 
		a[1]*Mathf.Abs(R[2][1]) + a[2]*Mathf.Abs(R[1][1]);
		
		rb = 
		b[0]*Mathf.Abs(R[0][2]) + b[2]*Mathf.Abs(R[0][0]);
		
		t = 
		Mathf.Abs( T[2]*R[1][1] - T[1]*R[2][1] );
		
		if( t > ra + rb )
			return false;
		
		//L = A0 x B2
		ra = 
		a[1]*Mathf.Abs(R[2][2]) + a[2]*Mathf.Abs(R[1][2]);
		
		rb = 
		b[0]*Mathf.Abs(R[0][1]) + b[1]*Mathf.Abs(R[0][0]);
		
		t = Mathf.Abs( T[2]*R[1][2] - T[1]*R[2][2] );
		
		if( t > ra + rb )
			return false;
		
		//L = A1 x B0
		ra = 
		a[0]*Mathf.Abs(R[2][0]) + a[2]*Mathf.Abs(R[0][0]);
		
		rb = 
		b[1]*Mathf.Abs(R[1][2]) + b[2]*Mathf.Abs(R[1][1]);
		
		t =Mathf.Abs( T[0]*R[2][0] - T[2]*R[0][0] );
		
		if( t > ra + rb )
			return false;
		
		//L = A1 x B1
		ra = 
		a[0]*Mathf.Abs(R[2][1]) + a[2]*Mathf.Abs(R[0][1]);
		
		rb = 
		b[0]*Mathf.Abs(R[1][2]) + b[2]*Mathf.Abs(R[1][0]);
		
		t = Mathf.Abs( T[0]*R[2][1] - T[2]*R[0][1] );
		
		if( t > ra + rb )
			return false;
		
		//L = A1 x B2
		ra = 
		a[0]*Mathf.Abs(R[2][2]) + a[2]*Mathf.Abs(R[0][2]);
		
		rb = 
		b[0]*Mathf.Abs(R[1][1]) + b[1]*Mathf.Abs(R[1][0]);
		
		t = Mathf.Abs( T[0]*R[2][2] - T[2]*R[0][2] );
		
		if( t > ra + rb )
			return false;
		
		//L = A2 x B0
		ra = 
		a[0]*Mathf.Abs(R[1][0]) + a[1]*Mathf.Abs(R[0][0]);
		
		rb = 
		b[1]*Mathf.Abs(R[2][2]) + b[2]*Mathf.Abs(R[2][1]);
		
		t = 
		Mathf.Abs( T[1]*R[0][0] - T[0]*R[1][0] );
		
		if( t > ra + rb )
			return false;
		
		//L = A2 x B1
		ra = 
		a[0]*Mathf.Abs(R[1][1]) + a[1]*Mathf.Abs(R[0][1]);
		
		rb = 
		b[0] *Mathf.Abs(R[2][2]) + b[2]*Mathf.Abs(R[2][0]);
		
		t = 
		Mathf.Abs( T[1]*R[0][1] - T[0]*R[1][1] );
		
		if( t > ra + rb )
			return false;
		
		//L = A2 x B2
		ra = 
		a[0]*Mathf.Abs(R[1][2]) + a[1]*Mathf.Abs(R[0][2]);
		
		rb = 
		b[0]*Mathf.Abs(R[2][1]) + b[1]*Mathf.Abs(R[2][0]);
		
		t = 
		Mathf.Abs( T[1]*R[0][2] - T[0]*R[1][2] );
		
		if( t > ra + rb )
			return false;
		
		/*no separating axis found,
		the two boxes overlap */
		
		return true;		
	}	
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值