Programowanie gier dla Symbian OS – szkielet aplikacji

background image

09/2008

66

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

67

M

aszyny, które jeszcze kilkadziesiąt
lat temu stanowiły jedynie mrzon-
kę wąskiej garstki specjalistów, dziś

mieszczą się w naszych kieszeniach. Mowa tu
oczywiście o nowoczesnych telefonach komór-
kowych. Na dzień dzisiejszy, za całkiem rozsąd-
ną cenę otrzymujemy urządzenie wyposażone
w kamerę o wysokiej rozdzielczości, kolorowy
wyświetlacz, dostęp do szerokopasmowego in-
ternetu, zintegrowany moduł GPS, wysokiej ja-
kości odtwarzacz dźwięku stereo, akcelerometr
i układ wspomagający renderowanie grafiki 3D
w czasie rzeczywistym. No i przy okazji – apa-
rat telefoniczny. Ten szalony pęd technologicz-
ny tworzy ogromną, otwartą przestrzeń dla no-
wych aplikacji, zaś lwią ich cześć stanowią gry.
Niniejszy tekst rozpoczyna cykl artykułów
traktujących o programowaniu gier dla Sym-
bian OS – jednego z czołowych graczy na ryn-
ku mobilnych systemów operacyjnych.

Zanim zaczniemy...

...kilka słów wstępu. Tak jak wspomniano wy-
żej, niniejszy artykuł traktuje o programowa-

