Przeglądaj kategorię

Poradniki

Kształty w SFML

W SFML masz dostęp z góry do kilku typów prostych kształtów, a także nic nie stoi na przeszkodzie abyś mógł stworzyć kilka własnych. Tutaj dowiesz się jak obsługiwać potrzebne do tego klasy.

Wspólne właściwości kształtów


Przekształcenia (pozycja, skala, rotacja)

Wszystkie te przekształcenia są dostępne dla każdego z kształtów w SFML i zostały opisane w poradniku Przekształcenia kształtów.

Kolor

Aby zmienić kolor wewnątrz obiektu musisz użyć setFillColor.

Kontur

Możesz ustawić także dowolny kolor konturu figury oraz jego grubość.

 

W przypadku gdy ustawimy grubość konturu > 0 to figura się powiększa o jej grubość np jeżeli mamy koło o promieniu 10 i dodamy kontur o grubości 5 to cała figura będzie miała promień o długości 15. Jeżeli chcesz aby kontur był rysowany wewnątrz figury, czyli żeby figura nie powiększała się ustaw grubość konturu na ujemną.

 

Tekstura

Tak jak sprite’y figury mogą korzystać z tektur:

Kontur nie jest teksturowany, aby odwołać używanie tektury użyj setTexture(NULL).

 

Rysowanie kształtów


Odbywa się ono w tradycyjny sposób dla wszystkich figur.

 

Wbudowane klasy kształtów


Prostokąt

Aby narysować prostokąt musisz użyć sf::RectangleShape. Konstruktor przyjmuje jako argument jego wielkość (szerokość i wysokość).

Koło

Ta figura jest reprezentowana przez sf::CircleShape. Posiada dwa atrybuty: promień oraz opcjonalny który decyduje o tym z ilu punktów się się składa. Decyduje to o „jakości” okręgu. Im większy kształt tym więcej boków będzie potrzebował.

Wielokąty foremne

Wielokąty foremne charakteryzują się tym że powstają na okręgu i ich boki są równej długości, dzięki temu je także robimy za pomocą sf::CircleShape.

Figury wypukłe

Do rysowania figur wypukłych służy klasa sf::ConvexShape. Co musisz pamiętać przy ich rysowaniu to aby rozmieszczać kolejne punkty figury w kierunku zgodnym lub przeciwnym do wskazówek zegara, nie rób tego w sposób losowy.

SFML nie posiada klasy do rysowania figur wklęsłych, ale możesz się w tym celu posłużyć tą klasą o ile pierwszym punktem będzie środek ciężkości figury.

Linie

Linie nie mają swojego odpowiednika w SFML z prostego powodu. Jeżeli twoja linia ma jakąś grubość to jest prostokątem, jeżeli nie może być narysowana za pomocą prymitywów.

Oraz linia-prymityw:

Aby dowiedzieć się więcej o prymitywach zapraszam do poradnika o VertexArray.

 

Tworzenie własnego kształtu-klasy


Możesz poszerzyć typy kształtów o te napisane przez siebie. Aby to zrobić musisz dziedziczyć po sf::Shape i napisać 2 funkcje:

  • getPointCount do pobrania liczby punktów
  • getPoint do pobrania punktu o określonym indexie

Musisz także używać klasy update() do zaktualizowania stanu swojej figury. Poniżej jest gotowy przykład gotowej klasy rysującej elipsę.

Antyaliasing w kształtach


Czyli wygładzenie krawędzi, które możemy ustawić w ustawieniach okna

Nie każda karta graficzna może go obsługiwać, więc efekt nie musi być widoczny na każdym komputerze.

Oryginalny artykuł

Sprite’y i tekstury

Na początku lekcji warto sobie uzmysłowić czym jest tekstura oraz czym jest sprite. Tekstura to jakiś obraz (np wczytany z pliku), a nazywamy go teksturą (Texture), a nie obrazem (Image) ponieważ my go będziemy mapowali do obiektów 2D. Sprite to nic innego jak „teksturowany” kwadrat.

