我正在研究一个物理引擎,我的想法是有一个Physics :: Object,所有物理对象都应该从中派生出来,它用一个全局的Physics :: System类(它是一个单独的模式)注册自己,这样用户就不会不需要跟踪物理计算中包含哪些对象,只需要调用像Physics :: System :: PerformTimestepCalculation(double dt)这样的函数.
这工作正常,我甚至使用单个派生类Physics :: Circle实现它,它是一个2d圆.我对预测性碰撞检测非常满意,即使我仍然需要优化它.
无论如何,当我开始添加其他原语以包括在计算中时,我遇到了麻烦,例如线. Physics :: System :: PerformTimestepCalculation(double dt)乱七八糟地调用了Object :: GetID()或类似的函数(可能会避免使用dynamic_cast<>),但我觉得很脏.
我做了一点reading并且意识到我的层次结构的元素不是可替代的(即两个圆之间的碰撞在两条线的碰撞之间是非常不同的).
我喜欢我的Physics :: Objects“自我注册”与System类的方式,因此它们会自动包含在计算中,我真的不想丢失它.
必须有一些其他合理的设计路径.我怎样才能更好地重新设计东西,以便不可替代的对象不会妨碍?
编辑FYI:
最后,我已经打破了实体和形状属性,类似于在接受的答案中描述的方式,类似于实体 – 组件 – 系统模型.这意味着我仍然有yuk逻辑“这是一个圆圈还是一条线,是一条线还是一条圆?”,但我不再假装多态在这里帮助我.这也意味着我使用某种工厂,可以同时发生多个计算世界!
解决方法
这是一个支持我的,无可否认的大胆断言的纲要:
Chipmunk – 写在C,足够说.
Box2d – 用C语言编写,这里有一些多态性.有一些形状的层次结构(基类b2Shape)和一些虚函数.尽管如此,抽象漏洞就像一个筛子一样泄漏,你会在整个源代码中找到很多关于叶子类的强制转换.还有一个“联系人”的层次结构,这证明更成功,虽然使用单个虚函数,在没有多态的情况下重写它是很简单的(我相信,花栗鼠使用函数指针). b2Body是用于表示刚体的类,它是非虚拟的.
Bullet – 用C语言编写,用于大量游戏.大量的功能,大量的代码(相对于其他两个).实际上有一个基类,刚体和软体表示扩展,但只有一小部分代码可以使用它.大多数基类的虚函数与序列化(保存/加载引擎状态)有关,剩下的两个虚函数软体无法实现一个TODO通知我们需要清理一些hack.在物理引擎中并不完全是对多态性的认可.
这是很多的话,我甚至没有真正开始回答你的问题.我想要回家的是,多态性不是在现有物理引擎中有效应用的东西.这可能不是因为作者没有“得到”OO.
所以无论如何,我的建议是:你的实体类沟通多态.你不会最终得到100种不可能在以后重构的不同类型,你的物理引擎的形状数据将是相当同质的(凸多边形,盒子,球体等),你的实体数据很可能是甚至更均匀(可能只是开始的刚体).
我觉得你做的另一个错误只是支持一个Physics :: System.能够独立于彼此模拟身体(例如,对于双人游戏)具有实用性,并且最简单的方法是支持多个Physics :: Systems.
考虑到这一点,最简洁的“模式”将是工厂模式.当用户想要创建一个刚体时,他们需要告诉Physics :: System(充当工厂)为他们做这件事,所以在你的Physics :: System中:
// returning a smart pointer would not be unreasonable,but I'm returning a raw pointer for simplicity: rigid_body_t* AddBody( body_params_t const& body_params );
并在客户端代码中:
circle_params_t circle(1.f /*radius*/); body_params_t b( 1.f /*mass*/,&circle /*shape params*/,xform /*system transform*/ ); rigid_body_t* body = physics_system.AddBody( b );
Anyhoo,有种咆哮.希望这是有帮助的.至少我想指向Box2d.它是用一种非常简单的C语言编写的,其中应用的模式将与您的引擎相关,无论是3D还是2D.