如何利用两个枚举生成无序唯一值,实现碰撞检测的Switch分支逻辑?
碰撞检测类型匹配的高效可读实现方案
针对你遇到的碰撞检测中(A,B)与(B,A)需要复用同一逻辑的问题,以下是几种C#中实用的实现方案:
方案1:排序枚举值生成唯一键
通过将两个枚举值按大小排序,生成统一的键,确保顺序不影响结果。这种方式简单直接,适合小型碰撞系统。
数值键实现
ColliderType typeA = colliderA.ColliderType; ColliderType typeB = colliderB.ColliderType; int valA = (int)typeA; int valB = (int)typeB; // 确保小值在前,大值在后,生成唯一键 int key = valA <= valB ? (valA * 100 + valB) : (valB * 100 + valA); switch (key) { case (int)ColliderType.Circle * 100 + (int)ColliderType.Circle: return CheckCircleCircle((CircleCollider)colliderA, (CircleCollider)colliderB); case (int)ColliderType.Circle * 100 + (int)ColliderType.Box: // 无需再写Box+Circle的case,因为键是统一的 return CheckCircleBox( colliderA is CircleCollider ? (CircleCollider)colliderA : (CircleCollider)colliderB, colliderA is BoxCollider ? (BoxCollider)colliderA : (BoxCollider)colliderB ); case (int)ColliderType.Box * 100 + (int)ColliderType.Box: return CheckBoxBox((BoxCollider)colliderA, (BoxCollider)colliderB); default: throw new NotSupportedException($"未支持的碰撞类型组合:{typeA} 和 {typeB}"); }
字符串键实现(可读性更强)
var key = typeA <= typeB ? $"{typeA}_{typeB}" : $"{typeB}_{typeA}"; switch (key) { case $"{ColliderType.Circle}_{ColliderType.Circle}": // 执行检测逻辑 break; case $"{ColliderType.Circle}_{ColliderType.Box}": // 执行检测逻辑 break; }
方案2:字典映射类型对到检测方法
用字典存储无序类型对与对应检测方法的映射,扩展性更好,新增碰撞类型只需添加字典条目,无需修改分支判断。
首先定义碰撞检测委托:
public delegate bool CollisionCheck(Collider a, Collider b);
初始化字典(建议在静态构造函数或启动时初始化):
private static readonly Dictionary<(ColliderType, ColliderType), CollisionCheck> _collisionHandlers = new() { { (ColliderType.Circle, ColliderType.Circle), CheckCircleCircle }, { (ColliderType.Circle, ColliderType.Box), CheckCircleBox }, { (ColliderType.Box, ColliderType.Box), CheckBoxBox } };
调用时标准化类型对:
ColliderType typeA = colliderA.ColliderType; ColliderType typeB = colliderB.ColliderType; // 生成有序的类型对,确保(A,B)和(B,A)对应同一个键 var typePair = typeA <= typeB ? (typeA, typeB) : (typeB, typeA); if (_collisionHandlers.TryGetValue(typePair, out var handler)) { return handler(colliderA, colliderB); } throw new NotSupportedException($"未支持的碰撞类型组合:{typeA} 和 {typeB}");
注意:CheckCircleBox方法需要兼容两种输入顺序,内部自动转换为固定顺序处理:
private static bool CheckCircleBox(Collider a, Collider b) { CircleCollider circle = a as CircleCollider ?? (CircleCollider)b; BoxCollider box = a as BoxCollider ?? (BoxCollider)b; // 执行圆-盒碰撞检测逻辑 return Vector2.Distance(circle.Center, box.ClosestPointTo(circle.Center)) <= circle.Radius; }
方案3:双分派模式(面向对象最优解)
利用多态特性实现双分派,让碰撞体自身处理碰撞检测逻辑,完全消除类型判断,符合开闭原则,适合需要频繁扩展的大型系统。
首先定义抽象基类:
public abstract class Collider { public abstract ColliderType ColliderType { get; } public abstract bool CheckCollision(Collider other); // 定义针对每种碰撞体的检测方法 protected abstract bool CheckCircleCollision(CircleCollider circle); protected abstract bool CheckBoxCollision(BoxCollider box); }
实现CircleCollider:
public class CircleCollider : Collider { public Vector2 Center { get; set; } public float Radius { get; set; } public override ColliderType ColliderType => ColliderType.Circle; public override bool CheckCollision(Collider other) { // 调用对方的对应检测方法,实现双分派 return other.CheckCircleCollision(this); } protected override bool CheckCircleCollision(CircleCollider circle) { // 圆-圆碰撞逻辑 return Vector2.Distance(Center, circle.Center) <= Radius + circle.Radius; } protected override bool CheckBoxCollision(BoxCollider box) { // 圆-盒碰撞逻辑 return Vector2.Distance(Center, box.ClosestPointTo(Center)) <= Radius; } }
实现BoxCollider:
public class BoxCollider : Collider { public Vector2 Min { get; set; } public Vector2 Max { get; set; } public override ColliderType ColliderType => ColliderType.Box; public override bool CheckCollision(Collider other) { return other.CheckBoxCollision(this); } protected override bool CheckCircleCollision(CircleCollider circle) { // 复用圆-盒的检测逻辑,因为顺序不影响结果 return circle.CheckBoxCollision(this); } protected override bool CheckBoxCollision(BoxCollider box) { // 盒-盒(AABB)碰撞逻辑 return Min.X <= box.Max.X && Max.X >= box.Min.X && Min.Y <= box.Max.Y && Max.Y >= box.Min.Y; } public Vector2 ClosestPointTo(Vector2 point) { return new Vector2( Math.Clamp(point.X, Min.X, Max.X), Math.Clamp(point.Y, Min.Y, Max.Y) ); } }
调用时只需一行代码:
bool isColliding = colliderA.CheckCollision(colliderB);
关于枚举标记(Flags)的说明
枚举标记([Flags])主要用于表示可组合的状态集合(如权限、选项),并不适合解决无序类型对的匹配问题,因此不推荐用这种方式实现你的需求。
内容的提问来源于stack exchange,提问作者Wolf
相关产品推荐
相关产品推荐

