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

[SFML] Precyzyjna kolizja (z których stron nachodzą na siebie obiekty)

Ostatnio zmodyfikowano 2014-01-07 18:35
Autor Wiadomość
kubawal
Temat założony przez niniejszego użytkownika
[SFML] Precyzyjna kolizja (z których stron nachodzą na siebie obiekty)
» 2013-12-10 17:29:03
Witam!

Robię platformówkę i potrzebuję algorytmu na kolizję ze stronami, tzn. że podczas kolizji wiem, z których stron nachodzą na siebie obiekty.
Doszedłem mniej więcej do takiego kodu:

C/C++
class Collision
{
public:
    enum Side
    // strony
    // układ wyliczenia jest taki, by można było stosować bitowe | do zaznaczenia kilku stron naraz
    {
        Top = 1,
        Bottom = 1 >> 1,
        Left = 1 >> 2,
        Right = 1 >> 3,
       
        All = Top | Bottom | Left | Right, // wszystkie
        None = 0 // żadna
    };
   
private:
    FloatRect r1, r2;
    Side s1, s2; // strony, z których na siebie nachodzą
    bool coll;
    bool a; // kolizja zaawansowana
   
    bool collision(); // to ta funkcja wykonuje większość roboty :)
   
public:
   
    Collision( FloatRect rr1, FloatRect rr2, bool adv = false );
    Collision( Sprite t1, Sprite t2, bool adv = false );
   
    bool operator bool() const { return coll; } // czy wystąpiła kolizja
    Side get1Side() const { return s1; }
    Side get2Side() const { return s2; }
};

//...

Player::Collision::Collision( FloatRect rr1, FloatRect rr2, bool adv )
{
    r1 = rr1;
    r2 = rr2;
    s1 = s2 = None;
    a = adv;
    collision();
}

Player::Collision::Collision( Sprite t1, Sprite t2, bool adv )
{
    r1 = t1.getGlobalBounds();
    r2 = t2.getGlobalBounds();
    s1 = s2 = None;
    a = adv;
    collision();
}

Player::Collision::collision()
{
    coll = r1.intersects( r2 ); // tylko podstawowa kolizja
    if( !a )
    // niezaawansowana kolizja, dalsze obliczenia niepotrzebne
         return;
    else
    // kolizja zaawansowana
    {
        if( !coll )
        // brak kolizji, dalsze obliczenia niepotrzebne
             return;
       
        Vector2f v11( r1.left, r1.top ), // wierzchołki r1
        v12( r1.left + r1.width, r1.top ),
        v13( r1.left, r1.top + r1.height ),
        v14( r1.left + r1.width, r1.top + r1.height );
        Vector2f v21( r2.left, r2.top ), // wierzchołki r2
        v22( r2.left + r2.width, r2.top ),
        v23( r2.left, r2.top + r2.height ),
        v24( r2.left + r2.width, r2.top + r2.height );
       
        // wykrywanie kolizji
       
        // i co dalej?
       
    }
   
}
Wierzchołki są w układzie

v1---------------v2
|                 |
|                 |
|                 |
v3---------------v4


Algorytmika nigdy nie była moją mocną stroną, więc prosiłbym o nakierowanie bądź gotowca(z wytłumaczeniem).
Zaznaczam, że nie chcę używać żadnej z dostępnych bibliotek fizycznych.
P-98910
maly
» 2013-12-11 12:44:48
Kiedyś ktoś wrzucił prosty kod z kolizją trochę się nim wtedy bawiłem i powstało coś takiego, może się przyda.
Komentarze odnośnie poprawności kodu jak i jego stylu niemile widziane;P
C/C++
#include <SFML/Graphics.hpp>
#include <iostream>
#include <vector>
#include <cmath>

std::vector < sf::RectangleShape *> Walls;
sf::RectangleShape player;

bool drawMap = true;

enum {
    SideLeft = 1,
    SideRight = 2,
    SideTop = 4,
    SideBottom = 8
};

// funkcja potrzebna tylko dla efektownej kamerki
sf::Vector2f getInterpolated( const sf::Vector2f & v1, const sf::Vector2f & v2, float d )
{
    float i = 1.0f - d;
    return sf::Vector2f(( v1.x * i + v2.x * d ),( v1.y * i + v2.y * d ) );
}

