因此,我正在研究从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
个语句,并且不要认为应该使用某种形式的Shape
在dynamic_casting
内完成此操作的逻辑,理想情况下Shape
不知道继承它的是什么。
是否有适合这些需求的解决方案?我听说过Templates
,但我不确定如何在这里实现,是否有一个人可以做的例子,也许?
答案 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