试图使用模板来双重调度物理碰撞

时间:2011-04-19 05:32:24

标签: c++ templates game-physics double-dispatch

我想让编译器构建物理碰撞系统的函数连接。我有测试碰撞功能:

template <typename T, typename U>
inline void Collision(T& t, U& u)
{
    if(u.CheckCollision(t.GetCollider()))
    {
        u.HitBy(t);
        t.Hit(u);
    }
}

然后我使用碰撞对象来保持碰撞对象。

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);
    }
};

我为效果函数添加了一个间接级别

class VictimEffect
{
public:
    template<typename C>
    void HitBy(C&)
    {;}
};

class MenaceEffect
{
public:
    template<typename C>
    void Hit(C&)
    {;}
};

我最终想要的是一种在具有正确功能签名的任何对象中调用函数的方法,但不需要不会命中的类型。

class Wall;
class Ball : public MenaceEffect
{
public:
    void Hit(Wall& wall);
    void Hit(Ball& ball);
};

class Wall : public VictimEffect
{
public:
    void HitBy(Ball& ball);
};

注意我不想让墙壁碰到墙壁的功能。我只想编写或提供应该发生的交互的函数。我知道这不起作用,因为VictimEffect没有“看到”派生函数。

我想发送而不必在任何地方指定模板参数。我想构建除可碰撞之外的可碰撞效果并将其一般地提供给可碰撞物体。我不希望每个可能碰撞的东西都有不同的可碰撞对象。基本上我想要

vector<collidable> movingThings;
vector<collidable> staticThings;
// get iterators ...
Collide(Moving, Static);

我需要更多的间接层,但我看不到它。我知道可以这样做,因为所有信息都可以在编译时获得。不需要动态多态解决方案。

非常感谢任何帮助!

更新 我想要做的是建立我自己的现代(如安德烈的书)游戏库。到目前为止,我已经解决了大部分关系,但是这个关系让我很难过。这不是时间紧迫的问题,而是结果关键问题。我已经在游戏中工作了很多年,我不喜欢我曾经做过的任何游戏引擎。这个意味着易于使用且易于扩展。它现在可以在任何平台上运行,因为控制台编译器并不可怕。

这是我想要写的一个简单例子。

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();
    }
}

所有物理学都是WIP,并不是我希望它最终结束的地方,但它已经到了那里。 System对象初始化Windows和特定于平台的渲染器。在这种情况下,它是openGL。虽然它被编译为控制台应用程序,但它会创建所有Windows组件。我不希望人们处理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);
    }
};

然后使用它我这样做

    Collidable<Boundary, BallCommand> boundC(bounds, ballBehavior);
    Collidable<CollisionBox> ballC(ballPhysics);

Omnifarious是对的。我通过传入基地丢失了类型信息。现在基地只是为了捕捉我不关心的事情。

对于我的容器,我想我可以使用类型列表来收集添加到容器中的每个类型,并使用它来声明特定于类型的容器,然后处理信息。但有一个问题是我无法使用普通的迭代器,因此更容易传递函数对象。

也许C ++ 0X可以帮助处理这个新的未知类型变量。 (我忘记了它的名字)。

2 个答案:

答案 0 :(得分:2)

也许是这样的? (只有命中部分,hitBy是对称的)

class Wall;

class MenaceBase {
    public:
    template<typename T>
    void hit(T& t) {
        t.hitImpl(*this);
    }
};

class VictimBase {
    public:
};

template<typename M>
class Menace {
    public:
    virtual void hit(M&) = 0;
};

template<typename V>
class Victim {
    public:
    virtual void hitBy(V&) = 0;
};

class Ball : public MenaceBase, public Menace<Wall>, public Menace<Ball> {
    public:
    void hit(Wall& wall) {}
    void hit(Ball& ball) {}
};

class Wall : public VictimBase, public Victim<Ball> {
    public:
    void hitBy(Ball& ball) {}
    void hitImpl(Menace<Wall>& menace) {
        menace.hit(*this);
    }
};

答案 1 :(得分:1)

行。这就是我的结局。

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);
    }
}

没有必要继承受害者或威胁效应。如果你想要大量的解决方案,它们就在那里。 M和V类型可以是任何对象,因此如果您想继承或不想继承,或者您想要使用它。我必须为ptrs等创建所有变体。如果所有使用对象继承自基类,那么它可以像使用动态多态写入一样使用。如果您不想继承而是保留特殊的单独列表,您也可以这样做。

使用它很容易

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;
        }
    }
};

您的代码中的某处使用它。

    Collidable<Boundary> boundC(bounds);
    Collidable<std::shared_ptr<CollisionBox>, BallCommand&> ballC(ballPhysics, ballBehavior);

    Collision(ballC, boundC);

还有更多的清理工作要做,但我认为这是一个非常好的开始。我将转换为std :: function以使其更容易。

感谢Omnifarious和Giovanni帮我思考。