我正在尝试制作一个用于学术目的的自定义碰撞引擎,但我被困在一个通用的 C++ 编程问题上 我已经拥有所有可以正常工作的几何形状,并且对于问题的范围,我有这个功能:
template<typename lhs_geometry, typename rhs_geometry>
bool intersects( const lhs_geometry& lhs, const rhs_geometry& rhs )
{
//returns true if does objects intersects
//(assume this functions works perfectly with every geometry type)
}
我还需要完成以下类
template<typename geometry_type>
class collidable_object
{
public:
explicit collidable_object( geometry_type& geometry ) :
m_geometry( geometry )
{
}
~collidable_object()
{
}
private:
geometry_type& m_geometry;
};
当我想创建一个 collidable_object
列表并测试它们是否有 2 乘 2 的交集时,我的问题就出现了。
我在 Google 上做了一些研究,发现 collidable_object
的基类允许我将对象存储到列表中。但在那之后,我该如何根据对象的特定几何形状来测试对象?
我曾尝试实现访问者模式,但每次都会卡住,因为我不想对所有可能的几何类型进行硬编码,因为我总是只调用 intersetcs()
。
我还在 cooperative visitor 上找到了一篇文章但这似乎太复杂了。
有没有人有简单有效的解决方案?
编辑:我想避免使用几何图形列表的原因是因为我希望添加新几何图形相对容易,而不必在树状结构中查找文件。
EDIT2:这是关于intersetcs
方法的更多信息:intersects 方法基于标签调度来找到正确的几何形状,但几乎所有凸形都使用 GJK 算法,该算法只要求对象可以返回给定方向上最远的点。对于非凸形状,形状被分割成凸子形状并且过程重新开始。
没有统一的标准来判断 intersects
是否能够处理给定的形状,大多数情况下使用 furthest_along
但 sphere on sphere 不会,sphere agagagations 也不需要使用furthest_along
附加信息:我使用 VS2012 和 C++11
最佳答案
如果不将所有可能的几何图形列表存储在某些 位置,您就无法逃脱。否则编译器将不知道要生成哪些模板实例化。但是我想出了一些代码,您必须在其中仅在一个位置声明该列表,即 GeometryTypes
的 typedef。其他一切都以此为基础。我在这里没有使用访问者模式,这样做的好处是您不必将样板代码添加到不同的几何类实现中。为所有组合实现intersects
就足够了。
首先包括:稍后我将使用 shared_ptr
,打印内容,并在未知几何类型的情况下中止。
#include <memory>
#include <iostream>
#include <cstdlib>
现在定义一些几何图形,使用可用于多态指针的通用基类。你必须至少包含一个虚函数,这样你才能得到一个虚函数表,以后可以用于dynamic_cast
。使析构函数多态化可确保即使通过多态指针删除派生类也能正确清理派生类。
struct Geometry {
virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };
现在是您的intersects
模板。我只为这个演示编写了一个包罗万象的实现。
template<typename lhs_geometry, typename rhs_geometry>
bool intersects(const lhs_geometry& lhs, const rhs_geometry& rhs) {
std::cout << __PRETTY_FUNCTION__ << " called\n"; // gcc-specific?
return false;
}
这是我们声明所有几何图形列表的地方。如果您有相互派生的几何图形,请确保首先拥有最具体的几何图形,因为将按顺序尝试这些几何图形以进行动态转换。
template<typename... Ts> class TypeList { };
typedef TypeList<Circle, Rectangle> GeometryTypes;
现在是一堆辅助代码。基本思想是迭代一个这样的 TypeList
并尝试对每种类型进行动态转换。第一个助手迭代 lhs 参数,第二个助手迭代 rhs 参数。如果未找到匹配项,则说明您的列表不完整,这将导致应用程序中止并显示一条很有用的错误消息。
template<typename TL1, typename TL2> struct IntersectHelper1;
template<typename T1, typename TL2> struct IntersectHelper2;
template<typename TL2, typename T1, typename... Ts>
struct IntersectHelper1<TypeList<T1, Ts...>, TL2> {
static bool isects(Geometry* lhs, Geometry* rhs) {
T1* t1 = dynamic_cast<T1*>(lhs);
if (!t1)
return IntersectHelper1<TypeList<Ts...>, TL2>::isects(lhs, rhs);
else
return IntersectHelper2<T1, TL2>::isects(t1, rhs);
}
};
template<typename T1, typename T2, typename... Ts>
struct IntersectHelper2<T1, TypeList<T2, Ts...>> {
static bool isects(T1* lhs, Geometry* rhs) {
T2* t2 = dynamic_cast<T2*>(rhs);
if (!t2)
return IntersectHelper2<T1, TypeList<Ts...>>::isects(lhs, rhs);
else
return intersects(*lhs, *t2);
}
};
// Catch unknown types, where all dynamic casts failed:
bool unknownIntersects(Geometry* g) {
std::cerr << "Intersection with unknown type: "
<< typeid(*g).name() << std::endl;
std::abort();
return false; // should be irrelevant due to abort
}
template<typename TL2>
struct IntersectHelper1<TypeList<>, TL2> {
static bool isects(Geometry* lhs, Geometry* rhs) {
return unknownIntersects(lhs);
}
};
template<typename T1>
struct IntersectHelper2<T1, TypeList<>> {
static bool isects(T1* lhs, Geometry* rhs) {
return unknownIntersects(rhs);
}
};
有了所有这些助手,您现在可以进行多态相交测试。我正在引入 shared_ptr
来存储此类多态指针,我建议您在 collidable_object
类中也这样做。否则,您必须负责确保只要可碰撞对象还活着,引用的几何图形就会保持活着,但最终会被清理干净。你想要那种责任吗?
typedef std::shared_ptr<Geometry> GeomPtr;
bool intersects(GeomPtr lhs, GeomPtr rhs) {
return IntersectHelper1<GeometryTypes, GeometryTypes>::
isects(lhs.get(), rhs.get());
}
最后是一些 main,这样您就可以在一个小示例中实际运行上述所有代码。
int main() {
GeomPtr g1(new Rectangle), g2(new Circle);
std::cout << intersects(g1, g2) << std::endl;
return 0;
}
关于c++ - 模板对象的类访问者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17181274/