Dev.log(2) – zmiana środowiska i pierwsza scena

W poprzednich częściach poradnika używaliśmy środowiska Xcode od Apple. Od tego momentu zdecydowałem, że lepszym rozwiązaniem dla mnie będzie CLion od JetBrains. Jest dla mnie zdecydowanie wygodniejszy i tylko dlatego to zrobię. Jeśli nie chcesz się z tym bawić lub nie interesuje cię środowisko, w którym piszę to zapraszam do czytania od pierwszego nagłówka.

Czemu to zrobiłem? Odpowiedź jest bardzo prosta – dla wygody. Studiuję i dość często wykorzystywałem Intellij do pisania aplikacji w Javie. Pisząc w Xcode zauważam, że wielu opcji i automatyzacji niektórych procesów mi brakuje. Z tego głównie powodu migruję. Jeśli tobie odpowiada oprogramowanie od Apple (czy czego używasz do pisania równo ze mną) to nie zmieniaj tylko dlatego, że ja tak powiedziałem. Tak się nie robi… Przejdźmy teraz do właściwej części wpisu.

Kamera! Akcja!

Jak w filmie, tak samo i w grze musimy mieć kamerę. Jest to wirtualny odpowiednik rzeczywistej kamery, która obserwuje dany obszar i wyświetla nam go na ekranie – w skrócie. No dobrze, ale jak to wygląda – spytacie? Nie wygląda – jest to wirtualny punkt, który obserwuje obszar, który programista mu wskaże. Nie będziemy zagłębiali się w rodzaje kamer w grach. W naszej grze użyjemy kamery równoległej, czyli takiej, która nie uwzględnia perspektywy w renderowaniu obiektów. Przykład znajduje się na obrazku poniżej.

Źródło: people.cas.sc.edu

Czemu nie będzie kodu dla kamery? Zwyczajnie nie ma go na początku. Z czasem pojawi się fragment odpowiedziany za renderowanie wielu warstw, ale aktualnie będziemy pracowali na jednej.

Pierwsza scena

Zbudujmy pierwszą scenę, która będzie wyświetlała logo gry na starcie. Jest to dość prymitywna forma, ponieważ nigdy się nie kończy i w trakcie jego wyświetlania nic się nie dzieje. Mamy za to obraz tego jak będzie wyglądała każda scena.

bool SplashScreen::create(RenderWindow &target) {
    if (!gameLogoTexture.loadFromFile(resourcePath() + "Caution.png")) {
        return false;
    } else {
        gameLogo.setTexture(gameLogoTexture);
    }

    Vector2u screenSize = target.getSize();
    gameLogo.setPosition(screenSize.x / 2 - gameLogoTexture.getSize().x / 2, screenSize.y / 2 - gameLogoTexture.getSize().y / 2);

    renderTarget = ⌖

    return true;
}

void SplashScreen::update(float deltaTime) {
    if (Keyboard::isKeyPressed(Keyboard::Right) || Keyboard::isKeyPressed(Keyboard::D)) {
        gameLogo.setPosition(gameLogo.getPosition().x + deltaTime * 80, gameLogo.getPosition().y);
    }

    if (Keyboard::isKeyPressed(Keyboard::Left) || Keyboard::isKeyPressed(Keyboard::A)) {
        gameLogo.setPosition(gameLogo.getPosition().x - deltaTime * 80, gameLogo.getPosition().y);
    }

    if (Keyboard::isKeyPressed(Keyboard::Up) || Keyboard::isKeyPressed(Keyboard::W)) {
        gameLogo.setPosition(gameLogo.getPosition().x, gameLogo.getPosition().y - deltaTime * 80);
    }

    if (Keyboard::isKeyPressed(Keyboard::Down) || Keyboard::isKeyPressed(Keyboard::S)) {
        gameLogo.setPosition(gameLogo.getPosition().x, gameLogo.getPosition().y + deltaTime * 80);
    }
}

void SplashScreen::draw() {
    renderTarget->draw(gameLogo);
}

Jak możesz łatwo zauważyć – są trzy główne metody oraz wskaźnik na okno renderowania:

bool create(RenderWindow &target);
void update(float deltaTime);
void draw();

RenderWindow* renderTarget;

Pierwsza z nich używana jest przy tworzeniu sceny. Wtedy też ładujemy tekstury, tworzymy obiekty potrzebne w danej scenie i zwracana jest wartość true po pozytywnie zakończonym procesie ładowania.

Kolejną metodą jest update(…), który odpowiada za wszelkie zmiany w danej scenie (zmianę pozycji, animacje, etc.). W tym przypadku możesz znaleźć w kodzie możliwość podstawowego sterowania pozycją loga na ekranie. Jako parametr przekazujemy deltaTime, czyli różnicę czasu między klatkami. Jest on potrzebny do regulacji płynności gry na różnych komputerach, z różnymi podzespołami. Jeśli zmiany w grze byłyby stałymi liczbami to wtedy gracz z mocniejszymi podzespołami miałby grę toczącą się szybciej niż gracz z podzespołami słabszymi.

draw() to metoda odpowiedzialna jest za rysowanie obiektów dla danej sceny. W tym przypadku tylko loga gry.

Ostatnia rzecz to pole renderTarget – pole przechowujące wskaźnik na główny obraz renderowany.

Jest już tak wiele, że fajnie byłoby sprawdzić czy program działa. Nastał więc moment pierwszego uruchomienia!

Licznik FPS

W lewym – górnym rogu widnieje licznik klatek na sekundę. Jest on zrealizowany za pomocą czasu z komponentu Clock z biblioteki SFML. Liczbę FPS (frames per second) liczymy w następujący sposób.

void WindowManager::displayLoop() {
    float deltaTimeFloat = 0;
    while (renderWindow.isOpen()) {

        ...

        /***
         * FPS COUNTER
         */
        float fps = 1.f / deltaTimeFloat;

        ... wyświetalnie liczby klatek na sekundę ...

        deltaTimeFloat = deltaTime.restart().asSeconds();
    }
}

Linijka odpowiedzialna za wyliczenie wartości zmiennej fps opiera się na wielkości fizycznej – częstotliwości. Tyle, nie ma w tym żadnej zaawansowanej logiki.

Podsumowanie

W tym wpisie mogłeś dowiedzieć się jak stworzyć swoją pierwszą scenę oraz jak policzyć ilość wyświetlanych klatek na sekundę. W kodzie przemyciłem również prosty sposób na przemieszczanie obiektów za pomocą klawiatury. To już będzie na tyle w tym wpisie. Co będzie dalej? Zaczniemy „realizować” karteczki z tablicy, którą przedstawię wam w jednym z kolejnych wpisów o zarządzaniu projektami.

Jak zawsze kod znajdziesz na moim GitHubie. Pamiętaj o społecznościówkach i…

… do następnego!

#shareShare on FacebookShare on Google+Tweet about this on TwitterShare on TumblrPin on PinterestShare on LinkedInShare on VKShare on RedditEmail this to someone