drawing

Rysowanie obiektów 2D

Z tego poradnika dowiesz się jakie obiekty można narysować obiekty, a także jak napisać własne klasy, które będziesz mógł narysować.

 

Rysowanie


Aby narysować obiekty w łatwy sposób potrzebujesz do tego specjalnej klasy sf::RenderWindow, która dziedziczy po sf::Window, co oznacza, że wszystko co się nauczyłeś jest wciąż aktualne i wygląda w ten sam sposób.

RenderWindow zawiera bardziej zaawansowane funkcje, które pozwalają nam manipulować oknem. Dzisiaj zajmiemy się 2 z nich: clear i draw. Robią dokładnie to jak się nazywają: clear czyści ekran, draw zajmuje się rysowaniem obiektów.

Wywoływanie metody clear jest ważne i musi odbyć się przed rysowaniem wszelkich obiektów, jeżeli jej nie wywołasz to mogą pozostać piksele z poprzedniego rysowania obiektów. Jeżeli byś poruszał wtedy postacią moglibyśmy uzyskać taki efekt:

(Win 7,8,8.1 straciły ten feature, ale na szczęście Win10 go odzyskał ;] )

Wywołanie display także jest konieczne to prawidłowego działania, ponieważ podczas wywoływania draw wszystkie obiekty są kopiowane do bufora (o którym porozmawiamy za chwilę), a wywołanie display powoduje uwolnienie tych obiektów z bufora i wyświetlenie ich na ekranie.

Powinno się wywoływać te metody wg kolejności:

  • clear – do czyszczenia ekranu
  • draw – do rysowania obiektów
  • display – do wyświetlenia obiektów

 

Jakie obiekty można rysować?


W SFML jest przygotowanych kilka typów obiektów, sprite’y (rysowanie obrazków), tekst (wiadomo) oraz kształty. Niektóre kształty są przygotowane, a także mamy klasę, która pozwala nam rysować własne wielokąty (VertexArray).

 

Rysowanie własnej klasy


Generalnie możemy narysować każdy obiekt, który dziedziczy po sf::Drawable. Zrobienie tego nie jest trudne, pokaże to na hipotetycznej klasie, w której mamy Sprite’a, który reprezentuje naszą postać w grze oraz kilka cech typowych dla gier:

To co musi mieć klasa dziedziczona po Drawable to metoda prywatna draw, tylko wtedy możemy wywołać narysowanie naszego obiektu poprzez window.draw(nasz_obiekt). Możesz powiedzieć, że to jest mało przydatne przecież można w tym wypadku podać sprite’a naszej klasy poprzez postac.sprite i narysować go bez bawienia się w dziedziczenie. Masz rację można, tylko że nie zawsze podajemy naszą teksturę jako publiczną oraz nie zawsze nasza postać składa się z jednego elementu. Załóżmy, że chcemy mieć super zaawansowaną kolizję, która będzie obejmowała trafienie w nogi, ręce, głowę i tors, które zrobimy z odpowiednio, linii, linii, koła i prostokąta:

Schematyczna postać składająca się z różnych elementów

A nasz kod rysujący by wyglądał tak:

Wtedy dziedziczenie nabiera sensu bo te wszystkie linie wywołasz poprzesz window.draw(postac), a gdy cię najdzie ochota na stworzenie kolejnej takiej postaci po prostu wywołasz window.draw(postac_2), a  nie kolejną masę tych samych linii. To tyle jeżeli chodzi o mój wywód „dlaczego warto dziedziczyć klasę Drawable”.

 

Rysowanie off-screen


Zanim przejdziemy dalej, to może uświadomimy sobie czym właściwie jest rysowanie off-screen (niestety polskiego określenia na to nie kojarzę). Jest to skopiowanie do pamięci następnej wyświetlonej klatki, tzn podczas rysowania obecnej klatki już w chwili obecnej jest tworzona kopia obrazu do klatki następnej. Najlepiej to prezentuje obrazek poniżej:

 

Przedstawiony tutaj sposób prezentuje narysowanie elementu na texture zamiast bezpośrednio do okna. Używamy tutaj sf::RenderTexture zamiast sf::RenderWindow.

 

Rysowanie z różnych wątków


Jeżeli korzystasz z wielu wątków, powinieneś wiedzieć jak napisać poprawnie wątek rysujący wszystkie elementy. Warto zapamiętać, że powinieneś zdezaktywować okno zanim zaczniesz je używać w innych wątkach, ponieważ okno nie może być aktywne w wieli wątkach jednocześnie.

Jak widać nie musisz włączać okna w wątku renderującym, SFML robi to za ciebie. Przypominam o tym, że najbezpieczniej jest tworzenie okna i przechwytywania eventów w głównym wątku.

Oryginalny artykuł


  • Propo

    Jeżeli sprite’y (sf::Drawable) potrzebują w naszej klasie rysującej metody
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const { target.draw(sprite) }
    To jaka metoda potrzebna jest do klasy, która ma rysować linie (sf::Vertex) ?

    • Dokładnie ta sama, jeżeli interesuje cię tylko narysowanie takiej linii to używasz: window.draw(vertices, 4, sf::Lines) chyba, że chodzi ci o napisanie klasy, która narysuje linie w wymyślony przez ciebie sposób: http://goo.gl/HUQPdg

      • Propo

        Dzięki wielkie 😉