2D空间的OBB碰撞实现
OBB全称Oriented bounding box,方向包围盒算法。其表现效果与Unity的BoxCollider并无二致。由于3D空间的OBB需要多考虑一些情况
这里仅关注2D空间下的OBB。
实现效果:
网上有许多OBB的讲解,具体步骤也未必一样,我的做法:
在两个凸多边形中找到一根轴,凸多边形所有在这根轴上的投影点不产生相交,则这两个凸多边形不相交。
这根轴一般取每个边的垂线,逐个投影进行测试。
这里先上一个BOX的版本,如下图:
可以看见在右侧方块的投影轴上,得到了非相交结果
Box版本代码如下:

using UnityEngine; //注意,只有2D空间(XY坐标)下有效 public class Obb : MonoBehaviour { public Vector2 size; public Color gizmosColor = Color.white; private Vector2 P0 => transform.localToWorldMatrix.MultiplyPoint3x4(-size * 0.5f); private Vector2 P1 => transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(size.x * 0.5f, -size.y * 0.5f, 0)); private Vector2 P2 => transform.localToWorldMatrix.MultiplyPoint3x4(size * 0.5f); private Vector2 P3 => transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(-size.x * 0.5f, size.y * 0.5f, 0)); public bool Intersects(Obb other) { Vector2 axis1 = (P1 - P0).normalized; Vector2 axis2 = (P3 - P0).normalized; Vector2 axis3 = (other.P1 - other.P0).normalized; Vector2 axis4 = (other.P3 - other.P0).normalized; if (IsIntersect(this, other, axis1)) return false; if (IsIntersect(this, other, axis2)) return false; if (IsIntersect(this, other, axis3)) return false; if (IsIntersect(this, other, axis4)) return false; return true; } bool IsIntersect(Obb x, Obb y, Vector2 axis) { float xP0 = Vector3.Dot(x.P0, axis); float xP1 = Vector3.Dot(x.P1, axis); float xP2 = Vector3.Dot(x.P2, axis); float xP3 = Vector3.Dot(x.P3, axis); float yP0 = Vector3.Dot(y.P0, axis); float yP1 = Vector3.Dot(y.P1, axis); float yP2 = Vector3.Dot(y.P2, axis); float yP3 = Vector3.Dot(y.P3, axis); float xMin = Mathf.Min(xP0, xP1, xP2, xP3); float xMax = Mathf.Max(xP0, xP1, xP2, xP3); float yMin = Mathf.Min(yP0, yP1, yP2, yP3); float yMax = Mathf.Max(yP0, yP1, yP2, yP3); if (yMin > xMin && yMin < xMax) return false; if (yMax > xMin && yMax < xMax) return false; if (xMin > yMin && xMin < yMax) return false; if (xMax > yMin && xMax < yMax) return false; return true; } private void OnDrawGizmos() { Gizmos.matrix = transform.localToWorldMatrix; Gizmos.color = gizmosColor; Gizmos.DrawWireCube(Vector3.zero, new Vector3(size.x, size.y, 1f)); } }

using System.Collections; using System.Collections.Generic; using UnityEngine; //注意,只有2D空间(XY坐标)下有效 public class Test : MonoBehaviour { public Obb a; public Obb b; private void Update() { bool isIntersects = a.Intersects(b); if (isIntersects) { a.gizmosColor = Color.red; b.gizmosColor = Color.red; } else { a.gizmosColor = Color.white; b.gizmosColor = Color.white; } } }
那么下面是凸多边形的版本,垂线通过叉乘获取:
脚本如下:

using System.Collections; using System.Collections.Generic; using UnityEngine; //凸多边形版本,注意只在2D空间(XY轴)下有效 public class Obb : MonoBehaviour { public Vector2[] points = new Vector2[0]; public int debug_Index; public Color gizmosColor = Color.white; private int mDebug_Index; public bool Intersects(Obb other) { bool isNotIntersect = false; mDebug_Index = 0; for (int i = 1; i <= points.Length; i++) { Vector3 p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]); Vector3 p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]); Vector3 axis = Vector3.Cross((p1 - p0), Vector3.forward).normalized; isNotIntersect |= ProjectionIsNotIntersect(this, other, axis); mDebug_Index++; } return isNotIntersect ? false : true; } private bool ProjectionIsNotIntersect(Obb x, Obb y, Vector2 axis) { float xMin, xMax, yMin, yMax; GetMinMax(x.transform.localToWorldMatrix, x.points, axis, out xMin, out xMax); GetMinMax(y.transform.localToWorldMatrix, y.points, axis, out yMin, out yMax); if (yMin >= xMin && yMin <= xMax) return false; if (yMax >= xMin && yMax <= xMax) return false; if (xMin >= yMin && xMin <= yMax) return false; if (xMax >= yMin && xMax <= yMax) return false; return true; } private void GetMinMax(Matrix4x4 matrix, Vector2[] points, Vector2 projectAxis, out float min, out float max) { min = float.MaxValue; max = float.MinValue; for (int i = 0; i < points.Length; i++) { Vector3 p = matrix.MultiplyPoint3x4(points[i]); float projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis)); if (projectValue > max) max = projectValue; } for (int i = 0; i < points.Length; i++) { Vector3 p = matrix.MultiplyPoint3x4(points[i]); float projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis)); if (projectValue < min) min = projectValue; } } private void OnDrawGizmos() { Gizmos.color = gizmosColor; for (int i = 1; i <= points.Length; i++) { Vector3 p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]); Vector3 p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]); Gizmos.DrawLine(p0, p1); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理