news 2026/4/17 21:18:17

游戏开发实战:用分离轴定理(SAT)搞定Unity 2D碰撞检测(附C#代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
游戏开发实战:用分离轴定理(SAT)搞定Unity 2D碰撞检测(附C#代码)

游戏开发实战:用分离轴定理(SAT)搞定Unity 2D碰撞检测(附C#代码)

在2D游戏开发中,碰撞检测是构建沉浸式体验的核心技术之一。无论是角色与环境的互动、子弹命中判定,还是物理模拟的精确性,都依赖于高效可靠的碰撞系统。Unity引擎虽然提供了基础的碰撞组件,但在处理复杂形状或需要更高性能的场景时,开发者往往需要自己实现底层算法。本文将深入探讨分离轴定理(SAT)这一专业级碰撞检测技术,通过可落地的C#代码示例,帮助开发者掌握从理论到实践的完整实现路径。

1. 为什么需要SAT:超越Unity原生碰撞系统

Unity内置的2D碰撞系统(如BoxCollider2D和CircleCollider2D)对于简单场景已经足够,但在以下场景会显现局限性:

  • 旋转物体精度问题:当矩形碰撞体旋转后,Unity实际使用的是轴对齐包围盒(AABB)的近似计算,导致检测区域与实际显示不匹配
  • 复杂形状支持不足:多边形碰撞体(PolygonCollider2D)在顶点较多时性能下降明显
  • 定制化需求:特殊碰撞响应(如穿透检测、滑动处理)需要访问底层算法逻辑
// Unity原生碰撞检测的典型问题示例 void OnCollisionEnter2D(Collision2D col) { // 当两个旋转的矩形相交时,可能无法准确触发 Debug.Log("碰撞发生:" + col.gameObject.name); }

SAT算法相比传统方法的优势:

检测方法精度旋转支持性能适用形状
AABB不支持矩形
圆形检测支持圆形
SAT(本文)支持任意凸多边形
网格检测极高支持任意形状

2. SAT原理精要:数学背后的游戏逻辑

分离轴定理的核心思想可以概括为:如果存在一条直线能够将两个凸多边形完全分开,那么这两个多边形不相交。具体实现时需要三个关键步骤:

  1. 获取潜在分离轴:每个多边形的每条边的法线都是候选轴
  2. 投影计算:将两个多边形的所有顶点投影到当前检测轴上
  3. 重叠判断:检查两个投影区间是否有重叠
// 向量结构体定义(简化版) public struct Vector2 { public float x, y; public static float Dot(Vector2 a, Vector2 b) { return a.x * b.x + a.y * b.y; } public Vector2 Normalize() { float mag = Mathf.Sqrt(x * x + y * y); return new Vector2(x / mag, y / mag); } public Vector2 Perpendicular() { return new Vector2(y, -x); // 获得法线向量 } }

重要提示:SAT仅适用于凸多边形。如果使用凹多边形,需要先进行三角剖分或凸分解处理。

3. 实战实现:从OBB检测到完整SAT系统

3.1 OBB碰撞检测实现

定向包围盒(OBB)是SAT最典型的应用场景,以下是完整的C#实现:

public class OBB { public Vector2 center; public Vector2[] axes = new Vector2[2]; // 本地坐标轴 public Vector2[] extents = new Vector2[2]; // 半长向量 public bool Intersects(OBB other) { // 检测4条可能的分离轴 if (!CheckAxis(axes[0], other)) return false; if (!CheckAxis(axes[1], other)) return false; if (!CheckAxis(other.axes[0], other)) return false; if (!CheckAxis(other.axes[1], other)) return false; return true; } private bool CheckAxis(Vector2 axis, OBB other) { axis = axis.Normalize(); // 计算当前OBB在轴上的投影半径 float r1 = Mathf.Abs(Vector2.Dot(extents[0], axis)) + Mathf.Abs(Vector2.Dot(extents[1], axis)); // 计算另一个OBB在轴上的投影半径 float r2 = Mathf.Abs(Vector2.Dot(other.extents[0], axis)) + Mathf.Abs(Vector2.Dot(other.extents[1], axis)); // 计算中心点连线在轴上的投影 Vector2 centerVec = center - other.center; float distance = Mathf.Abs(Vector2.Dot(centerVec, axis)); return distance <= (r1 + r2); } }

3.2 通用凸多边形检测

扩展上述基础实现,处理任意凸多边形:

public class ConvexPolygon { public Vector2[] vertices; public bool Intersects(ConvexPolygon other) { // 检查当前多边形的所有边 for (int i = 0; i < vertices.Length; i++) { Vector2 edge = vertices[(i + 1) % vertices.Length] - vertices[i]; Vector2 axis = edge.Perpendicular().Normalize(); if (!OverlapOnAxis(axis, other)) return false; } // 检查另一个多边形的所有边 for (int i = 0; i < other.vertices.Length; i++) { Vector2 edge = other.vertices[(i + 1) % other.vertices.Length] - other.vertices[i]; Vector2 axis = edge.Perpendicular().Normalize(); if (!OverlapOnAxis(axis, other)) return false; } return true; } private bool OverlapOnAxis(Vector2 axis, ConvexPolygon other) { float min1 = float.MaxValue, max1 = float.MinValue; float min2 = float.MaxValue, max2 = float.MinValue; // 计算当前多边形在轴上的投影范围 foreach (var vertex in vertices) { float projection = Vector2.Dot(vertex, axis); min1 = Mathf.Min(min1, projection); max1 = Mathf.Max(max1, projection); } // 计算另一个多边形在轴上的投影范围 foreach (var vertex in other.vertices) { float projection = Vector2.Dot(vertex, axis); min2 = Mathf.Min(min2, projection); max2 = Mathf.Max(max2, projection); } // 检查投影是否重叠 return max1 >= min2 && max2 >= min1; } }

4. 性能优化与工程实践

4.1 分层检测策略

在实际游戏中,通常会采用分层检测策略平衡精度与性能:

  1. Broad Phase(粗略检测):

    • 使用空间分区(四叉树/网格)
    • 基于AABB的快速剔除
    • 减少需要精确检测的对象对
  2. Narrow Phase(精确检测):

    • 对通过粗略检测的对象使用SAT
    • 根据形状选择最优算法(圆形用距离检测,矩形用OBB等)
// 四叉树实现示例 public class QuadTree { private Rect bounds; private int maxObjects; private List<Collider> objects; private QuadTree[] nodes; public void Insert(Collider collider) { if (nodes != null) { int index = GetIndex(collider.Bounds); if (index != -1) { nodes[index].Insert(collider); return; } } objects.Add(collider); if (objects.Count > maxObjects && level < MAX_LEVELS) { if (nodes == null) Split(); int i = 0; while (i < objects.Count) { int index = GetIndex(objects[i].Bounds); if (index != -1) { nodes[index].Insert(objects[i]); objects.RemoveAt(i); } else { i++; } } } } }

4.2 常见问题解决方案

  • 穿透问题:在高速移动物体中,添加连续碰撞检测(CCD)
  • 性能热点:对静态物体使用缓存投影计算结果
  • 浮点误差:引入小量容差值(epsilon)进行比较
// 处理高速物体穿透的射线检测法 public class Projectile : MonoBehaviour { public float speed; public LayerMask collisionMask; private Vector2 prevPosition; void Update() { prevPosition = transform.position; transform.Translate(Vector2.right * speed * Time.deltaTime); // 检查移动轨迹上的碰撞 RaycastHit2D hit = Physics2D.Linecast( prevPosition, transform.position, collisionMask); if (hit.collider != null) { OnHit(hit); } } }

5. Unity集成与调试技巧

5.1 可视化调试工具

在Scene视图中实时显示碰撞检测状态:

void OnDrawGizmos() { // 绘制OBB轮廓 Gizmos.color = Intersects(other) ? Color.red : Color.green; Vector2[] corners = GetCorners(); for (int i = 0; i < corners.Length; i++) { int next = (i + 1) % corners.Length; Gizmos.DrawLine(corners[i], corners[next]); } // 绘制当前检测的分离轴 if (debugAxis != Vector2.zero) { Gizmos.color = Color.blue; Vector2 center = (center + other.center) * 0.5f; Gizmos.DrawLine(center, center + debugAxis * 2); } }

5.2 Inspector参数配置

创建自定义编辑器增强工作流:

[CustomEditor(typeof(SATCollider))] public class SATColliderEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); SATCollider collider = (SATCollider)target; if (GUILayout.Button("Update Vertices")) { collider.UpdateVertices(); } EditorGUILayout.LabelField("Debug Info", EditorStyles.boldLabel); EditorGUILayout.Toggle("Colliding", collider.IsColliding); } }

在实现过程中,一个常见的陷阱是忘记归一化分离轴向量,这会导致投影计算错误。另一个实际经验是:对于移动平台,将SAT计算放在Job System中并行处理可以显著提升性能。当处理大量碰撞体时,建议采用对象池模式复用碰撞检测结果数据结构。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 21:14:19

5步彻底释放你的游戏本潜能:开源性能控制工具深度解析

5步彻底释放你的游戏本潜能&#xff1a;开源性能控制工具深度解析 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度&#xff0c;自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub OmenSuperHub是一款专为惠普OMEN游戏本设…

作者头像 李华
网站建设 2026/4/17 21:13:42

STM32H750VB FDCAN高速通信实战配置指南

1. 从传统CAN到FDCAN的升级必要性 如果你用过STM32F103这类经典MCU的CAN外设&#xff0c;第一次接触STM32H750VB的FDCAN时可能会有点懵。就像从功能手机切换到智能手机&#xff0c;虽然都能打电话&#xff0c;但后者带来的体验提升是颠覆性的。FDCAN&#xff08;Flexible Data…

作者头像 李华
网站建设 2026/4/17 20:58:31

软件决策树管理化的选择路径分析

**软件决策树管理化的选择路径分析&#xff1a;智能决策的利器** 在当今数据驱动的时代&#xff0c;企业和管理者常常面临复杂的决策问题。如何高效地分析多种可能性并选择最优路径&#xff1f;软件决策树管理化的选择路径分析提供了一种直观且科学的解决方案。决策树作为一种…

作者头像 李华