Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] [Fizyka 2D] Fizyka platformówki - problem

Ostatnio zmodyfikowano 2014-01-12 19:34
Autor Wiadomość
kubawal
Temat założony przez niniejszego użytkownika
[SFML] [Fizyka 2D] Fizyka platformówki - problem
» 2014-01-11 18:06:32
Witam!

Pisze platformówkę i mam kłopoty z fizyką. Kolizja góra-dół z platformami działają, ale ludzik, gdy najdzie z boku na platformę, nagle przeskakuje na jej dół lub górę.

Oto fragment fizyki mojego programu:
C/C++
// makra do obsługi wyliczeń bitowych
#define ENUM_ADDVAL(e, val) e = (decltype(e)) ((unsigned)e | (unsigned)(val))
#define ENUM_REMOVEVAL(e, val) e = (decltype(e)) ((unsigned)e & ~((unsigned)(val)))
#define ENUM_ISVAL(e, val) ((unsigned)e & (unsigned) (val))

//...

class Collision
{
public:
   
private:
    FloatRect r1, r2;
    bool coll;
   
    void collision();
   
public:
   
    Collision( FloatRect rr1, FloatRect rr2 );
    Collision( Sprite & t1, Sprite & t2 );
    Collision( Object & ob1, Object & ob2 );
   
    operator bool() const { return coll; }
    FloatRect get1Rect() const { return r1; }
    FloatRect get2Rect() const { return r2; }
   
    void recompute() { collision(); }
};

//...

class Player
{
public:
    //...
   
    class Jump
    {
        float jmpH; // [px]; wysokość
        float jmpG; // [px/s]; grawitacja
        Time cmlT; // czas skumulowany;
        float startY; // [px]; wysokość startowa
        float currY; // [px]; wysokość aktualna
        float relativeY; // [px]; wysokość aktualna; względna do startY; odwócona oś
    public:
        Jump( float starty, float jmph, float gravity = 1200.0f );
       
        void update( Time elapsed );
       
        float getY() const { return currY; }
        float getStartY() const { return startY; }
        float getJmpH() const { return jmpH; }
        float getRelativeY() const { return relativeY; }
    };
   
    class Fall
    {
        float falG; // [px/s]; grawitacja
        Time cmlT; // czas skumulowany
        float startY; // [px]; wysokość startowa
        float currY; // [px]; wysokość aktualna
       
    public:
        Fall( float starty, float gravity = 1200.0f );
       
        void update( Time elapsed );
       
        float getY() const { return currY; }
        float getStartY() const { return startY; }
        Time getCmlTime() const { return cmlT; }
    };
   
   
    enum Move
    {
        Left,
        Right,
    };
   
    enum class Dir
    {
        Up = 1,
        Down = 1 << 1,
        Left = 1 << 2,
        Right = 1 << 3,
       
        None = 0,
    };
   
    //...
   
   
private:
    //...
   
    Jump * jmp; // pam. wolna; tworzone przez jump(), zawiera dane skoku
    Fall * fall; // pam. wolna; tworzone podczas spadania, zawiera dane spadania
    unsigned dir; // aktualny kierunek
   
    //...
   
    bool onGround; // czy stoi na ziemi?
    bool onGroundCall; // odpytywanie wywołań onCollision(): kolizja od dołu
   
    //...
   
    void stopJmp() { if( jmp ) { delete jmp; jmp = NULL; ENUM_REMOVEVAL( dir, Dir::Up ); ENUM_REMOVEVAL( dir, Dir::Down ); } }
    void stopFall() { if( fall ) { delete fall; fall = NULL; ENUM_REMOVEVAL( dir, Dir::Down ); } }
    void startFall() { fall = new Fall( getPosition().y ); }
public:
    Player( Vector2f pos );
   
    //...
   
    Vector2f getPosition() const { return sp.getPosition(); }
    void setPosition( Vector2f pos ) { sp.setPosition( pos ); }
    Vector2f getSize() const { return Vector2f( sp.getGlobalBounds().width, sp.getGlobalBounds().height ); }
    //...
    void onCollision( Collision & c );
};

//...

void Player::onCollision( Collision & c )
{
    Vector2f pos = getPosition();
   
    if( ENUM_ISVAL( dir, Dir::Down ) || onGround )
    // kolizja od dołu
    {
        // zakończ skok i spadanie (jeśli są)
        if( jmp )
        {
            delete jmp;
            jmp = NULL;
        }
        if( fall )
        {
            delete fall;
            fall = NULL;
        }
       
        // usuń spadanie
        ENUM_REMOVEVAL( dir, Dir::Down );
       
        onGround = true;
       
        // wyrównaj do góry obiektu
        pos.y = c.get1Rect().top - getSize().y;
    }
   
    if( ENUM_ISVAL( dir, Dir::Up ) )
    {
        if( jmp )
        {
            delete jmp;
            jmp = NULL;
        }
       
        ENUM_REMOVEVAL( dir, Dir::Up );
        ENUM_ADDVAL( dir, Dir::Down );
       
        pos.y = c.get1Rect().top + c.get1Rect().height;
        fall = new Fall( pos.y );
    }
   
    setPosition( pos );
}

