#Piszemy gre w SFML’u – Lekcja 2

Podczas tej lekcji zajmiemy się poruszaniem postaci po mapie oraz animacją.

!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 jest tutaj.

Przed rozpoczęciem dalszej pracy warto przygotować sobie materiały do dzisiejszej lekcji. Będziemy potrzebowali oczywiście poszczególnych klatek animacji, dzięki którym nie będziemy poruszali statycznym obrazkiem. Do tego celu ja użyję gotowego assetu:

Warlock’s Gauntlet artists – rAum, jackFlower, DrZoliparia, Neil2D,

konkretnie obrazka player-move.png.

Jak zauważyłeś wszystkie klatki animacji są w 1 obrazku takie coś nazywamy atlasem animacji, ale możesz także spotkać nazwy anglojęzyczne t.j.: spritesheet, tileset (w przypadku kafelków).

Na początku dodajemy 2 klasy: Engine oraz Player. Czym będą się one zajmowały?

  • Engine będzie zarządzało naszą grą, czyli animacjami, obiektami na scenie, kolizjami, itd.
  • Klasa Player jest naszą reprezentacją gracza w kodzie gry, ta klasa przechowuje wszystkie potrzebne nam informacje o graczu (pozycję, prędkość, itd).

Zacznijmy od zaktualizowania klasy Game. W Game.h dopisujemy nową metodę void single(), następnie w Game.cpp w runGame() pod GameState::Game dopisujemy single() oraz break.

Oto jak prezentuje się metoda single():

Jak widać przy tworzeniu przekazujemy okno do konstruktora Engine, następnie uruchamiamy samą grę. Po wyjściu z gry zmienia nam się status gry na MENU.

Przejdźmy teraz do klasy Engine, która obecnie nie jest zbyt rozbudowana.

W konstruktorze jedynie co robimy to ustawiamy wskaźnik na okno, destruktor zostawiamy pusty.

Metoda runEngine() to funkcja która po prostu przechwytuje wciśnięcia klawiszy.

W naszej grze gracz będzie szedł w kierunku kursora, ale dopiero po wciśnięciu myszy.

Instrukcja window.draw(player)  jest legalna, ponieważ klasa Player jest dziedziczona po klasie Drawable oraz Transformable (więcej informacji o dziedziczeniu po tych klasach znajdziesz w kursie podstawowym SFML na moim blogu).

Przejdźmy teraz co nas bardziej interesuje i to co jest obecnie najbardziej rozbudowane, czyli klasa Player:

Gracz posiada status, czyli informację o tym, czy np. jest w spoczynku. Resztę zmiennych wyjaśnię w miarę jak będziemy ich używać.

W konstruktorze wczytujemy naszą teksturę oraz przypisujemy ją do sprite’a. Kolejnym krokiem jest ustawienie odpowiedniej klatki animacji, w IntRect podajemy argumenty: pierwsze dwa to pozycja x i y na teksturze, od której będzie pobierany mniejszy kwadrat (klatka animacji). Kolejne dwa to szerokość i wysokość klatki.

setOrigin służy do ustawienia punktu wg którego będziemy poruszali naszą postacią, obracali itd. Ja ustawiłem ją idealnie na środek.

Kolejny element to zmiana statusu postaci oraz ustawienie bazowych wartości prędkości i klatki animacji, którą właśnie się zajmiemy.

Tutaj zajmujemy się zmianą klatki animacji na odpowiednią. Chyba, że postać ma stać, wtedy pomijamy tą funkcję.

Metoda odpowiadająca za stanie nie wygląda specjalnie trudno:

Lecz ta odpowiadająca za chodzenie wymaga nieco więcej pracy.

Używamy tutaj sinusów i cosinusów, ponieważ chcemy aby postać poruszała się w kierunku, w którym patrzy. Z racji, że nasza postać zwrócona jest twarzą w górę to odwracamy naszego cosinusa za pomocą minusa (M_PI na makro zawarte w math.h).

Teraz zmienimy delikatnie update, ponieważ tam napiszemy kod dzięki któremu nasza postać będzie zawsze skierowana w kierunku myszy:

