4. díl - Programujeme Android hru - Render, delta a FPS

Java Android Programujeme hru Programujeme Android hru - Render, delta a FPS

Ahoj,
uděláme si kafe, hodíme se do pohody a pozvolna navážeme na minulý, tedy třetí díl, ze kterého již známe metody:

  • Gdx.app.log("tag", "nase zprava");
  • Gdx.graphics.get­Width();
  • Gdx.graphics.get­Height();

a třídy:

  • BitmapFont;
  • SpriteBatch;

V dnešním díle si vysvětlíme základní pojmy render, delta a fps, ale nejdříve začneme menší "úmluvičkou", protože na ní přichází čas právě teď.

ÚMLUVIČKA

Abych nemusel neustále dokola opakovat to samé, tak se dohodněme, že když napíšu:

  1. "Přidáme importy", tak to bude znamenat, že prostě stiskneme klávesovou zkratku ctrl + shift + O a Eclipse si potřebné importy provede samo.
  2. "Vytvoříme si nový balíček", bude o tom, že v projektové složce core, klikneme pravým tlačítkem myši na podsložku src a tím vyvoláme nabídku, ze které vybereme položku New a poté Package, do kolonky name zadáme jméno balíčku a potvrdíme. Vyzkoušejte si vytvořit balíček s libovolným názvem a pak ho vymažte kliknutím na něj pravým tlačítkem a položkou delete.
  3. "Vytvoříme si novou třídu", znamená, že v projektové složce core, klikneme pravým tlačítkem myši na balíček, ve kterém chceme třídu vytvořit. Vyskočí na nás nabídka, ze které vybereme položku New a poté Class, do kolonky name zadáme jméno naší nové třídy, které bude vždy začínat velkým písmenem a potvrdíme. Opět si vyzkoušejte, vytvořit balíček a do něj třídu a pak celé vymazat. Je jasné, že když vymažeme balíček, tak vymažeme i všechny třídy v něm obsažené. V jazyce Java je konvencí, že se něčím sobě společné třídy umisťují do balíků.
  4. "Přidat zdroj(e), obrázek, zvuk nebo hudbu", bude to vždy přidání souboru do projektové složky android do podsložky assets, všimněte si, že je tam vestavěný soubor badlogic.jpg a je to právě ten smajlík, který se nám objevil v druhém díle, v případě, že se nám projekt vygenerovaný knihovnou Libgdx podařilo v Eclipse rozjet.
  5. "Spustíme", myslím tím, že spustíme složku destkop jako Java aplikaci přímo v Eclipse (Run as - Java Application)

Přejdeme tedy dále. Spustíme Eclipse a v projektové složce core si otevřeme naší třídu WackyChicken.java a provedeme několik úprav, na následující kód:

package com.wackychicken;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;

public class WackyChicken extends Game {


        @Override
        public void create () {
                Gdx.app.log("WackyChicken", "started Libgdx");
                setScreen(new GameScreen());
        }

        @Override
        public void render () {
        super.render(); //dulezity!!!
        }

        @Override
    public void dispose() {
    super.dispose();
    }
}

Přepíšeme kód uvedený výše do naší třídy WackyChicken.java (ctrl+c & ctrl+v), nezapomene přidat importy a uložit. Eclipse nám vyčte, že nezná třídu GameScreen - to je zatím v pořádku.

Neexistuje třída GameScreen

Jen zevrubně co jsme změnili a proč. Namísto ApplicationAdapter jsme dali Game, protože třída Game je v Libgdx speciální třída pro hry, obsahující metodu setScreen, zatímco ApplicationAdapter ji neobsahuje. Metoda setScreen nám vytvoří herní obrazovku podle našich požadavků. Odstranili jsme proměnné, které již nepotřebujeme. Změnili a odstranili jsme Gdx.app.log. V metodě render() jsme vše smazali a nechali pouze řádek super.render(), klíčovým slovíčkem super voláme metodu v nadtřídě, ale tím se nyní nezabývejme. Jako poslední jsme přidali metodu dispose(), která volá metodu stejného jména v nadtřídě a slouží k vyklizení paměti od nepotřebných zvukových a grafických objektů.