W SFML do przechowywania tekstur służy sf::Texture, a do sprite’ów sf::Sprite.

 

Wczytywanie tekstur


Możemy wczytywać tekstury na 3 sposoby: z pliku (loadFromFile), z pamięci (loadFromMemory) oraz z jakiegoś napisanego przez nas strumienia danych (loadFromStream). Jednak my podczas tego tutoriala zajmiemy się najbardziej popularnym wczytywaniem z pliku.

W przypadku gdy nie uda nam się wczytać pliku (np. jest nieprawidłowe rozszerzenie lub zła nazwa pliku) funkcja zwraca false. Listę wspieranych przez SFML formatów obrazów znajdziesz w dokumentacji.

Wszystkie funkcje do wczytywania posidają także opcjonalny argument, który pozwala nam wczytać fragment tekstury.

sf::IntRect to klasa, która w matematyczny sposób reprezentuje kwadrat. Pierwsze 2 argumenty to pozycja na której się zaczyna, kolejne dwa to szerokość i wysokość.

 

Jeżeli nie chcesz wczytywać tekstury, a raczej wolisz wypełnić ją bezpośrednio z jakiejś tablicy pikseli możesz to zrobić. Na początku musisz stworzyć teksturę, którą możesz wypełnić później.

Pamiętaj, że teraz zawartość tekstury jest niezdefiniowana. Aby ją uzupełnić możesz to zrobić na kilka sposobów:

Te sposoby przedstawiają sposób zaktualizowania całej tektury, możesz także zaktualizować fragment, ale o tym informacje znajdziesz w dokumentacji.

Istnieją 2 właściwości, które decydują o tym w jaki sposób będzie renderowana tekstura.

Pierwszą z nich jest wygładzenie (smooth), dzięki któremu piksele są mniej widoczne. Można to włączyć używając:

Druga właściwość to powtarzanie, które działa tylko wtedy gdy obrazek który ma być wyświetlany jest większy niż tekstura, czyli np wyświetlany kwadrat o wymiarach 100×100, a nasza tekstura ma wymiary 25×25, wtedy może zostać powtórzona.

Sprite’y


Skopiowanie tekstury do sprite’a jest proste. Możemy podać teksturę w konstruktorze sprite’a lub poprzez użycie metody:

sf::Sprite sprite(texture);
sprite.setTexture(texture);

Rysowanie wygląda w ten sposób:

Możesz wczytać także fragment tekstury:

Możesz także zmieniać kolor sprite’a, ewentualnie możesz też tego użyć do zmiany przezroczystości.

Sprite’y poniżej mają tę samą teksturę, lecz różne kolory:

Sprite’y możesz także przekształcać:

Standardowo przekształcenia pokazane powyżej są dokonywane względem lewego górnego rogu, jeżeli chcesz je wykonywać względem innego punktu np środa sprite’a musisz skorzystać z:

 

Problem białego kwadratu


Jeżeli ci się zdarzyło, że mimo wczytania tekstury bezproblemowo i ustawienia jej sprite’owi, po wyświetleniu masz biały kwadrat to ten paragraf poradnika jest skierowany dla ciebie.

Jest to bardzo często spotykany błąd, problem leży w tym, że gdzieś w kodzie przestał istnieć wskaźnik do twojej tekstury, bo sprite nie kopiuje jej lecz pobiera wskaźnik do niej. W momencie gdy tekstura jest niszczona wskaźnik wskazuje na puste bity w pamięci. Prawdopodobnie twoja funkcja wygląda mniej więcej tak:

Musisz zawsze posiadać swoją teksturę w pamięci inaczej ten błąd będzie się powtarzał.

 

Używaj jak najmniej tekstur


