TOPlist

Vyvíjíme pro WP v XNA: Životní cyklus aplikací a hudba (12. díl)

Po krátké odmlce se vracíme k seriálu o vývoji her v XNA pro Windows Phone. V dnešním díle se podíváme na chování aplikací a přehrávání zvuků/hudby.

Od začátku bylo tomuto systému vytýkáno, že nenabízel žádnou formu multitaskingu. Kromě přehrávání hudby na pozadí mohla být vždy spuštěna jen jedna aplikace najednou, ostatní byly deaktivované a odnačtené z paměti. Toto se v průběhu času postupně měnilo, hlavní změna přišla spolu s aktualizací Mango. Z určitých dobrých důvodů (šetření baterie a systémových prostředků) zde sice stále není umožněn běh více aplikací najednou, to je ale nahrazeno dvěma hlavními principy fungování. Je zde k dispozici takzvané rychlé přepínání aplikací (Fast App Switching) a možnost běhu agentů na pozadí (Background Agents).

V současné době to v Mangu funguje takto: Když si spustíme aplikaci a odejdeme z ní pryč klávesou zpět, ukončí se. Když z ní odejdeme stiskem tlačítka Home nebo Bing (nebo například přijmeme telefonní hovor), aplikace přejde do takzvaného Dormant stavu. Neboli, uspí se na pozadí, ale stále ještě zůstane uložená v operační paměti. Do této paměti se takto vejde až pět aplikací. Pokud si potom naši aplikaci zpět zobrazíme (krátkým stiskem tlačítka zpět, nebo dlouhým stiskem zpět a výběrem z nabídky posledních spuštěných aplikací), obnoví se přesně tam, kde přestala – a pokračuje bez problémů dál. Toto chování máme k dispozici v podstatě zadarmo, stačí nám, když si naši aplikaci překompilujeme pod novým SDK Windows Phone 7.1 a vše nám bude správně fungovat.

Pokud počkáme delší dobu, nebo postupně za sebou spustíme více aplikací, bude se moci stát, že naše aplikace přejde ze stavu Dormant do dalšího stavu – takzvaného Tombstoned. V tomto případě je aplikace v podstatě zabita, odnačtena z paměti, je jí pouze umožněno si na určité místo uložit informaci o svém stavu (například jaká byla její poslední zobrazená stránka, poslední rozehraný level apod.). Při následné obnově klávesou zpět se aplikace znovu spustí a ve vyvolané události bude mít možnost si obnovit svůj uložený stav. V režimu Dormant se tedy načtení původního stavu děje automaticky, pokud u naší aplikace nastane tombstoning (a my nic nebudeme měnit), spustí se nám aplikace znovu od začátku.

Schéma tohoto exekučního modelu můžeme vidět na přiloženém obrázku. Ze stavu Running (běžící aplikace) může aplikace přejít do stavu Dormant, případně Tombstoned. Při přechodu do stavu Dormant se volá událost Deactivated, při zobrazení aplikace zpět potom událost Activated. K těmto událostem lze doplnit obslužný kód tak, že si do konstruktoru Game1 vložíme tyto dva řádky (opět nám bude stačit napsat this.Activated += a poté zmáčknout dvakrát klávesu Tab), vygenerují se nám potřebné metody.

this.Activated += new EventHandler<EventArgs>(Game1_Activated);
this.Deactivated += new EventHandler<EventArgs>(Game1_Deactivated);

Je pravda, že ve většině případů si stav Tombstoned nebudeme muset ošetřovat. Až na pár drobností určitě neuděláme chybu, pokud si stav naší hry budeme ukládat průběžně – a nějaký ten seznam rozehraných levelů si stejně budeme načítat tak jako tak při každém spouštění. O tombstoningu se na svém blogu podrobněji rozepsal René Stein, poměrně podrobný je také anglický článek na MSDN. Neměli bychom na tento stav zapomínat jen tehdy, pokud se rozhodneme ve hře přehrávat hudbu na pozadí (popíšeme si zachvilku). Pokud bychom se také například rozhodli, že uživateli při každém návratu zpět do hry budeme zobrazovat nejdřív obrazovku pauzy, tuto logiku si také budeme moci dopsat do události Activated.

K tomu, jak to v současné době funguje na Windows Phone, zbývá doplnit poslední věc. Pokud se do uspané aplikace nevrátíme klávesou zpět, ale znovu ji vybereme kliknutím na její ikonku v nabídce programů, aplikace bude spuštěna znovu od začátku. Její stará instance bude zahozena, i když byla například zapamatovaná v Dormant stavu.

Obecně bychom se u našich aplikací na Windows Phone měli snažit o co nejunifikovanější vzhled a o co nejvíce stejné chování, podle daných doporučení. Při každém novém spuštění aplikace bychom měli vždy zobrazit její úvodní stránku. O obnovování posledních otevřených oken bychom se měli pokoušet jen v případě tombstoningu. Jak už bylo ale řečeno, ve většině případů tento životní cyklus aplikace ani nebude potřeba řešit (zvlášť ne u jednoduchých prográmků a her), bude nám stačit výchozí chování.

-  

Druhou částí specifického multitaskingu na Windows Phone jsou takzvaní Background Agents. Díky nim může naše aplikace provádět určitou činnost, i když neběží. Pokud telefon není aktuálně zaneprázdněn něčím jiným, tito agenti mohou být spouštěni každých 30 minut a provádět nějakou operaci až po dobu 15 sekund. Ve hrách je ale většinou nevyužijeme, nebudeme se jim tu dále zabývat. V některém z příštích dílů, které se budou věnovat vývoji aplikací v Silverlightu, tito agenti budou určitě ještě zmíněni, stejně tak jako další zajímavé vlastnosti Windows Phone (jak si například naprogramovat push notifikace, nebo jak ovlivnit chování aktivních dlaždic připnutých na domovské obrazovce).

