#Piszemy gre w SFML’u – Lekcja 3, część 2

Druga część 3 lekcji, tutaj dowiesz się jak napisać kolizję używającą bounding box’ów

!UWAGA! Kurs leży w kategorii ‚Obsolete’ co oznacza, że może być nieaktualny, zawierać błędy i nie polecam z niego korzystać. [INFO]

Kod z poprzedniej lekcji

Podczas tej lekcji napiszemy system kolizji pomiędzy graczem, a otoczeniem, jednak zanim przejdziemy dalej chciałbym przedstawić dokładniej sposób wykrywania kolizji przedstawiony w tej lekcji.

Generalnie w świecie 2D istnieje kilka sposobów wykrywania kolizji:

  • bounding box (box collider) metoda, którą zajmiemy się dzisiaj. Polega ona na „nałożenie” czworokąta na jakiś obiekt i sprawdzeniu czy box innego obiektu nie przecina naszego obiektu. Ten sposób ma taką wadę, że jest mało dokładny (możemy go zobaczyć na obrazku powyżej).
  • circle collider – analogiczna do box collider, tyle że zamiast czworokąta używa się okręgu, zwykle używa się go w połączeniu z box collider
  • polygon collider – zdecydowanie najdokładniejszy sposób wykrywania kolizji, ale jednocześnie najtrudniejszy w implementacji, skorzystamy z niego w kolejnych lekcjach tego poradnika

SFML posiada gotowe narzędzia do tworzenia bounding box’ów, między innymi do wykrywania kolizji pomiędzy dwoma czworokątami metodę intersects.

Zacznijmy od napisania kilku nowych metod w klasie Player.

Napisaliśmy sobie kilka metod pomocniczych: do pobrania pozycji (trzeba pamiętać, że to pozycja środka gracza), statusu (aby wiedzieć czy się poruszamy), bounding box’a (do wykrywania kolizji) oraz do pobrania prędkości.

Przejdźmy do klasy Engine, gdzie utworzymy sobie metodę check_collision().

Teraz zaczyna się ciekawa rzecz, w zasadzie moglibyśmy utworzyć sobie podwójną pętlę o wymiarach poziomu i sprawdzać dla każdego kafla czy wykryto kolizję, ale jest to mało efektywne.

Pierwszym krokiem wykrycia kolizji jest oczywiście pobrania box’a oraz zaktualizowanie go do takiej wartości gdyby gracz się poruszył.

Następnie sprawdzamy 4 wierzchołki naszej figury, numer kafelka (dotyczy tablic dwuwymiarowych) o danej współrzędnej można policzyć w ten sposób: = pozycja (x lub y) / szerokość_kafla i co ważne zmienna musi być typu int aby pozbyć się liczb po przecinku.

Kolejne współrzędne wierzchołków otrzymywałem poprzez dodawanie szerokości/wysokości kafla do pozycji lewego górnego rogu. Oto wszystkie warunki:

Ostatnim krokiem jest zmiana metody Player::idz() poprzez przeniesienie z niej sprite.move(…) do update.

Chciałbym Was także ostrzec, że ta metoda w obecnym przykładzie, czyli widoku z góry na postać, która dodatkowo się obraca jest mało efektywna, a to dlatego, że postać właśnie się obraca i nakładany na nią box nie zawsze jest tej samej wielkości, czasami jest szerszy, czasami węższy zależnie od rotacji, co nie wpływa dobrze na wykrywanie kolizji, dużo lepszym sposobem do wykrywania kolizji byłby circle collider. Ewentualnie można dodać do obecnego sposobu kilka warunków aby nie zawsze sprawdzać kolizję dla wszystkich rogów.

Zmiana rozmiaru box collidera