Staraj się używać jak najmniejszej liczby tekstur jak to możliwe, pamiętaj że zmiana tekstury to ciężka operacja dla karty graficznej. Rysowanie wielu sprite’ów używających tej samej tekstury przynosi lepsze efekty. Dlatego lepiej jest korzytsać np. z tzw. tilesets (atlasów tekstur). Jest to zdecydowanie wydajniejsze.

Oryginalny artykuł

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ł

Klawiatura, mysz i joystick

Wstęp


W tej części poradnika zajmiemy się omówieniem instrukcji warunkowych klawiatury, myszy i joysticka. Przydają się one do sprawdzenia, czy określony klawisz jest wciąż wciśnięty.

 

Klawiatura


Klasa, która daje nam dostęp do klawiszy na klawiaturze to sf::Keyboard. Ta klasa zawiera tylko jedną metodę: isKeyPressed, która sprawdza czy klawisz jest wciśnięty, jeżeli tak to zwraca true, w przeciwnym razie false. Jako argument przyjmuje klawisz, który ma zostać sprawdzony. Co trzeba zaznaczyć to fakt, że to metoda sprawdza bezpośrednio stan klawisza, czyli działa nawet wtedy gdy okno jest niekatywne.

 

Mysz


Klasą odpowiedzialną za obsługę myszy jest sf::Mouse. Podobnie jak w przypadku klawiatury mysz posiada instrukcję warunkową wciśnięcia klawiszy.

Przyciski myszy zawarte są w enum sf::Mouse::Button.

Możesz także pobrać pozycję myszy względem pulpitu lub okna gry.

Analogicznie można też ustawić pozycję myszy:

 

Joystick


Klasa, która daje nam dostęp do obsługi pada to sf::Joystick. Tak jak reszta klas w tym tutorialu posiada wyłącznie funkcje statyczne (z przedrostkiem static).

Każdy Joystick posida swoje id numerowane od 0-7 (bo SFML obsługuje maksymalnie 8 padów). Pierwszy argument każdej metody sf:Joysticks to jego id.

Możesz sprawdzić, czy Joystick o określonym id jest podłączony w następujący sposób:

Można także sprawdzić niektóre parametry joysticków:

Osie są zdefiniowane w enum sf::Joystick::Axis. A przyciski są ponumerowane od 0 do 31.

W ten sposób możesz sprawdzić czy jakiś klawisz jest wciśnięty, a także możesz sprawdzić pozycję osi:

Oryginalny artykuł

Zarządzanie eventami

Czym są eventy?


Eventy, czyli po polsku zdarzenia to czynności, które mógł wykonać użytkownik w naszym programie, a my możemy je obsłużyć w odpowiedni sposób. Eventem może być np wciśnięcie klawisza.

W SFML klasa eventów nazywa się po prostu sf::Event i standardowo są one obsługiwane w pętli z użyciem pollEvent, czy też waitEvent. Przykładowe kod z użyciem eventów:

Ewentualnie można napisać to tak jak poniżej:

 

Event żądania zamknięcia


Jest to event, który się uaktywania w przypadku gdy użytkownik kliknie przycisk X (Zamknij) lub użyje skrótu zamknięcia programu np Alt + F4

 

Event zmiany rozmiaru okna


Ten event jest uaktywaniany gdy użytkownik w sposób manualny zmieni rozmiar okna za pomocą myszy. Dzięki tej informacji możesz np samodzielnie przeskalować textury itp. Event podaje nam rozmiary aktualnego okna:

 

Event sprawdzający czy okno jest aktywne


Czyli sprawdzenie czy okno jest „kliknięte”. Może to nam się przydać gdy chcemy aby podczas gdy okno jest niekatywne gra się spauzowała:

 

Wprowadzanie tekstu


W przypadku gdy chcemy aby użytkownik wprowadził jakiś tekst do programu (np. swój nick) to gdy będziemy się chcieli za to zabrać będzie mogło się okazać, że sprawdzenie jaka litera została wciśnięta może być niewygodne, bo trzeba by było wziąć pod uwagę wszystkie klawisze. N a szczęście istnieje prosta metoda przechwytywania tekstu:

 

