#Piszemy gre w SFML’u – Lekcja 1

Witam w pierwszej lekcji, w której zajmiemy się ogólną koncepcją gry, jak będzie wyglądał mniej więcej schemat gry oraz napiszemy proste menu.

!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 tej i kolejnych lekcji znajdziesz na samym dole poszczególnych wpisów, w razie błędów, problemów czy pytań zostaw komentarz, a wtedy na pewno odpowiem.

Założenia

  • nasza gra będzie grą 2D z widokiem top down,
  • gra będzie survivalem, naszym celem będzie przetrwanie jak najdłużej,
  • wsparcie sterowania padem,
  • sterowanie będzie się odbywało za pomocą klawiatury i myszki lub za pomocą pada,
  • warto aby gra miała jakiś tytuł, dlatego zamiast myśleć długo nad nazwą skorzystałem z Video Game Name Generator, nazwa naszej gry to: Mechanized Techno Explorer.

Powyższe wymagania spełnimy w trakcie tutoriala, a także dojdą nowe które będziemy uzupełniać na bieżąco. Skoro mamy założenia to jak będzie prezentowała się struktura klas?

Jak widać jest to dopiero sam początek, teraz opisze co która klasa będzie robić: na początku funkcja main() uruchomi klasę Game, która będzie odpowiadała za działanie gry (np wyświetlanie menu, uruchomienie właściwej gry, etc).

Engine zajmie się wyświetleniem obiektów na scenie oraz sprawdzi czy nie zachodzi kolizja.

Zaczynamy kodzić!


Ja korzystam z Visual Studio 2010, a więc tworzę nowy projekt oraz ustawiam projekt tak aby dział na nim SFML zgodnie z instrukcjami na stronie autorów (instrukcje w języku polskim dla: Visual Studio / CodeBlocks).

Czas na pierwsze linie kodu:

Skoro mamy już funkcję główną to czas na klasę Game, która będzie zarządzała naszą grą, na początku czas na deklaracje (zaraz wszystko wyjaśnię co będzie do czego służyło)

Na początek zajmijmy się analizą enum GameState , jest to jak sama nazwa wskazuje lista wyliczeniowa statusu gry, będzie nam służyła do określenia tego, czy okno gry ma pokazywać teraz menu, grę, itp.

Font font przechowuje dla nas czcionkę.

Pierwszą „funkcją” będzie konstruktor. To co robi widać na pierwszy rzut oka: ustawia status gry na koniec gry, ładuje czcionkę jeśli jej nie znajdzie to wyrzuca komunikat o błędzie oraz zwraca nic kończąc tym samym grę. Jeśli wszystko się powiedzie ustawia status gry na MENU.

void runGame()  to metoda, którą już znamy z funkcji main() gdzie jej użyliśmy do uruchomienia naszej gry, wygląda ona tak:

Jak widać kod jest prosty, jest to to pętla która wykonuje się dopóki status gry nie zostanie ustawiony na END, w zależności od wcześniejszego wyboru użytkownika uruchamia odpowiednią funkcję.

Następną i zarazem ostatnią metodą jest void menu(), która uruchamia i wyświetla menu, przyjrzyjmy jej się nieco bliżej:

Na początku nadajemy nagłówek naszej grze po czym ustawiamy go na środku ekranu korzystając z prostej zależności:

(szerokość_ekranu / 2) – (szerokość_napisu / 2)

To samo robimy z napisami „Menu” oraz „Exit”, przy czym zostawiliśmy sobie zmienną dzięki której jeśli będziemy chcieli będziemy mogli łatwo dorobić kolejny napis (tak chodzi mi tutaj o const int ile ).

Dalej widzimy pętle która sprawia że menu się wyświetla, dopóki użytkownik chce być w menu.

W naszej pętli tworzymy obiekt Vector2f mouse, który pokazuje nam aktualną pozycję myszy na ekranie, jest to wygodniejsze niż pisanie za każdym razem Mouse::getPosition(), mimo że getPosition() zwraca nam pozycje jako Vector2i, my skorzystamy z Vectora2f, ponieważ większość metod z niego korzysta.

