FishPlayer

一个喜欢摸鱼的废物

0%

简易检测,目标是否在扇形区域(2D)

最近在项目组里做一些小游戏原型,深感自己菜得如虫豸。为了慢慢提升还是决定做一些记录,虽然只是很垃圾的功能。
因为做的是手游,所以对性能蛮看重的,说是原型,为了快也只是项目里开了个单独的新场景做的,3C都有,做好了可以直接打包APK试玩。
废话不多说,这次做的事很简单,在扇形区域内检测一些目标。因为听说Vector3. Angle()里会跑开方的计算,故尝试避免使用这个方法。

思路

需要解决的需求是在检测者 前方 的一个扇形内检测目标物体,其实可以化简为2D平面的检测。
我决定多次使用点乘和叉乘来解决这个问题。

已知数据:
检测者的位置,朝向,检测角度大小,半径大小
检测目标的位置

步骤:

  1. 用检测者的前方向量和检测者到目标的方向向量点乘来判断目标物体是否在检测者前方。
  2. 用检测者前方方向向量和检测区域的两个边界方向分别叉乘得到两个法向向量A,B。
  3. 用检测者到目标的方向向量和检测区域的两个边界方向分别叉乘得到两个法向向量C,D。
  4. 用点乘检测C,D是否同向,是,则在检测区外
  5. 是否同时满足向量A,C同向,向量B,D同向,是,则在检测区内

其实我自己都感觉这中文,写得很乱,还不如直接看代码23333
全部代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

public class TargetCheckTester : MonoBehaviour
{
private Transform m_transform = null;

[SerializeField]
private float m_checkAngle = 60.0f;

[SerializeField]
private float m_checkRadius = 6.0f;

[SerializeField]
private Transform m_targetTransform = null;

private void CheckTarget1()
{
if (null == m_targetTransform)
return;

Vector3 myPos = m_transform.position;
Vector3 myForward = m_transform.forward;
float halfAngle = m_checkAngle * 0.5f;
Vector3 dirUp = Quaternion.Euler(0.0f, -halfAngle, 0.0f) * myForward;
Vector3 dirDown = Quaternion.Euler(0.0f, halfAngle, 0.0f) * myForward;

Vector3 upCheck = Vector3.Cross(myForward, dirUp);
Vector3 downCheck = Vector3.Cross(myForward, dirDown);

Vector3 targetPos = m_targetTransform.position;
// hack, to make them on a 2d plane :)
targetPos.y = myPos.y;
Vector3 toTargetDir = targetPos - myPos;

// draw line
Vector3 point1 = myPos + dirUp * m_checkRadius;
Vector3 point2 = myPos + dirDown * m_checkRadius;
Vector3 point0 = myPos + myForward * m_checkRadius;

Debug.DrawLine(myPos, point1, Color.yellow, Time.deltaTime);
Debug.DrawLine(myPos, point2, Color.yellow, Time.deltaTime);
Debug.DrawLine(myPos, point0, Color.yellow, Time.deltaTime);
Debug.DrawLine(point1, point0, Color.yellow, Time.deltaTime);
Debug.DrawLine(point2, point0, Color.yellow, Time.deltaTime);
Debug.DrawLine(myPos, targetPos, Color.red, Time.deltaTime);
// draw line

if (Vector3.Dot(toTargetDir, myForward) < 0.0f || toTargetDir.sqrMagnitude > m_checkRadius * m_checkRadius)
return;

Vector3 targetUpCheck = Vector3.Cross(toTargetDir, dirUp);
Vector3 targetDownCheck = Vector3.Cross(toTargetDir, dirDown);

float selfCheck = Vector3.Dot(targetUpCheck, targetDownCheck);
float dotCheckUp = Vector3.Dot(upCheck, targetUpCheck);
float dotCheckDown = Vector3.Dot(targetDownCheck, downCheck);
if (selfCheck <= 0.0f && dotCheckUp * dotCheckDown >= 0.0f)
{
Debug.Log("target in sight");
}

}

#region mono method

private void Awake()
{
m_transform = this.transform;
}

private void Update()
{
CheckTarget1();
}

#endregion

}