形状碰撞C++的设计模式
Design pattern for Shape Collision C++
所以我目前正在研究从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
,但我不确定我将如何在这里实现它,也许有人可以举一个例子吗?
你好,托马斯,谢谢你的问题,
一般来说,使用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();
}
这样,您的代码是类型安全的,如果您愿意,所有的冲突检测都可以在一个或多个文件中实现,但请注意您的命名空间,这可能会导致方法查找中出现一些奇怪的行为。
如果有多个形状,则将具有特定形状作为第二个参数的所有方法映射到具有该形状作为第一个参数的同一方法。
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
}
这不是一个很好的解决方案,但它封装了你的问题,不会把你的代码弄乱。
我希望我能帮助你。还有什么问题,请告诉我。
Michael
您需要首先决定是否需要在编译时知道对象的类型。也就是说,这种方法:
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所指出的,这是一个已知的双重调度问题。维基百科文章中的链接提供了一个解决方案(与您的非常相似)。
然而,如果你知道什么与编译时的内容冲突,你可以使用模板,正如你正确猜测的那样:
// 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
我认为您无法绕过专门的算法来检测/改进碰撞检测。对于一些形状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中选择一个专门或通用的算法(基于多边形)。
您可能会想使用模板进行冲突检测,但您的模板在动态多态性中是无用的(除非您调度)。
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 资源管理设计模式
- 用于在回调中调用解析器的设计模式
- 设计帮助 - 为不同类型的消息处理通用接口的设计模式
- 在这种情况下我应该使用哪种设计模式
- C++中物体改变识别的设计模式?
- 确保所有构造函数调用相同的函数 c++ 设计模式
- 需要实例化不同类/对象并在启动时确定的硬件插槽的设计模式
- 设计模式,以避免不必要地添加抽象函数以适应新功能
- 工厂设计模式优化
- 使用C++模板的数据映射器设计模式
- 为什么以及如何使用原型设计模式
- 具有多个继承共享一个资源的对象 - 寻找良好的设计模式
- 在C++中创建观察器设计模式的好方法
- 现代C++在多大程度上消除了对设计模式的需求?
- 对于存储另一个类所需信息的类,例如其构造,是否有设计模式?
- 下面抽象工厂设计模式的实现是正确的吗
- sql记录集函数的状态设计模式
- 是否有可以处理方法调用依赖关系的设计模式?
- 使用 C++ 设计模式