Grafikai elemek azonos arányú megjelenítése felbontás függetlenül különböző felbontású és képarányú Android készülékeken

Posted by | · · · · | Szoftverfejlesztés | Nincs hozzászólás a(z) Grafikai elemek azonos arányú megjelenítése felbontás függetlenül különböző felbontású és képarányú Android készülékeken bejegyzéshez

A mobil eszközökre (kiváltképp Androidos) való fejlesztés során folyamatosan felmerül a kérdés: “Vajon az összes készüléken ugyanúgy fog kinézni a játékom?”

A válasz igen, ha figyelmesen és alaposan betartunk néhány szabályt.


Ezzel a problémával akkor találkoztam először amikor  a Sprinkfield című játékunk iOS verzióját kezdtem átportolni Android platformra.

Azt mindannyian tudjuk, hogy az Android készülékek felbontása igencsak változatos, megszámlálhatatlan különböző felbontású készülék van a piacon, a képarányokról nem is beszélve.

Ezzel szemben az iPhone, iPad és iPodTouch eszközök képernyőinek felbontása megszámlálható, képarányban pedig csak az iPhone 5. generációja tér el. Azért említem a iOS készülékeket, mert az első kiadott játékunk iOS platformra készült el elöszőr, és az Androidra portolás során cél volt a grafikai elemek módosítás nélküli újrafelhasználása. Emiatt az iOS verziónál használt három textúra méretet, – mint iPhone, iPad és iPad HD – megtartottuk és megfeleltetést kerestünk az Android készülékekkel. Így lett az iPhone-ra méretezett, 480×320 pixel méretből az “sd”, az iPad-re készült 1024×768 pixel méretre optimalizált változatból a “hd” és a Retina felbontású 2048×1536 pixel méretből “xhd” textúra kategória. Így megfelelő minőségű textúrákat tudunk megjeleníteni felbontástól függően. Ettől azonban még nem illeszkednek a grafikai elemek bármely eszköz képernyőjéhez. A varázslathoz be kell vezetnünk egy méretarányt amit minden megjelenő grafikai elemre tudunk alkalmazni, így arányosítva az eszközök képernyő méretéhez.

Ezt az arányt és a textúra kategóriáját/forrását a képernyő (vagy a GLSurfaceView) magasságának segítségével a következőképpen határozhatjuk meg:

float designScale;
String textureCategory;
if (screenHeight > 768) {
   designScale = screenHeight / 1536;
   textureCategory = "xhd";
} else if (screenHeight > 320) {
   designScale = screenHeight / 768;
   textureCategory = "hd";
} else
   designScale = screenHeight / 320;
   textureCategory = "sd";
}

Így meghatároztuk a “textureCategory” változót amit mi a betöltendő grafikai elemek forrásmappáival feleltettünk meg, így egyszerűen csak az adott mappából kell betöltenünk az adott képfájlt, ami ugyanazzal a fájlnévvel a többi kategóriának megfelelő mappában is jelen van.
A “designScale” változó pedig a megjelenítés során arányosításhoz nyújt segítséget.

Felmerül a pozicionálás kérdése is a különböző méreteknél. Ha a (20,310) pontra szeretnénk helyezni egy elemet az a képernyő különböző pontján fog megjelenni a különböző méretű kijelzőkön.
Erre jó lenne valami a “designScale”-hez hasonló arányszám, de mihez is viszonyítsunk. Ennek a legnagyobb haszna akkor volt, amikor minimális változtatással fel tudtam használni az iOS verzióban leírt pozíciókat, így nem kellett újra pozicionálni minden egyes elemet a játékban.
Az alapötlet erre a számra is az iOS verzióból született, ott egy IPHONIZE nevű arányszámot használtunk a pozíciók x és y értékeinek szorzására, ez a következőképpen számolódott ki:

float IPHONIZE=1;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
   IPHONIZE = 0.5;
}

Én ezt az Android verzióban a fenti, designScale kiszámítását kiegészítve oldottam meg, a következőképpen:

float designScale;
float resolutionScale = screenHeight / 768;
String textureCategory;

if (screenHeight > 768) {
   designScale = screenHeight / 1536;
   textureCategory = "xhd";
} else if (screenHeight > 320) {
   designScale = screenHeight / 768;
   textureCategory = "hd";
} else {
   resolutionScale = screenHeight / (320 * 2);
   designScale = screenHeight / 320;
   textureCategory = "sd";
}

Ezzel a “resolutionScale”-nek elnevezett arányszámmal azt érhetjük el, hogy ha egy elemet a (10,310) pontra tervezünk 1024×768 méretű kijelzőn, azt egy kisebb kijelzőn a képernyő arányosan azonos részén tudjuk megjeleníteni bármely méretű eszközön, ha a (10*resolutionScale,310*resolutionScale) pontra pozícionáljuk.
Természetesen ha a képernyő közepére (vagy ahhoz viszonyítva) szeretnénk pozícionálni elemeket, akkor a 4:3 képarányú kijelzőn tervezett elem nem a képernyő megfelelő részén fog megjelenni. Erre megoldás ha az eredeti szándékot megtartva a képernyő közepéhez viszonyítunk, pl: (screenWidth/2,100*resolutionScale) vagy pl: (screenWidth/2+10*resolutionScale,100*resolutionScale).

Ide kapcsolódó probléma volt, a különböző képarányú eszközökön megfelelően megjeleníteni a Sprinkfield főmenüjének hátterét, ami kör alakú és folyamatosan forog.

Nem lehetett meghatározni előre, hogy mekkora méretben kell megjeleníteni a képet, mivel sok különböző felbontású és képarányú eszközön fut majd a játék.
A kép tartalma kör alakú és néhány eszközön előfordult, hogy a sarkokig nem jutott a képből.

device-2013-04-12-154432

Ezután jött az ötlet, hogy a kép átlójának felét és a kör alakú kép sugarát, azaz a szélességének, vagy magasságának a felét kell arányosítani.

Az alábbi példához adott a képernyő szélessége, magassága valamint háttérkép magassága.

double radius = Math.sqrt(screenWidth * screenWidth + screenHeight * screenHeight) / 2;
float scale = radius / (bgImageWidth / 2);

Az így kiszámított aránnyal megjelenítve lefedi a képernyő közepétől számított legtávolabbi pontot is, azaz a képernyő sarkaiban nem lesznek kimaradó területek.

device-2013-04-12-153157

A fenti módszerrel jó eredményeket értünk el, de a visszajelzések alapján még itt-ott finomítottunk a kódon, így javítva a felhasználói élményen és reszponzivításon. Viszont azt is látjuk, hogy van még min finomítani.


No Comments

Leave a comment