slab指的就是一组平行线之间的距离
AABB的width为平行于y轴的两条边之间的距离,叫x-slab;height为平行于x轴的两条边之间的距离,y-slab;
x-slab和y-slab重叠的部分,就是矩形框;
如果射线和AABB相交,则射线与x-slab相交部分和y-slab相交部分必定有重叠
射线与AABB相交的条件就是判断共线线段是否重叠的逻辑:max(两线段的min端点) <= min(两线段的max端点)
设射线方程为:r=o+t*dir,其中o为射线起点,dir为射线方向,t为射线方向的距离(是一个变量)
射线与AABB的交点肯定也在射线上,所以交点的坐标可以表示为:
,所以:t = (x-o.x) / dir.x,或 t= (y-o.y) / dir.y
点与x-slab相交时,交点的x坐标就是min.x,max.x,此时可以用t = (x-o.x) / dir.x来求出t:
ta=(min.x-o.x)/dir.x,tb=(max.x-o.x)/dir.x, ta < tb
点与y-slab相交时,交点的y坐标就是min.y, max.y,此时可以用t= (y-o.y) / dir.y来求出t:
tc=(min.y-o.y)/dir.y,td=(max.y-o.y)/dir.y, tc < td
所以,判断的依据可以转变成:max(ta, tc) <= min(tb, td),即:max(ta, tc) > min(tb, td)则不相交
//求射线矩形交点
public static bool IsRayRectIntersect(Vector2 o, Vector2 dir, Vector2 min, Vector2 max, out Vector2 pNear, out Vector2 pFar)
pNear = Vector2.zero;
pFar = Vector2.zero;
float tmin = 0;
float tmax = 1;
if (Mathf.Approximately(dir.x, 0)) //y轴平行
if (o.x < min.x || o.x > max.x)
return false;
tmin = (min.y - o.y) * dir.y; //dir.y不是1就是-1
tmax = (max.y - o.y) * dir.y;
if (tmin > tmax)
float temp = tmin;
tmin = tmax;
tmax = temp;
else if (Mathf.Approximately(dir.y, 0)) //x轴平行
if (o.y < min.y || o.y > max.y)
return false;
//用上面那种: *dir.x的方式也是一样的
if (dir.x > 0)
tmin = min.x - o.x;
tmax = max.x - o.x;
tmin = o.x - max.x;
tmax = o.x - min.x;
float invDirX = 1 / dir.x;
float tx1 = (min.x - o.x) * invDirX; //x-slab第1个交点
float tx2 = (max.x - o.x) * invDirX; //x-slab第2个交点
if (tx1 > tx2) //射线在x方向上从右往左时
float temp = tx1;
tx1 = tx2;
tx2 = temp;
float invDirY = 1 / dir.y;
float ty1 = (min.y - o.y) * invDirY; //y-slab第1个交点
float ty2 = (max.y - o.y) * invDirY; //y-slab第2个交点
if (ty1 > ty2) //射线在y方向上从上往下时
float temp = ty1;
ty1 = ty2;
ty2 = temp;
//共线线段无重叠:max(两线段的min端点) > min(两线段的max端点)
tmin = Mathf.Max(tx1, ty1);
tmax = Mathf.Min(tx2, ty2);
if (tmin > tmax) //线段没相交
return false;
if (tmax < 0) //射线起点不在AABB内
return false;
pFar = o + dir * tmax;
if (tmin < 0)
pNear = pFar;
pNear = o + dir * tmin;
return true;
using System;
using UnityEditor;
using UnityEngine;
public class RayRectTest : CollideTestBase
public Transform m_RayEnd; //射线指向位置
public Transform m_Min;
public Transform m_Max;
public Vector2 m_Point1; //交点1
public Vector2 m_Point2; //交点2
private Vector3 m_CubeSize = new Vector3(0.02f, 0.02f, 0.01f);
void Update()
m_IsIntersect = false;
m_Point1 = Vector3.zero;
m_Point2 = Vector3.zero;
if (m_RayEnd && m_Min && m_Max)
var origin = this.transform.position;
var dir = m_RayEnd.position - origin;
dir.Normalize();
var t1 = DateTime.Now;
switch (m_ApiType)
case 1:
for (int i = 0; i < m_InvokeCount; ++i)
m_IsIntersect = Shape2DHelper.IsRayRectIntersect(origin, dir, m_Min.position, m_Max.position, out m_Point1, out m_Point2);
break;
CheckTimeCost(t1, 1);
private void OnDrawGizmos()
if (m_RayEnd && m_Min && m_Max)
var origin = this.transform.position;
origin.z = 0;
var endPoint = m_RayEnd.position;
endPoint.z = 0;
if (m_IsIntersect)
Gizmos.color = Color.red;
Gizmos.DrawLine(origin, endPoint);
DrawRect(m_Min.position, m_Max.position);
Gizmos.color = Color.green;
DrawPoint(m_Point1, ref m_CubeSize);
DrawPoint(m_Point2, ref m_CubeSize);
Gizmos.color = Color.white;
Gizmos.DrawLine(origin, endPoint);
DrawRect(m_Min.position, m_Max.position);
private static void DrawRect(Vector2 min, Vector2 max)
var leftTop = new Vector2(min.x, max.y);
var rightBottom = new Vector2(max.x, min.y);
Gizmos.DrawLine(min, leftTop);
Gizmos.DrawLine(leftTop, max);
Gizmos.DrawLine(max, rightBottom);
Gizmos.DrawLine(rightBottom, min);
private static void DrawPoint(Vector2 point, ref Vector3 pointCubeSize)
float handleSize = HandleUtility.GetHandleSize(point) * 0.1f;
pointCubeSize.Set(handleSize, handleSize, 0.01f);
Gizmos.DrawCube(point, pointCubeSize);
检测射线与矩形相交_射线是否穿过矩形-CSDN博客
射线与AABB相交检测-CSDN博客
相交的情况参考