关于 Unity 中触摸屏幕坐标与物体世界坐标的判定方法(以矩形为例)
Published in:2023-02-22 |
Words: 932 | Reading time: 4min | reading:

关于 Unity 中触摸屏幕坐标与物体世界坐标的判定方法(以矩形为例)

处理思路

  • 1.获取用户触摸的屏幕坐标(touch_vector3)
  • 2.转换屏幕坐标为世界坐标(Camera.main.ScreenToWorldPoint(touch_vector3))
  • 3.根据对应算法比较触摸点与目标点差值
  • 4.差值在预期内,判定为用户选中;否则,未选中

算法选择

交点(射线)法

  • 1.基本思路:

  • 从任意位置画一条到目标点的水平线,计算该水平线进出平面边界的次数,如果是偶数,则点在平面外;如果是奇数,则点在平面内。点正好落在定点或边界时会出现特殊的判断。

  • 如图:

  • 2.代码实现

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
/// <summary>
/// 判断点是否在矩形框内- app
/// </summary>
/// <param name="APoints">库位点集合</param>
/// <param name="pointBottomCenter">目标点</param>
/// <returns></returns>
public static bool isPolygonContainsPoint(List<Vector3> APoints, Vector3 pointBottomCenter)
{
int nCross = 0;
for (int i = 0; i < APoints.Count; i++)
{
Vector3 p1 = APoints[i];
Vector3 p2 = APoints[(i + 1) % APoints.Count];
// 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数
// p1p2是水平线段,要么没有交点,要么有无限个交点
if (p1.y == p2.y)
{
double minX = Math.Min(p1.x, p2.x);
double maxX = Math.Max(p1.x, p2.x);
// point在水平线段p1p2上,直接return true
if ((pointBottomCenter.z == p1.z) && (pointBottomCenter.x >= minX && pointBottomCenter.x <= maxX))
{
return true;
}
continue;
}
// point 在p1p2 底部 --> 无交点
if (pointBottomCenter.z < Math.Min(p1.z, p2.z))
continue;
// point 在p1p2 顶部 --> 无交点
if (pointBottomCenter.z >= Math.Max(p1.z, p2.z))
continue;
// 求解 point点水平线与当前p1p2边的交点的 X 坐标
double x = (pointBottomCenter.z - p1.z) * (p2.x - p1.x) / (p2.z - p1.z) + p1.x;
if (x > pointBottomCenter.x) // 当x=point.x时,说明point在p1p2线段上
nCross++; // 只统计单边交点
}
// 单边交点为偶数,点在多边形之外 ---
return (nCross % 2 == 1);
}

面积法

  • 1.基本思路
  • 通过将矩形切割为多个三角形计算面积,与原面积比较差值;若大于 0 则不在,否则在。
  • 所有边和目标点组成的三角形面积和是否等于总的多边形面积,如果相等,则在内部。反之在外部。
  • 2.代码实现
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

/// <summary>
/// 计算距离
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
static double dis(Vector3 p1, Vector3 p2)
{
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z));
}

/// <summary>
/// 计算三角形面积
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns></returns>
public static double triangleArea(Vector3 p1, Vector3 p2, Vector3 p3)
{

double a = dis(p1, p2);
double b = dis(p2, p3);
double c = dis(p3, p1);
double p = (a + b + c) * 0.5;
return Math.Sqrt(p * (p - a) * (p - b) * (p - c));
}

/// <summary>
/// 判断某点是否落在矩形内
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <param name="d"></param>
/// <param name="p">目标点</param>
/// <returns></returns>
public static bool pInQuadrangle(Vector3 a, Vector3 b, Vector3 c, Vector3 d, Vector3 p)
{
double dTriangle = triangleArea(a, b, p) + triangleArea(b, c, p) + triangleArea(c, d, p) + triangleArea(d, a, p);
double dQuadrangle = triangleArea(a, b, c) + triangleArea(c, d, a);// s5 + s6;
if (Math.Abs(dTriangle - dQuadrangle) < 0.25f) //说明理论上应该是相等的,但因为计算本身的原因可能会有细微不同,所以选择小于1,根据实际情况来设置
{
return true;
}
else
{
return false;
}

}

Prev:
关于 Unity 不同方式的导出与详细使用的说明(基于 Android 平台)
Next:
Android 作为服务器向 UNITY 客户端发送数据