float getLength( const sf::Vector2f & v1, const sf::Vector2f & v2 )
{
    sf::Vector2f t = v1 - v2;
    return std::sqrt( t.x * t.x + t.y * t.y );
}

// ujowa ta nazwa funkcji
void setIf( sf::Vector2f & v, float len )
{
    v.x = v.x < 0 ? - len: v.x > 0 ? len: 0;
    v.y = v.y < 0 ? - len: v.y > 0 ? len: 0;
}

bool isCollision( const sf::RectangleShape & rect, const std::vector < sf::RectangleShape *> & walls )
{
    const sf::FloatRect frect = rect.getGlobalBounds();
    for( int i = 0; i <( int ) walls.size(); ++i )
    if( frect.intersects( walls[ i ]->getGlobalBounds() ) )
         return true;
   
    return false;
}

void movePlayer( const sf::Vector2f & oldPos, const sf::Vector2f & newPos, bool & coll, int & collSide )
{
    const float steplen = 1.0;
    const float len = getLength( oldPos, newPos );
    const int div = len / steplen;
    sf::Vector2f vstep = -( oldPos - newPos );
    setIf( vstep, steplen );
   
    // była optymalizacja a tera nima:P
    #define tWalls Walls
   
    for( int i = 0; i < div; ++i )
    {
        sf::Vector2f lp = player.getPosition();
        player.move( vstep );
        if( isCollision( player, tWalls ) )
        {
            player.setPosition( lp );
            coll ^= true;
            break;
        }
    }
   
    // jesli przebyta droga jest mniejsza niz powinna
    // sprobojemy dodatkowo ruszyc w pionie lub poziomie
    float alen = getLength( oldPos, player.getPosition() );
    if( alen < len )
    {
        int div = steplen; // decyduje o długooci poolizgu
       
        // probojemy w poziomie
        for( int i = 0; i <= div; ++i ) {
            sf::Vector2f nPos = player.getPosition();
            player.move( vstep.x, 0 );
            if( isCollision( player, tWalls ) ) {
                player.setPosition( nPos );
               
                if( vstep.x < 0 )
                     collSide ^= SideLeft;
                else
                     collSide ^= SideRight;
               
                break;
            }
        }
        // probojemy w pionie
        for( int i = 0; i <= div; ++i ) {
            sf::Vector2f nPos = player.getPosition();
            player.move( 0, vstep.y );
            if( isCollision( player, tWalls ) ) {
                player.setPosition( nPos );
               
                if( vstep.y < 0 )
                     collSide ^= SideTop;
                else
                     collSide ^= SideBottom;
               
                break;
            }
        }
    }
}