Nyní otestujeme, zda si pamatujete dohodičku o balíčku/třídě. Vytvoříme si nový balíček, pojmenujeme ho com.wackychic­ken.screens a do tohoto balíčku si rovnou vytvoříme novou třídu, kterou pojmenujeme GameScreen. Jak již sami názvy vypovídají, balíček bude obsahovat třídy starající se o obrazovky a třída GameScreen potom (nejen) o herní obrazovku. Tuto třídu si otevřeme a za slova public class GameScreen dopíšeme implements Screen. Eclipse vyhodí chybu, že rozhranní Screen nezná, přidáme tedy importy.

Nicméně Eclipse vyhodí další hlášku, že GameScreen musí implementovat jisté metody, uděláme to automaticky a to tak, že klikneme levým tlačítkem myši na žárovku. Vyskočí na nás nabídka a z ní vybereme Add unimplemented methods, potvrdíme a Eclipse za nás požadované metody doplní. Uložíme si.

Přidat neimplementované metody

Přeskočíme zpět do třídy WackyChicken.java a přidáme importy. Eclipse nám přidá řádek import com.wackychic­ken.screens.Ga­meScreen s naší třídou, která se nám bude starat nejen o obrazovku. Uložíme a měli by nám zmizet všechny chyby. Pokud se tak stalo, můžeme třídu WackyChicken.java zatím zavřít a věnovat se slíbeným pojmům uvedeným na začátku tohoto dílu. Dnes se již budeme věnovat pouze námi vytvořené třídě GameScreen.java, přejdeme do ní a do začátku třídy si přidáme nám již známé proměnné:

private BitmapFont font;
private SpriteBatch batcher;
private String ourText;
private float storedDelta;
private int counter;

a dále konstruktor, ve kterém si tyto proměnné inicializujeme:

public GameScreen(){
font = new BitmapFont();
batcher = new SpriteBatch();
ourText = "Toto je nas prvni text, ktery vypiseme na obrazovku.";
storedDelta=0;
counter=0;
}

do metody render napíšeme:

public void render(float delta) {

Gdx.gl.glClearColor(0, 0, 0, 1); // pozadi nastavime na pozadovanou barvu
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batcher.begin();
batcher.setColor(1, 1, 1, 1);
font.draw(batcher, ourText ,50,450);
batcher.end();

    if(counter<100)
    {
    storedDelta+=delta;
    counter++;
    }

    else
    {
    batcher.begin();
    font.draw(batcher, "Delta: "+ storedDelta/100 ,50,400);
    batcher.end();
    }
}

Uložíme, přidáme importy a spustíme. Očekávám, že to funguje tak, že do proměnné storedDelta uložíme jakýsi součet sta hodnot delta. Po zvýšení proměnné counter na hodnotu 100 tento součet vydělený hodnotou 100 vypíšeme na obrazovku a obdržíme jakýsi průměr hodnoty delta. V mém případě na tabletu to bylo nějakých 0.0184 sekundy.

Delta time

Delta je krátký časový úsek, který uběhne mezi dvěma po sobě jdoucími voláními funkce render. Z tohoto si odvodíme počet snímků za sekundu otázkou, kolik těchto krátkých časových úseků se vejde do jedné sekundy? V mém případě to bylo 1 / 0.0184 tedy přibližně 54 snímků za sekundu. Musíme si ale uvědomit, že toto volání je zatím naprázdno, nic moc se zatím nevykresluje a zatím se neobsluhují žádné herní objekty. Delta má normalizační schopnost, představte si, že na násobek delty navážete pohyb třeba autíčka ve hře, no a když bude horší hardware, tak se delta zvětší, tím se zvětší i tento součin a tím se urychlí i pohyb autíčka. V opačném případě, kdy bude silný hardware, bude sice delta menší, ale zato bude funkce render volána častěji, takže autíčko se na rozdílném hardwaru bude pohybovat podobně. Samozřejmě tohle funguje do určité míry, v extrému by pohyb byl silně trhaný a naše hra by nebyla hratelná.