Wciśnięcie i puszczenie klawiszy


SFML wyróżnia 2 typy użycia klawiszy: KeyPressed (klawisz wciśnięty) oraz KeyReleased (klawisz puszczono).  Standardowo zdarzenie KeyPressed jest generowane zgodnie z opóźnieniem OS dopóki ten klawisz jest wciśnięty, działa to na tej samej zasadzie co wypisywanie tej samej litery gdy trzymamy wciśnięty klawisz w notatniku. Możemy to wyłączyć używając window.setKeyRepeatEnabled(false), oczywiście KeyReleased nie jest nigdy powtarzany.

Możemy także używać kombinacji klawiszy:

 

Obsługa myszy


Jeżeli chcemy możemy sprawdzać zdarzenia dotyczące rolki (kółka) myszy:

Można także obsługiwać przyciski myszy, SFML obsługuje maksymalnie pięć przycisków: lewy, prawy, środkowy (kółko/rolka) i dwa dodatkowe. Możemy także sprawdzać czy przycisk jest wciśnięty czy puszczony (Event::MouseButtonPressed / Event::MouseBottonReleased).

W przypadku gdy mysz się poruszyła można sprawdzić jej nową (aktualną) pozycję względem okna:

Dosyć przydatną rzeczą może być też sprawdzenie czy mysz jest poza oknem:

 

Joystick


Jeżeli chciałbyś obsługiwać pada w grze to nie ma problemu. SFML wspiera przynajmniej większość kontrolerów. Zanim zaczniesz używać swojego musisz sprawdzić czy kontroler jest podłączony:

Te eventy się uaktywniają gdy pad zostanie podłączony dopiero podczas działania tej pętli. Istnieje jeszcze inny sposób na sprawdzenie tego, ale o tym w innym poradniku.

Tak jak w przypadku innych klawiszy, tak też i tutaj można sprawdzać czy klawisz jest wciśnięty czy puszczony. SFML wspiera maksymalnie do 8 kontrolerów oraz po 32 przyciski na każdy z nich.

Osie joysticków są zazwyczaj bardzo czułe dlatego w SFML stosuje się próg detekcji poruszenia gałką. Możesz go zmienić używajac Window::setJoystickThreshold. Wspierane jest 8 osi joystick’ów: X, Y, Z, R, U, V, POV X i POV Y. Sposób ich mapowania zależy od ich sterownika.

Zdarzenie poruszenia posiada id joysticka, nazwę osi oraz obecną pozycję (w przedziale <-100,100>).

Oryginalny artykuł

Zarządzanie oknem w SFML

Tworzenie okna


W SFML można utworzyć okno w bardzo łatwy sposób, najwygodniej jest je utworzyć poprzez konstruktor, dzięki czemu nie będziemy się musieli bawić w ustawianie go używając dużej ilości metod.

Aby móc używać okna można dodać <SFML/Graphics.hpp> zamiast Window.hpp.

Konstruktor ma kilka możliwych form, w przykładzie powyżej pierwszym argumentem jest konstruktor VideoMode, który przyjmuje argumenty: szerokość okna, wysokość okna, tryb kolorów (opcjonalnie), kolejnym jest nazwa okna, tzw „caption”, czyli napis wyświetlany na pasku tytułu.

Konstruktor może także przyjmować kolejny argument, którym są style okna:

  • Style::None – okno bez żadnych dodatków, nie może być łączony z innymi stylami
  • Style::Titlebar – okno z paskiem tytułu
  • Style::Resize – można zmieniać wielkość okna przez użytkownika, posiada także przycisk do maksymalizowania
  • Style::Close – okno posiada przycisk X (do zamykania)
  • Style::Fullscreen – okno uruchamia się w trybie pełnego ekranu, nie może być łączone z innymi stylami oraz wymaga odpowiednio ustawionego VideoMode
  • Style::Default – standardowy styl, połączenie Resize | Close | Titlebar

