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

发布于 2024-11-02 05:10:46 字数 4824 浏览 5 评论 0原文

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

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

万能是对的。我通过传递基数丢失了类型信息。现在基地只是为了捕捉我不关心的东西。

对于我的容器,我认为我可以使用类型列表来收集添加到容器中的每种类型,并使用它来声明然后处理信息的特定于类型的容器。但有一个问题是我不能取出普通的迭代器,这样传入函数对象会更容易。

也许 C++ 0X 可以帮助处理新的未知类型变量。 (我忘记叫什么了)。

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

短叹 2024-11-09 05:10:47

也许是这样的? (只有hit部分,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);
    }
};

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);
    }
};
幽蝶幻影 2024-11-09 05:10:47

好的。这就是我的结论。

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 类型可以是任何对象,因此无论您是否想要继承或如何使用它。我必须为 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;
        }
    }
};

在代码中的某个地方使用它。

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

    Collision(ballC, boundC);

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

感谢 Omnifarious 和 Giovanni 帮助我解决这个问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文