HexaLines – optimalizace a vykreslování na Windows Mobile

Tomáš Slavíček       14. 9. 2010       C#, Grafika, .NET Tips       6463 zobrazení

Ještě jednou bych se chtěl vrátit k tématu programování hry HexaLines a trochu podrobněji poodkrýt, jak jsem řešil vykreslování. Nepředpokládám, že by tady bylo mnoho lidí, kteří by se chtěli pouštět do programování her pro Windows Mobile, velice podobný princip se ale dá uplatnit i při kreslení čar a tvarů na počítači. Připomínám, že tento článek se bude týkat technologie GDI+, takového toho běžného kreslení, které může být prováděno například v události Form1_Paint() pomocí volání metod graphics.DrawLine(), graphics.FillPolygon() apod.

Nástin architektury

Projekt jsem si rozdělil do několika logických částí, především jsem se snažil, aby byly oddělené herní objekty od vykreslovací logiky. Cílem bylo, pokud bych chtěl v budoucnu portovat hru na jinou platformu, abych s tím neměl tolik problémů. V praxi jsou tedy třídy umístěny do několika jmenných prostorů (namespace) – HexaLines.GameObjects, HexaLines.Drawers apod.

Důležité bylo se zamyslet, které objekty na sebe mohou “vidět”. Herní plán by neměl mít tušení, jaký vykreslovací objekt nad ním stojí. Měl by si držet informace o herní situaci, rozmístění buněk apod., ale neměl by se už snažit ovlivňovat, jak například tlustý okraj by měly buňky mít. Naopak objekt vykreslování by měl shlížet shora na tento plán, prohlížet si jeho vlastnosti a podle nich si kreslit. Tohle je zařízeno tak, že objekt GDIDrawer v namespace HexaLines.Drawers má ve svém kódu nahoře uvedeno using HexaLines.GameObjects;

Obdobně musí být hernímu objektu jedno, který hráč k němu přistupuje, jestli je to přímo osoba mačkající tlačítka na mobilu, umělá inteligence nebo kamarád přes bluetooth. Takových případů by se dalo najít víc, například objekt herní buňky, umístěný v namespace HexaLines.GameObjects.Cells, by nejspíš neměl vědět nic o tom, kdo je na tahu a jestli vyhrává, nanejvýš tak to, které další buňky leží vedle něj.

Jen tak pro informaci, z ostatních jmenných prostorů obsahují HexaLines např. .Sounds, .Menus nebo .HitTests.

Je jasné, že nakonci vždycky zjistíte, že se ta architektura dala navrhnout jinak a lépe. V tomto případě ale hra nebyla až tak velkého logického rozsahu a víc se tam už asi ani nedalo vymyslet.

Optimalizace vykreslování

Jak už jsem psal, nakonec jsem zvolil způsob vykreslování, kdy se jednotlivé buňky neukládaly do bitmap, ale vždy vykreslovaly odznova. Tak, jak jsem si hru totiž navrhnul, jsem si to celé dost zkomplikoval. Pokud by se vždy na plánu měnila jen nějaká malá část, stačilo by překreslovat jen ten výřez a hodně by se to urychlilo. Pokud by se nemohla měnit velikost buňek (při přibližování herního plánu), mohli bychom si je ukládat předkreslené do bitmap. Nejlépe, pokud by byly čtvercové, abychom se při jejich spojování nemuseli zabývat průhledností. Jak se z toho dostat ven?

Nejproblematičtější bylo pochopitelně posouvání herního plánu. Při tom bylo potřeba překreslit všechny buňky, posunuté pozadí, herní ovládací prvky a případně nějaké probíhající animace. Především jsem se snažil vykreslovat buňky s co nejjednodušší vektorovou grafikou, žádnými zapuštěnými stíny, jenom s jednoduchým odleskem. Při posouvání je poznat, že se grafika ještě zjednoduší, nekreslí se například ztmavené čáry pod vnitřními čarami. Také si pamatuji, které buňky jsou aktuálně vidět na plánu, ostatní dokresluji až tehdy, když se dokončí drag & drop.