Pierwszym krokiem jaki robimy to policzenie wektora pomiędzy pozycją myszy i interesującego nas obiektu, czyli środkiem gracza.

Następnym elementem jest policzenie kąta za pomocą funkcji atan2.

Z racji, że ta funkcja zwraca wynik w radianach zamieniamy je na stopnie w kolejnej linii, a poprzez dodanie 90 otrzymujemy poprawny kąt i od teraz obiekt obraca się poprawnie. Na końcu ustawiamy odpowiednią rotację.

Aby obracanie działało poprawnie i bez zacinania przenieśmy zegar z animacją do klasy Player.

Oczywiście można naszą animację jeszcze dopracować, np poprawiając szybkość postaci i zmieniając czas trwania klatki na taki, który bardziej by pasował do animacji, ale zarówno to jak i np dodanie innych animacji pozostawiam Wam.

Kod źródłowy z tej lekcji:

>>Pobierz<< | >>GitHub<<


  • menhils

    Witam, co jeśli po pobraniu całego kodu, skompilowaniu go nic się nie działą po kliknięciu „Play”? Podczas kompilacji żadnych błędów.

    • Mówimy o kodzie z początku (de facto z poprzedniej) lekcji, czy z końca?

      • JanekJR

        mi też nie działa PLAY. mowa o kodzie z tej lekcji. Code::Blocks 😀

        • Kod się kompiluje bez problemu jak rozumiem? Z racji, że C::B, nie lubi używać nazwy enuma jako przestrzeni nazw, to prawdopodobnie pomoże ich usunięci, czyli zamiana kodu na taki: http://pastebin.com/Zv7pA8v5 . Jak nie to jeszcze pokombinujemy

          • JanekJr

            dobra, znalazłem błąd już 😀 ale pytanie: jak zrobić aby postać poruszała się po kliknięciu i przetrzymaniu myszy. Jak zamieni się eventy w if’ie na lewy przycisk myszy, tj. naciśnij aby ruszyła i puść aby stała, to postać przesuwa się tylko o jeden kawałek do przodu a dalej idzie w miejscu 😀

          • W jaki sposób to zrobiłeś (poproszę o kod, a raczej link do tego fragmentu na pastebin.com )?

          • JanekJr

            http://pastebin.com/n5KuZ1BZ
            takie cóś wykombinowałem 😀

          • Za bardzo to ten kod nie ma prawa działać, w linku 2 alternatywne wersje, które powinny działać: http://pastebin.com/yEyAUQQE

          • JanekJr

            Faktycznie 😀 bo ten mój działa tylko jeśli się wcisnęło, jeśli dalej wciśnięty jest to nic się nie dzieje 😀 nie znałem wcześniej isButtonPressed 😀 z tego co ogarniam to działą on dopóki klawisz jest wciśnięty, szukałem czegoś w ten deseń ale w internetach nie znalazłem 😀 dzięki 😀

          • Nie ma problemu, a ta funkcja była w dokumentacji do której polecam zawsze zaglądać 😉

          • JanekJr

            2 problemy kolejne xD sory że męczę xD
            1. Postać porusza się tylko gdy mysz się porusza lub gdy inny klawisz jest wciśnięty.
            2. Gdy postać zbliża się do lewego dolnego rogu, gra przechodzi do menu, nie zawsze tak jest, nie ogarniam tego 😛

          • hmm, ciekawe, musisz mi dać czas na potestowanie to bo „na sucho” bez faktycznego napisania kodu nie powiem ci dlaczego tak się dzieje, ale to dopiero jutro

          • JanekJr

            też się właśnie dziwię 😀 spoko 😀 tak po za tym dzięki za czas poświęcony i za poradnik, bo po za cpp0x.pl to w internetach za dużo uporządkowanej wiedzy o sfml nie za dużo po polsku, a w dodatku na cpp0x nie ma za dużo tego 😀

          • Kiedyś był jeszcze Wem, ale jego poradniki zniknęły jak ja zaczynałem pisać bloga, a na cpp0x też zaczynałem się uczyć i właśnie tamten nieco niedokończony poradnik natchnął mnie do napisania własnego. Poniższy kod nie posiada wymienionych przez ciebie problemów, tylko że przydałoby się dodać w nim jako nowy argument do player.idz delta_time (czas jaki minął pomiędzy klatkami) i przemnażać tą wartość przez prędkość poruszania się gracza, aby zawsze poruszał się tak samo ( sprite.move(vx*speed*delta_time,vy*speed*delta_time) )
            http://pastebin.com/8i8VTSD8

          • JanekJr

            Dzięki wielkie 😀 nawet nie wiesz (a może i wiesz) jaką radość przynosi dla gimbusa(III) wpajanie nowej wiedzy bez żadnego nacisku, i uczenie się tego co się chce a nie tego co trzeba 😀 jak mniej więcej wyglądałby ten kod z tym aby poruszał się zawsze tak samo? Jestem z gimbazy jeszcze i nie ogarniam co ty do mnie piszesz xD nie koniecznie chodzi mi o czysty kod ale o wytłumaczenie dokładne co gdzie i jak, jeśli można 😉

          • Kod wygląda następująco: http://pastebin.com/nbPFH8rj . Ale to o czym piszę nie wymaga wcale jakiejś zaawansowanej wiedzy, wystarczy uzmysłowić co oznacza słowo „delta”, czyli przyrost czegoś i resztę robi się intuicyjnie.

          • JanekJR

            Działa, i nawet rozumiem! 😀 i w player.h do metody idz trzeba dodać, float delta bo inaczej błąd wyskakuje. Jeśli to jest oczywiste to sory, dopiero się uczę 😀 kolejne rzeczy których nie ogarniam(naprawa jednego błędu generuje 2 następne xD)
            1. Po kliknięciu myszy, animacja „przeskakuje” sporo do przodu.
            2. Idąc w dół lub do góry, postać przyśpiesza w stosunku do tego jak szła w lewo bądź w prawo.

          • JanekJR

            2 to mój błąd, ponieważ ustawiłem Style::Fullscren, nie zmieniając rozdzielczości pikseli 😀
            a 1 zmieniłem tak, że float delta time, wpisałem poza while(!menu), a w while(window.pollEvent(event))
            dałem zdarzenie które uruchamia się po kliknięciu LPM i zeruje time=0 i delta_time na 1.
            Sęk w tym że wtedy delta_time nie ma żadnego zastosowania(chyba), ale i tak nie wiem po co ono dokładniej było 😀

          • Tak wygląda kod po zmianach i raczej działa: http://pastebin.com/K5BA13UF . Pamiętałeś żeby zmienić wartość zmiennej speed na większą?

          • JanekJR

            Dobra, mniejsza o to, zrobiłem co innego i działa 😀 ale…
            1. Gdy „ludzik” jest w miejscu kursora i każe mu iść zaczyna się „kręcić” z zawrotną szybkością. Aby to zmienić wystarczy zrobić mniej więcej tak: jeśli ludzik ma taką samą pozycję co kursor, to player.stop();
            Tylko że próbowałem to zrobić wszystkimi sposobami jakie mi wpadły do głowy i nic… Jakiś pomysł? 😀

          • Najprościej jest chyba sprawdzić to na kole, tzn niech pozycja gracza to będzie środek okręgu, jego promień to granica błędu wokół której gracz będzie stał. Dalej sprawa jest prosta, skoro wiesz jaki promień ma koło (okrąg) to można łatwo sprawdzić czy określony punkt do niego należy, czyli liczysz d tj. odległość pomiędzy myszą a pozycją gracza, jeżeli d > r, to gracz się porusza w danym kierunku. jeżeli nie to stoi.

          • JanekJr

            Jak wyznaczyć ten okrąg, bo nie za bardzo ogarniam?

          • JanekJR

            ma to związek z tym ile czasu czekamy, im dłużej tkwimy w bez ruchu tym dalszy „skok”

          • JanekJr

            Ale jednak chwila 😀 wersja 1 działa tak samo jak to co ja wysłałem, zaś wersja 2 ma taki problem że działa tylko gdy porusza się myszką 😛

          • Ponieważ aby ten event wciśnięcia został wykryty to musi być poza pętlą do zdarzeń (pollevent), http://pastebin.com/EgUJNc17

  • menhils

    Jak się pozbyć tego „rozpędzania” (po kliknięciu W nasza postać stoi w miejscu) prawdopodobnie po 7 klatkach dopiero rusza.

    • Tutaj należałoby w sumie zrobić coś o czym nie wspomniałem, a powinienem, czyli aby niezależenie od ilości fps w danej chwili postać poruszała się o taką samą wartość należy przemnożyć wartość o jaką się porusza przez delta time.

  • Patrycjerz

    Po co w klasie Player jest tekstura? Czy nie lepiej teksturę trzymać „na zewnątrz” i nie ładować jej ciągle przy tworzeniu obiektu?

    • W zasadzie można i nawet ma to większy sens przy większej ilości graczy. Pozdrawiam.

  • Nie musisz wyznaczać okręgu, tylko dałem tutaj uwagę, że można zauważyć, że „martwa strefa” myszki, czyli tak gdzie mysz jest nad graczem może przypominać koło, dalej sprawa jest dość trywialna (mam nadzieję że rysunek pomoże).

  • Someone

    Jedno pytanie. Przed wgraniem twoich plików z tej lekcji, wszystko mi działało spoko, a teraz jak wgrałem twoje i zmieniłem sobie z fullscreen na resize, to menu nie reaguje na to jak najedziesz myszką na którąś z opcji. Ani Play ani Exit nie działa jak powinno. Reaguje dopiero jak myszkę przesunę o pewną odległość w lewo a następnie w górę. Tak jak by napisy były wyświetlane gdzie indziej niż jest pozycja stringu. Jakiś pomysł jak to rozwiązać?

    • Someone

      No i jeszcze zapomniałem dodać, że na fullscreenie wszystko działa jak powinno.

      • Someone

        ahhh, sory że napisałem tutaj, miałem napisać w lekcji „Piszemy gre w SFML’u Lekcja 3 zmiana koncepcji”

    • Zdaje się, że przy odpowiedniej lekcji pisałem uwagę odnośnie tego. Przy Mouse::getPosition jako argument należy podać okno: Mouse::getPosition(window)

      • Someone

        Rzeczywiście, zapomniałem o tym 😀
        Dzięki wielkie 🙂

  • Karol

    Mam 2 pytania:
    1.Gdzie mogę znaleźć lekcje pierwszą?
    2.Dlaczego są 3 lekcje trzecie?

    • 1. Z menu powyżej: Poradniki => Piszemy grę w SFML’u => Lekcja 1; lub wybierz w katergirach Piszemy grę w SFML’u => Starsze Wpisy => Lekcja 1.

      2. W trakcie pisania poradnika postanowiłem zmienić nieco sam design gry.

      • Karol

        Dziękuję za odpowiedź

  • Krzychu

    Cześć, ten poradnik to genialna sprawa i dobra robota, ale mam problem ze zrozumieniem procedury idz(), nie potrafię sobie tego wizualnie rozrysować, jeśli byłby ktoś tak uprzejmy i wytłumaczył by mi te 2 linijki, w jaki sposób działają, co daje nam tam Pi etc., to będę bardzo wdzięczny 😉

    • Krzychu

      chodzi mi o te linie kodu
      flot vx = sin(( rotacja * M_PI ) / 180.0f );
      float vy = -cos(( rotacja * M_PI ) / 180.0f );

      • W ten sposób pobierasz wartości wektora przemieszczenia (znormalizowanego), dodatkowo kąty zamieniam na radiany.

        • Krzychu

          po przymusowej przerwie wracam do kursu, podszedłem do problemu jeszcze raz, przestudiowałem funkcje trygonometryczne od nowa. Zostało mi tylko jedno pytanie. W okręgu jednostkowym, współrzędne przecięcia prostej z okręgiem to (cos,sin), dlaczego podajemy wartości tutaj odwrotnie (sin,cos) ? Czy to dlatego że są to kąty ujemne?