Mouse::getPosition() ma jeszcze opcjonalny argument jakim jest okno, względem którego będzie liczona pozycja myszy, tzn w przypadku pełnego ekranu możemy zostawić to pole puste bo pozycja myszy będzie liczona względem pulpitu, lecz jeśli będziemy chcieli napisać grę w oknie to aby wszystko działało poprawnie musimy dodać jako argument nasze okno, czyli Mouse::getPosition(okno).

Kod poniżej sprawia że gdy najeżdżamy kursorem nad napis to podświetla on się na inny kolor.

Screen poniżej przedstawia nasze dzieło w pełnej okazałości

Kod z tej lekcji:
>>POBIERZ<< | >>GitHub<<


  • Sebastian

    Mam pytanie, przy zapisie case GameState::Menu: wyskakuje mi błąd ponieważ „GameState” nie jest klasą ani przestrzenią nazw (namespace), używam Code::Blocks’a, jaka może być przyczyna?

    • w takim razie spróbuj zamiast „GameState::Menu” napisać „Menu”

      • Sebastian

        Ruszyło 😉 Dzięki wielkie za odpowiedź 😉 Aczkolwiek chyba najpierw ruszę kurs podstawowy ;-). Dzięki wielkie, za ten kurs, odwalasz kawał dobrej roboty 😉

        • Dzięki za opinię 😉

  • Bartek

    Czemu po tej lekcji wyskakuje bug i drugi problem przepisuje fragment kodu od lii 27 i 39 metody game::menu i nie chce sie kompilowac a kiedy skopiuje ctrl c ctrl v wszystko dziala
    jaka jest przyczyna?

    • Czy może Pan bardziej rozwinąc wypowiedź odnośnie tego jaki bug wyskakuje(„Czemu po tej lekcji wyskakuje bug”), a co do drugiego problemu to jedyna przyczyna jaka przychodzi mi do głowy bez znajomości logu błędu to złe przepisanie kodu (gdybym miał log błędu w np pastebin.com to mógłbym prawdopodobnie powiedzieć coś więcej o przyczynie błędu). Pozdrawiam

      • Bartek

        z tym bugiem to chodzi o to ze gra sie kompiluje ale zaraz wyswietla sie komunkat acces violaion i trzeba zamknac, odnosnie drugiego problemu

        Error 5 error C2275: ‚sf::Event’ : illegal use of this type as an expression d:sfml_allegrogragragragame.cpp 67 1 gra

        Error 6 error C2228: left of ‚.KeyPressed’ must have class/struct/union d:sfml_allegrogragragragame.cpp 67 1 gra

        takie bledy,, choć kod przepisuje slowo w słowo tak jak w tutorialu

      • Bartek

        ok juz dziala, skonfigurowalem od nowa i skopiowalem caly kod

        • W takim razie cieszę się, że sobie poradziłeś z tymi błędami, w razie kolejnych problemów jak zwykle zapraszam do systemu komentarzy pod odpowiednią lekcją 😉

  • Konrad

    Dobra lekcja, mam problem z synchronizacją myszki z grą tzn. o ile korzystam z sf::Style::Fullscreen to nie ma problemu, ale gdy chcę mieć grę w okienku to buttony „łapią”, że się nie najeżdrza w zupełnie innym miejscu niż się znajdują, są tak jakby przesunięte. Da się coś z tym zrobić?

    EDIT: Dobra nvm ogarnąłem wystarczy w argumencie funkcji getPosition(okno) podać nazwę wcześniej zdefiniowanego okna. Może komuś się to przyda.

  • Kamil

    2 pytanka mam. Da się tu jakoś w prosty sposób zrobić tło, które by obejmowało całe okno bez względu na to czy jest w trybie okienkowym lub pełnym, oraz bez względu na wielkość okna? Oraz drugie: Jeśli wyświetlimy animowanego gifa, to czy będzie on animowany?

    • 1. Jeżeli masz tło o wymiarach np 1280×720 i chcesz aby ono się przeskalowywało do innych rozmiarów to najprawdopodobniej pomoże ci klasa sf:View. 2. Nie, SFML nawet nie wczyta ci gifa, jedyny sposób w jaki można animować to za pomocą sprite’ów o czym napisałem w jednej z kolejnych lekcji.

  • Dawid

    Takie pytanie. Czy jeśli zrobię okno
    RenderWindow window(VideoMode(1920,1080),”Gra”,Style::Fullscreen);
    I zdefiniuje inta int windowWidth = window.getSize().x;
    to czy jeśli mój kolega ma monitor np. 1280×720 to czy ten int będzie miał 360, 540 czy się coś zepsuje?

    • Jeżeli zrobisz okno 1920×1080 i twój znajomy będzie miał mniejszy ekran to w przypadku Fullscreen SFML postara się to przeskalować. Ciężko mi powiedzieć jak dokładnie zachowa się SFML, aby mieć 100% pewność że np nuttony, czy elementy gui będą na swoim miejscu przy dowolnych rozmiarach ekranu to praca na znormalizowanych wartościach [0,1]

  • maxi

    How change the size pf window if you don’t play in full screen please ?

  • Szymon

    Dlaczego przy pierwszym wystąpieniu identyfikatora window, linia 27 void Game::menu(), występuje mi błąd undeclared identifier, mimo że w includach mam ?

    • Biblioteka windows.h nie jest potrzebna, możesz podać cały log błędu?

  • Ignoscite_Mihi

    Witam. Na podstawie wyżej napisanego programiku chciałem napisać swój własny lecz po wklepaniu kodu zaznaczonego komentarzem przestaje działac. Mogę liczyc na pomoc?
    Kod:
    http://wklej.org/id/1708012/

  • To zaprzestanie działania w jaki sposób się objawia? Tzn. Program po prostu kończy pracę, wyskakuje jakiś błąd przy kompilacji/debugowaniu? Jeżeli tak to jaki to błąd?

    W tym kodzie najbardziej prawdopodobnym błędem jest próba dostępu do obszaru pamięci, który już nie istnieje, albo po prostu nie istnieje, poniżej przedstawiam najbardziej prawdopodobne sytuacje błędów w kodzie przy tekście. http://pastebin.com/RtbvCkQg

    W każdym razie czekam na dalsze informacje odnośnie twojego problemu.

    • Ignoscite_Mihi

      Program nie kompiluje się. Nie wydaje mi się żeby problem leżał po stronie fontu ze względu na to, że sam tekst pojawia się na ekranie a zostawienie linijki „tekst[i].setColor(sf::Color::Red);” która jest na końcu zmienia kolor napisu poprawnie.
      Mam wrażenie że coś jest nie tak z „Vector2f mysz (…);” oraz jest to powiązane z „tekst[1].getGlobalBounds().contains(mysz)”. Próbowałem również zapisu (…).contains(mysz.x, mysz.y)

      Tu jeszcze niezbyt zrozumiały dla mnie build.
      http://wklej.org/id/1708113/

      • Moje uwagi dotyczyły kompiluje się, ale wyłącza po uruchomieniu. Tutaj wina leży w czym innym, zawsze możesz zmienić Vector2f na Vector2i, które i tak jest zwracane przy pobieraniu pozycji myszy, jedyny powód dla którego używam wersji float to fakt, że w SFML częściej trzeba podawać wersję float niż int i nie trzeba się wtedy bawić w rzutowanie.

        W razie dalszych problemów napisz mi na maila.

  • Mateusz Keller

    Dlaczego jak usunie sie „Style::Fullscreen” i skompiluje program zmiana koloru przy najechaniu na napis nie dziala?

    • Ponieważ pobieramy pozycję myszy względem pulpitu, a nie okna. Aby działało w każdym przypadku przy pobieraniu pozycji myszy należy dodać jako argument okno (o czym było napisanie zarówno w lekcji, jak i można było przeczytać w komentarzach poniżej): Mouse::getPosition(window).

  • Atki

    „1>main.obj : error LNK2028: unresolved token (0A000407) „public: __thiscall Game::~Game(void)” (??1Game@@$$FQAE@XZ) referenced in function „int __cdecl main(void)” (?main@@$$HYAHXZ)
    1>main.obj : error LNK2019: unresolved external symbol „public: __thiscall Game::~Game(void)” (??1Game@@$$FQAE@XZ) referenced in function „int __cdecl main(void)” (?main@@$$HYAHXZ)”

    przy kompilowaniu wyskakują mi takie oto błędy, jakiś pomysł jak można je rozwiązać?

  • Konrad Naklicki

    Mam parę pytań:
    1) Gdzie umieszczam folder data w którym mam np. czcionkę „mecha”?
    2) Wyskakuje mi błąd dla MessageBox: nie może skonwertować stringa do LPCWSTR w drugim parametrze. „ERROR” też mi podświetla..
    3)

    Vector2f mouse(Mouse::getPosition(/*możemy tutaj podać okno*/));
    Event event;

    while(window.pollEvent(event))
    {

    Tutaj zaznacza, że window (przed poliEvent) powinno być strukturą, unią, klasą itp. Dlaczego?

    • 1) W folderze głównym projektu lub w tym samym folderze co plik wykonywalny (zależy od IDE, w VisualStudio jest to miejsce gdzie leży kod źródłowy, w Qt Creator to folder Debug, w C::B nie mam pojęcia).
      2) Aby odpowiedzieć na dalsze pytania musisz mi powiedzieć: czy zmieniałeś kod, kopiowałeś go ze strony lub czy pobrałeś i dołączyłeś ten z pliku (zakładam, że błąd mógł się wkraść podczas kopiowania ze strony); jakie IDE posiadasz? Na jakim systemie operacyjnym piszesz (zakładam że Windows).
      3) Mogłeś źle zlinkować projekt, musisz mi podać więcej szczegółów abym mógł ci pomóc.

      • Konrad Naklicki

        Dzięki za szybką odpowiedź!
        1) Folder umieściłem tam gdzie .dll z SFML, mam nadzieję że będzie dobrze.
        2) Nie zmieniałem kodu poza ścieżką do fonta (ale bez tej zmiany i tak wyskakuje to samo). Używam Visual Studio 2012 na Windowsie 7 🙂
        3) Czy definicje funkcji, konstruktorów itp daje się w pliku Game.cpp? Bo ja dałem do game.cpp includując tym samym #include „Game.h”. Zasugerowałem się tym co stworzył mi Visual Studio kiedy dodałem nową klasę (dwa pliki Game.h i Game.cpp z zalążkiem definicji)

        EDIT: pobrałem kod i podmieniłem pliki, od razu zniknął problem numer 3. Poradziłem sobie również z problemem nr 1. Co do problemu nr 2 – może były jakieś zmiany w kolejnych wersjach sfml? Ja używam 2.3.2 – ma to jakieś znaczenie? P.S. dzięki za pomoc

  • Stett

    Przy deklarowaniu czcionki w klasie Game i ładowaniu jej z pliku w Game::Game(void), pojawia się błąd: „missing type specifier – int assumed. Note C++ does not support default-int” oraz ” ‚[Nazwa czcionki]’: unknown override specifier”. W związku z tymi błędami pojawia się też „left of ‚,loadFromFile’ must have class/struct/union”, co jest raczej zrozumiałe.

    Jaki popełniam błąd?

    Dodam, że deklaracja i ładowanie umieszczone w Game:menu() działa poprawnie.

    • Mówiąc szczerze nie mam pojęcie, i takie info: „!UWAGA! Kurs leży w kategorii ‚Obsolete’ co oznacza, że może być nieaktualny, zawierać błędy i nie polecam z niego korzystać.” Ten poradnik stracił moje wsparcie.