I když samotný herní kód nebylo potřeba tolik optimalizovat (operace GDI+ byly opravdu největší brzda), u samotné vykreslovací procedury jsem si musel dávat hodně pozor, co tam píšu. Všechny buňky už v tuto chvíli měly spočítané pozice bodů, doplněné o případné posunutí na plánu. Je zajímavé, že v této metodě odstranění jedné nepotřebné if podmínky, která se volala při vykreslování každé buňky, znamenalo zrychlení asi o třetinu. Kdo ví, jak tam šly v praxi procesorové instrukce, jestli kód potom nebyl optimalizován například jiným způsobem, ale překvapilo mě to. Pro měření času jsem používal Profiler dostupný v Team System edicích Visual Studia.

Vykreslování v praxi

V praxi jsem si tedy veškerou grafiku vykresloval sám, standardních ovládacích prvků jsem nevyužíval. Objekt Form1 tak zůstal vlastně prázdný, jen jsem mu nastavil ControlBox = false; a WindowsState = Maximized;. Všechny ostatní potřebné objekty (časovače apod.) jsem si načítal dynamicky za běhu.

Pro vykreslování jsem přetížil metodu OnPaintBackground(), kterou jsem nechal prázdnou, a metodu OnPaint(), ve které jsem volal metodu Draw objektu GDIDrawer, s předaným objektem Graphics. Ten už si tam podle stavu hry vykreslil potřebné součásti (pozadí, okraj hracího plánu apod.), stejně tak jako buňky.

Ještě bych zmínil jedno specifikum implementace GDI+ v .NET Compact Frameworku. Pro volání metod jsou zde většinou přístupné jen celočíselné (int) parametry, ne float. Ono je to i logické, výsledný displej má také celočíselný počet pixelů, na které umísťujeme grafiku a v této ořezané implementaci ani není možnost antialisingu (vyhlazení hran na pomezí pixelů). Musel jsem si tedy dávat hodně pozor, aby na sebe čáry správně navazovaly při každém přiblížení. Obzvláště to bylo pocítitelné u těch šestiúhelníkových tvarů, kde se to jen hemžilo čísly, jako je odmocnina ze tří.

Využíval jsem tedy celé řady korekcí, například že šířka buňky musí být vždy sudá, levý horní roh buňky se určuje podle již spočítaného pravého dolního rohu buňky, ke které by navázal apod. Tyto předpočítané parametry jsem si ukládal do objektu CellProps, podle kterého se pak řídily konkrétní buňky.

DPI a různá rozlišení

Další výzva byla, aby aplikace vypadala na všech mobilech pokud možno stejně. Aby v tom byl dobrý guláš, na tohle existují v .NET CF dvě různé vlastnosti. Jednou je jemnost displeje (DPI, počet bodů na palec), podle kterého se řídí grafika. Většina QVGA (240x320 pix) zařízení a obdobných (WQVGA apod.) má 96 DPI. Většina zařízení s VGA a vyšším má 192 DPI. Pak existují také “hybridní” zařízení, jako např. HTC HD mini, který má Androidovské rozlišení 320x480 pix a 128 DPI. Další radost přichází, pokud byste chtěli podporovat například starší smartphony s WM, které mají různé libůstky, jako např. rozlišení 176x220 pix, nebo 131 DPI.

DPI zařízení naštěstí nijak neovlivňuje vykreslování v GDI+, prvky velké stejně pixelů budou jen vykresleny menší.

Druhým parametrem je nastavení systémového písma. To má své jednotky (podobně jako např. v MS Word), písmo velikosti 10 by tedy mělo vypadat všude stejně (když si ho změříte pravítkem na reálném mobilu). V praxi je sice občas trochu větší nebo menší (speciálně u těch nezvyklých DPI), ale je to v rozumné míře. Chce to ale pamatovat, že je to systémový font se vším všudy, lze měnit jeho velikost i typ, může být vyhlazovaný, nebo bez ClearType. Alternativou by bylo vykreslovat si texty sám pomocí malých obrázků, mohl by ale nastat pozdější problém, například s možnou lokalizací do dalších jazyků.

Pro zjištění přesné velikosti textu (pokud okolo něj např. potřebujeme vykreslit rámeček) existuje naštěstí, stejně jako ve velkém .NET Frameworku, metoda graphics.MeasureString().

