Unity 攻击范围检测
?眾所周知moba中的每個英雄都有一套自己的技能的攻擊范圍方式,有如廉頗一樣的圓形范圍,有火舞一樣的直線范圍,呂布的扇形方天戟范圍,還有上圖的牛魔大招時的矩形范圍等等。
????一些技能是通過物理的碰撞檢測來判斷的,一些則是通過這樣的范圍來檢測的。物理檢測的詬病就在于開銷過大,在能考慮不用物理來檢測的情況下更傾向來自己通過算法模擬實現。
小菜的學習研究中,將這些自己算法檢測的攻擊范圍劃分了幾種類型,并做了幾個demo的演示。
?
如上演示,小菜簡單的講這些類型劃分成了如下幾類:
1). Circle? ? ? ? ? ? ? ? ? ?? ? ?圓形
2).?Triangle? ? ? ? ? ? ? ? ? ??三角形
3).?Fanshaped? ? ? ? ? ? ? ??扇形
4).?Rectangle? ? ? ? ? ? ? ? ?矩形
5).?Sector? ? ? ? ? ? ? ? ? ? ? ?扇面
6). Ring? ? ? ? ? ? ? ? ? ? ? ? ??環形
?
[Circle 圓形]
這應該是最簡單的類型,只要去判斷self和target的distance就可以做到了。
我們希望能直觀看到范圍的情況,故使用Debug.DrawLine做了調試的繪制。
繪制編碼:
小菜不想由于各個對象高度的不同帶來的檢測差異,故用NormalizePosition將位置的y信息都歸置成了0。
?
范圍檢測編碼:
?
[Triangle?三角形]
三角形范圍的判定,實際就是點在三角形內的判定。
數學上檢測點在三角形內有三種推論方法。內角和法/同向法/重心法。
對數學感興趣的可以參考:https://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html
小菜這里直接套用了重心法檢測。
檢測編碼:
?
繪制編碼:
?
[Fanshaped?扇形]
?
扇形的范圍檢測我們實際可以抽象成兩個步驟。
1).判斷self和target的distance.
2).由于點乘,使用點乘dot來計算self到target的單位向量,與self的forward向量(本身也是單位向量)來計算得夾角cos值。使用Mathf.Acos將其轉化為弧度,再轉換成角度做一次判斷就好了。
?
繪制編碼:
?
范圍檢測編碼:
?
[Rectangle?矩形]
矩形的檢測小菜大概是有兩種方法:
1).通過點在矩形內的數學推導公式來計算。
2).通過點乘和distance來計算。
?
還是先將矩形繪制出來吧。
繪制編碼:
?
范圍檢測:
通過點在矩形內的數學推導公式來計算矩形范圍
判斷一個點是否在兩條線段之間夾著就轉化成,判斷一個點是否在某條線段的一邊上,就可以利用叉乘的方向性,來判斷夾角是否超過了180度?。
只要判斷(AB X AE ) * (CDX CE) ?>= 0 就說明E在AB,CD中間夾著,同理計算另兩邊DA和BC就可以了。
最后就是只需要判斷
(AB X AE ) * (CD X CE) ?>= 0?&& (DA X DE ) * (BC X BE) >= 0 。
?
范圍檢測編碼:
?
通過點乘和distance來計算矩形范圍
還是先上一張圖輔助理解吧。
兩次點乘的結果在于判斷target的前后和左右關系。
編碼看似比上面的少很多,實際關聯的理解可一點都不簡單。
?
[Sector?扇面]
扇面和扇形的檢測很相似,不同的只是多了一層距離的檢測。
?
繪制編碼:
?
?
范圍檢測編碼:
?
[Ring?環形]
環形和圓形的檢測很相似,也只是多了一層距離的檢測。
繪制編碼:
?
范圍檢測編碼:
附上完整代碼:
using System.Collections; using System.Collections.Generic; using UnityEngine;public enum CheckType {None,/// <summary> 圓形 </summary>Circle,/// <summary> 三角形 </summary>Triangle,/// <summary> 扇形 </summary>Fanshaped,/// <summary> 矩形 </summary>Rectangle,/// <summary> 扇面 </summary>Sector,/// <summary> 環形 </summary>Ring, }[ExecuteInEditMode] public class RangeCheckScript : MonoBehaviour {public CheckType currType = CheckType.None;public Transform mPalyer;public Transform mTarget;public bool mCheckOpen = true;void Update(){if (!mCheckOpen)return;if (null != mPalyer)Debug.DrawLine(mPalyer.position, mPalyer.position + mPalyer.forward * 8,Color.yellow);bool bResult = false;switch (currType){case CheckType.None:break;case CheckType.Circle:bResult = CircleCheck(mPalyer, mTarget, 6);break;case CheckType.Triangle:bResult = TriangleCheck(mPalyer, mTarget, 1, 10);break;case CheckType.Fanshaped:bResult = FanshapedCheck(mPalyer, mTarget, 45, 5);break;case CheckType.Rectangle://bResult = SimulateRectangleCheck(mPalyer, mTarget, 2, 8);bResult = RectangleCheck(mPalyer, mTarget, 2, 8);break;case CheckType.Sector:bResult = SectorCheck(mPalyer, mTarget, 45, 5, 8);break;case CheckType.Ring:bResult = RingCheck(mPalyer, mTarget, 4, 8);break;default:break;}if (bResult)Debug.LogError("檢測到目標");}/// <summary>/// 圓形范圍檢測/// </summary>private bool CircleCheck(Transform self, Transform target, float distance){if (null == self || null == target)return false;//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);int nCircleDentity = 360;Vector3 beginPoint = selfPosition;Vector3 endPoint = Vector3.zero;float tempStep = 2 * Mathf.PI / nCircleDentity;bool bFirst = true;for (float step = 0; step < 2 * Mathf.PI; step += tempStep){float x = distance * Mathf.Cos(step);float z = distance * Mathf.Sin(step);endPoint.x = selfPosition.x + x;endPoint.z = selfPosition.z + z;if (bFirst)bFirst = false;elseDebug.DrawLine(beginPoint, endPoint, Color.red);beginPoint = endPoint;}//---------------------范圍檢測-----------------------------------float currDistance = Vector3.Distance(selfPosition, targetPosition);if (currDistance <= distance)return true;return false;}/// <summary>/// 三角形范圍檢測/// </summary>private bool TriangleCheck(Transform self, Transform target, float halfWidth,float distance){if (null == self || null == target)return false;//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);Quaternion tempQuat = self.rotation;//三角形的三個點Vector3 leftPoint = selfPosition + (tempQuat * Vector3.left) * halfWidth;Vector3 rightPoint = selfPosition + (tempQuat * Vector3.right) * halfWidth;Vector3 forwardPoint = selfPosition + (tempQuat * Vector3.forward) * distance;Debug.DrawLine(leftPoint,rightPoint,Color.red);Debug.DrawLine(rightPoint, forwardPoint, Color.red);Debug.DrawLine(forwardPoint, leftPoint, Color.red);//---------------------范圍檢測-----------------------------------bool bResult = IsPointInTriangle(leftPoint, forwardPoint, rightPoint, targetPosition);return bResult;}/// <summary>/// 扇形范圍檢測/// </summary>private bool FanshapedCheck(Transform self, Transform target, float halfAngle, float distance){if (null == self || null == target)return false;//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);Quaternion selfQuat = self.rotation;int nCircleDentity = 360;Vector3 firstPoint = Vector3.zero;Vector3 beginPoint = selfPosition;Vector3 endPoint = Vector3.zero;float tempStep = 2 * Mathf.PI / nCircleDentity;float leftRadian = Mathf.PI / 2 + Mathf.Deg2Rad * halfAngle;float rightRadian = Mathf.PI / 2 - Mathf.Deg2Rad * halfAngle;bool bFirst = true;for (float step = 0; step < 2 * Mathf.PI; step += tempStep){float x = distance * Mathf.Cos(step);float z = distance * Mathf.Sin(step);endPoint.x = selfPosition.x + x;endPoint.z = selfPosition.z + z;if (step >= rightRadian && step <= leftRadian){if (bFirst){firstPoint = endPoint;bFirst = false;}Debug.DrawLine(beginPoint, endPoint, Color.red);beginPoint = endPoint;}}Debug.DrawLine(selfPosition, firstPoint, Color.red);Debug.DrawLine(selfPosition, beginPoint, Color.red);//---------------------范圍檢測-----------------------------------//計算距離float currDis = Vector3.Distance(selfPosition, targetPosition);if (currDis > distance)return false;//計算self到target的向量Vector3 dir = targetPosition - selfPosition;//點乘dir向量和自身的forward向量 cosqfloat dotForward = Vector3.Dot(dir.normalized, (selfQuat * Vector3.forward).normalized);//得到夾角弧度并轉換成角度float radian = Mathf.Acos(dotForward);float currAngle = Mathf.Rad2Deg * radian;if (Mathf.Abs(currAngle) <= halfAngle)return true;return false;}/// <summary>/// 矩形范圍檢測(數學點和矩形關系判斷)/// </summary>private bool SimulateRectangleCheck(Transform self, Transform target, float halfWidth, float distance){if (null == self || null == target)return false;//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);Vector3 selfEulerAngles = self.rotation.eulerAngles;Quaternion selfQuat = self.rotation;//矩形的四個點Vector3 leftPoint = selfPosition + (selfQuat * Vector3.left) * halfWidth;Vector3 rightPoint = selfPosition + (selfQuat * Vector3.right) * halfWidth;Vector3 leftUpPoint = leftPoint + (selfQuat * Vector3.forward) * distance;Vector3 rightUpPoint = rightPoint + (selfQuat * Vector3.forward) * distance;Debug.DrawLine(selfPosition, leftPoint, Color.red);Debug.DrawLine(selfPosition, rightPoint, Color.red);Debug.DrawLine(leftPoint, leftUpPoint, Color.red);Debug.DrawLine(rightPoint, rightUpPoint, Color.red);Debug.DrawLine(leftUpPoint, rightUpPoint, Color.red);//---------------------范圍檢測-----------------------------------Vector2 point = Vector2.zero;point.x = targetPosition.x;point.y = targetPosition.z;Vector2 point1 = Vector2.zero;point1.x = leftUpPoint.x;point1.y = leftUpPoint.z;Vector2 point2 = Vector2.zero;point2.x = rightUpPoint.x;point2.y = rightUpPoint.z;Vector2 point3 = Vector2.zero;point3.x = rightPoint.x;point3.y = rightPoint.z;Vector2 point4 = Vector2.zero;point4.x = leftPoint.x;point4.y = leftPoint.z;bool bResult = IsPointInRectangle(point1, point2, point3, point4, point);return bResult;}/// <summary>/// 矩形范圍檢測(點乘方式)/// </summary>private bool RectangleCheck(Transform self, Transform target, float halfWidth, float distance){if (null == self || null == target)return false;//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);Vector3 selfEulerAngles = self.rotation.eulerAngles;Quaternion selfQuat = self.rotation;//矩形的四個點Vector3 leftPoint = selfPosition + (selfQuat * Vector3.left) * halfWidth;Vector3 rightPoint = selfPosition + (selfQuat * Vector3.right) * halfWidth;Vector3 leftUpPoint = leftPoint + (selfQuat * Vector3.forward) * distance;Vector3 rightUpPoint = rightPoint + (selfQuat * Vector3.forward) * distance;Debug.DrawLine(selfPosition, leftPoint, Color.red);Debug.DrawLine(selfPosition, rightPoint, Color.red);Debug.DrawLine(leftPoint, leftUpPoint, Color.red);Debug.DrawLine(rightPoint, rightUpPoint, Color.red);Debug.DrawLine(leftUpPoint, rightUpPoint, Color.red);//---------------------范圍檢測-----------------------------------//計算self到target的向量Vector3 dir = targetPosition - selfPosition;//點乘dir向量和自身的forward向量float dotForward = Vector3.Dot(dir, (selfQuat * Vector3.forward).normalized);//target處于self的前方的height范圍if (dotForward > 0 && dotForward <= distance){float dotRight = Vector3.Dot(dir, (selfQuat * Vector3.right).normalized);//target處于self的左右halfWidth的范圍if (Mathf.Abs(dotRight) <= halfWidth)return true;}return false;}/// <summary>/// 扇面范圍檢測/// </summary>private bool SectorCheck(Transform self, Transform target, float halfAngle, float nearDis, float farDis){if (null == self || null == target)return false;if (nearDis > farDis){float tempDis = nearDis;nearDis = farDis;farDis = tempDis;}//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);Quaternion selfQuat = self.rotation;int nCircleDentity = 360;Vector3 nearFirstPoint = Vector3.zero;Vector3 nearBeginPoint = selfPosition;Vector3 nearEndPoint = Vector3.zero;Vector3 farFirstPoint = Vector3.zero;Vector3 farBeginPoint = selfPosition;Vector3 farEndPoint = Vector3.zero;float tempStep = 2 * Mathf.PI / nCircleDentity;float leftRadian = Mathf.PI / 2 + Mathf.Deg2Rad * halfAngle;float rightRadian = Mathf.PI / 2 - Mathf.Deg2Rad * halfAngle;bool bFirst = true;for (float step = 0; step < 2 * Mathf.PI; step += tempStep){float nearX = nearDis * Mathf.Cos(step);float nearZ = nearDis * Mathf.Sin(step);float farX = farDis * Mathf.Cos(step);float farZ = farDis * Mathf.Sin(step);if (step >= rightRadian && step <= leftRadian){//-------繪制近扇面nearEndPoint.x = selfPosition.x + nearX;nearEndPoint.z = selfPosition.z + nearZ;//-------繪制遠扇面farEndPoint.x = selfPosition.x + farX;farEndPoint.z = selfPosition.z + farZ;if (bFirst){nearFirstPoint = nearEndPoint;farFirstPoint = farEndPoint;bFirst = false;}else{Debug.DrawLine(nearBeginPoint, nearEndPoint, Color.red);Debug.DrawLine(farBeginPoint, farEndPoint, Color.red);}nearBeginPoint = nearEndPoint;farBeginPoint = farEndPoint;}}Debug.DrawLine(nearFirstPoint, farFirstPoint, Color.red);Debug.DrawLine(nearEndPoint, farEndPoint, Color.red);Debug.DrawLine(selfPosition, nearFirstPoint, Color.blue);Debug.DrawLine(selfPosition, nearEndPoint, Color.blue);//---------------------范圍檢測-----------------------------------//計算距離float currDis = Vector3.Distance(selfPosition, targetPosition);if (currDis < nearDis || currDis > farDis)return false;//計算self到target的向量Vector3 dir = targetPosition - selfPosition;//點乘dir向量和自身的forward向量 cosqfloat dotForward = Vector3.Dot(dir.normalized, (selfQuat * Vector3.forward).normalized);//得到夾角弧度并轉換成角度float radian = Mathf.Acos(dotForward);float currAngle = Mathf.Rad2Deg * radian;if (Mathf.Abs(currAngle) <= halfAngle)return true;return false;}/// <summary>/// 雙圓范圍檢測/// </summary>private bool RingCheck(Transform self, Transform target, float nearDis, float farDis){if (null == self || null == target)return false;if (nearDis > farDis){float tempDis = nearDis;nearDis = farDis;farDis = tempDis;}//---------------------繪制圖形-----------------------------------Vector3 selfPosition = NormalizePosition(self.position);Vector3 targetPosition = NormalizePosition(target.position);int nCircleDentity = 360;Vector3 nearBeginPoint = selfPosition;Vector3 nearEndPoint = Vector3.zero;Vector3 farBeginPoint = selfPosition;Vector3 farEndPoint = Vector3.zero;float tempStep = 2 * Mathf.PI / nCircleDentity;bool bFirst = true;for (float step = 0; step < 2 * Mathf.PI; step += tempStep){float nearX = nearDis * Mathf.Cos(step);float nearZ = nearDis * Mathf.Sin(step);nearEndPoint.x = selfPosition.x + nearX;nearEndPoint.z = selfPosition.z + nearZ;float farX = farDis * Mathf.Cos(step);float farZ = farDis * Mathf.Sin(step);farEndPoint.x = selfPosition.x + farX;farEndPoint.z = selfPosition.z + farZ;if (bFirst)bFirst = false;else{Debug.DrawLine(nearBeginPoint, nearEndPoint, Color.red);Debug.DrawLine(farBeginPoint, farEndPoint, Color.red);}nearBeginPoint = nearEndPoint;farBeginPoint = farEndPoint;}//---------------------范圍檢測-----------------------------------float currDistance = Vector3.Distance(selfPosition, targetPosition);if (currDistance >= nearDis && currDistance <= farDis )return true;return false;}/// <summary>/// 規范位置(去除高度帶來的影響)/// </summary>private Vector3 NormalizePosition(Vector3 position,float hight = 0.0f){Vector3 tempPosition = Vector3.zero;tempPosition.x = position.x;tempPosition.y = hight;tempPosition.z = position.z;return tempPosition;}/// <summary>/// 三角形檢查/// </summary>private bool IsPointInTriangle(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 targetPoint){Vector3 v0 = point2 - point1;Vector3 v1 = point3 - point1;Vector3 v2 = targetPoint - point1;float dot00 = Vector3.Dot(v0, v0);float dot01 = Vector3.Dot(v0, v1);float dot02 = Vector3.Dot(v0, v2);float dot11 = Vector3.Dot(v1, v1);float dot12 = Vector3.Dot(v1, v2);float inverDeno = 1 / (dot00 * dot11 - dot01 * dot01);float u = (dot11 * dot02 - dot01 * dot12) * inverDeno;if (u < 0 || u > 1)return false;float v = (dot00 * dot12 - dot01 * dot02) * inverDeno;if (v < 0 || v > 1)return false;return u + v <= 1;}/// <summary>/// 判斷點p是否在p1 p2 p3 p4構成的矩形內/// </summary>private bool IsPointInRectangle(Vector2 point1, Vector2 point2, Vector2 point3, Vector2 point4, Vector2 point){return GetCross(point1, point2, point) * GetCross(point3, point4, point) >= 0&& GetCross(point2, point3, point) * GetCross(point4, point1, point) >= 0;}/// <summary>/// 計算 |p1 p2| X |p1 p|/// </summary>private float GetCross(Vector2 point1, Vector2 point2, Vector2 point){return ((point2.x - point1.x) * (point.y - point1.y) - (point.x - point1.x) * (point2.y - point1.y));} }?
總結
以上是生活随笔為你收集整理的Unity 攻击范围检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ | PaddleOCR GPU版
- 下一篇: Py网络编程及应用(urllib、soc