int main()
{
    sf::RenderWindow window( sf::VideoMode( 800, 600 ), "" );
    window.setFramerateLimit( 60 );
   
    player.setSize( sf::Vector2f( 32, 32 ) );
    sf::Vector2f orig = player.getSize();
    player.setOrigin( orig.x *.5, orig.y *.5 );
    player.setPosition( 400, - 100 );
   
    // Generowanie mapy
    srand( time( NULL ) );
    int cnt = 0;
    const int maxcnt = 200;
    for( int y = 0; y < 60; ++y ) {
        for( int x = 0; x < 60; ++x ) {
            float sx = rand() %(( int ) player.getSize().x * 4 ) + 10;
            float sy = rand() %(( int ) player.getSize().y * 4 ) + 10;
           
            float px = x * player.getSize().x * 4 + sx;
            float py = y * player.getSize().y * 4 + sy;
            sf::RectangleShape tmp( sf::Vector2f( sx, sy ) );
            tmp.setPosition( px, py );
            std::cout << "gen map:" << maxcnt << "/" << cnt << "\r";
            if( !isCollision( tmp, Walls ) ) {
                sf::RectangleShape * w = new sf::RectangleShape( sf::Vector2f( sx, sy ) );
                w->setFillColor( sf::Color( sx, sy, px ) );
                w->setPosition( px, py );
                Walls.push_back( w );
                ++cnt;
                if( cnt == maxcnt )
                     goto fuck;
               
            }
        }
    }
    fuck:
    std::cout << "total shapes " << cnt << std::endl;
   
    sf::Vector2f campos = window.getView().getCenter();
   
    while( window.isOpen() )
    {
        sf::Event event;
        while( window.pollEvent( event ) ) {
            if( event.type == sf::Event::KeyPressed ) {
                if( event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape )
                     window.close();
               
                if( event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D )
                     drawMap = !drawMap;
               
            }
        }
       
        sf::Vector2f dir( 0, 0 );
        bool playerMove = false;
        if( sf::Keyboard::isKeyPressed( sf::Keyboard::Left ) ) {
            dir.x = - 1;
            playerMove = true;
        } else if( sf::Keyboard::isKeyPressed( sf::Keyboard::Right ) ) {
            dir.x = 1;
            playerMove = true;
        }
       
        if( sf::Keyboard::isKeyPressed( sf::Keyboard::Up ) ) {
            dir.y = - 1;
            playerMove = true;
        } else if( sf::Keyboard::isKeyPressed( sf::Keyboard::Down ) ) {
            dir.y = 1;
            playerMove = true;
        }
       
        int collSide = 0;
        bool coll = false;
        if( playerMove )
        {
            const float speed = 2.0;
           
            dir.x *= speed;
            dir.y *= speed;
           
            movePlayer( player.getPosition(), player.getPosition() + dir, coll, collSide );
        }
       
        window.clear( sf::Color( 200, 200, 200 ) );
        // super mega fast draw
        if( drawMap )
        for( int i = 0; i <( int ) Walls.size(); ++i )
             window.draw( * Walls[ i ] );
       
        window.draw( player );
        window.display();
       
        std::string str;
        str += "[Simple collision 3] ";
        if( collSide & SideLeft ) str += " z lewej ";
       
        if( collSide & SideRight ) str += " z prawej ";
       
        if( collSide & SideTop ) str += " z gory ";
       
        if( collSide & SideBottom ) str += " z dolu ";
       
        window.setTitle( str );
       
        // efektowna kamerka
        sf::View view = window.getDefaultView();
        sf::Vector2f inetr = getInterpolated( player.getPosition(), campos, 0.95 );
        view.setCenter( inetr );
        view.zoom(.8 );
        window.setView( view );
        campos = inetr;
    }
    return 0;
}
P-98966
kubawal
Temat założony przez niniejszego użytkownika
» 2014-01-07 16:56:12
Odświeżam temat, bo nadal tego potrzebuję(nie chcę zakładać nowego tematu)-
Zakodziłem trochę i doszedłem do wniosku, że potrzebuje wzoru na obliczenie, jaki kąt stanowi wektor przesunięcia do osi pionowej
Niby czytałem coś o tych azymutach i innych szajstwach, ale nadal tego nie rozumiem.

C/C++
class Offset
{
    Vector2f curr; // pozycja obecna
    Vector2f next; // pozycja następna
   
public:
    Offset( Vector2f c, Vector2f n )
        : curr( c )
        , next( n )
    { }
   
    float getAngle() { // co tutaj? }
        float getLength() { return distance( curr, next ); }
       
        Vector2f getCurr() const { return curr; }
        Vector2f getNext() const { return next; }
        void setCurr( Vector2f c ) { curr = c; }
        void setNext( Vector2f n ) { next = n; }
    };
P-101512
pekfos
» 2014-01-07 17:36:06
C/C++
Bottom = 1 >> 1,
Left = 1 >> 2,
Right = 1 >> 3,
Zły operator.
P-101516
kubawal
Temat założony przez niniejszego użytkownika
» 2014-01-07 17:39:49
No tak.

Ale to nadal nie rozwiązuje głównego problemu.
P-101517
pekfos
» 2014-01-07 17:44:45
potrzebuje wzoru na obliczenie, jaki kąt stanowi wektor przesunięcia do osi pionowej
Poczytaj o atan2().
P-101519
DejaVu
» 2014-01-07 18:35:20
Podobnych tematów było sporo:
Frazy, które należy wpisać w wyszukiwarkę google:

http://cpp0x.pl/forum/temat/​?id=5268

Poza tym jeden temat = jeden problem. Pomyśl jak zmienić temat, aby niósł jakąś wartość (adekwatną do rozwiązywanego problemu), bo obecnie jego nazwa nadaje się do kosza... (nazwa nie mówi krótko i precyzyjnie czego wątek dotyczy).
P-101524
« 1 »
  Strona 1 z 1