1.前言
前面,通过文章详解向量与Unity中向量类Vcetor3的使用(一)我们学习向量及向量的基本运算,在文章详解向量与Unity中向量类Vcetor3的使用(二)中对Unity中向量类Vector3结合向量原理进行了详细的了解与其实现。
本篇文章,我们结合向量了解下Unity中直线与直线相交的交点坐标求解,结合自己使用,介绍下面两种便捷且容易理解的方法:方程组求解与向量法。
2.直线与直线相交-方程组求解
2.1求解过程
已知条件:直线上的两点,,直线上的两点,,求两条直线的交点。
我们熟知的直线方程式的表达式为斜截式,一般式为(A、B、C不同时为零),知道两点坐标可求斜率:
再将点代入式子中,可得我们可得到直线的斜截式表达式:
我们将其转化为一般式方程式:
简洁表示为:
其中:
同理可得直线的一般式方程式如下:
简洁表示为:
其中:
解上述两个二元一次方程组得到根:
可以看到上述的根分母是一致的为,当分母时,表示无解,在几何意义上就是两条直线没有交点,即。
2.2代码演示
上述就是直线与直线相交的求解过程,假设坐标点位于X-Z平面内,下面代码实现:
/// <summary>
/// 计算平面中直线与直线的交点.
/// </summary>
/// <param name="point1">第一条直线上的点1</param>
/// <param name="point2">第一条直线上的点2</param>
/// <param name="point3">第二条直线上的点1</param>
/// <param name="point4">第二条直线上的点2</param>
/// <param name="point5">若两条直线平行,则返回该点</param>
/// <returns>两条直线的交点.</returns>
private Vector3 IntersectionOfStraightLine(
Vector3 point1,
Vector3 point2,
Vector3 point3,
Vector3 point4,
Vector3 point5)
{
var a1 = point2.z - point1.z;
var b1 = point1.x - point2.x;
var c1 = (point1.x * point2.z) - (point2.x * point1.z);
var a2 = point4.z - point3.z;
var b2 = point3.x - point4.x;
var c2 = (point3.x * point4.z) - (point4.x * point3.z);
var denominator = (a1 * b2) - (a2 * b1);
if (denominator != 0)
{
//两条直线不平行,有交点
var x = ((b2 * c1) - (b1 * c2)) / denominator;
var z = ((a1 * c2) - (a2 * c1)) / denominator;
Debug.Log($"交点坐标为{x},{point1.y},{z}");
return new Vector3(x, point1.y, z);
}
Debug.Log($"无交点坐标,两条直线不相交");
return point5;
}
执行代码如下:
void Start()
{
Vector3 a = new Vector3(10, 0, 10);
Vector3 b = new Vector3(10, 0, 0);
Vector3 c = new Vector3(20, 0, 10);
Vector3 d = new Vector3(20, 0, 0);
//点a与原点的直线与点b与点c的直线平行,无交点
var p1 = IntersectionOfStraightLine(Vector3.zero, a, b, c, Vector3.zero);
//点a与点d的直线与点b与点c的直线相交
var p2 = IntersectionOfStraightLine(a, d, b, c, Vector3.zero);
}
输出结果如下:
3.直线与直线相交-向量法
3.1求解过程
已知条件不变:直线上的两点,,直线上的两点,,求两条直线的交点。如下图所示:
过点做垂线分别交于直线,交点为,连接点,,设向量:
观察可知,上侧平行四边形的面积:
下侧平行四边形的面积:
设两个平行四边形的面积的比值等于:
由向量的叉乘可知,两个向量的叉乘得到的向量的模长等于这两个向量围成的平行四边形的面积,所以平行四边形的面积可通过叉乘得到:
那么,求到两个平行四边形面积的比值,我们怎么求的交点呢?由图可知,三角形与三角形是相似三角形,可以得到以下比例关系:
知道了线段与线段的比值,且知道向量的模长及单位向量,就能求出线段的长度,从而求出点的坐标。 线段的长度为:
设向量的单向向量为:
点的坐标为:
3.2代码演示
我们在Unity脚本中利用向量类Vector3实现上面向量法计算交点的方法,如下:
/// <summary>
/// 计算直线与直线的交点-向量法
/// </summary>
/// <param name="point1">第一条直线上的点1</param>
/// <param name="point2">第一条直线上的点2</param>
/// <param name="point3">第二条直线上的点1</param>
/// <param name="point4">第二条直线上的点2</param>
/// <returns>两条直线的交点.</returns>
private Vector3 IntersectionOfLine(
Vector3 point1,
Vector3 point2,
Vector3 point3,
Vector3 point4)
{
var d1 = point2 - point1;
var d2 = point4 - point3;
//通过点积求的两个向量夹角,判断是否平行
var cos = Vector3.Dot(d1, d2) / (d1.magnitude * d2.magnitude);
if (Math.Abs(cos) - 1 == 0)
{
Debug.Log($"无交点坐标,两条直线不相交");
return Vector3.zero;
}
var d3 = point1 - point3;
var d4 = point2 - point3;
var d1Norm = d1 / d1.magnitude; //d1的单位向量
var t = Vector3.Cross(d2, d3).magnitude / Vector3.Cross(d2, d4).magnitude; //面积的比值t
var result = point1 + (t / (t + 1) * d1.magnitude * d1Norm); //交点坐标
Debug.Log($"采用向量法求得交点坐标为{result.x},{result.y},{result.z}");
return result;
}
执行代码如下,将方程组求解法与向量法的结果做下比较:
void Start()
{
Vector3 a = new Vector3(10, 0, 10);
Vector3 b = new Vector3(10, 0, 0);
Vector3 c = new Vector3(20, 0, 10);
Vector3 d = new Vector3(20, 0, 0);
//点a与原点的直线与点b与点c的直线平行,无交点
var p1 = IntersectionOfStraightLine(Vector3.zero, a, b, c, Vector3.zero);
//点a与点d的直线与点b与点c的直线相交
var p2 = IntersectionOfStraightLine(a, d, b, c, Vector3.zero);
//点a与原点的直线与点b与点c的直线平行,无交点
var p3 = IntersectionOfLine(Vector3.zero, a, b, c);
//点a与点d的直线与点b与点c的直线相交
var p4 = IntersectionOfLine(a, d, b, c);
}
输出结果如下:
4.结语
上面我们介绍了两种在Unity平面内两条直线相交的交点的求解方法,向量法的复杂度感觉稍微简单点,具体运用过程中就看个人喜好。后面有机会继续介绍直线与圆的相交性检测!若有不足之处,望大家批评指正。