5 Stimmen

Versuch, Vorlagen zu verwenden, um Physik-Kollisionen doppelt zu versenden

Ich möchte, dass der Compiler die Verbindungen der Funktionen für ein Physik-Kollisionssystem erstellt. Ich habe die Funktion test collision:

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

Dann verwende ich ein Collider-Objekt, um das Kollisionsobjekt zu halten.

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

Ich habe eine indirekte Ebene für die Effektfunktionen hinzugefügt

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

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

Was ich am Ende mit ist ein Weg, um Funktionen in jedem Objekt, das die richtige Funktionssignatur hat, aber nicht erfordern Typen, die nicht getroffen werden aufrufen.

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

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

Beachten Sie, dass ich keine Funktion für das Auftreffen einer Wand auf eine Wand haben möchte. Ich möchte nur Funktionen für Interaktionen schreiben oder bereitstellen, die stattfinden sollen. Ich weiß, dass dies nicht funktioniert, weil der VictimEffect die abgeleitete Funktion nicht "sieht".

Ich möchte den Versand durchführen, ohne die Vorlagenparameter überall angeben zu müssen, wo sie verwendet werden. Ich möchte meinen kollidierbaren Effekt unabhängig von collidable erstellen und ihn dem collidable-Objekt generisch übergeben. Ich möchte nicht für jede Sammlung von Dingen, mit denen man kollidieren kann, ein anderes kollidierbares Objekt haben. Im Grunde möchte ich

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

Ich brauche hier mehr indirekte Ebenen, aber ich kann sie einfach nicht sehen. Ich weiß, dass es möglich ist, weil alle Informationen zur Kompilierzeit verfügbar sind. Es besteht keine Notwendigkeit für eine dynamische polymorphe Lösung.

Jede Hilfe ist sehr willkommen!

更新情報 Ich versuche, meine eigene moderne (wie in Andrei's Buch) Spielebibliothek aufzubauen. Bis jetzt habe ich die meisten Beziehungen geklärt, aber bei diesem Spiel bin ich ratlos. Dies ist kein zeitkritisches Problem, sondern eher ein ergebniskritisches Problem. Ich arbeite seit vielen, vielen Jahren an Spielen und ich mag keine der Spiel-Engines, an denen ich gearbeitet habe. Diese Engine soll einfach zu benutzen und leicht zu erweitern sein. Sie wird auf jeder Plattform funktionieren, da die Konsolen-Compiler nicht mehr so schlecht sind.

Hier ist ein einfaches Beispiel dafür, was ich schreiben können möchte.

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

Die gesamte Physik ist WIP und ist nicht dort, wo ich sie haben möchte, aber sie ist auf dem Weg dahin. Das System-Objekt initialisiert Windows und den plattformspezifischen Renderer. In diesem Fall ist es OpenGL. Es erstellt alle Windows-Komponenten, obwohl es als Konsolenanwendung kompiliert ist. Ich wollte nicht, dass sich die Leute mit main() vs winmain() beschäftigen. Außer bei Physics gibt es keine Vererbung. Die drei Render-Aufrufe werden durch einen ersetzt, sobald ich Scenes in das System integriert habe. Ich habe vor, etwas zu verwenden, das ich vor einiger Zeit geschrieben habe, kombiniert mit der View Template Library von Gary Powell und Martin Weiser.

Ich weiß, dass ich es einfach nicht richtig anschaue. Der Besucher könnte mir helfen, meinen Ansatz zu ändern.

Das ist es, was ich zu tun versuche.

Aktualisierung 2 GUT. Ich habe mich von den Kommentaren und Antworten inspirieren lassen und bin zu diesem Punkt gekommen, aber ich bin noch nicht fertig.

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

Um sie zu verwenden, muss ich dann Folgendes tun

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

Omnifarious hatte Recht. Ich habe die Typinformationen verloren, indem ich die Basis einfügte. Jetzt ist die Basis nur noch dazu da, um Dinge aufzufangen, die mir egal sind.

Ich denke, dass ich für meinen Container eine Typenliste verwenden kann, um jeden Typ, der dem Container hinzugefügt wird, zu sammeln, und diese verwenden kann, um typspezifische Container zu deklarieren, die dann Informationen verarbeiten. Ein Haken ist allerdings, dass ich keine normalen Iteratoren verwenden kann, es wäre einfacher, Funktionsobjekte zu übergeben.

vielleicht kann C++ 0X bei dieser neuen Variablen unbekannten Typs helfen. (Ich habe vergessen, wie sie heißt).

2voto

Giovanni Funchal Punkte 8569

Vielleicht so etwas wie das hier? (nur der Trefferteil, das hitBy ist symmetrisch)

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

1voto

Tavison Punkte 1475

OK. Das ist mein Ergebnis.

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

Es ist nicht notwendig, von den Effekten "Opfer" oder "Bedrohung" zu erben. Sie sind da, wenn man Auffanglösungen will. Die M- und V-Typen können jedes beliebige Objekt sein, also ob man sie erben will oder nicht oder wie auch immer man sie verwenden will. Ich muss alle Varianten für ptrs usw. erstellen. Wenn alle verwendenden Objekte von Basisklassen erben, kann es so verwendet werden, als ob es mit dynamischer Polymorphie geschrieben wurde. Wenn Sie nicht erben wollen und stattdessen spezielle separate Listen führen wollen, können Sie auch das tun.

Die Benutzung ist einfach

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

Irgendwo in Ihrem Code verwenden Sie es.

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

    Collision(ballC, boundC);

Es muss noch mehr aufgeräumt werden, aber ich denke, das ist ein ziemlich guter Anfang. Ich werde zu std::function konvertieren, um es noch einfacher zu machen.

Danke an Omnifarious und Giovanni, die mir geholfen haben, darüber nachzudenken.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X