Lekce 6 - Programujeme Android hru - Úvaha nad obrazovkami
V minulé lekci, Programujeme Android hru - Základní vestavěné třídy podruhé, jsme si ukázali vykreslování základních geometrických tvarů.
Při tvorbě jakékoli appky pro Android narazíme na problém s přizpůsobením pro mnoho displejů. Musíme se vypořádat s tím, že naše hra bude běžet na zařízeních s rozdílným rozlišením a poměrem obrazovky. Uvažujme tři konkrétní příklady tabletů:
- Prestigio Multipad PMP 5780D s rozlišením 1024 x 768 pixelů tj. poměr 4:3
- Samsung Galaxy Tab SM-T535NYKAXEZ s obrazovkou 1280 x 800 px tj. poměr 16:10
- Alcatel One Touch Pop7 P310X s 1024 x 600 tj. téměř poměr 16:9
Problém nám v libgdx pomáhá řešit třída OrthographicCamera, díky které si v její instanci můžeme nastavil virtuální rozlišení, v této souvislosti mě napadají tři možnosti, jak orthocameru nastavit:
1. Nic neřešit a prostě orthokameře poručit svoje rozlišení. V tomhle vidím velký nedostatek, pokud například na obrazovce se skutečným poměrem obrazovky 16:9 nastavím rozlišení s poměrem 4:3, tak se obrázek neúměrně roztáhne, pokud to udělám opačně, tak se neúměrně smrskne a také se deformuje.

2. Ve virtuálním rozlišení využít prostě pouze poměr 4:3, protože předpokládám, že valná většina displejů je právě minimálně v poměru 4:3 (Všechny? Patří sem přece i množina 16:9.). No a zbývající pixely, které zůstanou navíc nad tento poměr, prostě nechat prázdné, či do nich umístit reklamu. Nazval bych to "preferovat obrazovky s poměrem 4:3". Jako výhodu tohoto postupu vidím jednoduchost, jako nevýhodu vidím to, že naše appka nechá na displejích s poměrem obrazovky větším než 4:3 nevyužité místo.

3. Další možnost bych nazval "preferovat širokoúhlé obrazovky" a spočívala by v tom, že obrázek v poměru např. 16:9 bych na obrazovce např. 4:3 zobrazil tak, že ho celý výrazně zmenším, ale stále bude v poměru 16:9, avšak dosti zmenšený na to, aby se vešel na obrazovku 4:3. Nevýhodu tohoto postupu vidím v tom, že uživatelé s displeji např. 4:3 uvidí obrázek dosti zmenšený (přijdou o mnoho detailů) a také v tom, že bude zbývat nějaké nevyužité místo. Naopak velkou výhodu spatřuji v tom, že budou využity "širokoúhlé" obrazovky v plné své síle a to bude také cesta, kterou se vydám, přičemž co je "širokoúhlé" a co už není, jsem si stanovil sám a to tak, že obrazovku, která má poměr 1,6 a více považuji za "širokoúhlou" a obrázky na ní nezmenšuji a na obrazovce, která je pod touto hodnotou obrázky zmenšuji. Tuto hodnotu jsem zvolil záměrně, protože výrobci tabletů, se kolem tohoto čísla točí opravdu často a také protože osobně považuji obrazovky s poměrovou hodnotou pod 1,6 za jakýsi "podstandard". Takže obrazovky 16:9 nebudou o detaily ochuzeny. Obrazovky 16:10 budou plně využity a to co je více a více pod 16:10 bude více a více ochuzeno, avšak stále funkční.

Už jsem toho zase napsal dost, je čas jít něco nakódovat. Otevřeme si třídu GameScreen.java z minulého dílu a odstraníme v ní všechny proměnné, všechno z konstruktoru a vše také vyprázdníme z metody render (float delta). Přidáme importy (klávesová zkratka, aby nám naopak zmizely), takže to máme nějak takhle:
package com.wackychicken.screens; import com.badlogic.gdx.Screen; public class GameScreen implements Screen{ public GameScreen(){ } @Override public void show() { // TODO Auto-generated method stub } @Override public void render(float delta) { } @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 } }
Do třídy si přidáme kód pro obsluhu nastavení obrazovky a celá třída GameScreen.java tak bude vypadat následovně:
package com.wackychicken.screens; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Screen; import com.badlogic.gdx.math.Vector2; public class GameScreen implements Screen{ private final float SCREENRATIO = 1.6f; private float orthoWidth,orthoHeight; private final Vector2 DEMANDED_SCREEN,SCREEN_BOUND_BEGIN,SCREEN_BOUND_END; public GameScreen(){ DEMANDED_SCREEN=new Vector2(800,500); float w = Gdx.graphics.getWidth(); float h = Gdx.graphics.getHeight(); if(w/h>=SCREENRATIO)//je to sirokouhly? { orthoWidth = w * (DEMANDED_SCREEN.y / h); orthoHeight = DEMANDED_SCREEN.y; SCREEN_BOUND_BEGIN=new Vector2( (orthoWidth - DEMANDED_SCREEN.x)/2, 0); SCREEN_BOUND_END=new Vector2(SCREEN_BOUND_BEGIN.cpy().add(DEMANDED_SCREEN)); } else { orthoHeight = h * (DEMANDED_SCREEN.x / w); orthoWidth = DEMANDED_SCREEN.x; SCREEN_BOUND_BEGIN=new Vector2(0, (orthoHeight - DEMANDED_SCREEN.y) / 2); SCREEN_BOUND_END=new Vector2(SCREEN_BOUND_BEGIN.cpy().add(DEMANDED_SCREEN)); } } @Override public void show() { // TODO Auto-generated method stub } @Override public void render(float delta) { } @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 } }
Uložíme, přidáme importy a zkusíme si spustit, zda nám Eclipse nevyhodí chybu. Zatím nebude v okně nic vidět, zatím nic nevykreslujeme. Virtuální rozlišení máme tímto ošetřené a jdeme trošku plánovat naší hru více do tříd. Podle naší dohody vytvořte:
- nový balík com.wackychicken.gameobjects a v něm třídu Chicken.java
- nový balík com.wackychicken.managers a v něm třídy AssetManager.java, GameManager.java, InputManager.java, ObjectManager.java
- nový balík com.wackychicken.rendering a v něm třídu Renderer.java
tady je obrázek pro orientaci:

Třídy zatím ponecháme prázdné, psát do nich budeme od dalšího dílu, dnes si ještě řekneme, co budou mít jednotlivé třídy na starost, i když už je to patrné z jejich názvů.
- Chicken.java - vytvoří instanci našeho hloupého kuřete
- AssetManager.java - stará se o naloadování zdrojů do paměti (obrázky, zvuky)
- GameManager.java - stará se o enum stav hry (ready,running,game over), počítá skóre
- InputManager.java - obsluhuje události uživatelského vstupu, v našem případě to bude klik myší nebo dotyk prstem
- ObjectManager.java - obsluhuje všechny pohyblivé objekty ve hře (slepici a žrádlo)
- Renderer.java - promítačka, vykreslovač na virtuální obrazovku
Téměř všechny instance těchto tříd nám rozhýbe již předtím vytvořená třída GameScreen.java. To by bylo pro dnešek vše.
Příště, Programujeme Android hru - Rozdělení hry do tříd I, navážeme. Celý zdrojový kód je samozřejmě přiložen.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 17x (631.92 kB)
Aplikace je včetně zdrojových kódů v jazyce Java