//...

Chcę, żeby gracz po wpadnięciu na bok platformy spadał, a nie wskakiwał na nią.
Próbowałem jakoś to zaimplementować, ale się mi nie udawało.
Postaram się zaraz wrzucić filmik, żeby zobrazować problem.

PS. Czy opłaca się pisać własna fizykę do platformówki? Czy może lepiej użyć jakiegoś gotowego silnika(np.Box2d)?
P-101869
kubawal
Temat założony przez niniejszego użytkownika
» 2014-01-11 19:28:53
Na serio, nikt tutaj nie pisał platformówki?
P-101877
MrPoxipol
» 2014-01-11 19:30:41
Może nikomu się nie chce szukać błędów w Twoim kodzie? Może niektórzy nie mają czasu? :)
P-101878
RazzorFlame
» 2014-01-12 12:16:10
Zapisuj przejście między klatkami danego obiektu do jakiegoś vectora:
C/C++
sf::Vector2f a = obiekt.getPosition(); //stara pozycja
obiekt.wykonajRuch();
sf::Vector2f b = obiekt.getPosition(); //nowa pozycja
Teraz:
C/C++
bool czyKolizja =[]( klasaObiekt ob1, klasaObiekt ob2 )->bool {
    if( ob1.x >= ob2.x && ob1.x <= ob2.x + ob2.w && ob1.y >= ob2.y && ob1.y <= ob2.y + ob2.h ) return true;
    else return false;
   
}( obiekt, platforma );
Aby sprawdzić czy dir = Up lub dir = Left lub dir = Right: Jeżeli: czyKolizja = true oraz a.y+obiekt.h <= platforma.y oraz b.y >= platforma.y oraz b.y <= platforma.y + platforma.h TO obiekt koliduje górną krawędzią (lub jednym z boków po górnej stronie). Teraz aby dokładnie sprawdzić który bok koliduje z platformą musisz sprawdzić czy abs(a.x-b.x) < abs(a.y-b.y). Jeżeli tak to znaczy że obiekt nachodzi z góry.
Przykład:
C/C++
if( abs( a.x - b.x ) < abs( a.y - b.y ) )
{
    //obiekt bardziej nachodzi z góry niż z boku tzn. że kolizja nastąpiła z góry
}
else
{
    sf::Vector2f offset; //obiekt pomocniczy
    offset.x = b.x - platforma.x;
    offset.y = b.y - platforma.y;
    if( offset.x < platforma.w / 2 )
    {
        //kolizja nastąpiła z lewego boku
    }
    else //kolizja z prawego boku
}
Podobnie dla kolizji następującej od dołu.
Pamiętaj że w przykładzie otrzymujesz czy kolizja nastąpiła po górnej stronie, dzięki temu możesz wykryć 8 kolizji (góra, prawy górny róg, lewy górny róg, dół, prawy dolny róg, lewy dolny róg, lewa strona (lewy górny && lewy dolny) i prawa strona (prawy górny && prawy dolny)).
P-101919
kubawal
Temat założony przez niniejszego użytkownika
» 2014-01-12 19:34:39
Nie działa. Ludzik po próbie chodzenia po platformie "teleportuje" się za lewą lub prawą stronę platformy.

Kod:
C/C++
// odbijanie się od dołu platformy
if( ENUM_ISVAL( dir, Dir::Up ) )
{
    if( jmp )
    {
        delete jmp;
        jmp = NULL;
    }
   
    ENUM_REMOVEVAL( dir, Dir::Up );
    ENUM_ADDVAL( dir, Dir::Down );
   
    pos.y = c.get1Rect().top + c.get1Rect().height;
    fall = new Fall( pos.y );
}

//// algorytm kolizji @RazzorFlame
else if( abs( old.x - pos.x ) < abs( old.y - pos.y ) )
{
    onGroundCall = true;
   
    // zakończ skok i spadanie (jeśli są)
    stopJmp();
    stopFall();
   
    // usuń spadanie
    ENUM_REMOVEVAL( dir, Dir::Down );
   
    onGround = true;
   
    // wyrównaj do góry obiektu
    pos.y = c.get1Rect().top - getSize().y;
}
else
{
    sf::Vector2f offset; //obiekt pomocniczy
    offset.x = pos.x - obj.left;
    offset.y = pos.y - obj.top;
    if( offset.x < obj.width / 2 )
    {
        ENUM_REMOVEVAL( dir, Dir::Right );
        startFall();
        st = NoMove; // nie przesuwaj sie ani w lewo ani w prawo
        pos.x = obj.left - this->getSize().x; // wyrównanie
    }
    else
    {
        ENUM_REMOVEVAL( dir, Dir::Left );
        startFall();
        st = NoMove; // nie przesuwaj się ani w lewo ani w prawo
        pos.x = obj.left + obj.width; // wyrównanie do prawej
    }
}

PS. jak miałby wyglądać ten algorytm dla kolizji od dołu?
PPS. pisząc
Podobnie dla kolizji następującej od dołu
 masz na myśli dół gracza czy platformy?
P-102012
« 1 »
  Strona 1 z 1