Ewentualnie możemy w konstruktorze podać uchwyt do okna i zostanie ono utworzone poprzez użycie np. openGL. Istnieje jeszcze jeden sposób poza konstruktorem który pozwoli nam w łatwy sposób utworzyć nasze okno:

 

Powoływanie okna do życia


Skoro nasz okno zostało już utworzone, przydałoby się je uruchomić. Poniżej mamy standardową główną pętlę gry w SFML

Prześledźmy działanie kodu, pierwsze linie są nam znane jednak dojdźmy do pierwszej pętli while, jest to tzw. pętla główna programu, która powtarza się dopóki nie zostanie utworzone żądanie zakończenia programu. W tym przykładzie używamy metody typu bool, która sprawdza czy okno jest wciąż otwarte.

Następną ciekawostką jest Event, który jest kontenerem zdarzeń i to w nim przechowuje się informacje o np. wciśnięciu określonych klawiszy. Dzięki temu możemy w odpowiedni sposób zareagować.

W kolejnej pętli jest sprawdzenie czy nasz kontener Event jest pełny, jeżeli tak to wykonywane są odpowiednie instrukcje. W tej pętli mamy warunek dla wciśnięcia X (Zamknij) na naszym oknie, konsekwencją tego jest zamknięcie okna. Mógłbyś się zapytać: „czemu okno po wciśnięciu X nie jest zamykane automatycznie?”, a to dlatego że w ten sposób gracz mógłby stracić postępy w grze, dlatego w ten sposób możemy najpierw wywołać funkcję zapisującą grę i dopiero później zamknąć okno.

 

Przykłady niektórych metod


SFML może przyjmować także okna które przygotowaliśmy wcześniej w QT, wxWidgets, czy openGL, jednak nie posiada obsługi niektórych zaawansowanych elementów. Pamiętajmy, że SFML został zbudowany na openGL. Dlatego istnieje także parę metod, które być może będziesz musiał użyć, gdy użyjesz okien z innych bibliotek

Jeżeli jesteś ciekawy działania i użycia innych metod zapraszam do dokumentacji sf::Window.

Tak jak wspomniałem można podać uchwyt (handle) do innego okna. Robi się to w następujący sposób:

Możesz także pobrać uchwyt do okna stworzonego w SFML i użyć w ten sposób funkcji specyficznych dla danego OS.

Instrukcje jak należy to zrobić znajdziesz na różnych stronach, czy też forach. Nie będę się tutaj nad tym rozpisywał.

 

Kontrola FPS


Może się zdarzyć, że podczas działania okna, gdy będzie aplikacja działała szybko będziesz mógł doznać tzw. artefaktów, które będą powodowane przez niezsynchronizowanie odświeżania twojej aplikacji z monitorem. Można to łatwo naprawić:

Po tym wywołaniu twoja aplikacja będzie działała na równi z odświeżaniem monitora czyli 60 fps. Jeżeli chcesz możesz użyć także funkcji:

 

Rzeczy, które powinieneś wiedzieć o oknach


 

  • zamiast sf::Window preferuje się używanie sf::RenderWindow, jego użycie jest takie samo jak metod powyżej
  • możesz stworzyć i mieć uruchomionych więcej niż jedno okno w jednej aplikacji
  • w tej wersji SFML nie jest wspierane wiele monitorów
  • metoda pollEvent musi zostać wywołane w tym samym wątku, w którym zostało utworzone okno
  • na OS X okna i eventy muszą być tworzone (okna) i obsługiwane (eventy) w głównym wątku (main thread)
  • na systemie Windows okno, które jest większe niż pulpit nie zachowuje się prawidłowo, tzn są większe niż okno

Oryginalny artykuł