Kod źródłowy:
>>Pobierz<< | >>GitHub<<


  • michał94

    Dzień dobry !
    1.Coś obrazek pod koniec nie chce zbytnio wyświetlać ?
    2.Miałbym prośbę: mógłbyś stworzyć jakiś artykuł o zdarzeniach na mapie (gdy bohater gdzieś dojdzie, w miejsce ustalone przeze mnie)- to po prostu wyświetli się jakiś napis bądź zmiana mapy na inną lokalizację !
    Z góry dziękuje i pozdrawiam :).

    • Witam, obrazek już powinien wyświetlać się poprawnie, a co do drugiego pytania to owszem mogę, tylko mówiąc szczerze jest to dość proste do napisania (chociażby taki warunek: if(player.position == object.position) loadLevel(„level2”) ), ale na dniach coś powinno się pojawić na ten temat.

    • Zacząłem pisać artykuł jednak podczas pisania uświadomiłem sobie po raz kolejny że napisanie takich eventów to kwestia dość indywidualna i jak wspomniałem we wcześniejszym komentarzu sprowadza się do napisana kilku if’ów w odpowiedniej sekcji swojej gry i w większości powtarzałbym siebie, a konkretnie elementy z tego poradnika. Jeżeli masz problem z jakimś konkretnym elementem pisz, polecam także przeanalizować w jaki sposób działa wczytywanie poziomów w mojej grze SQUARE (https://github.com/sheadovas/SQUARE) w Game.cpp jest główna mechanika ładowania kolejnych poziomów, a w Level.cpp jest sposób wczytywania map posiadające dodatkowe elementy takie jak teleportowanie do określonych pozycji.

      Pozdrawiam

  • michal94

    Witam ponownie !
    Niestety, link do gierki SQUARE nie działa :/ A jeśli chodzi o problemy to mam w zasadzie już tylko jak np. wstawić jakiś obiekt na mapie(np.skrzynie) i przy styknięciu z nim wywołać zdarzenie, a nie gdy na niego wejdziemy, ponieważ by mi to pomogło przy wrogach gdzie po prostu stworze ich na wzór gracza i będą chodzić po jakiejś ścieżce- i gdy się z nimi styknę wywołam coś(np.odebranie życia bohaterowi). Na razie to tyle- dzięki za porady. Też pozdrawiam :).

  • michal94

    Witam raz jeszcze !
    Dzięki udało mi się co nieco potworzyć na podstawie twojego kodu; tylko jedna rzecz jest dla mnie utrapieniem. Chodzi o ten boudingbox, który musimy zwarunkować z każdej strony z każdym obiektem (w tym teleporterze). Mógłbyś dać radę zakodzić circlebox-a by tylko jednym warunkiem na przekroczenie linii horyzontu, sprawdzać czy nasz bohater koliduje z czymś ? Mimo prób coś chyba źle robię :(. Z góry lajki i jeszcze więcej dzięki za to.

    • w przypadku boxa masz wbudowaną instrukcję do sprawdzania (box1.getGlobalBounds().intersects(box2), a co do kolizji koła to pod pojęciem circlebox’a masz na myśli kolizja koło-box? generalnie jest to dość łatwo zrobić w tym przypadku sprawdzasz (tak jak na matematyce) czy prosta przecina okrąg (styczność okręgu), jakiś kod podrzucę nieco później

    • dobra, nie chce mi się zbytnio konkretnego kodu pisać więc powiem ci z czego korzystać, mam nadzieję że to wystarczy, daj znać czy na pewno potrzebujesz kodu: http://pastebin.com/raw.php?i=txv14bUh

  • Fen

    Witam
    Ściągnąłem twój program i widzę, że masz taki sam problem jak Ja w swoim programie, a mianowicie po wykryciu kolizji postac sie zatrzymuje i dalej nie chce się ruszać w żadną stronę…Masz może jakiś pomysł jak to rozwiązać?
    Pozdrawiam

    • Ogólna idea polega na wcześniejszym wykryciu kolizji i zezwoleniu na jak największe przybliżenie do collidera, lecz nie przecięcia go, wtedy powinno być wszystko ok. W chwili wolnego czasu mogę do tego przysiąść ale obecnie mam trochę projektów na głowie, a deadline’y gonią 😉