开发者

Trying to use templates to double dispatch physics collision

I want to let the compiler build the connections of functions for a physics collision system. I have the test collision function:

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

Then I use a collider object to hold the collision object.

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

I added a level of indirection for the effect functions

class VictimEffect
{
public:
    template<typename C>
    void HitBy(C&)
    {;}
}开发者_运维百科;

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

What I want to end up with is a way to call functions in any object that has the right function signature, but does not require types that will not hit.

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

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

Notice I do not want to have to have a function for a wall hitting a wall. I want to only write or provide functions for interactions that should happen. I know this does not work because the VictimEffect does not "see" the derived function.

I want to dispatch without having to specify the template parameters everywhere they are used. I want to build my collidable effect apart from collidable and give it to the collidable object generically. I don't want to have different collidable objects for every collection of things that can be collided with. Basically I want

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

I need more layers of indirection here, but I just can't see it. I know it can be done because all of the information is available at compile time. There is no need for a dynamic polymorphic solution.

Any help is greatly appreciated!

Update What I am trying to do is build my own modern (as in Andrei's book) game library. So far I have most of the relationships worked out, but this one has me stumped. This isn't a time critical issue, but rather a results critical issue. I've been working in games for many many years and I don't like any game engine I've worked on. This one is meant to be simple to use and easy to extend. It will work on any platform now that console compilers have gotten not terrible.

Here's a simple example of what I want to be able to write.

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

All the Physics is WIP and is not where I want it to end up, but it is getting there. The System object initializes Windows and the platform specific renderer. In this case it's openGL. It creates all the windows components though it is compiled as a console app. I didn't want people to deal with main() vs winmain(). Except for Physics there is no inheritance. The three render calls will be replaced with one after I get Scenes into the system. I plan on using something I wrote a while back combined with Gary Powell's and Martin Weiser's View template library.

I know I'm just not looking at it right. Visitor could help shift my approach.

So that's what I'm trying to do.

Update 2 OK. I've gotten some inspiration from the comments and answer so far and this is where I went, but I'm not finished yet.

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

Then to use it I do this

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

Omnifarious was right. I was losing the type info by passing in the base. Now the base is there just to catch things I don't care about.

For my container I think I can use a type list to collect every type that is added to the container and use that to declare type specific containers that then process info. One catch though is I cannot take normal iterators out it would be easier to pass function objects in.

maybe C++ 0X can help with that new unknown type variable. (I forget what it's called).


Maybe something like this? (only the hit part, the hitBy is symmetric)

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


OK. Here's what I ended with.

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

It is not necessary to inherit from Victim or Menace effects. They are there if you want catchall solutions. The M and V types can be any object so if you want to inherit or not or however you want to use it. I have to create all the variations for ptrs etc. If all using objects inherit from base classes then it can be used as if it was written with dynamic polymorphism. If you don't want to inherit and instead keep special separate lists you can do that too.

Using it is easy

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

Somewhere in your code you use it.

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

    Collision(ballC, boundC);

There's more cleanup work to do, but I think it's a pretty good start. I'll convert to std::function to make it even easier.

Thanks to Omnifarious and Giovanni for helping me think it out.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