V praxi jsem si tedy na začátku zjistil DPI z vlastnosti .DpiX nějaké bitmapy (například jen jednopixelové, vytvořené a disposenuté jen pro tento účel), potom jsem si ho pamatoval jako násobek základního 96 DPI. Herní plán a buňky jsem vykresloval přesně podle tohoto násobku, na všech mobilech by tedy měly vypadat stejně. Ostatní grafiku jsem měl připravenou ve dvou rozlišeních – QVGA a VGA, například pro vykreslení loga, hvězdiček a dalších objektů. Na nestandardních DPI se vykreslí právě jedna z nich, možná tam tedy budou některé prvky menší, ale funkčnost by měla být zachována. Smartphone telefony (bez dotykového displeje) jsem se nakonec pro jejich malé rozšíření rozhodl nepodporovat. Navíc bych musel vymýšlet nějaké použitelné ovládání jen pomocí klávesnice.

Ostatní grafiku, jako tloušťku čar menu apod. jsem vykresloval také ve dvou variantách – měl jsem pevně danou jejich šířku, kterou jsem v případě VGA jen vynásobil dvěma.

Animace

Jak jste si asi mohli všimnout, i přes nepřívětivost GDI+ je nakonec ve hře dostupné mnoho animací, pod položkami menu se prolínají kruhy a šestiúhelníky, umělá inteligence si sama otáčí buňkami, které v případě rozlití barvy mizí. Na začátku se ukazuje animace, kam se má přidat buňka, dole bliká šipka.

V .NET CF existuje několik různých časovačů (Timer). Já jsem nakonec použil běžný Windows.Forms.Timer, protože jsem potřeboval, aby běžel ve stejném vlákně jako vykreslování. Ostatní mi dělaly problémy a do nějaké synchronizace jsem se pouštět rozhodně nechtěl. Při spouštění hry časovače zinicializuji, nastavím jim interval a přidám událost Tick. V případě pozastavení hry jen nastavuji vlastnost Enabled = false;.

Problém může nastávat, pokud ještě nějaký Timer běží a já chci zničit herní objekt (např. při ukončení a spouštění nové hry, tutoriálu apod.). Běžící časovač se sám neodalokuje, musíme dát pozor, abychom při ukončování hry nastavili jeho vlastnost Enabled = false; a samotný časovač na null.

Ostatní

To by bylo asi ohledně vykreslování vše. Pokud by vás ještě zajímalo nějaké téma, můžete si o něj napsat do diskuze.

Věřím, že zajímavý článek by byl například o tom, jak se dá taková hra prodávat a propagovat. Dosáhl jsem zatím už poměrně mnoha úspěchů (4. nejprodávanější aplikace pro WM na PocketGearu, článek na XDA developers, mnoho kladných recenzí), na takový článek se zatím ale ještě nechystám. Ještě mám pár nápadů, jak hru dále propagovat, které bych chtěl zkusit realizovat.

GUI_navrh hexa008_maxi

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Diskuse: HexaLines – optimalizace a vykreslování

hi,

Do you have plans to migrate Hexalines to iOS?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Hi, thank you for your interest. I would like to finish the Windows Phone 7 version in a few months (sometimes in the summer). I was thinking about Android and iOS version too, but it's not a priority now. But I'm also working with my friends on the other game for WP7 now, so stay tuned :)

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Hi, it's me again and now I've got wp7, so please send me any news about release date of hexaline on wp7 to Sergey.yakimchuk(at)Gmail.com. Thanks!

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: HexaLines – optimalizace a vykreslování

Proč zrovna C# a ne Visual Basic .NET?

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

No, to je zajímavá otázka. Ale asi proto, že na střední nás k C# docela směřovali. Z nějaké soutěže jsem už předtím měl doma knížku a poslední rok zároveň s VB6 jsem se také setkal se základy čistého céčka. A toho přechodu nelituji, myslím si, že přeci jenom C# má o něco větší podporu - častěji se vyskytuje v knížkách, v internetových samplech apod. Navíc si nejsem jistý, jak je to například s podporou VB.NETu v současném CTP XNA 4.0.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback