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

时间:2011-05-28 18:03:14

标签: c# design-patterns

我使用各种形状进行碰撞检测(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

4 个答案:

答案 0 :(得分:12)

您可以使用Visitor Patternhere is a C# example

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

编辑 - 这是一个示例实现,如果没有可以在Shape中使用的共享功能,那么使用接口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

答案 1 :(得分:5)

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

答案 2 :(得分:1)

如果支票无论如何都必须驻留在某处。

您可以向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();
    }
}

答案 3 :(得分:1)

对于您的问题,没有简单的解决方案。您需要的是“双重调度”,它仅在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”。您可以使用反射来查找最佳匹配方法。详细信息取决于您的具体要求以及如何根据性能来衡量复杂性。但是,通过简单的直接实现开始很容易。因为所有内容都封装在一个地方,所以以后很容易优化。在我看来,这是一个很好的方法。 ; - )