Tav*_*son 5 c++ templates double-dispatch game-physics
我想让编译器为物理碰撞系统构建函数的连接.我有测试碰撞功能:
template <typename T, typename U>
inline void Collision(T& t, U& u)
{
if(u.CheckCollision(t.GetCollider()))
{
u.HitBy(t);
t.Hit(u);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我使用碰撞对象来保持碰撞对象.
template <typename T>
class Collidable
{
T m_Collider;
VictimEffect m_HitBy;
MenaceEffect m_Hit;
public:
void SetCollider(const T& t) { m_Collider = t; }
void SetVictim(VictimEffect effect) { m_HitBy = effect; }
void SetMenace(MenaceEffect effect) { m_Hit = effect; }
T& GetCollider()
{
return m_Collider;
}
template <typename V>
void HitBy(V& menace)
{
m_HitBy.HitBy(menace.GetCollider());
}
template <typename V>
void Hit(V& victim)
{
m_Hit.Hit(victim.GetCollider());
}
template <typename V>
bool CheckCollision(V& menace)
{
return m_Collider.CheckCollision(menace);
}
};
Run Code Online (Sandbox Code Playgroud)
我为效果函数添加了一个间接级别
class VictimEffect
{
public:
template<typename C>
void HitBy(C&)
{;}
};
class MenaceEffect
{
public:
template<typename C>
void Hit(C&)
{;}
};
Run Code Online (Sandbox Code Playgroud)
我想要最终得到的是一种在具有正确功能签名的任何对象中调用函数的方法,但不需要不会命中的类型.
class Wall;
class Ball : public MenaceEffect
{
public:
void Hit(Wall& wall);
void Hit(Ball& ball);
};
class Wall : public VictimEffect
{
public:
void HitBy(Ball& ball);
};
Run Code Online (Sandbox Code Playgroud)
请注意,我不想让墙壁碰到墙壁.我只想编写或提供应该发生的交互的函数.我知道这不起作用,因为VictimEffect没有"看到"派生函数.
我想发送而不必在任何地方指定模板参数.我想构建除可碰撞之外的可碰撞效果并将其一般地提供给可碰撞物体.我不希望为每个可能碰撞的东西集合都有不同的可碰撞对象.基本上我想要
vector<collidable> movingThings;
vector<collidable> staticThings;
// get iterators ...
Collide(Moving, Static);
Run Code Online (Sandbox Code Playgroud)
我需要更多的间接层,但我看不到它.我知道可以这样做,因为所有信息都可以在编译时获得.不需要动态多态解决方案.
任何帮助是极大的赞赏!
更新 我想要做的是建立我自己的现代(如安德烈的书)游戏库.到目前为止,我已经完成了大部分的关系,但是这个让我很难过.这不是时间紧迫的问题,而是结果关键问题.我已经在游戏中工作了很多年,我不喜欢我曾经做过的任何游戏引擎.这个意味着易于使用且易于扩展.它现在可以在任何平台上运行,因为控制台编译器并不可怕.
这是我想要写的一个简单的例子.
int main()
{
// System must come first
System system(640, 480, false);
Renderer renderer;
Mouse mouse;
Keyboard keys;
// Make the ball
Bounce ball;
Sprite ballSprite("02.bmp");
CollisionBox ballPhysics;
BallCommand ballBehavior;
ball.SpriteMover = boost::bind(&Sprite::Observer<Pos2D>, &ballSprite, _1);
ball.PhysicsMover = boost::bind(&CollisionBox::Move, &ballPhysics, _1);
// Make the bounds
Boundary bounds;
// Set up Physics
Collidable<Boundary> boundC;
boundC.SetCollider(bounds);
boundC.SetVictim(ballBehavior);
Collidable<CollisionBox> ballC;
ballC.SetCollider(ballPhysics);
PretendPhysics physics;
physics.SetBall(ballC);
physics.SetBounds(boundC);
while(!mouse.LeftClick() && !keys.AnyKey())
{
ball.MoveRel(ballBehavior.Delta());
physics.Update();
renderer.Clear();
renderer.Render(ballSprite);
renderer.SwapBuffers();
}
}
Run Code Online (Sandbox Code Playgroud)
所有的物理学都是WIP,并不是我希望它最终结束的地方,但它已经到了那里.System对象初始化Windows和特定于平台的渲染器.在这种情况下,它是openGL.虽然它被编译为控制台应用程序,但它会创建所有窗口组件.我不希望人们处理main()vs winmain().除物理学外,没有遗传.在我将场景放入系统后,三个渲染调用将替换为一个.我打算使用我写的一些东西,结合Gary Powell和Martin Weiser的View模板库.
我知道我只是没有正确看待它.访客可以帮助改变我的方法.
这就是我想要做的.
更新2 确定.到目前为止,我从评论和答案中得到了一些灵感,这就是我去过的地方,但我还没有完成.
template <typename T, typename V = VictimEffect, typename M = MenaceEffect>
class Collidable
{
T m_Collider;
V m_HitBy;
M m_Hit;
public:
Collidable(T collide, V victim, M menace) : m_Collider(collide), m_HitBy(victim), m_Hit(menace) {;}
Collidable(T collide) : m_Collider(collide) {;}
Collidable(T collide, V victim) : m_Collider(collide), m_HitBy(victim) {;}
T& GetCollider()
{
return m_Collider;
}
template <typename V>
void HitBy(V& menace)
{
m_HitBy.HitBy(menace.GetCollider());
}
template <typename V>
void Hit(V& victim)
{
m_Hit.Hit(victim.GetCollider());
}
template <typename V>
bool CheckCollision(V& menace)
{
return m_Collider.CheckCollision(menace);
}
};
Run Code Online (Sandbox Code Playgroud)
然后使用它我这样做
Collidable<Boundary, BallCommand> boundC(bounds, ballBehavior);
Collidable<CollisionBox> ballC(ballPhysics);
Run Code Online (Sandbox Code Playgroud)
Omnifarious是对的.我通过传入基地丢失了类型信息.现在基地只是为了捕捉我不关心的事情.
对于我的容器,我想我可以使用类型列表来收集添加到容器中的每个类型,并使用它来声明特定于类型的容器,然后处理信息.但有一个问题是我无法使用普通的迭代器,因为它更容易传递函数对象.
也许C++ 0X可以帮助处理新的未知类型变量.(我忘了它的名字).
好的。这就是我的结论。
class VictimEffect
{
public:
template<typename C>
void HitBy(C&)
{;}
};
class MenaceEffect
{
public:
template<typename C>
void Hit(C&)
{;}
};
template <typename T, typename M = MenaceEffect, typename V = VictimEffect>
class Collidable
{
T m_Collider;
M m_WeHit;
V m_WeWereHit;
public:
Collidable(T collide, M menaceEffect, V victimEffect) : m_Collider(collide), m_WeHit(menaceEffect), m_WeWereHit(victimEffect) {;}
Collidable(T collide) : m_Collider(collide) {;}
Collidable(T collide, M menaceEffect) : m_Collider(collide), m_WeHit(menaceEffect) {;}
T& GetCollider()
{
return m_Collider;
}
template <typename V>
void HitBy(V& menace)
{
m_WeWereHit.HitBy(menace.GetCollider());
}
template <typename V>
void Hit(V& victim)
{
m_WeHit.Hit(victim.GetCollider());
}
template <typename V>
bool CheckCollision(V& menace)
{
return m_Collider.CheckCollision(menace);
}
};
template <typename T, typename U>
inline void Collision(T& t, U& u)
{
if(u.CheckCollision(t.GetCollider()))
{
u.HitBy(t);
t.Hit(u);
}
}
Run Code Online (Sandbox Code Playgroud)
没有必要继承受害者或威胁效果。如果您想要包罗万象的解决方案,它们就在那里。M 和 V 类型可以是任何对象,因此无论您是否想要继承或如何使用它。我必须为 ptr 等创建所有变体。如果所有使用对象都继承自基类,那么它可以像用动态多态性编写的一样使用。如果您不想继承而是保留特殊的单独列表,您也可以这样做。
使用起来很简单
class Boundary
{
// Put whatever code here.
bool CheckCollision(CollisionBox bounce)
};
class BallCommand
{
public:
void Hit(Boundary& bounds)
{
if(bounds.XHit())
{
xDir *= -1;
}
if(bounds.YHit())
{
yDir *= -1;
}
}
};
Run Code Online (Sandbox Code Playgroud)
您在代码中的某个地方使用了它。
Collidable<Boundary> boundC(bounds);
Collidable<std::shared_ptr<CollisionBox>, BallCommand&> ballC(ballPhysics, ballBehavior);
Collision(ballC, boundC);
Run Code Online (Sandbox Code Playgroud)
还有更多的清理工作要做,但我认为这是一个很好的开始。我将转换为 std::function 以使其更容易。
感谢 Omnifarious 和 Giovanni 帮助我解决这个问题。