To je pro dnešek vše, příště si vykreslíme na obrazovku pár základních obrazců. Níže nechávám výpis, jak by měla vypadat celá naše třída GameScreen.java, celý zdrojový kód je ke stažení.

package com.wackychicken.screens;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GameScreen implements Screen{

        private BitmapFont font;
        private SpriteBatch batcher;
        private String ourText;
        private float storedDelta;
        private int counter;

        public GameScreen(){

        font = new BitmapFont();
        batcher = new SpriteBatch();
        ourText = "Toto je nas prvni text, ktery vypiseme na obrazovku.";
        storedDelta=0;
        counter=0;

        }


        @Override
        public void show() {
                // TODO Auto-generated method stub

        }

        @Override
        public void render(float delta) {

                Gdx.gl.glClearColor(0, 0, 0, 1); // pozadi nastavime na pozadovanou barvu
                Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
                batcher.begin();
                batcher.setColor(1, 1, 1, 1);
                font.draw(batcher, ourText ,50,450);
                batcher.end();

                if(counter<100)
                {
                storedDelta+=delta;
                counter++;
                }

                else
                {
                batcher.begin();
                font.draw(batcher, "Delta: "+ storedDelta/100 ,50,400);
                batcher.end();
                }
        }

        @Override
        public void resize(int width, int height) {
                // TODO Auto-generated method stub

        }

        @Override
        public void pause() {
                // TODO Auto-generated method stub

        }

        @Override
        public void resume() {
                // TODO Auto-generated method stub

        }

        @Override
        public void hide() {
                // TODO Auto-generated method stub

        }

        @Override
        public void dispose() {
                // TODO Auto-generated method stub

        }

}

 

Stáhnout

Staženo 2x (626.84 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

  Aktivity (3)

Článek pro vás napsal Jaroslav Polívka
Avatar
Autor se věnuje převážně jazykům JAVA a C++

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 



 

 

Komentáře

Avatar
Ján Kucan
Člen
Avatar
Ján Kucan:

ale libgdx vypočítava delta čas aj sám a dá sa zavolať metódou Gdx.graphics.get­DeltaTimer();

 
Odpovědět 4. června 17:08
Avatar
Odpovídá na Ján Kucan
Jaroslav Polívka:

jo myslíš asi Gdx.graphics.get­DeltaTime(), no tu metodu sice zavolat můžu, ale nechápu proč, když přece delta je implicitně obsažena v parametru metody render, která je volána neustále dokola. Tady jde přece o to, zaznamenat do proměnné nějaký počet okamžiků delta (zde v příkladu 100) než se řádně rozjede VM a pak z toho udělat nějaký průměr, který nám dá nějakou vypovídací hodnotu. Znovu zdůrazňuji, že deltu nepočítám, ta je parametrem fce render, já jí pouze ukládám do proměnné. Hlavně tady o to vůbec nejde, šlo mi o to, prezentovat, co vůbec delta je a naznačit, že zde může probíhat "herní smyčka". V kontextu projektu, kde mám určitou vymyšlenou objektovou šablonu, vestavěnou metodu Gdx.graphics.get­DeltaTime() nebudu zřejmě ani potřebovat.

Odpovědět 4. června 23:52
Velice často si věci žijí svým životem
Avatar
berykubik
Člen
Avatar
berykubik:

Pro zájemce doporučuju tenhle skvělý článek o tom, co vlastně ta delta a timestep znamenají jaké jsou alternativy pro jejich výpočet: http://gafferongames.com/…ur-timestep/.

 
Odpovědět 10. června 9:43
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.