检查形状之间碰撞的设计模式

我使用各种形状进行碰撞检测( RectangleCircleConeRing等)。所有这些形状都是从基础抽象Shape类派生出来的。 我的游戏对象具有Shape类型的属性。

 class GameObject { (...) public Shape CollisionShape { get; set; } } 

在初始化过程中,我决定将为每个对象使用什么形状,例如:

 GameObject person = new GameObject(); person.CollisionShape = new Circle(100); // 100 is radius 

现在,当我想检查两个对象是否相交时,我使用以下类:

 public class IntersectionChecker { public bool Intersect(Shape a, Shape b) { Type aType = a.GetType(); Type bType = b.GetType(); if( aType == typeof(Rectangle) && bType == typeof(Rectangle)) return Intersect(a as Rectangle, b as Rectangle); if( aType == typeof(Rectangle) && bType == typeof(Circle)) return Intersect(a as Rectangle, b as Circle); // etc. etc. All combinations } private bool Intersect(Rectangle a, Rectangle b) { // check intersection between rectangles } } 

所以我的代码看起来像:

 IntersectionChecker ic = new IntersectionCHecker(); bool isIntersection = is.Intersect(personA.CollisionShape, personB.CollisionShape); 

有没有更好的方法来实现我的目标,没有在IntersectionChecker类中进行数十次’if’检查和类型检查?

编辑:

请注意,检查形状A和B之间的交点的方法可用于检查B和A之间的交点。 在许多答案中(感谢您的所有想法!)建议从形状本身而不是IntersectionChecker对象调用交叉检查。 我想这会迫使我重复代码。 现在我可以这样做:

  if( aType == typeof(Rectangle) && bType == typeof(Circle)) return Intersect(a as Rectangle, b as Rectangle); if( aType == typeof(Circle) && bType == typeof(Rectangle)) return Intersect(b as Rectangle, a as Circle); // same method as above 

您可以使用访问者模式 , 这是一个C#示例

这将允许您简单地使用Shape.Intersect(Rectangle),Shape.Intersect(Circle),…每个派生形状实现的方法。 它会阻止您以额外的方法调用为代价对类型进行任何reflection。

编辑 – 这是一个示例实现,如果没有可以在Shape中使用的共享function,那么使用接口IShape可能会更清晰,但我只是停留在一个抽象基类中。

 public class GameObject { private Shape _collisionShape; public GameObject(Shape collisionShape) { _collisionShape = collisionShape; } public bool Intersects(GameObject other) { return _collisionShape.IntersectVisit(other._collisionShape); } } public abstract class Shape { public abstract bool IntersectVisit(Shape other); public abstract bool Intersect(Circle circle); public abstract bool Intersect(Rectangle circle); } public class Circle : Shape { public override bool IntersectVisit(Shape other) { return other.Intersect(this); } public override bool Intersect(Circle circle) { Console.WriteLine("Circle intersecting Circle"); return false; //implement circle to circle collision detection } public override bool Intersect(Rectangle rect) { Console.WriteLine("Circle intersecting Rectangle"); return false; //implement circle to rectangle collision detection } } public class Rectangle : Shape { public override bool IntersectVisit(Shape other) { return other.Intersect(this); } public override bool Intersect(Circle circle) { Console.WriteLine("Rectangle intersecting Circle"); return true; //implement rectangle to circle collision detection } public override bool Intersect(Rectangle rect) { Console.WriteLine("Rectangle intersecting Rectangle"); return true; //implement rectangle to rectangle collision detection } } 

并调用它的示例代码:

 GameObject objectCircle = new GameObject(new Circle()); GameObject objectRect = new GameObject(new Rectangle()); objectCircle.Intersects(objectCircle); objectCircle.Intersects(objectRect); objectRect.Intersects(objectCircle); objectRect.Intersects(objectRect); 

产生输出:

 Circle intersecting Circle Rectangle intersecting Circle Circle intersecting Rectangle Rectangle intersecting Rectangle 

您可以按照Shape类执行碰撞检查,向IntersectsWith(Shape other)添加IntersectsWith(Shape other)方法。 我还建议在IntersectsWith(GameObject other)添加一个IntersectsWith(GameObject other) ,它允许你将CollisionShape保密。

如果检查必须在某处居住。

您可以将Intersects方法添加到Shape

 abstract class Shape { public abstract Boolean Intersects(Shape other); } 

然后将Intersect方法IntersectionChecker public static并为每个具体形状实现Intersects方法,如下所示:

 class Rectangle : Shape { public override Boolean Intersects(Shape other) { if (other is Rectangle) { return IntersectionChecker.Intersect(this, (Rectangle)other); } else if (other is Circle) { return IntersectionChecker.Intersect(this, (Circle)other); } throw new NotSupportedException(); } } 

对于您的问题,没有简单的解决方案。 您需要的是“双重调度”,它仅在Smalltalk或Lisp等语言中受支持。 如果添加一个新类,所有提议的解决方案都将强制您更改所有派生类。 这是糟糕的代码!

我会像这样解决这个问题:在没有任何交集代码的情况下实现Shape派生类。 然后像这样实现一个Intersection类:

 public class Intersection { public bool Intersect(Shape a, Shape b) {....} private bool Intersect(Rectangle a, Circle b) {...} private bool Intersect(Circle a, Circle b) {...} } 

公共方法分析传入的形状并将工作调度( – >双重调度)工作到匹配的私有方法。 其中包含原始交叉逻辑。 Intersect的实现不需要“ifs”。 您可以使用reflection来查找最佳匹配方法。 细节取决于您的确切要求以及如何根据性能来衡量复杂性。 但是,通过简单的直接实现开始很容易。 因为所有内容都封装在一个地方,所以以后很容易优化。 在我看来,这是一个很好的方法。 😉