-

V XNA na Windows Phone odlišujeme, jestli přehráváme podkreslující hudbu na pozadí, nebo jen krátké jednotlivé zvuky (ruchy, výbuchy a podobně). Přehrávání krátkých zvukových efektů je jednoduché, do projektu se tyto soubory načtou stejně jako obrázky, pomocí Content Pipeline. Pro jejich přehrávání se využije třída SoundEffect. Nějaké zvuky volně ke stažení lze najít například na webech freesound.com nebo findsounds.com. Stáhneme si tedy nějaké soubory zvuků a přetáhneme si je do našeho projektu do složky Content. Abychom si mohli zvuk přehrát, do deklarací ve třídě Game1 si přidáme položku typu SoundEffect:

SoundEffect sndClick;

V metodě LoadContent() si do této proměnné načteme soubor (předpokládáme, že ho máme v podsložce Sounds):

sndClick = contentManager.Load<SoundEffect>("Sounds\\click");

A v metodě Update() si zvuk kdykoliv přehrajeme zavoláním tohoto příkazu:

sndClick.Play();

Při finálním vylaďování hry se nám určitě bude hodit, že metoda Play() má ještě jednu variantu. Může se zde zadat hlasitost (nula je absolutní ticho, jednička původní hlasitost). Druhý parametr Pitch nám udává výšku zvuku (můžeme si zvuk posunout až o jednu oktávu dolu, nebo nahoru). Parametr Pan pak udává posunutí, odkud zvuk hraje, –1 je úplně zleva, 0 je z prostředka, 1 je zprava. Kombinací těchto hodnot lze dorovnat mnoho rozdílů, které se nám nepodařily vyladit ve zvukovém editoru. Zvuků můžeme přehrávat více najednou (až 16), při jejich velkém počtu se jen bude moci stát, že jejich výstup bude znít zkresleně.

sndClick.Play(0.8f, 0f, 0f);

Z objektu SoundEffect lze odvodit i objekt typu SoundEffectInstance, zavoláním sndClick.CreateInstance(). Zde se může navíc nastavit opakování ve smyčce, může se zvuk pozastavovat a znovu spouštět. Jen pro doplnění, existuje také třída DynamicSoundEffectInstance, pomocí které se dá ke zvukům přistupovat ještě “hlouběji”. Může se nastavit například velikost přehrávaného bufferu, zvuk lze rozkouskovat na malé části a simulovat na něm různé efekty, jako například průběžné zesilování hlasitosti nebo parametru Pitch. XACT audio engine, známý z XNA frameworku pro PC a Xbox 360, bohužel na Windows Phone dostupný není.

Při kompilování projektu se zvuky do výsledného balíčku interně převádí do určitého “rozbaleného” formátu. Můžeme si všimnout, že to má dost velký vliv na velikost našeho XAP souboru. Naštěstí si lze pro každý soubor zvuku v paletce Properties zvolit jeho kvalitu komprese, pod položkou Content Processor (rozbalte si šipečku nalevo, je tam ještě položka Compression Quality). Pro naše použití na mobilních telefonech nám většinou bude stačit kvalita Medium.

-

Přehrávání podkreslující hudby je v XNA o něco složitější. Aby naše hra úspěšně prošla certifikací do marketplace, musí splňovat několik požadavků. Pokud si bude uživatel pouštět nějakou svoji hudbu, naše hra by mu do ní neměla začít přehrávat jinou. Měla by místo toho zobrazit okénko, ve kterém uživateli nabídne možnost, jestli chce pozastavit jeho hudbu a spustit tu ve hře. Může dokonce nastat i taková situace, že si uživatel spustí naši hru včetně hudby, odejde z ní klávesou zpět (čímž hru uspí do Dormant stavu), spustí si svoje přehrávání hudby a zase se do naší hry vrátí. I v tomto případě by měla naše hra správně zareagovat a svoji hudbu si pozastavit. Další problém při přehrávání může nastat, pokud uživatel připojí mobil k PC a začne probíhat synchronizace se Zune.

Pro přehrávání podkreslující hudby se používá třída MediaPlayer. Soubor s hudbou (například ve formátu mp3) se opět do projektu načte pomocí Content Pipeline, využije se objekt typu Song. Do třídy Game1 si přidáme tuto deklaraci:

Song gameplayMusic;

Načtení a spuštění přehrávání hudby provedeme v metodě LoadContent():

gameplayMusic = Content.Load<Song>("Sounds\\Song1");
MediaPlayer.Play(gameplayMusic);
MediaPlayer.IsRepeating = true;

Při spouštění přehrávání bychom měli zkontrolovat, jestli už nějaká hudba nehraje. Volání metody Play() si tedy obalíme touto podmínkou:

if (MediaPlayer.GameHasControl)
{
}

Obdobně bychom to měli kontrolovat v události Activated, případně i v metodě Update(). Microsoft naštěstí pro vývojáře připravil pomocnou komponentu BackgroundMusicManager, která všechny tyto problémy řeší za ně. Dá se stáhnout na stránkách App Hubu (Music Management code sample).

V příštím díle si uvedeme několik posledních tipů a triků pro XNA na Windows Phone, případně si popíšeme, jak by se taková hra dala naportovat pro PC nebo Xbox 360 a jaké by tam byly hlavní rozdíly. Následně na tyto články naváže seriál Roberta Vargy o programování aplikací v Silverlightu pro Windows Phone, takže se máte na co těšit.

Autor článku Tomáš Slavíček
Tomáš Slavíček

Kapitoly článku