Unity實(shí)現(xiàn)圖形相交檢測(cè)
前言
圖形相交檢測(cè)常常用在傷害判定,使用自定義的圖形相交檢測(cè),可以在一定程度上控制性能。
比如2D格斗游戲中使用的矩形包圍盒(AABB),一些動(dòng)作游戲中常常出現(xiàn)的扇形攻擊。
2D的圖形相交檢測(cè)能夠滿(mǎn)足大部分的需求,且可以拓展成為柱狀的3D物體,2D比3D的計(jì)算復(fù)雜度會(huì)低很多,3D的圖形檢測(cè)原理與2D相似,本文會(huì)實(shí)現(xiàn)幾個(gè)圓形與其他2D圖形的相交檢測(cè):
1、圓形與圓形
2、圓形與膠囊體
3、圓形與扇形
4、圓形與凸多邊形
5、圓形與AABB
6、圓形與OBB
通過(guò)簡(jiǎn)單化處理,把被判定物都處理成由圓柱或多個(gè)圓柱構(gòu)成的區(qū)域,所以只需要考慮圓形與其他形狀的相交。
圓形與圓形
兩個(gè)圓形的相交檢測(cè)非常簡(jiǎn)單直觀(guān),只需要判斷半徑只和與距離的大小。
定義圓形區(qū)間:
/// <summary> /// 圓形區(qū)間 /// </summary> public struct CircleArea { public Vector2 o; public float r; }
o ——圓心坐標(biāo)
r ——圓半徑
相交判斷:
/// <summary> /// 判斷圓形與圓形相交 /// </summary> /// <param name="circleArea"></param> /// <param name="target"></param> /// <returns></returns> public static bool Circle(CircleArea circleArea, CircleArea target) { return (circleArea.o - target.o).sqrMagnitude < (circleArea.r + target.r) * (circleArea.r + target.r); }
分離軸定理
分離軸定理(separating axis theorem, SAT)分離軸定理是指,兩個(gè)不相交的凸集必然存在一個(gè)分離軸,使兩個(gè)凸集在該軸上的投影是分離的。
判斷兩個(gè)形狀是否相交,實(shí)際上是判斷分離軸是否能把兩個(gè)形狀分離。若存在分離軸能使兩個(gè)圖形分離,則這兩個(gè)圖形是分離的。
基于以上理論,尋找分離軸是我們要做的工作,重新考慮兩個(gè)圓形的相交檢測(cè),實(shí)際上我們做的是把圓心連線(xiàn)的方向作為分離軸:
上圖中兩圖形的投影在分離軸上是分離的,存在分離線(xiàn)將兩者隔開(kāi),于是我們可以斷定兩圖形是分離的。
膠囊體的本質(zhì)
定義一個(gè)線(xiàn)段 u,距離 d。膠囊體實(shí)際上是與線(xiàn)段 u 的最短距離小于 d 的點(diǎn)的集合。判斷一個(gè)點(diǎn) x 處于膠囊體內(nèi)部,就是判斷點(diǎn)與線(xiàn)段的距離。
求點(diǎn) x 與線(xiàn)段 u 最短距離的過(guò)程是:
1、求出點(diǎn) x 在線(xiàn)段 u 所在直線(xiàn)上的投影點(diǎn) P;
2、將投影點(diǎn) P 限制在線(xiàn)段的范圍內(nèi)(如右圖中投影點(diǎn)不在線(xiàn)段內(nèi),則限定到線(xiàn)段內(nèi));
3、x 與 P 的距離即為所求;
/// <summary> /// 線(xiàn)段與點(diǎn)的最短距離。 /// </summary> /// <param name="x0">線(xiàn)段起點(diǎn)</param> /// <param name="u">線(xiàn)段向量</param> /// <param name="x">求解點(diǎn)</param> /// <returns></returns> public static float SqrDistanceBetweenSegmentAndPoint(Vector2 x0, Vector2 u, Vector2 x) { float t = Vector2.Dot(x - x0, u) / u.sqrMagnitude; return (x - (x0 + Mathf.Clamp01(t) * u)).sqrMagnitude; }
為避免開(kāi)方計(jì)算,結(jié)果使用距離的平方。
圓形與膠囊體
分離軸是線(xiàn)段上距離圓心最近的點(diǎn)P與圓心所在方向。
定義膠囊體:
/// <summary> /// 膠囊體 /// </summary> public struct CapsuleArea { public Vector2 X0; public Vector2 U; public float d; }
相交判斷:
/// <summary> /// 判斷膠囊體與圓形相交 /// </summary> /// <param name="capsuleArea"></param> /// <param name="circleArea"></param> /// <returns></returns> public static bool Capsule(CapsuleArea capsuleArea, CircleArea circleArea) { float sqrD = SegmentPointSqrDistance(capsuleArea.X0, capsuleArea.U, circleArea.o); return sqrD < (circleArea.r + capsuleArea.d) * (circleArea.r + capsuleArea.d); }
圓形與扇形
當(dāng)扇形角度大于180度時(shí),就不再是凸多邊形了,不能適用于分離軸理論。我們可以找出相交時(shí)圓心的所有可能區(qū)域,并把區(qū)域劃分成可以簡(jiǎn)單驗(yàn)證的幾個(gè)區(qū)域,逐個(gè)試驗(yàn)。
這里共劃分了2個(gè)區(qū)間
1、半徑為兩者半徑和的扇形區(qū)間,角度方向同扇形。驗(yàn)證方法是;驗(yàn)證距離與夾角。
2、扇形邊為軸,圓形半徑為大小組成的膠囊體空間,由于扇形的對(duì)稱(chēng)性,我們可以通過(guò)把圓心映射到一側(cè),從而只需要計(jì)算1條邊。
定義扇形:
/// <summary> /// 扇形區(qū)間。 /// </summary> public struct SectorArea { public Vector2 o; public float r; public Vector2 direction; public float angle; }
相交檢測(cè):
/// <summary> /// 判斷圓形與扇形相交。 /// </summary> /// <param name="sectorArea"></param> /// <param name="target"></param> /// <returns></returns> public static bool Sector(SectorArea sectorArea, CircleArea target) { Vector2 tempDistance = target.o - sectorArea.o; float halfAngle = Mathf.Deg2Rad * sectorArea.angle / 2; if (tempDistance.sqrMagnitude < (sectorArea.r + target.r) * (sectorArea.r + target.r)) { if (Vector3.Angle(tempDistance, sectorArea.direction) < sectorArea.angle / 2) { return true; } else { Vector2 targetInSectorAxis = new Vector2(Vector2.Dot(tempDistance, sectorArea.direction), Mathf.Abs(Vector2.Dot(tempDistance, new Vector2(-sectorArea.direction.y, sectorArea.direction.x)))); Vector2 directionInSectorAxis = sectorArea.r * new Vector2(Mathf.Cos(halfAngle), Mathf.Sin(halfAngle)); return SegmentPointSqrDistance(Vector2.zero, directionInSectorAxis, targetInSectorAxis) <= target.r * target.r; } } return false; }
圓形與凸多邊形
定義多邊形:
/// <summary> /// 多邊形區(qū)域。 /// </summary> public struct PolygonArea { public Vector2[] vertexes; }
相交檢測(cè):
/// <summary> /// 判斷多邊形與圓形相交 /// </summary> /// <param name="polygonArea"></param> /// <param name="target"></param> /// <returns></returns> public static bool PolygonS(PolygonArea polygonArea, CircleArea target) { if (polygonArea.vertexes.Length < 3) { Debug.Log("多邊形邊數(shù)小于3."); return false; } #region 定義臨時(shí)變量 //圓心 Vector2 circleCenter = target.o; //半徑的平方 float sqrR = target.r * target.r; //多邊形頂點(diǎn) Vector2[] polygonVertexes = polygonArea.vertexes; //圓心指向頂點(diǎn)的向量數(shù)組 Vector2[] directionBetweenCenterAndVertexes = new Vector2[polygonArea.vertexes.Length]; //多邊形的邊 Vector2[] polygonEdges = new Vector2[polygonArea.vertexes.Length]; for (int i = 0; i < polygonArea.vertexes.Length; i++) { directionBetweenCenterAndVertexes[i] = polygonVertexes[i] - circleCenter; polygonEdges[i] = polygonVertexes[i] - polygonVertexes[(i + 1)% polygonArea.vertexes.Length]; } #endregion #region 以下為圓心處于多邊形內(nèi)的判斷。 //總夾角 float totalAngle = Vector2.SignedAngle(directionBetweenCenterAndVertexes[polygonVertexes.Length - 1], directionBetweenCenterAndVertexes[0]); for (int i = 0; i < polygonVertexes.Length - 1; i++) totalAngle += Vector2.SignedAngle(directionBetweenCenterAndVertexes[i], directionBetweenCenterAndVertexes[i + 1]); if (Mathf.Abs(Mathf.Abs(totalAngle) - 360f) < 0.1f) return true; #endregion #region 以下為多邊形的邊與圓形相交的判斷。 for (int i = 0; i < polygonEdges.Length; i++) if (SegmentPointSqrDistance(polygonVertexes[i], polygonEdges[i], circleCenter) < sqrR) return true; #endregion return false; }
圓形與AABB
定義AABB:
/// <summary> /// AABB區(qū)域 /// </summary> public struct AABBArea { public Vector2 center; public Vector2 extents; }
AABB是凸多邊形的特例,是長(zhǎng)寬邊分別與X/Y軸平行的矩形,這里我們要充分的利用他的對(duì)稱(chēng)性。
1 利用對(duì)稱(chēng)性將目標(biāo)圓心映射到,以AABB中心為原點(diǎn)、兩邊為坐標(biāo)軸的坐標(biāo)系,的第一象限
2 將目標(biāo)圓心映射到,以AABB第一象限角點(diǎn)為原點(diǎn)、兩邊為坐標(biāo)軸的坐標(biāo)系,的第一象限
3 最后只需要判斷圓形半徑與步驟2中映射點(diǎn)的向量大小
相交檢測(cè):
/// <summary> /// 判斷AABB與圓形相交 /// </summary> /// <param name="aABBArea"></param> /// <param name="target"></param> /// <returns></returns> public static bool AABB(AABBArea aABBArea, CircleArea target) { Vector2 v = Vector2.Max(aABBArea.center - target.o, -(aABBArea.center - target.o)); Vector2 u = Vector2.Max(v - aABBArea.extents,Vector2.zero); return u.sqrMagnitude < target.r * target.r; }
圓形與OBB
定義OBB:
/// <summary> /// OBB區(qū)域 /// </summary> public struct OBBArea { public Vector2 center; public Vector2 extents; public float angle; }
OBB相對(duì)于A(yíng)ABB,矩形邊不與坐標(biāo)軸重合,對(duì)于它和圓形的相交檢測(cè)只需要把圓形旋轉(zhuǎn)到OBB邊所在坐標(biāo)系中,剩下的步驟與AABB的相同。
相交檢測(cè):
/// <summary> /// 判斷OBB與圓形相交 /// </summary> /// <param name="oBBArea"></param> /// <param name="target"></param> /// <returns></returns> public static bool OBB(OBBArea oBBArea, CircleArea target) { Vector2 p = oBBArea.center - target.o; p = Quaternion.AngleAxis(-oBBArea.angle, Vector3.forward) * p; Vector2 v = Vector2.Max(p, -p); Vector2 u = Vector2.Max(v - oBBArea.extents, Vector2.zero); return u.sqrMagnitude < target.r * target.r; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Unity3D 計(jì)時(shí)器的實(shí)現(xiàn)代碼(三種寫(xiě)法總結(jié))
這篇文章主要介紹了Unity3D 計(jì)時(shí)器的實(shí)現(xiàn)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Unity shader實(shí)現(xiàn)高斯模糊效果
這篇文章主要為大家詳細(xì)介紹了Unity shader實(shí)現(xiàn)高斯模糊效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02c# 解決IIS寫(xiě)Excel的權(quán)限問(wèn)題
使用以上方法必須對(duì)dcom進(jìn)行配置,給用戶(hù)使用office的權(quán)限2012-10-10C#中自定義高精度Timer定時(shí)器的實(shí)例教程
這篇文章主要介紹了C#中自定義高精度Timer定時(shí)器的實(shí)例教程,多線(xiàn)程的Timer編寫(xiě)需要注意線(xiàn)程安全的問(wèn)題,需要的朋友可以參考下2016-04-04基于C#實(shí)現(xiàn)12306的動(dòng)態(tài)驗(yàn)證碼變成靜態(tài)驗(yàn)證碼的方法
這篇文章主要介紹了基于C#實(shí)現(xiàn)12306的動(dòng)態(tài)驗(yàn)證碼變成靜態(tài)驗(yàn)證碼的方法的相關(guān)資料,需要的朋友可以參考下2015-12-12C#中parallel.foreach實(shí)現(xiàn)多線(xiàn)程處理
Parallel.ForEach方法是C#中的一個(gè)并行循環(huán)方法,它可以并行地對(duì)一個(gè)集合進(jìn)行迭代操作,本文主要介紹了C#中parallel.foreach實(shí)現(xiàn)多線(xiàn)程處理,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02在C#里面給PPT文檔添加注釋的實(shí)現(xiàn)代碼
平常開(kāi)會(huì)或者做總結(jié)報(bào)告的時(shí)候我們通常都會(huì)用到PowerPoint演示文稿,我們可以在單個(gè)幻燈片或者全部幻燈片里面添加注釋?zhuān)@樣觀(guān)眾可以從注釋內(nèi)容里面獲取更多的相關(guān)信息,需要的朋友可以參考下2017-01-01新手小白用C# winform 讀取Excel表的實(shí)現(xiàn)
這篇文章主要介紹了新手小白用C# winform 讀取Excel表的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01