niu gier na telefony komórkowe działające pod
kontrolą Symbian OS. Cykl poświęcony jest
przede wszystkim dla programistów języka
C++, którzy nie znają, lub znają słabo system
operacyjny Symbian, a chcieliby na poważnie
zainteresować się programowaniem gier na
urządzenia mobilne pracujące pod jego kon-
trolą. Symbian jest tworem potężnym i z pro-
gramistycznego punktu widzenia dość kon-
trowersyjnym (ciekawą dyskusję na ten temat
przedstawia artykuł pt. Unix – piszemy pro-
gram na komórkę
autorstwa Bartosza Taudula,
przedstawiony w numerze 06/2008 Software
Developer's Journal). Cykl moich artykułów
ma na celu przedstawienie tych udogodnień
oferowanych przez Symbiana, które są klu-
czowe przy programowaniu gier. Aby nieco
uprościć to niełatwe zadanie, przedstawione
rozważania ograniczać się będą do dziewiątej
wersji wspomnianego systemu oraz platformy
Series60. W niniejszym tekście przedstawiony
zostanie szkielet aplikacji dostosowany do po-
trzeb mobilnych gier, stanowiący bazę dla ko-
lejnych, bardziej zaawansowanych rozważań.
W ramach omówienia wspomnianego szkie-
letu przeanalizowane zostaną takie zagadnie-
nia jak implementacja głównej pętli gry, ryso-
wanie, obsługa interakcji aplikacji z użytkow-
nikiem poprzez klawiaturę, oraz obsługa pod-
stawowych zdarzeń systemowych.

Środowisko pracy

Przygodę z programowaniem gier dla Sym-
bian OS rozpoczniemy od przygotowania
środowiska pracy. Zakładam, że Czytelnik
posiada system operacyjny Windows XP. Na
początek musimy zainstalować dystrybu-
cję Perl'a oraz środowisko uruchomieniowe
języka Java. W dalszej kolejności będziemy
potrzebowali SDK (Software Development
Kit), zawierającego podstawowy zestaw na-
rzędzi potrzebnych do tworzenia aplikacji
dla Symbian OS. Nasz wybór pada w tym
przypadku na pakiet S60 3rd Edition SDK
for Symbian OS, Feature Pack 2. Pakiet ten
dostępny jest za darmo na portalu Forum No-
kia – przed jego ściągnięciem wymagana jest
jednak rejestracja (ramka W Sieci). Aby roz-
począć instalację wystarczy rozpakować po-
brane archiwum z SDK i uruchomić plik se-
tup.exe
. Proces powinien odbyć się bezpro-
blemowo. Na sam koniec instalator zapyta
się o zgodę na doinstalowanie zestawu na-
rzędzi kompilacyjnych dla procesora ARM.
Na pytanie to należy odpowiedzieć twierdzą-
co – bez tego zestawu nie będziemy w sta-
nie zbudować naszej aplikacji w wersji dzia-
łającej na urządzeniu. Jeśli chodzi o wymaga-
nia dla SDK, to przed jego instalacją powin-
niśmy uzbroić się w około 1GB wolnej prze-
strzeni dyskowej oraz odrobinę wolnego cza-
su (ze względu na dużą liczbę małych pli-
ków w archiwum instalacja trwa dość dłu-
go). Pracę ze środowiskiem warto rozpocząć
od uruchomienia emulatora. Można to zro-
bić wybierając odpowiednią opcję z Menu
Start lub – prościej – wpisując z linii pole-
ceń komendę epoc. Jeśli wszystko poszło do-
brze, to nasze dotychczasowe wysiłki nagro-
dzone zostaną uruchomieniem się emulatora
(Rysunek 1).

Po uruchomieniu emulatora należy zareje-

strować SDK wybierając z menu opcję Help

Programowanie
gier dla Symbian OS

Niniejszy tekst rozpoczyna cykl artykułów traktujących o programowaniu

gier na telefony komórkowe działające pod kontrolą Symbian OS. W

pierwszym odcinku cyklu przedstawiona jest implementacja szkieletu aplikacji

zawierającego takie podstawowe udogodnienia jak pętla gry, rysowanie, a także

przechwytywanie i obsługa zdarzeń klawiatury oraz interakcja z systemem.

Dowiesz się:

• Jak rozpocząć pracę z narzędziami do progra-

mowania aplikacji dla Symbian OS;

• Jak zaimplementować pętlę gry w aplikacji dla

Symbian OS;

• Jak w grach pisanych pod Symbian OS efek-

tywnie renderować grafikę, obsługiwać zda-
rzenia klawiatury i przechwytywać podstawo-
we zdarzenia systemowe.

Powinieneś wiedzieć:

• Średnio zaawansowana wiedza ogólna z za-

kresu programowania w języku C++;

• Podstawowa wiedza z zakresu programowa-

nia orientowanego obiektowo w języku C++.

Poziom trudności

Szkielet aplikacji

background image

09/2008

66

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

67

–>

Register. Drugim narzędziem składającym

się na nasze środowisko pracy – w odniesie-
niu do tego artykułu – będzie Carbide.C++
v1.3. Carbide to IDE stworzone w oparciu o
platformę Eclipse, dedykowane jako wspar-
cie dla programistów tworzących aplikacje
pod Symbian OS. Narzędzie to w darmowej
wersji Express można pobrać z portalu Fo-
rum Nokia (ramka W Sieci).

Przy pierwszym uruchomieniu Carbide

prosi o ustalenie folderu roboczego – najwy-
godniej jest w tym celu stworzyć sobie kata-
log przeznaczony na symbianowe projekty
(najlepiej na tym samym dysku, na którym
zainstalowane było SDK). Po uruchomieniu
IDE automatycznie załaduje wtyczki z SDK
i poprosi o restart. Po ponownym urucho-
mieniu IDE nasze środowisko będzie goto-
we do pracy.

Szkielet aplikacji – przegląd

Rozważania przedstawione w dalszej czę-
ści artykułu opierać się będą na kodach źró-
dłowych przykładowej aplikacji – GameSke-
leton
. Kody te można pobrać z witryny SDJ.
Kluczowe fragmenty wspomnianych ko-
dów przedstawione będą na listingach w ni-
niejszym artykule, aczkolwiek gorąco nama-
wiam do pobrania całej paczki, chociażby w
celu uruchomienia przykładowego progra-
mu i poeksperymentowania z kodem źró-
dłowym.

Symbian uważany jest za system trudny do

nauki. Wynika to chociażby z faktu, że apli-
kacja typu Hello World składa się z około 50
kilobajtów kodu i konfiguracji rozmieszczo-
nych w kilkunastu plikach. Dodatkowo Sym-
bian narzuca pewne specyficzne idiomy ko-

Rysunek 1. Okno emulatora przy pierwszym
uruchomieniu po instalacji SDK

Listing 1. Definicja klasy CGameSkeletonApplication

class

CGameSkeletonApplication

:

public

CAknApplication

{

public

:

TUid

AppDllUid

()

const

;

protected

:

CApaDocument

*

CreateDocumentL

();

}

;

//

class

CGameSkeletonApplication

Listing 2. Implementacja metod klasy CGameSkeletonApplication

const

TUid

KUidGameSkeletonApp

=

{

0xA000958F

}

;

CApaDocument

*

CGameSkeletonApplication

::

CreateDocumentL

()

{

return

(

static_cast

<

CApaDocument

*

>(

CGameSkeletonDocument

::

NewL

(

*

this

)

)

);

}

TUid

CGameSkeletonApplication

::

AppDllUid

()

const

{

return

KUidGameSkeletonApp

;

}

Listing 3. Interfejs klasy CGameSkeletonDocument

class

CGameSkeletonDocument

:

public

CAknDocument

{

public

:

static

CGameSkeletonDocument

*

NewL

(

CEikApplication

&

aApp

);

static

CGameSkeletonDocument

*

NewLC

(

CEikApplication

&

aApp

);

virtual

~

CGameSkeletonDocument

();

CEikAppUi

*

CreateAppUiL

();

private

:

CGameSkeletonDocument

(

CEikApplication

&

aApp

);

void

ConstructL

();

}

;

//

class

CGameSkeletonDocument

Listing 4. Interfejs klasy CGameSkeletonAppUi

class

CGameSkeletonAppUi

:

public

CAknAppUi

{

public

:

CGameSkeletonAppUi

();

virtual

~

CGameSkeletonAppUi

();

void

ConstructL

();

private

:

void

HandleCommandL

(

TInt

aCommand

);

TKeyResponse

HandleKeyEventL

(

const

TKeyEvent

&

aKeyEvent

,

TEventCode

aType

);

private

:

CGameSkeletonContainer

*

iAppContainer

;

}

;

//

class

CGameSkeletonAppUi

background image

09/2008

68

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

69

dowania, które mocno odbiegają od standar-
du języka C++. W przypadku pisania progra-
mów dla Symbian OS stosuje się często kre-
atory generujące szkielety aplikacji. W dal-
szej części artykułu opiszę właśnie taki szkie-
let, stanowiący punkt wyjścia przy progra-
mowaniu gier. Symbian oferuje cały szereg
architektur aplikacji z graficznym interfej-
sem (np. architektura jednowidokowa lub
wielowidokowa). Z punktu widzenia pro-
gramisty gier, dużą cześć wiedzy na temat
wspomnianych architektur można pominąć.
W naszej szkieletowej aplikacji zastosujemy
bardzo prostą architekturę bazującą na jed-
nej, pełnoekranowej kontrolce. Osoby zainte-
resowane szczegółami technicznymi związa-
nymi z architekturą aplikacji w Symbian OS
zapraszam do ramki Bibliografia gdzie przed-
stawiłem tytuły nawiązujące do tego tematu.
W niniejszym podpunkcie przedstawię jedy-
nie to, co konieczne jest do zrozumienia pre-
zentowanego przykładu. Na początek – szyb-
ki przegląd klas wchodzących w skład naszej
przykładowej aplikacji. Zacznijmy od klasy

C

GameSkeletonApplication

. definicja tej kla-

sy przedstawiona jest na Listingu 1.

Jak widać na wspomnianym Listingu, de-

finicja ta nie jest specjalnie skomplikowana.
Klasa

CXXXApplication

(gdzie wedle kon-

wencji Symbianowej XXX to nazwa projek-
tu) odgrywa rolę punktu wejścia aplikacji
(ang. entry point). Kluczowa jest tu metoda
fabrykująca

CreateDocumentL()

, która od-

powiada za tworzenie dokumentu aplikacji
(

CApaDocument

). Osoby zaciekawione zna-

czeniem magicznych prefiksów i postfiksów
(np.

C

lub

L

) w nazwach prezentowanych klas

oraz ich metod zapraszam do przeczytania

Listing 5. Definicja konstruktora klasy CGameSkeletonAppUi

void

CGameSkeletonAppUi

::

ConstructL

()

{

CAknAppUiBase

::

SetFullScreenApp

(

ETrue

);

BaseConstructL

();

CAknAppUi

::

SetKeyBlockMode

();

iAppContainer

=

new

(

ELeave

)

CGameSkeletonContainer

;

iAppContainer

->

ConstructL

(

ApplicationRect

()

);

AddToStackL

(

iAppContainer

);

}

Listing 6. Definicja destruktora klasy CGameSkeletonAppUi

CGameSkeletonAppUi

::

~

CGameSkeletonAppUi

()

{

if

(

iAppContainer

)

{

RemoveFromStack

(

iAppContainer

);

delete

iAppContainer

;

}

}

Listing 7. Definicja metody HandleCommandL

void

CGameSkeletonAppUi

::

HandleCommandL

(

TInt

aCommand

)

{

switch

(

aCommand

)

{

case

EEikCmdExit

:

{

Exit

();

break

;

}

default

:

break

;

}

}

Listing 8. Funkcje zdefiniowane w pliku GameSkeleton.cpp

LOCAL_C

CApaApplication

*

NewApplication

()

{

return

new

CGameSkeletonApplication

;

}

GLDEF_C

TInt

E32Main

()

{

return

EikStart

::

RunApplication

(

NewApplication

);

}

Listing 9. Schemat pętli gry

while

(

Gra

trwa

)

{

1

.

Oblicz

czas

kt

ó

ry

min

ął

od

poprzedniego

przebiegu

p

ę

tli

.

2

.

Uaktualnij

model

gry

na

podstawie

r

óż

nicy

czasu

,

kt

ó

ry

up

ł

yn

ął

od

ostatniego

przebiegu

p

ę

tli

czasu

.

3

.

Narysuj

na

podstawie

modelu

kolejn

ą

ramk

ę

gry

i

wy

ś

wietl

j

ą

na

ekranie

.

4

.

Zsynchronizuj

d

ź

wi

ę

k

z

modelem

gry

.

}

Rysunek 2. Efekt działania aplikacji
GameSkeleton

background image

09/2008

68

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

69

ramki zatytułowanej Idiomy kodowania przy
programowaniu aplikacji dla Symbian OS
.
Wracając do metody

CreateDocumentL()

,

w jej ciele (Listing 2) jest dynamicznie two-
rzona i jednocześnie zwracana instancja kla-
sy

CGameSkeletonDocument

. Druga meto-

da klasy

CGameSkeletonApplication

, czyli

AppDllUid()

zwraca tzw. UID aplikacji. Pi-

sząc w uproszczeniu, UID to niepowtarzal-
ny identyfikator aplikacji. Skąd i w jaki spo-
sób taki UID można pobrać, to już odręb-
na kwestia – będę o tym pisał w dalszej czę-
ści artykułu. Dociekliwi Czytelnicy zaintere-
sują się zapewne dlaczego instancja obiektu

CGameSkeletonDocument

tworzona jest przy

pomocy tajemniczej metody statycznej

NewL

.

Tychże Czytelników odsyłam tym razem do
ramki zatytułowanej Idiomy kodowania przy
programowaniu aplikacji dla Symbian OS
.

W dalszej kolejności warto zainteresować

się klasą

CGameSkeletonDocument

. Interfejs

tej klasy przedstawiony jest na Listingu 3.

Według nomenklatury symbianowej klasa

CXXXDocument

reprezentuje część M (model)

z wzorca MVC (ang. Model-View-Controller)
na którym bazuje architektura aplikacji UI.
Innymi słowy – klasa ta odpowiada za zarzą-
dzanie danymi tej aplikacji (np. wczytywanie
i zapisywanie danych na dysk w przypadku
tzw. aplikacji opartych na plikach). W przy-
padku naszego szkieletu gry nie będziemy ko-
rzystać z klasycznego MVC i w tym kontek-
ście klasa

CGameSkeletonDocument

ma cha-

rakter pomocniczy. To co nas w tej klasie inte-
resuje to metoda fabrykująca

CreateAppUiL

,

która istnieje jedynie po to, aby zwrócić in-
stancję obiektu klasy

CGameSkeletonAppUi

.

Klasa ta (jej interfejs pokazany jest na Li-
stingu 4) odpowiada za zarządzanie graficz-
nym interfejsem użytkownika – reprezentu-
je część V (ang. view, czyli widok) we wspo-
mnianym wcześniej wzorcu MVC.

Klasa

CGameSkeletonAppUi

nawiązuje do

interesujących nas tematów, więc przeanali-
zujemy ją bardziej szczegółowo. Gdyby nasz
szkielet reprezentował aplikację z systemo-
wym
interfejsem graficznym dla Symbia-
na, to właśnie ta klasa byłaby odpowiedzial-
na za przechowywanie i kontrolę jej widoku
(lub widoków). Widoki, z kolei odgrywają ro-
lę kontenerów kontrolek. W przypadku pi-
sania gry niepotrzebny nam jest cały ten na-
rzut, dlatego skorzystamy z prostego konte-
nera (

CGameSkeletonContainer

), który re-

prezentuje prostą kontrolkę przykrywają-
cały wyświetlacz urządzenia. Do szcze-
gółów samej kontrolki przejdziemy w dal-
szej części artykułu – teraz skupmy się na
implementacji poszczególnych metod klasy

CGameSkeletonAppUi

. Na początek konstruk-

tor

ConstructL()

(Czytelników zdziwio-

nych dlaczego nazywam zwyczajną metodę
konstruktorem, zapraszam do lektury ram-
ki Idiomy kodowania przy programowaniu na-

Listing 10. Interfejs klasy CGameSkeletonContainer

class

CGameSkeletonContainer

:

public

CCoeControl

{

public

:

static

TInt

Tick

(

TAny

*

aCallback

);

~

CGameSkeletonContainer

();

void

ConstructL

(

const

TRect

&

aRect

);

void

FocusChanged

(

TDrawNow

aDrawNow

);

void

SizeChanged

();

TInt

CountComponentControls

()

const

;

CCoeControl

*

ComponentControl

(

TInt

aIndex

)

const

;

TKeyResponse

OfferKeyEventL

(

const

TKeyEvent

&

aKeyEvent

,

TEventCode

aType

);

private

:

void

Draw

(

const

TRect

&

aRect

)

const

;

void

StartTickGenerator

();

void

StopTickGenerator

();

void

OnTick

();

void

Draw

(

CGraphicsContext

&

aGc

)

const

;

void

Update

(

TInt64

aDt

);

CWsBitmap

*

iBackupBitmap

;

CFbsBitmapDevice

*

iBitmapDevice

;

CFbsBitGc

*

iBitmapGc

;

CFpsCounter

*

iFpsCounter

;

CPeriodic

*

iTickGenerator

;

TTime

iTickStart

;

TTime

iTickStop

;

TKeyState

iKeyState

;

}

;

//

class

CGameSkeletonContainer

Listing 11. Definicja metody CGameSkeletonContainer::ConstructL

void

CGameSkeletonContainer

::

ConstructL

(

const

TRect

&

aRect

)

{

CreateWindowL

();

SetBlank

();

SetRect

(

aRect

);

iBackupBitmap

=

new

(

ELeave

)

CWsBitmap

(

iCoeEnv

->

WsSession

()

);

iBackupBitmap

->

Create

(

Rect

()

.

Size

()

,

iEikonEnv

->

DefaultDisplayMode

()

);

iBitmapDevice

=

CFbsBitmapDevice

::

NewL

(

iBackupBitmap

);

User

::

LeaveIfError

(

iBitmapDevice

->

CreateContext

(

iBitmapGc

)

);

iTickGenerator

=

CPeriodic

::

NewL

(

CActive

::

EPriorityStandard

);

iFpsCounter

=

CFpsCounter

::

NewL

(

KFpsCounterIntervalInSeconds

);

ActivateL

();

}

background image

09/2008

70

Programowanie urządzeń mobilnych

www.sdjournal.org

71

tywnych aplikacji dla Symbian OS). Ciało tej
funkcji przedstawione jest na Listingu 5.

Pierwsza instrukcja tej metody, czy-

li wywołanie funkcji

SetFullScreenApp()

z klasy bazowej

CAknAppUiBase

odpowia-

da za przełączenie aplikacji w tryb pełno-

ekranowy. Kolejna instrukcja (wywołanie

BaseConstructL()

) odpowiada za inicjali-

zację składowych kontrolki odziedziczonych
z klasy bazowej. Cel wywoływania funkcji

SetKeyBlockMode()

opisany będzie w dalszej

części artykułu. W następnym kroku mo-

żemy zaobserwować dwufazową konstruk-
cję naszej kontrolki (patrz: Idiomy kodowa-
nia przy programowaniu natywnych aplikacji
dla Symbian OS
). Na końcu metody umiesz-
czono kod odpowiedzialny za bardzo istotną
operację – dodanie wspomnianej kontrolki
do stosu kontrolek. Gdybyśmy pominęli ten
krok, to nasza kontrolka nie otrzymywałaby
systemowych powiadomień związanych z ob-
sługą klawiatury i nie bylibyśmy w stanie ob-
służyć interakcji użytkownika z aplikacją.

W destruktorze klasy

CGameSkeletonAppUi

(Listing 6) nie dzieje się nic ciekawego oprócz
zdjęcia naszego kontenera ze stosu kontrolek
oraz zniszczenia go przy pomocy operato-
ra

delete

.

Definicja metody

HandleCommandL

(Listing

7) może wydawać się mało ciekawa, ale warto
przeanalizować jej treść.

W systemie Symbian, w kontekście interak-

cji z użytkownikiem mamy do czynienie z dwo-
ma typami komunikatów: są to komendy oraz
zdarzenia klawiatury. Komendy reprezentu-
ją pewne zdarzenie logiczne, związane z sys-
temowymi komponentami graficznego inter-
fejsu użytkownika (np. wybór opcji w menu).
W przypadku programowania gier, z kompo-
nentów tych rzadko się korzysta. Jeśli potrze-
bujemy menu w grze, to raczej narysujemy je
ręcznie i obsłużymy przy pomocy niskopozio-
mowych zdarzeń klawiatury. W tej sytuacji po-
wstaje pytanie: po co implementujemy metodę

HandleCommandL()

w naszym szkielecie? Otóż

istnieje ku temu konkretny powód. Chodzi o
to, że każda aplikacja symbianowa musi w po-
prawny sposób obsłużyć systemową komendę

EEikCmdExit

. Komenda ta przychodzi do apli-

kacji od systemu w takich sytuacjach jak zabi-
cie aplikacji
:

• z poziomu listy aktywnych zadań syste-

mowych;

• przy pomocy czerwonego klawisza;
• z powodu jej deinstalacji;
• z powodu niskiego poziomu pamięci w

systemie.

Zignorowanie tej komendy nie przynosi ra-
czej nic dobrego. Teoretycznie moglibyśmy nie
przeładowywać definicji

HandleCommandL()

z

klasy bazowej, która poprawnie obsługuje ten
komunikat, aczkolwiek w takiej sytuacji traci-
my możliwość reakcji na to istotne zdarzenie
(można przy tej okazji spróbować np. zapisać
stan gry na dysk, aby następnym razem nie za-
czynać rozgrywki od początku).

Na końcu pozostaje nam rozważyć definicję

metody

HandleKeyEventL()

. Metoda ta służy

do obsługi niskopoziomowych zdarzeń genero-
wanych przez klawiaturę. W naszym przypad-
ku implementacja tej metody jest trywialna i
mieści się w jednej linii:

return EKeyWasNotConsumed;

Listing 12. Definicja metody StartTickGenerator odpowiedzialnej za uruchomienie timera

void

CGameSkeletonContainer

::

StartTickGenerator

()

{

if

(

!

iTickGenerator

->

IsActive

()

)

{

iTickGenerator

->

Start

(

KDesiredFramesPerSecond

,

KDesiredFramesPerSecond

,

TCallBack

(

CGameSkeletonContainer

::

Tick

,

this

)

);

iTickStart

.

HomeTime

();

}

}

Listing 13. Definicja metody CGameSkeletonContainer::Tick()

TInt

CGameSkeletonContainer

::

Tick

(

TAny

*

aCallback

)

{

ASSERT

(

aCallback

);

CGameSkeletonContainer

*

gameSkeletonContainer

=

static_cast

<

CGameSkeletonContainer

*

>(

aCallback

);

gameSkeletonContainer

->

OnTick

();

return

ETrue

;

}

Listing 14. Implementacja metody CGameSkeletonContainer::Draw()

void

CGameSkeletonContainer

::

Draw

(

const

TRect

&

aRect

)

const

{

CWindowGc

&

gc

=

SystemGc

();

gc

.

BitBlt

(

aRect

.

iTl

,

iBackupBitmap

,

aRect

);

}

Listing 15. Implementacja metody CGameSkeletonContainer::OnTick()

void

CGameSkeletonContainer

::

OnTick

()

{

iTickStop

.

HomeTime

();

TInt64

dt

=

iTickStart

.

Int64

()

-

iTickStop

.

Int64

();

iTickStart

=

iTickStop

;

Update

(

dt

);

Draw

(

*

iBitmapGc

);

DrawNow

();

iFpsCounter

->

Update

();

}

background image

09/2008

70

Programowanie urządzeń mobilnych

www.sdjournal.org

71

Listing 16. Implementacja metody uaktualniającej zawartość tylnego bufora

void

CGameSkeletonContainer

::

Draw

(

CGraphicsContext

&

aGc

)

const

{

aGc

.

Reset

();

aGc

.

SetBrushColor

(

KRgbBlack

);

aGc

.

SetBrushStyle

(

CGraphicsContext

::

ESolidBrush

);

aGc

.

DrawRect

(

Rect

()

);

TBuf

<

KFpsMessageMaxLength

>

fpsMessage

;

fpsMessage

.

Format

(

KFpsMessageFormatString

,

iFpsCounter

->

Fps

()

);

aGc

.

UseFont

(

iEikonEnv

->

NormalFont

()

);

aGc

.

SetPenColor

(

KRgbWhite

);

aGc

.

DrawText

(

fpsMessage

,

TPoint

(

KFpsMessagePosX

,

KFpsMessagePosY

)

);

aGc

.

DiscardFont

();

}

Listing 17. Obsługa zdarzeń klawiatury

TKeyResponse

CGameSkeletonContainer

::

OfferKeyEventL

(

const

TKeyEvent

&

aKeyEvent

,

TEventCode

aType

)

{

TKeyResponse

response

=

EKeyWasConsumed

;

TKeyState

input

=

KKeyNull

;

switch

(

aKeyEvent

.

iScanCode

)

{

case

EStdKeyDevice0

:

input

=

KKeySoftLeft

;

break

;

case

EStdKeyDevice1

:

input

=

KKeySoftRight

;

break

;

case

EStdKeyUpArrow

:

input

=

KKeyUp

;

break

;

case

EStdKeyDownArrow

:

input

=

KKeyDown

;

break

;

case

EStdKeyLeftArrow

:

input

=

KKeyLeft

;

break

;

case

EStdKeyRightArrow

:

input

=

KKeyRight

;

break

;

case

EStdKeyDevice3

:

input

=

KKeyFire

;

break

;

case

'0'

:

input

=

KKey0

;

break

;

case

'1'

:

input

=

KKey1

;

break

;

case

'2'

:

input

=

KKey2

;

break

;

case

'3'

:

input

=

KKey3

;

break

;

case

'4'

:

input

=

KKey4

;

break

;

case

'5'

:

input

=

KKey5

;

break

;

case

'6'

:

input

=

KKey6

;

break

;

case

'7'

:

input

=

KKey7

;

break

;

case

'8'

:

input

=

KKey8

;

break

;

case

'9'

:

input

=

KKey9

;

break

;

default

:

response

=

EKeyWasNotConsumed

;

break

;

}

if

(

EEventKeyDown

==

aType

)

{

iKeyState

|=

input

;

}

else

if

(

EEventKeyUp

==

aType

)

{

iKeyState

&=

~

input

;

}

return

response

;

}

background image

09/2008

72

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

73

Zwrócenie takiej wartości przez tę funkcję
jest sygnałem dla silnika aplikacji, że zda-
rzenia klawiatury będą obsługiwane gdzie
indziej – w naszym przypadku będą one
przekierowywane bezpośrednio do naszej
kontrolki.

Tak oto przebrnęliśmy przez klasy stanowią-

ce fundament szkieletu gry. Ostatnia klasa z
tego zestawu, czyli

CGameSkeletonContainer

zawiera to, co stanowi istotę niniejszego ar-
tykułu – czyli wszystkie podstawowe mecha-
nizmy obsługi gry. Wspomniane mechani-
zmy będą opisywane w kolejnych podpunk-
tach artykułu.

Aby zakończyć przegląd prezentowanego

szkieletu aplikacji, warto wyjaśnić znacznie
funkcji zdefiniowanych w pliku GameSkele-
ton.cpp
(Listing 8).

Funkcje te definiują główny punkt wejścia

całego projektu – taki odpowiednik funk-
cji

main()

w konsolowych programach pisa-

nych w języku C lub C++. Prawdziwa funk-
cja

main()

ukryta jest w systemowym silni-

ku aplikacji (ang. application framework), któ-
ry wykorzystuje funkcje przedstawione na Li-
stingu 8 w celu stworzenia instancji naszej
aplikacji.

Duża część przedstawionego wyżej kodu

ma charakter powtarzalny – przy tworze-
niu nowego projektu trzeba go napisać od no-
wa, podmieniając jedynie pewne jego części.
Aby uprościć ten proces, przy tworzeniu no-
wych projektów aplikacji symbianowych, wy-
korzystuje się różnego rodzaju generatory ko-
du. W drugiej części cyklu artykułów o pro-
gramowaniu gier dla Symbian OS, w którym
będę między innymi pokazywał, jak na przy-
kładzie zaprezentowanego tu szkieletu stwo-
rzyć prostą grę, zaprezentuję narzędzie auto-
matyzujące ten proces.

Pętla gry

Większość aplikacji przeznaczonych dla Sym-
bian OS działa w oparciu o zdarzenia gene-
rowane na bazie interakcji z użytkownikiem.
Typowa aplikacja symbianowa (na przykład
program do pisania SMS-ów) czeka na reak-
cję użytkownika (np. wciśnięcie klawisza).
Jeśli wspomniana reakcja nie następuje, to
aplikacja po prostu nie robi nic. Podejście ta-
kie jest jak najbardziej uzasadnione w przy-
padku programów dedykowanych systemom
mobilnym. Przy jego stosowaniu, w sytuacji
kiedy użytkownik nie generuje żadnych zda-
rzeń, wątek aplikacji może być zawieszony,
dzięki czemu urządzenie zużywa mniej cen-
nej energii. W przypadku gier sytuacja wy-
gląda nieco inaczej: wnika to z faktu, że w
przypadku tego rodzaju aplikacji tematem
przewodnim jest zazwyczaj akcja. Jeśli użyt-
kownik nie reaguje, to (zazwyczaj) przegry-
wa, gdyż akcja w międzyczasie toczy się da-
lej. Innymi słowy – brak reakcji użytkowni-
ka w grze nie oznacza, że będzie ona stać bez-
czynnie. W przypadku tego rodzaju aplikacji
– wymagających ciągłej aktualizacji zarówno
w warstwie graficznej (widok) jak i logicznej
(model) – jako mechanizm sterujący stosuje
się tzw. pętlę gry. Pętla taka przedstawiona
jest w postaci pseudokodu na Listingu 9.

W dalszej części tego podpunktu przedsta-

wię, w jaki sposób można zrealizować wspo-
mnianą pętlę w aplikacji dedykowanej dla
Symbian OS. Już na samym początku trzeba
mocno podkreślić, że blokująca pętla

while

,

podobna do tej, którą pokazałem na Listingu
9, absolutnie nie wchodzi w rachubę. Gdy-
by ktoś pokusił się o jej zastosowanie, to po
kilkunastu sekundach miałby okazję zaob-
serwować skutki wystąpienia niechlubnego,
systemowego panika

EVwsViewEventTimeOut

(panik to w nomenklaturze Symbian OS pe-
wien rodzaj assercji), stanowiącego zmorę
wielu programistów symbianowych. W prak-
tyce fakt ten objawia się wystąpieniem sys-
temowej notki z napisem ViewSrv 11 i za-
trzymaniem aplikacji. Aby w pełni zrozu-
mieć skąd bierze się ten komunikat, należa-
ło by wyjaśnić ideę tzw. aktywnych obiektów
(ang. active objects), czyli mechanizmu lek-
kich wątków zaimplementowanych w syste-
mie Symbian. Temat ten wykracza jednak
poza ramy niniejszego artykułu, zaś jego zro-
zumienie nie jest konieczne do pisania gier
bazujących na przedstawionym tu szkielecie.
Osoby zainteresowane pogłębieniem swojej
wiedzy w zakresie aktywnych obiektów odsy-
łam do ramki Bibliografia, w której przedsta-
wiłem listę tytułów zgłębiających między in-
nymi i tę tematykę.

Pisząc w dużym uproszczeniu, opisany

wyżej problem wynika z faktu, że aktywne
obiekty, na których opiera się wielowątko-
wość w Symbian OS, nie podlegają wywłasz-
czaniu i kręcąca się bez końca pętla

while()

sprawiłaby, iż inne wątki działające w sys-
temu nie mogłyby dojść do głosu. Kluczem
do rozwiązania tego problemu jest wyko-
rzystanie tzw. timera (będącego w rzeczywi-
stości aktywnym obiektem), czyli mechani-
zmu, który powoduje cykliczne wywoływa-
nie funkcji zwrotnej (ang. callback). W na-
szym przypadku, każde takie wywołanie od-
powiadać będzie jednemu przebiegowi opisa-
nej wyżej pętli gry. W tym momencie, w gło-
wach niektórych Czytelników może pojawić
się myśl o tym, aby wykorzystać wielokrotne
timery do kontroli logiki aplikacji. Pomysły
takie należy raczej odrzucić – ich stosowanie
prowadzi do powstawania trudnych proble-
mów synchronizacyjnych, zaś wynikowy kod
trudno się rozwija i pielęgnuje. Dodatkowo,
używanie wielu aktywnych obiektów pro-
wadzi do zużywania zasobów jądra systemu
i znacznie obniża wydajność aplikacji. Jeden
timer stanowiący serce naszej gry będzie jak
najbardziej wystarczający. W tym momencie
proponuję spojrzeć na Listing 10 zawierający
interfejs klasy

CGameSkeletonContainer

.

Do zawartości wspomnianego Listingu

będę odnosił się wielokrotnie. W tym miej-
scu proponuję zwrócić uwagę na wskaź-
nik do obiektu

CPeriodic

. Obiekt ten, two-

rzony dynamicznie w konstruktorze klasy

CGameSkeletonContainer

(Listing 11) repre-

zentuje wspomniany timer.

Dostęp do obiektu otrzymujemy przez

wskaźnik

iTickGenerator

. Działanie timera

jest bardzo proste. Tuż po stworzeniu jest on
nieaktywny. Sposób jego uruchomienia przed-
stawiony jest na Listingu 12.

Na początek musimy sprawdzić czy ti-

mer nie jest przypadkiem już uruchomio-
ny. Jeśli ten warunek jest spełniony to mo-
żemy rozpoczynać pracę. Uruchomienie ti-

Listing 18. Obsługa zdarzenia utraty aktywności

void

CGameSkeletonContainer

::

FocusChanged

(

TDrawNow

aDrawNow

)

{

if

(

IsFocused

()

)

{

StartTickGenerator

();

}

else

{

if

(

iTickGenerator

->

IsActive

()

)

{

StopTickGenerator

();

}

}

if

(

aDrawNow

)

{

DrawNow

();

}

}

background image

09/2008

72

Programowanie urządzeń mobilnych

Programowanie gier dla Symbian OS – szkielet aplikacji

www.sdjournal.org

73

mera wykonujemy za pośrednictwem funk-
cji

Start()

. Funkcja ta przyjmuje trzy argu-

menty – pierwszy z nich określa po jakim
czasie timer ma ruszyć, druga definiuje in-
terwał pomiędzy kolejnymi tyknięciami zaś
ostatni opakowuje funkcję zwrotną. Obiekt
opakowujący przyjmuje adres funkcji (w na-
szym przykładzie jest to statyczna metoda

CGameSkeletonContainer::Tick

) oraz adres

obiektu, który do tej metody będzie przeka-
zywany przy każdym jej uruchomieniu. W
naszym przypadku jest to wskaźnik na siebie
samego (

this

). Ten prosty trik pozwala nam

przekazać do funkcji zwrotnej obiekt, który
kontroluje timer. Spójrzmy teraz na defini-
cję funkcji

CGameSkeletonContainer::Tick

(Listing 13).

Ta statyczna metoda rzutuje otrzymany

wskaźnik typu

TAny*

(symbianowy odpo-

wiednik typu

void*

– ramka zatytułowana

Symbian a definicje typów) na wskaźnik do
kontenera (

CGameSkeletonContainer*

) i wy-

wołuje na nim metodę

OnTick()

. Ta ostatnia

jest już zwyczajną metodą niestatyczną i ja-
ko taka posiada dostęp do wszystkich skład-
ników klasy. W metodzie tej możemy umie-
ścić ciało pętli gry.

Uważni czytelnicy analizujący implemen-

tację metod klasy

CGameSkeletonContainer

zauważyli być może pewien potencjalny de-
fekt. Chodzi o to, że w konstruktorze kla-
sy nie występuje inicjacja wszystkich skła-
dowych (np.

iTickStart

,

iTickStop

czy

iKeyState

). Wbrew pozorom, efekt ten jest

zamierzony i wiąże się z tym, że składowe w
klasach oznaczonych prefiksem

C

, czyli – we-

dług symbianowych konwencji kodowania
– dziedziczących po klasie bazowej

CBase

,

są automatycznie zerowane przez konstruk-
tor

CBase

.

Zanim przejdziemy do omawiania kolej-

nych zagadnień, warto jeszcze ustosunkować
się do wykorzystania stałej

KDesiredFrames

PerSecond.

Stała ta określa częstotliwość tyka-

nia naszego timera i co za tym idzie – maksy-
malną liczbę ramek rysowanych w trakcie jed-
nej sekundy, czyli tzw. współczynnik FPS (ang.
Frames Per Second). W przypadku prezentowa-
nego przykładu wartość wspomnianej stałej
wynosi 1000000/30. 1000000 określa licz-
bę mikrosekund w jednej sekundzie (timer
przyjmuje interwały zapisane w mikrosekun-
dach). W tej sytuacji zakładamy, że timer uru-
chomi funkcję

OnTick()

około trzydziestu razy

w trakcie jednej sekundy. Dociekliwi czytelni-
cy mogą zastanawiać się dlaczego dobrałem tę
wartość w taki, a nie inny sposób. Oto wyjaśnie-
nie. W systemie Symbian w wersji 9 standardo-
wa rozdzielczość timera wynosi 1/64 sekundy
(dokładnie 15.625 milisekund) – ogranicze-
nie to dotyczy zarówno emulatora jak i sprzę-
tu. Inaczej mówiąc – timer nie jest w stanie ty-
kać
szybciej niż 64 razy na sekundę. W tej sytu-
acji, jeśli ustawimy interwał timera na 20 mili-

sekund, to tyknięcia i tak będą występować co
około 30 milisekund (2 x 15.625 milisekund).
Stąd wniosek, że w celu uzyskania równomier-
nego tykania warto przydzielać interwały o
wielokrotności 15 milisekund. Dobrana prze-
ze mnie wartość daje stosunkowo równomier-
ną częstotliwość odświeżania ekranu na pozio-
mie 30/31 FPS. Częstotliwość ta jest stosunko-
wo optymalna dla większości gier uruchamia-
nych na telefonach komórkowych. Pętlę gry, w
zależności od potrzeb, można zaimplemento-
wać na bazie innych timerów, np.

CHeartBeat

,

CTimer

czy

CIdle

. Niektóre, alternatywne roz-

wiązania w tym zakresie przedstawię w kolej-
nych artykułach z cyklu Programowanie Gier
Dla Symbian OS
.

Rysowanie

System Symbian oferuje cały szereg API prze-
znaczonych do rysowania. Najprostszy spo-
sób renderowania grafiki wiąże się z wyko-
rzystaniem standardowego kontekstu gra-
ficznego (

CWindowGc

). Dostęp do tego kon-

tekstu można uzyskać następująco:

CWindowGc& gc = SystemGc();

Za odrysowanie kontrolki na ekranie odpo-
wiada funkcja

Draw()

w klasie ją reprezen-

tującej.

Do funkcji tej przekazywany jest prostokąt

określający fragment ekranu, na którym kon-
trolka może rysować (w naszym przypadku
jest to pełny obszar wyświetlacza). Na Listingu
14 przedstawiona jest implementacja metody

CGameSkeletonContainer::Draw()

. Do zawar-

tości tego Listingu powrócimy za moment.

Kolejna alternatywna metoda rysowania

polega na bezpośrednim odwoływaniu się
do bufora ekranu przy pomocy dedykowane-

go API (DSA, czyli Direct Screen Access), bądź
wykorzystaniu biblioteki OpenGL ES. W na-
szym przykładzie wykorzystane jest jeszcze
inne podejście, powszechnie znane jako po-
dwójne buforowanie (ang. double buffering).
Podejście to w kontekście Symbian OS może
być z powodzeniem wykorzystywane zarów-
no przy implementacji stosunkowo statycz-
nych gier logicznych, jak i szybkich, dyna-
micznych gier akcji. W przypadku stosowa-
nia tej techniki aplikacja wykorzystuje tzw.
tylny bufor (ang. back buffer), na którym ry-
suje się scenę. Później bufor ten jest przeno-
szony do bufora ramki (czytaj: na ekran) przy
pomocy jednej, szybkiej operacji binarnego
kopiowania (ang. blitting). Z racji tego, że po-
jedyncze operacje rysowania w buforze ram-
ki są zazwyczaj bardzo czasochłonne, to ryso-
wanie poszczególnych elementów sceny gry
bezpośrednio na ekran jest zazwyczaj wą-
skim gardłem wydajnościowym i na doda-
tek powoduje nieprzyjemne dla oka migota-
nie. Korzystając z techniki podwójnego bu-
forowania unikamy obydwu problemów. W
przypadku aplikacja GameSkeleton, do imple-
mentacji opisanej wyżej techniki wykorzy-
stamy klasę

CWsBitmap

, która posłuży nam ja-

ko tylny bufor. Kod odpowiedzialny za two-
rzenie i inicjację obiektu tej klasy znajduje
się w metodzie

CGameSkeletonContainer::

ConstructL

(Listing 11). Oprócz samej bit-

mapy (

iBackupBitmap

) potrzebne są nam

dwie dodatkowe składowe:

iBitmapDevice

oraz

iBitmapGc

. Obiekty reprezentowane

przez te składowe niezbędne są do stworze-
nia kontekstu graficznego związanego z bit-
mapą reprezentującą tylny bufor. W rezulta-
cie możemy używać

iBitmapGc

jak zwyczaj-

nego kontekstu graficznego, zaś wyniki prze-
prowadzanych na nim operacji zostaną zapi-

Konwencje oraz idiomy kodowania
przy programowaniu aplikacji dla Symbian OS

Symbian narzuca na swoich programistów cały szereg konwencji oraz idiomów kodowa-
nia, które dość znacząco wpływają na postać kodu źródłowego aplikacji tworzonych pod
ten system. Jeśli chodzi o konwencje kodowania, to najbardziej w oczy rzucają się defini-
cje typów podstawowych (TInt, TReal itd.); prefiksy w nazwach klas, atrybutów i składowych
oraz postfiksy w nazwach funkcji. Co do nazw klas – wyróżniane są cztery podstawowe ich
rodzaje i w tym kontekście stosuje się różne nazewnictwo. Klasy T reprezentują typu pod-
stawowe oraz ich złożenia, klasy C alokują zasoby, klasy M reprezentują interfejsy zaś kla-
sy R – uchwyty do zasobów. Najczęściej spotykany postfiks w nazwach funkcji to L – ozna-
cza on tzw. funkcje leave'ujace. Mechanizm ucieczki (ang. leave) to symbianowa podróbka
wyjątków. Z mechanizmem tym wiąże się idiom dwufazowej konstrukcji obiektów (metody

NewL()

/

NewLC()

/

ConstructL()

) oraz tzw. stos czyszczenia (ang. cleanup stack). Wszystkie te

konstrukcje stosuje się o to, aby uniknąć gubienia zasobów systemowych (głównie pamię-
ci). Co do symbianowych konwencji kodowania, to trzeba przyznać, że posiadają one swo-
je plusy (np. narzucają spójność i porządek w kodach źródłowych). Idiomy kodowania nie-
stety wypadają nieco gorzej – stanowią one zazwyczaj źródło nadmiarowej pracy i utrudnia-
ją pielęgnację programów. Pozytywne jest to, że Symbian dostrzega powoli potrzebę stoso-
wania alternatyw w odniesieniu do idiomatycznego sposobu kodowania i wspiera nowe roz-
wiązania (np. wtyczki Open C/C++). W kolejnych odsłonach cyklu przedstawię bliżej te spo-
śród symbianowych idiomów kodowania, które są niezbędne dla programistów gier. Opo-
wiem także o wspomnianych wtyczkach (Open C/C++), które dla tychże programistów są
szczególnie ciekawe.

background image

09/2008

74

Programowanie urządzeń mobilnych

www.sdjournal.org

75

sane (czytaj: narysowane) w

iBackupBitmap

.

W tym miejscu warto wrócić do przedsta-
wionej wcześniej funkcji odrysowującej za-
wartość kontrolki na ekranie (Listing 14).
Jak widać na wspomnianym listingu, funk-
cja ta odpowiada jedynie za skopiowanie za-
wartości tylnego bufora na ekran. Operację tę
realizujemy przy pomocy funkcji

BitBlit()

.

W tym momencie warto przeanalizować ko-
lejność operacji związanych z rysowaniem w
naszej przykładowej aplikacji. Kluczową rolę
w tym procesie odgrywa kod zawarty w me-
todzie

CGameSkeletonContainer::OnTick()

(Listing 15).

Na początku tej metody wyliczamy różnicę

czasu, na podstawie której uaktualniać będzie-
my logikę gry – zakładamy, że odpowiadać bę-
dzie za to funkcja

Update()

, która jest wywoły-

wana w dalszej kolejności. Następnie mamy wy-
wołanie funkcji

Draw()

. Warto w tym miejscu

zauważyć, że wywołana funkcja ma sygnaturę:

void CGameSkeletonContainer::Draw( CGra-
phicsContext& aGc ) const;

Innymi słowy – nie jest to systemowa funkcja
rysująca zawartość kontrolki na ekranie, tyl-
ko zdefiniowana przez użytkownika metoda
służąca do renderowania tylnego bufora. Ge-
neralnie, przy programowaniu gier w Sym-
bian OS dobrze jest definiować w klasach re-
prezentujących wizualne elementy aplikacji
metody rysujące, które przyjmują argument
o typie

CGraphicsContext

, stanowiący klasę

bazową dla wszystkich kontekstów graficz-
nych. Jeśli skorzystamy z tego interfejsu, to
wynikowy kod źródłowy będzie niezależny
od zastosowanej metody renderowania.

Analizując zawartość klasy

CGameSkele

tonContainer

, warto zwrócić uwagę na skła-

dową

iFpsCounter

. Składowa ta jest wskaźni-

kiem do obiektu klasy

CFpsCounter

. W ra-

mach tej klasy zaimplementowano prosty
mechanizm zliczania ilości narysowanych
klatek na sekundę (ang. frames per second,
FPS). Z punktu widzenia klientów tej klasy
najistotniejsze jest wywoływanie na jej obiek-
cie metody

Update()

po narysowaniu każdej,

kolejnej klatki (co też czynimy w prezento-
wanym przykładzie – Listing 15). Na Listin-
gu 16 pokazana jest implementacja metody

Draw()

, odpowiedzialnej za uaktualnianie za-

wartości tylnego bufora.

Pierwsza część metody

Draw()

odpowia-

da za zamalowanie całego prostokąta ekra-
nu czarnym kolorem. Część druga odpowia-
da za rysowanie licznika FPS (wartość liczni-
ka pobierana jest z obiektu reprezentowane-
go przez składową

iFpsCounter

). Rysowanie

za pomocą kontekstu graficznego (

CGraphic-

sContext

) jest stosunkowo nieskomplikowa-

ne i nie odbiega specjalnie od konwencji spo-
tykanych na innych platformach. Ciekawość
Czytelników może pobudzić za to zmienna
lokalna zdefiniowana jako:

TBuf< KFpsMessageMaxLength > fpsMessage;

Zmienna ta jest przykładem tzw. deskrypto-
ra – czyli obiektu reprezentującego napis w
programach pisanych pod Symbian OS. W
porównaniu do standardowego szablonu re-
prezentującego napisy w języku C++ (

std:

:string

), deskryptory to dość skompliko-

wany twór i na tym etapie pominiemy ich
szczegółowy opis. Do tematu wrócimy w ko-
lejnych odcinkach cyklu.

Podsumowując zawartość tego podpunk-

tu warto zauważyć, że przedstawiony tu me-
chanizm rysowania nie jest najbardziej efek-
tywnym z możliwych, aczkolwiek reprezen-
tuje złoty środek pomiędzy prostotą imple-

mentacji, a wydajnością. Bardziej zaawanso-
wane techniki renderowania w Symbian OS
planuję zaprezentować w jednym z kolejnych
odcinków cyklu.

Interakcja z użytkownikiem

Interakcja z użytkownikiem w systemie
Symbian OS odbywa się przede wszystkim
przy pomocy klawiatury. Jak już wcześniej
wspominałem, w naszym szkielecie gry za
obsługę tejże interakcji odpowiada klasa

CGameSkeletonContainer

.

Implementacja

tego mechanizmu umieszczona jest w funk-
cji

OfferKeyEventL

(Listing 17).

Funkcja ta jest asynchronicznie wywo-

ływana przez silnik aplikacji w przypad-
ku występowania kolejnych zdarzeń klawia-
tury. W naszym przypadku system obsłu-
gi wspomnianych zdarzeń opiera się na uak-
tualnianiu odpowiednich bitów w składowej

iKeyState

. W przypadku gdy dany klawisz

jest wciśnięty, to odpowiedni bit w tej skła-
dowej zostaje zapalony. W sytuacji odwrotnej
– bit jest gaszony. Maski dla poszczególnych
klawiszy zdefiniowane są w oddzielnym pli-
ku nagłówkowym (KeyState.h), tak aby moż-
na było je wykorzystywać wielu plikach źró-
dłowych. Testowanie wciśnięcia danego kla-
wisza można łatwo zrealizować przy pomo-
cy operatorów bitowych. Dla przykładu, na-
stępujący fragment kodu testuje czy wciśnię-
ty został klawisz 7:

if ( iKeyState & KKey7 ) { /* ... */ }

Prezentowana aplikacja obsługuje klawisze
kierunkowe oraz enter, a także klawisze ste-
rujące (tzw. softkeys – duże klawisze znajdu-
jące się zazwyczaj bezpośrednio pod wyświe-
tlaczem telefonu komórkowego) oraz klawi-
sze numeryczne. Warto w tym miejscu dodać,
że w aplikacjach Symbian OS równoczesna ob-
sługa wielu klawiszy jest domyślnie wyłączo-
na. Aby włączyć tę obsługę, należy dodać w
konstruktorze klasy

CGameSkeletonAppUi

(Li-

sting 4) wywołanie:

CAknAppUi::SetKeyBlockMode( ENoKeyBlock );

Niestety, metoda ta dostępna jest tylko na
platformie Series60.

Interakcja z systemem

Dość specyficzną cechą dotyczącą progra-
mowania gier na urządzenia mobilne, jest
obsługa tzw. przerwań (bądź interrupcji).
Wyobraźmy sobie, że podczas trwania akcji
gry dzwoni telefon. W tej sytuacji warto by
wstrzymać pętlę gry – przynajmniej do cza-
su zakończenia rozmowy. Powodów ku temu
jest wiele. Najważniejsze z nich to:

• automatyczna ochrona użytkownika przed

skutkami nagłego wyrwania się z akcji gry –

W Sieci

ftp://ftp.activestate.com/ActivePerl/Windows/5.6/ActivePerl-5.6.1.635-MSWin32-x86.msi

dystrybucja Perla potrzebna do tworzenia aplikacji dla Symbian OS

http://java.sun.com/ – stąd można pobrać środowisko uruchomieniowe języka Java
http://www.forum.nokia.com/ – portal dla programistów piszących oprogramowania dla

telefonów firmy Nokia; można stąd pobrać bezpłatne narzędzia programistyczne wyko-
rzystywane w niniejszym artykule

http://www.newlc.com/ – portal programistów aplikacji dla Symbain OS; można tu zna-

leźć cały szereg interesujących informacji – zarówno dla początkujących jak i zaawanso-
wanych

Bibliografia

Developing Software for Symbian OS: An Introduction to Creating Smartphone Applications

in C++; Steve Babin; John Wiley & Sons, 2005. Ta pozycja dobra jest dla Czytelników, któ-
rzy chcieliby stosunkowo szybko poznać podstawy programowania aplikacji dla Sym-
bian OS

• Symbian OS Explained: Effective C++ Programming for Smartphones; Jo Stichbury; John

Wiley & Sons, 2004. Ta pozycja dobra jest dla czytelników, którzy chcieli by poznać bar-
dziej specjalistyczne tematy związane z programowaniem Symbian OS, np. szczegóły
dotyczące implementacji i korzystania z aktywnych obiektów

background image

09/2008

74

Programowanie urządzeń mobilnych

www.sdjournal.org

75

można w tym przypadku założyć, że mało
kto będzie w stanie kontynuować grę w trak-
cie rozmowy;

• oszczędzanie energii telefonu – wszak

kręcąca się pętla gry tę cenną energię zu-
żywa.

Na dodatek, Symbian jest systemem wielo-
zadaniowym i użytkownik może w każdej
chwili przeskoczyć do innej aplikacji – w tej
sytuacji spauzowanie pętli gry byłoby rów-
nież jak najbardziej na miejscu. Wszystkie
omówione powyżej sytuacje można uogól-
nić do jednego systemowego zdarzenia ja-
kim jest utrata aktywności (ang. focus). Zda-
rzenie to występuje w aplikacji wtedy, gdy
inna aplikacja przejmuje kontrolę (mogliby-
śmy użyć analogii ze świata Windows i po-
wiedzieć: kiedy inne okienko wyskakuje na
wierzch
). Tak właśnie dzieje się, kiedy w tele-
fonie symbianowym rozpoczyna się rozmo-
wa (za obsługę połączenia głosowego odpo-
wiada dedykowana aplikacja), tak samo jest
gdy użytkownik wybierze z listy zadań in-
ny program.

Zdarzenie utraty aktywności obsługuje-

my z poziomu klasy kontrolki (

CGameSke-

letonContainer

). W przypadku wystąpie-

nia tego zdarzenia, silnik aplikacji asyn-
chronicznie wywoła na kontrolce funkcję

FocusChanged()

. Przyjrzyjmy się, jak funkcja

ta jest zaimplementowana w naszej kontrol-
ce (Listing 18).

Implementacja jest prosta: na począt-

ku sprawdzamy czy aplikacja utraciła bądź
odzyskała kontrolę (służy do tego funk-
cja

IsFocused()

). W pierwszym z tych

dwóch przypadków wywołujemy pomocni-
czą funkcję

StopTickGenerator()

, spraw-

dziwszy uprzednio czy aby nasz timer
wcześniej był uruchomiony (

iTickGenera-

tor->IsActive()

). Implementacja funkcji

StopTickGenerator()

jest trywialna i spro-

wadza się do wywołania funkcji

Cancel()

na obiekcie timera, co powoduje jego zatrzy-
manie:

void CGameSkeletonContainer::StopTickGe-
nerator()
{ iTickGenerator->Cancel(); }

W przypadku odwrotnym (tj. kiedy apli-
kacja

odzyskuje

kontrolę)

wywołuje-

my omówioną już wcześniej funkcję

StartTickGenerator()

, odpowiedzialną za

uruchomienie timera (Listing 12). Dzięki
temu prostemu zabiegowi, jesteśmy w sta-
nie obsłużyć cały szereg wydarzeń zakłóca-
jących naszą grę.

Oczywiście, interakcja z systemem nie

kończy się na obsłudze zdarzenia utraty
kontroli przez aplikację. Kolejne przydatne
mechanizmy związane z tą tematyką (mię-
dzy innymi obsługę zmiany orientacji ekra-

nu oraz wykrywanie braku aktywności użyt-
kownika) będę omawiał w kolejnych artyku-
łach z cyklu.

Testujemy!

Nadszedł wreszcie upragniony moment
przetestowania naszej przykładowej aplika-
cji. Aby tego dokonać, należy wykonać nastę-
pujący szereg czynności. Na początek aplika-
cję trzeba zbudować. W tym celu posłużymy
się środowiskiem Carbide.C++. Archiwum z
aplikacją należy rozpakować, a następnie za-
importować do Carbide korzystając z opcji
File –> Import –> Symbian OS –> Symbian OS
Bld.inf
file. Wspomniany plik Bld.inf znaj-
duje się w podkatalogu group projektu. Ro-
lę tego, oraz pozostałych plików konfigura-
cyjnych w projektach symbianowych omó-
wię w kolejnym odcinku z serii moich ar-
tykułów. Teraz skupimy się na uruchomie-
niu aplikacji. Po zaimportowaniu projek-
tu należy go zbudować (Project –> Build Pro-
ject
). Jako, że domyślnie ustawioną konfigu-
racją w tym projekcie jest Emulator Debug,
to efektem naszego ostatniego działania bę-
dzie stworzenie binarnej wersji naszej aplika-
cji działającej pod emulatorem. Przy pomocy
komendy Run –> Run (Ctrl + F11) możemy
obejrzeć efekt działania aplikacji GameSkele-
ton
(Rysunek 2).

Wspomniany efekt – czyli czarny ekran z

narysowanym licznikiem FPS – nie jest mo-
że porażający, aczkolwiek mając na uwadze
przedstawioną wyżej analizę, wiemy, że stoi
za nim dość obszerna mechanika.

W dalszej kolejności warto by zająć się

uruchomieniem naszego przykładu na fi-
zycznym urządzeniu. Niestety, ze wzglę-
du na dość skomplikowany proces certyfi-
kacji (Symbiana wymaga, by każda aplikacja
działająca na telefonie została podpisana od-
powiednim certyfikatem bezpieczeństwa),
kwestię tę chwilowo pominiemy (wrócimy
do niej w kolejnym artykule z cyklu).

Zadanie domowe

Przedstawiony wyżej szkielet aplikacji sta-
nowi solidny wstęp do bardziej zaawanso-
wanych i ciekawszych tematów związanych z
programowaniem gier. Czytelników, którym
przypadł do gustu niniejszy tekst i planują
kontynuować lekturę mojego cyklu w kolej-
nym numerze SDJ, zachęcam do wykonania
kilku ćwiczeń związanych z przedstawiony-
mi tu materiałami. Po pierwsze, warto zapo-
znać się bliżej z narzędziem Carbide, a szcze-
gólnie z debuggerem. Jako pierwszą część za-
dania domowego zalecam powstawianie w
różnych miejscach naszej przykładowej apli-
kacji punktów wstrzymania (ang. breakpo-
ints
), uruchomienie jej w trybie debug (F11)
i przeprowadzenie praktycznych obserwacji
mechaniki działania szkieletu (pętla głów-
na, zdarzenia itd.). Jako drugie zadanie, pro-

ponuję zapoznać się z metodami dostępny-
mi w ramach klasy

CGraphicsContext

. Ko-

rzystając ze wspomnianych metod warto w
ramach ćwiczenia zaimplementować me-
chanizm wizualizacji zdarzeń klawiatury.
W tym celu można narysować zbudowaną z
prymitywów graficznych oraz tekstów imita-
cję
klawiatury telefonu i w zależności od sta-
nu zmiennej

iKeyState

podświetlać (np. ry-

sować innym kolorem) te jej fragmenty, dla
których wystąpiło naciśnięcie klawisza. Me-
chanizm ten można wykorzystać do przete-
stowania systemu obsługi zdarzeń klawiatu-
ry – warto zaimplementować go tak, aby we-
ryfikował obsługę wciśnięć wielu klawiszy
jednocześnie.

Podsumowanie

W niniejszym artykule przedstawiłem szkie-
let aplikacji dla Symbian OS, stanowiący so-
lidny punkt wyjścia przy pisaniu gier. W ko-
lejnych podpunktach pokazałem jak oprogra-
mować pętlę gry, renderowanie zawartości
ekranu, prosty system obsługi zdarzeń kla-
wiatury oraz podstawowe mechanizmy inte-
rakcji z systemem. W kolejnym artykule z cy-
klu pokażę jak:

• zaimplementować prostą grę bazującą na

przedstawionym szkielecie;

• zaprogramować operacje odczytu i zapi-

su danych z/do plików;

• dołączyć do przedstawionego szkieletu

obsługę dźwięku;

• wykorzystać idiomy programistyczne

Symbian OS przy tworzeniu gier;

• uruchomić grę na telefonie.

Na koniec chciałbym wymienić osoby, któ-
re dzięki swoim wnikliwym recenzjom przy-
czyniły się do znacznej poprawy jakości ni-
niejszego artykułu. Wspomniane osoby to:
David de Rosier, Jacek Noe Cybularczyk oraz
Mateusz Kula. Chciałbym również podzięko-
wać mojej ukochanej żonie – Oli, za cierpli-
wość, którą okazała mi w trakcie pisania te-
go artykułu.

RAFAŁ KOCISZ

Pracuje na stanowisku Dyrektora Techniczne-
go w firmie Gamelion, wchodzącej w skład Gru-
py BLStream. Rafał specjalizuje się w technolo-
giach związanych z produkcją oprogramowa-
nia na platformy mobilne, ze szczególnym na-
ciskiem na tworzenie gier. Grupa BLStream po-
wstała by efektywniej wykorzystywać potencjał
dwóch, szybko rozwijających się producentów
oprogramowania – BLStream i Gamelion. Firmy
wchodzące w skład grupy specjalizują się w wy-
twarzaniu oprogramowania dla klientów korpo-
racyjnych, w rozwiązaniach mobilnych oraz pro-
dukcji i testowaniu gier.
Kontakt z autorem: rafal.kocisz@gmail.com

Programowanie gier dla Symbian OS – szkielet aplikacji


Wyszukiwarka

Podobne podstrony:
programynaprojektyzarchitektury12, GARAZ WIELOPOZIOMOWY DLA SAM OS, WARSZTAT NAPRAWY SAMOCHODÓW
programynaprojektyzarchitektury12, GARAZ WIELOPOZIOMOWY DLA SAM OS, WARSZTAT NAPRAWY SAMOCHODÓW
fizyka dla programistow gier BUUYAH77E6CETAXOB2NJWQPLUYVSIOOHGHTIK6A
Fizyka dla programistow gier fiprog
C dla programistow gier Wydanie II cpprog
Programowanie w jezyku C dla chetnych A Poznanski
Indywidualny program terapeutyczny dla uczniów upośledzonych, DOKUMENTY AUTYZM< REWALIDACJA, re
Program terapeutyczny dla uczniów ryzyka dysleksji, szkoła, Rady Pedagogiczne, wychowanie, profilakt
Program praktyk dla I roku Ratownictwa Medycznego
Program integracji dla klasy I C w Szkole Podstawowej nr 2 w Toruniu
FermaCena program kosztorysujący dla systemu suchej zabudowy FERMACELL fermacena 2 sucha zabudowa F
Programowanie w Visual?sic dla Excel
Plan i program praktyki dla studentów PEDAGOGIKI rozpoczynających studia w roku akadem 10 2011 i pó

więcej podobnych podstron