Shape Collision C ++的设计模式

时间:2013-12-06 12:13:10

标签: c++ design-patterns inheritance

因此,我正在研究从Shape继承的对象(圆形,矩形等)的最佳设计。我目前正在使用动态调度,但是在添加新形状时需要进行大量修改。理想情况下,我喜欢一些类似的东西,这样当我添加一个新形状时,我不必花费一个年龄来更新多个文件。

public class Shape() {
    public:
    virtual bool detectCollision(Shape *shape);
};

然后让其他类能够以不同的方式派生出来,IE

public class Circle : public Shape {
    public:
    bool detectCollision(Square *square);
    bool detectCollision(Circle *circle);
};

如果可能的话,我希望避免使用长if/else个语句,并且不要认为应该使用某种形式的Shapedynamic_casting内完成此操作的逻辑,理想情况下Shape不知道继承它的是什么。

是否有适合这些需求的解决方案?我听说过Templates,但我不确定如何在这里实现,是否有一个人可以做的例子,也许?

3 个答案:

答案 0 :(得分:3)

我认为您无法绕过专门的算法来检测/改进碰撞检测。有一些形状A和B你需要一个知道两种类型的函数来应用正确的算法。这导致:

class Shape() {
    public:
    // Not virtual!
    // Avoiding pointers.
    bool detectCollision(const Shape&) const;
};

bool Shape::detectCollision(const Shape& other)  const {
   if(is_rectangle(*this) && is_rectangle(other)) 
       return rectangle_rectangle_collision(*this, other); 
       // Where rectangle_rectangle_collision is a local function
       // in a source file 

       // and so on ...
}

概括它,你可以有一个碰撞函数的映射,在Shape中注册新的算法,并在detectCollision中选择一个专门的或通用的(基于多边形)。

你可能很想使用模板进行碰撞检测,但是你的动态多态性是没用的(除非你发送)。

答案 1 :(得分:3)

嗨托马斯,谢谢你的提问,

一般情况下使用dynamic_cast会更糟。大多数类型特定的方法调用应该在编译期间而不是运行时解决。因此,您必须使用RTTI,浪费性能,并且您的代码不会像编译器中的检查类型安全那样容易出错。

class Shape
{
public:
  bool colidesWithOtherShape( Shape other ) { return CollisionManager::areShapesCollding( *this, other );  }
}

class CollisionManager
{
public:
  template< typename _T, typename _N >
  static bool areShapesColliding( _T, _N );
};

template <>
bool CollisionManager::areShapesColliding( Circle c, Circle c2 )
{
  return Point::distance( c.getMidPoint(), c.getMidPoint() ) < c.getRadius() + c2.getRadius(); 
}

这样你的代码是类型安全的,如果你想注意你的命名空间,你可以在一个或多个文件中实现所有的碰撞检测,这可能会导致方法查找一些奇怪的行为。

如果多个Shapes将具有特定Shape的所有方法映射为同一Method的第二个参数,并且形状为第一个参数。

template< typename _T >
bool CollisionManager::areShapesColliding( _T shape, Circle c )
{
  return CollisionManager::areShapesColliding( c, shape );
}

现在只需从Circle实现所有其他形状的碰撞检测。 IE浏览器。对于轴对齐的矩形:

template<>
bool CollisionManager::areShapesColliding( Circle circle, Rectangle rectangle )
{
  Point closestPointToCircle = circle.midPoint();

  if( closestPointToCircle.x() > rectangle.right() ) closestPointToCircle.setX( rectangle.right() );
  else if( closestPointToCircle.x() < rectangle.left() ) closestPointToCircle.setX( rectangle.left() );

  if( closestPointToCircle.y() > rectangle.bottom() ) closestPointToCircle.setY( rectangle.bottom() );
  else if( closestPointToCircle.y() < rectangle.top() ) closestPointToCircle.setY( rectangle.top() );

  return point_distance( closestPointToCircle, circle.midPoint() ) <= circle.radiant();
}

template<>
bool CollisionManager::areShapesColliding( Rectangle rect1, Rectangle rect2 )
{
  Point distancePoint = rect1.getMidPoint() - rect2.getMidPoint();
  unsigned long xAxisDistance = abs( distancePoint.x() );
  unsigned long yAxisDistance = abs( distancePoint.y() );

  return xAxisDistance <= ( rect1.width() + rect2.width() ) / 2 &&
         yAxisDistance <= ( rect1.heigth() + rect2.height() ) / 2;
}

如果您使用C ++ 11(-std = c ++ 11 Linkeflag),您可以在编译时检测未实现的碰撞检测。

template< typename _T, typename _N >
bool CollisionManager::areShapesColliding( )
{
  static_assert( false, "your assert" );
}

对于运行时解决方案,您需要在运行时解决类型。因为我的时间不多了这里只是一个例子:

template<>
bool CollisionManager::areShapesColliding( Shape* shape1, Shape* shape2 )
{
  if( ( Circle* c1 = dynamic_cast<Circle*>( shape1 ) ) != nullptr )
  {
    if( ( Circle* c2 = dynamic_cast<Circle*> ) != nullptr )
    {
      return CollisionManager::areShapesColliding( *c1, *c2 );
    }
    else if( ( Square* s2 = dynamic_cast< Square* >( shape2 ) ) != nullptr )
    {
      return CollisionManager::areShapesColliding( *c1, *s2 );
    }
  }
  //else... if first is square...
  return false; //no matching type found
}

这不是一个很好的解决方案,但它可以解决您的问题并且不会在您的形状中弄乱您的代码。

我希望我能帮助你。如有其他问题,请告诉我。

迈克尔

答案 2 :(得分:2)

首先需要确定是否需要在编译时知道对象的类型。即,这样的方法:

public class Shape{
    public:
    virtual bool detectCollision(Square *square);
    virtual bool detectCollision(Circle *circle);
};

Shape* shape1,shape2;
shape1 = GetNewCircleOrSquareByRandom();
shape2 = GetNewCircleOrSquareByRandom();
shape1->detectCollision(shape2);

不起作用,因为编译器不会(也不能)知道哪个对象实际存在。

正如@linuxfever指出的那样,这是double dispatch的一个已知问题。维基百科文章中提供了一个解决方案(与您的非常相似)。

但是,如果您知道什么时候与编译时发生冲突,则可以使用模板,正如您所猜测的那样:

// In header
template<class _T1, class _T2> bool detectCollision(_T1* object1, _T2* object2) {
    // Generic implementation
    // Or just an #error "Sorry! Don't know how to collide those two objects"
}

template<> bool detectCollision(Square* object1, Circle* object2) {
    // Specific implementaiton of Square vs. Circle
}

template<> bool detectCollision(Circle* object1, Square* object2) {
    return detectCollision(object2,object1);
}
// etc