dareks_

  • Dokumenty2 821
  • Odsłony753 730
  • Obserwuję431
  • Rozmiar dokumentów32.8 GB
  • Ilość pobrań361 988

Programista 2012 01 (micro)

Dodano: 6 lata temu

Informacje o dokumencie

Dodano: 6 lata temu
Rozmiar :5.9 MB
Rozszerzenie:pdf

Programista 2012 01 (micro).pdf

dareks_ CZASOPISMA
Użytkownik dareks_ wgrał ten materiał 6 lata temu.

Komentarze i opinie (0)

Transkrypt ( 25 z dostępnych 139 stron)

Witamy Szanowni twórcy oprogramowania i specjaliści branży IT Przed Wami pierwsze wydanie magazynu „Programista”, które ukazało się również w postaci drukowanej. Od niniejszego numeru magazyn staje się miesięcznikiem. Przychyliliśmy się też do Waszych próśb i przygotowaliśmy magazyn w wersji elektronicznej w plikach ePub i .mobi oraz .pdf. Rośnie ilość i złożoność otaczających nas systemów informatycznych. Rynek tabletów i smartfonów ciągle odnotowuje wzrosty i naturalną koleją rzeczy powstają tysiące nowych aplikacji na te urządzenia: od gier po programy użytkowe. Rozwijają się języki programowania, powstają kolejne wersje „mobilnych” systemów operacyjnych. W tej sytuacji nie ma wyjścia: chcąc pozostać konkurencyjnym w zawodzie Programisty, trzeba się na bieżąco rozwijać, dokształcać. Naszym celem jest Wam to zadanie ułatwić. W bieżącym numerze przedstawiamy bibliotekę Cocos2D: jeden z najpopularniejszych silników do tworzenia gier na platformę iOS. Ponadto polecamy praktyczny artykuł na temat języka Objective-C, w którym autor omawia przydatne jego właściwości, pozwalające znacznie usprawnić proces tworzenia aplikacji na urządzenia mobilne ze stajni Apple. Znajdziecie u nas również ciekawy artykuł traktujący o popularnym ostatnio podejściu Domain Driven Design. Ponadto kontynuujemy tematykę poruszoną w premierowym wydaniu naszego miesięcznika: omawiamy możliwości nowego standardu języka C++ oraz prezentujemy ciekawe tematy z zakresu inżynierii oprogramowania. Na koniec pragniemy serdecznie podziękować za konstruktywne opinie dotyczące pierwszej, elektronicznej edycji magazynu. Wydanie to udostępniliśmy bezpłatnie, by każdy mógł wyrobić sobie opinię o projekcie. Olbrzymie zainteresowanie, jakim cieszył się premierowy egzemplarz, utwierdza nas w przekonaniu, że tego typu wydawnictwa brakowała na polskim rynku i dopinguje do jeszcze bardziej wytężonej pracy nad magazy​nem. Cieszy nas, że zdecydowana większość z Was pozytywnie oceniła inicjatywę! Dziękujemy za wsparcie i życzymy przyjemnej lektury! Z wyrazami szacunku, Redakcja

Spis treści BIBLIOTEKI I NARZĘDZIA Biblioteka Cocos2D: wprowadzenie Rafał Kocisz JĘZYKI PROGRAMOWANIA C++11 część I Bartosz Szurgot, Mariusz Uchroński, Wojciech Waga Wybrane elementy języka Objective-C i ich wykorzystanie Łukasz Mazur Erlang - język inny niż C++ czy Java Marek Sawerwain PROGRAMOWANIE GRAFIKI Direct3D – podstawy Wojciech Sura PROGRAMOWANIE URZĄDZEŃ Wykorzystanie sensora Kinect w systemie Windows Łukasz Górski INŻYNIERIA OPROGRAMOWANIA Domain Driven Design krok po kroku część II: Zaawansowane modelowanie DDD – techniki strategiczne: konteksty i architektura zdarzeniowa Sławomir Sobótka KLUB LIDERA IT Dokumentowanie architektury. Jak zorganizować proces rozwoju architektury? Michał Bartyzel, Mariusz Sieraczkiewicz KOMIKS Maciej Mazurek WDROŻENIA Highsky.com – projekt, oprogramowanie i wdrożenie pla​tformy inwestycyjnej highsky.com zintegro​wanej z platformą MetaTrader 5. Wojciech Holisz

Redakcja Wydawca: Anna Adamczyk annaadamczyk@programistamag.pl Redaktor naczelny: Łukasz Łopuszański lukaszlopuszanski@programistamag.pl Redaktor prowadzący: Rafał Kocisz rafal.kocisz@gmail.com Korekta: Tomasz Łopuszański Kierownik produkcji: Krzysztof Kopciowski bok@keylight.com.pl DTP: Krzysztof Kopciowski Dział reklamy: reklama@programistamag.pl tel. +48 663 220 102 tel. +48 604 312 716 Prenumerata: prenumerata@programistamag.pl Współpraca: Michał Bartyzel, Mariusz Sieraczkiewicz, Sławomir Sobótka, Artur Machura, Marek Sawerwain, Łukasz Mazur, Rafał Kułaga Adres wydawcy: Dereniowa 4/47 02-776 Warszawa Druk: Zamów wydanie w wersji papierowej przez www.programistamag.pl O ile nie zaznaczono inaczej, wszelkie prawa do wszystkich materiałów zamieszczanych na łamach magazynu Programista są zastrzeżone. Kopiowanie i rozpowszechnianie ich bez zezwolenia jest wzbronione. Naruszenie praw autorskich może skutkować odpowiedzialnością prawną, określoną w szczególności w przepisach ustawy o prawie autorskim i prawach pokrewnych, ustawy o zwalczaniu nieuczciwej konkurencji i przepisach kodeksu cywilnego oraz przepisach prawa prasowego. Redakcja magazynu Programista nie ponosi odpowiedzialności za szkody bezpośrednie i pośrednie, jak również za inne straty i wydatki poniesione w związku z wykorzystaniem informacji prezentowanych na łamach magazy​nu Programista. Wszelkie nazwy i znaki towarowe lub firmowe występujące na łamach magazynu są zastrzeżone przez odpowiednie firmy.

Biblioteka Cocos2D: wprowadzenie Rafał Kocisz iOS to platforma, która rozbudza wyobraźnię wielu programistów. Któż z nas nie marzy o karierze niezależnego twórcy gier i setkach tysięcy dolarów zarobionych dzięki sprzedaży aplikacji na AppStore? W niniejszym artykule przedstawiona jest biblioteka, która może być kluczem do spełnienia tych marzeń. Głównym celem niniejszego artykułu jest zapoznanie Czytelnika z podstawowymi blokami budulcowymi, które oferuje biblioteka Cocos2D. Po jego przeczytaniu będziesz wiedział, czego możesz spodziewać się po tym silniku i na ile przydatny będzie on w Twoich projektach. Cocos2D to potężna biblioteka i szczegółowe jej opisanie to temat, który kwalifikuje się bardziej na książkę niż na artykuł. Z tego względu niniejszy tytuł kładzie nacisk na ogólne zrozumienie koncepcji, na których opiera się biblioteka Cocos2D, oraz przedstawienie relacji między nimi. W jednym z ostatnich sekcji artykułu wskazane są materiały, z których zainteresowani Czytelnicy będą mogli skorzystać w celu poszerzenia swojej wiedzy na temat prezentowanej tu biblioteki. DLACZEGO COCOS2D? Zanim przejdziemy do omówienia możliwości biblioteki Cocos2D, spróbujmy odpowiedzieć sobie na podstawowe pytanie: co sprawia, że warto zainteresować się właśnie tym konkretnym rozwiązaniem? Pierwszy ważny powód, dla którego warto rozważyć używanie Cocos2D, to fakt, iż biblioteka ta jest całkowicie darmowa, zaś jej licencja pozwala tworzyć zarówno aplikacje komercyjne, jak i niekomercyjne. Licencja Cocos2D jest otwarta, silnik rozpowszechniany jest razem z kodem źródłowym. Oznacza to, że nic nie stoi na przeszkodzie, aby w razie potrzeby zajrzeć do kodu źródłowego biblioteki czy wręcz wprowadzić do niej własne modyfikacje. Cocos2D napisany jest w języku Objective-C. Biorąc pod uwagę, że Cocos2D obsługuje urządzenia z rodziny iOS oraz OS X, wybór ten wydaje się bardzo trafny: Objective-C to natywny język programowania w wymienionych systemach. Dla osób, które znają inny język obiektowy (np. C++, Java, C#), nauka Objective-C nie powinna sprawić większych trudności. Osobom rozpoczynającym przygodę z programowaniem sugerowałbym poświęcenie nieco czasu na solidne zapoznanie się z Objective-C, zanim na poważnie zaczną zajmować się programowanie gier pod iOS przy użyciu biblioteki Cocos2D. Jak sugeruje nazwa biblioteki, Cocos2D wspomaga programowanie gier 2D. Warto podkreślić jednak, że mowa tutaj o nowoczesnych grach 2D, w których na porządku dziennym są wykonywane w czasie rzeczywistym transformacje obrazów (np. rotacja czy skalowanie), obsługa przeźroczystości czy post-processing (wszystko to oczywiście wspomagane akceleracją sprzętową). Jeśli jesteś początkującym programistą gier, to zabawa z dwuwymiarem jest wręcz zalecana (chociażby dlatego, że algorytmy stosowane w tego typu grach są o wiele łatwiejsze w implementacji w stosunku do ich odpowiedników stosowanych w grach 3D). Dodatkowo, za Cocosem stoi bardzo liczna, prężna i otwarta społeczność złożona w dużej

mierze z niezależnych programistów gier, co daje możliwość stosunkowo łatwego uzyskania wsparcia. Ze względu na swoją popularność Cocos2D może poszczycić się posiadaniem dużej ilości wysokiej jakości materiałów edukacyjnych (samouczków, książek, forów dyskusyjnych) oraz narzędzi, które wspierają i ułatwiają pracę z tą biblioteką. Podsumowując, Cocos2D jest niewątpliwie biblioteką, z którą warto się zapoznać. Jeśli masz ochotę zanurkować w jego barwny świat, zapraszam do lektury dalszej części niniejszego artykułu. GRAF SCENY Najważniejsza, centralna koncepcja, wokół której zbudowana jest biblioteka Cocos2D to tzw. graf sceny (zwany czasami drzewem sceny bądź hierarchią sceny). Zrozumienie tej koncepcji jest kluczowe w przypadku gdy ktoś chce efektywnie korzystać z prezentowanego tu silnika. Wyobraź sobie, że masz zaimplementować fragment graficznego interfejsu użytkownika w grze, coś podobnego do menu przedstawionego na Rysunku 1. Spróbuj spojrzeć na ten obraz okiem programisty i zastanów się, jak można by zorganizować model takiego interfejsu użytkownika na poziomie kodu źródłowego. Pierwsza myśl, która zapewne przyjdzie Ci do głowy, to przechowywanie płaskiej listy obiektów reprezentujących wszystkie widoczne elementy na ekranie (elementy tła, przyciski i elementy tekstowe). Obiekty takie przechowywałyby informacje o stanie poszczególnych elementów menu (np. ich pozycja, stan, widoczność). Informacji tych można by użyć do rysowania całej sceny, obsługi zdarzeń użytkownika itp. Rysunek 1. Proste menu gry (źródło: opracowanie własne) Podejście takie można by oczywiście z powodzeniem zastosować, ale... zanim zaczniesz kodować, zastanów się, czy nie można by było zorganizować tego lepiej? Powiedzmy, że chciałbyś zaimplementować prosty efekt polegający na tym, że druga warstwa tła z umieszczonymi na niej kontrolkami płynnie wsuwa się przy rozpoczęciu gry, zaś przy jej zakończeniu wysuwa się poza ekran (patrz: Rysunek 2).

Rysunek 2. Efekt wsuwania się menu (źródło: opracowanie własne) Sprawa niby prosta, jednakże przy zastosowaniu płaskiej listy jako struktury danych reprezentującej kolekcję kontrolek, implementacja takiego efektu staje się nieco uciążliwa: trzeba ręcznie wybrać wszystkie obiekty, które chcemy wsuwać\wysuwać, i dla każdego z nich odpowiednio modyfikować ich pozycje. W tej sytuacji aż się prosi, aby potraktować drugą warstwę tła jako płaszczyznę, na której leżą wszystkie pozostałe kontrolki, i przesunąć ją, razem ze wszystkim elementami, które są na niej umieszczone. W tym celu możemy zastosować właśnie graf sceny. Graf sceny to drzewiasta struktura danych pozwalająca reprezentować obrazy (zarówno 2D i 3D) tak, aby zachować informację o hierarchii obiektów na nich występujących. Kluczowym elementem grafu sceny jest węzeł (ang. node), który spełnia dwojaką rolę: po pierwsze, reprezentuje wybrany element sceny; po drugie, jest kontenerem, który może przechowywać inne węzły, stanowiące jego dzieci (ang. children). Węzeł posiadający dzieci nazywany jest rodzicem (ang. parent). W grafie sceny występuje jeden węzeł, który nie posiada rodzica; nazywamy go korzeniem (ang. root). Znamienne dla grafu sceny jest to, że każdy rodzic definiuje dla swoich dzieci swoisty lokalny układ współrzędnych. Oznacza to, iż współrzędne dzieci (a także inne ich właściwości) rozpatrywane są w odniesieniu do układu rodzica. Np. jeśli węzeł rodzic (korzeń) ma pozycję (50, 50), zaś jego potomek znajduje się w punkcie (10, 15), to rzeczywista (ekranowa) pozycja tego drugiego wyniesie (60, 75). W takim ujęciu, gdy zmienimy pozycję danego węzła, to automatycznie przemieszczone zostaną jego dzieci. Każdy węzeł w grafie sceny posiada identyczny interfejs, opisany zazwyczaj przez abstrakcyjną klasę bazową. Po tej klasie dziedziczą inne klasy, reprezentujące konkretne węzły. Czytelnicy, którzy mieli styczność z tzw. wzorcami projektowymi (ang. design patterns), słusznie skojarzą przedstawiony tutaj opis ze wzorcem kompozyt (ang. composite); de facto graf sceny jest niemalże wzorcowym przykładem takiego podejścia projektowego. Spróbujmy odnieść przedstawione wyżej rozważania do naszej przykładowej sceny. Na Rysunku 3 przedstawiona jest wspomniana scena z oznaczeniem elementów hierarchii w grafie. Korzeniem grafu jest pierwsza warstwa tła (czerwony prostokąt otaczający). Korzeń ma jednego potomka: drugą warstwę tła (niebieski prostokąt otaczający). Ten z kolei posiada czwórkę dzieci: przyciski (żółte prostokąty otaczające). Każdy przycisk posiada jednego potomka, którym jest umieszczony pod nim napis. Do reprezentacji takiej sceny potrzebowalibyśmy dwóch konkretnych klas-węzłów reprezentujących statyczny obrazek (elementy tła i przyciski) oraz tekst (napisy pod przyciskami). Mając do dyspozycji tak zorganizowaną scenę, zaprogramowanie efektu wsuwania się drugiej warstwy tła z umieszczonymi na niej kontrolkami staje się bardzo proste; wystarczy jedynie odpowiednio zmodyfikować pozycję węzła reprezentującego tę warstwę, a o właściwe pozycjonowanie pozostałych elementów zadba graf sceny.

Rysunek 3. Hierarchia sceny dla prostego menu w grze (źródło: opracowanie własne) Graf sceny to nieocenione narzędzie przy tworzeniu aplikacji wyświetlających złożone obrazy zbudowane z grup powiązanych ze sobą obiektów (pod tę kategorię można z powodzeniem podciągnąć większość nowoczesnych gier 2D oraz 3D). Jest on również bardzo przydatny w innych zastosowaniach, np. określanie widoczności obiektów czy detekcja kolizji. Na tym etapie ważne jest, abyś zrozumiał podstawową ideę tego wzorca projektowego, gdyż stanowi on serce biblioteki Cocos2D. KLASA BAZOWA CCNODE Tak jak wspominałem w poprzednim punkcie, kluczowymi elementami w grafie sceny są węzły. Każdy węzeł dziedziczy po klasie bazowej, która definiuje spójny interfejs dla wszystkich obiektów umieszczanych w hierarchii. W przypadku biblioteki Cocos2D klasa ta nazywa się CCNode . Po tej klasie dziedziczą kolejne klasy, reprezentujące konkretne obiekty, które można umieszczać w grafie sceny. Na Rysunku 4 przedstawiona jest hierarchia klas wywodzących się z klasy bazowej CCNode .

Rysunek 4. Hierarchia klas wywodzących się z CCNode (źródło: http://www.cocos2d-iphone.org/ ). Schemat w większej rozdzielczości. Jak widać, klas tych jest całkiem sporo. Cocos2D to bardzo aktywnie rozwijana biblioteka, więc w momencie kiedy czytasz niniejszy artykuł, hierarchia ta może wyglądać nieco inaczej od tej, która jest tutaj przedstawiona (dotyczy ona Cocos2D w wersji 1.0.1), jednakże szereg koncepcji reprezentowanych przez wybrane klasy niewątpliwie pozostaną niezmienne. Klasy te będą szczegółowo omówione w kolejnych podpunktach niniejszego artykułu. Zanim jednak do tego przejdziemy, przyjrzyjmy się bardziej szczegółowo interfejsowi klasy CCNode . Jak już wcześniej wspominałem, jest to klasa abstrakcyjna i nie ma bezpośredniej reprezentacji wizualnej, jednakże pełni bardzo istotną rolę, gdyż zawiera pola i metody wspólne dla wszystkich węzłów przechowywanych w grafie sceny. Oto lista najważniejszych z nich: tworzenie nowego węzła: CCNode* childNode = [CCNode node]; dodawanie węzła-dziecka: [node addChild:child z:0 tag:73]; wyłuskiwanie węzła-dziecka: CCNode* child = [node getChildByTag:73]; usuwanie węzła-dziecka określonego za pomocą identyfikatora: (z opcjonalnym

czyszczeniem, polegającym na zastopowaniu wszystkich akcji przypisanych do usuwanego węzła): [node removeChildByTag:73 cleanup:YES]; usuwanie węzła-dziecka za pośrednictwem wskaźnika: [node removeChild:child]; usuwanie wszystkich dzieci z węzła: [node removeAllChildrenWithCleanup:YES]; usuwanie siebie samego z węzła-rodzica: [node removeFromParentAndCleanup:YES]; Jak łatwo się można domyśleć, parametr z przekazywany w metodzie addChild określa głębokość (ang. depth) węzła w scenie i pozwala kontrolować kolejność rysowania elementów przechowywanych w grafie. Zasada jest prosta: elementy z małymi wartościami z rysowane są jako pierwsze, zaś te, które mają największą głębokość – jako ostatnie. Jeśli część węzłów posiada taką samą wartość parametru z , to porządek ich rysowania określony jest kolejnością ich dodawania do węzła-rodzica. Z kolei parametr tag to rodzaj identyfikatora, za pomocą którego możemy szybko wyłuskiwać czy usuwać węzły za pomocą takich metod jak getChildByTag czy removeChildByTag . Korzystając z tagów, należy pamiętać o tym, że Cocos2D nie rozwiązuje problemu ich unikalności, tj. jeśli umieścisz w scenie dwa, lub więcej węzłów o identycznych identyfikatorach, to będziesz w stanie dostać się tylko do pierwszego z nich; pozostałe będą niedostępne. Cocos2D wykorzystuje tagi również do identyfikacji tzw. akcji (co to są akcje, dowiesz się już za moment); w tym miejscu chciałbym tylko wspomnieć, że identyfikatory węzłów i akcji nie kolidują ze sobą, tak więc dopuszczalna jest sytuacja, gdy zarówno węzeł, jak i przypisana do niego akcja mają ten sam tag. Jak już kilka razy wspomniałem, węzły mogą mieć przypisane akcje (ang. actions). Koncepcję akcji opiszę szczegółowo w oddzielnym podpunkcie niniejszego artykułu; tutaj przedstawię je tylko pokrótce, tak abyś mógł zrozumieć relację pomiędzy nimi a węzłami. Pisząc w największym skrócie, akcje to obiekty reprezentujące działania wykonywane na

obiektach sceny. Wyobraź sobie chcesz zaimplementować graficzny element na ekranie (np. przycisk), który miarowo pulsuje (na przemian powiększa się i zmniejsza) albo miarowo miga (na przemian pojawia się i znika) bądź po prostu przemieszcza się pomiędzy dwoma punktami. Wszystkie te operacje możesz zrealizować przypisując do węzła reprezentującego wspomniany element odpowiednie akcje. O tym, jakie są kategorie akcji, porozmawiamy za moment; teraz kilka słów na temat tego, jak można obsługiwać je z poziomu interfejsu klasy CCNode . Na początek stwórzmy prostą akcję reprezentującą proces migania węzła i przypiszmy jej identyfikator: CCAction* action = [CCBlink actionWithDuration:5 blinks:10]; action.tag = 37; Akcja ta przypisana do określonego węzła sprawi, że zamiga on 10 razy w przeciągu 5 sekund. Klasa CCNode pozwala kontrolować akcje w następujący sposób: uruchomienie akcji: [node runAction:action]; wyłuskanie akcji określonej za pomocą identyfikatora: CCAction* retrievedAction = [node getActionByTag:37]; zastopowanie akcji określonej za pomocą identyfikatora: [node stopActionByTag:37]; zastopowanie akcji określonej za pomocą wskaźnika: [node stopAction:action]; zastopowanie wszystkich akcji przypisanych do danego węzła: [node stopAllActions]; Ostatnią właściwością klasy CCNode , o której chciałbym opowiedzieć, to możliwość tzw. harmonogramowania wiadomości (ang. message scheduling). Brzmi to nieco tajemniczo, jednakże w praktyce rzecz jest bardzo prosta. Przede wszystkim należy wyjaśnić, że w nomenklaturze języka Objective-C wysłanie wiadomości do obiektu oznacza po prostu wywołanie metody na tym obiekcie. W takim ujęciu harmonogramowanie wiadomości oznacza cykliczne wywoływanie określonej metody. Wyobraź sobie, że chciałbyś mieć możliwość oprogramowania jakiejś dedykowanej logiki dla

danego typu węzłów (np. detekcja kolizji bądź sprawdzanie warunku zakończenia gry). Tego typu logikę umieszcza się zazwyczaj w metodzie update bądź process , która wywoływana jest po narysowaniu kolejnej ramki gry, przyjmującej jako parametr przyrost czasu od poprzedniego jej wywołania (ang. time delta). Jeśli potrzebujesz takiej funkcjonalności w węźle, to musisz odpowiednio przeładować metodę scheduleUpdateMethod (patrz: Listing 1). Listing 1. Harmonogramowanie prostej funkcji obsługującej logikę węzła -(void) scheduleUpdateMethod { [self scheduleUpdate]; } -(void) update:(ccTime)delta { // Ta metoda będzie wywoływana po narysowaniu // kolejnej ramki gry. } REŻYSER, SCENA, WARSTWA Chciałbym przedstawić teraz trzy klasy, które pełnią w bibliotece Cocos2D bardzo ważne role. Mowa tu o klasach CCDirector , CCScene oraz CCLayer . Dwie ostatnie z wymienionych dziedziczą z CCNode i podobnie jak ona, nie mają wizualnej reprezentacji. CCScene to kontener dla wszystkich obiektów znajdujących się na scenie. Obiekty tej klasy reprezentują zazwyczaj ekrany gry: menu główne, tabelę wyników czy wreszcie – właściwą rozgrywkę. Z kolei klasa CCLayer (warstwa) służy do grupowania obiektów, głównie w celu zapewnienia właściwej kolejności ich renderowania. Można sobie np. wyobrazić, że ekran rozgrywki składa się z trzech warstw: statycznego tła, części dynamicznej (ruchome obiekty gry) oraz paska statusu przedstawiającego liczbę zdobytych punktów oraz żyć. Ważną cechą klasy CCLayer jest to, że potrafi ona przechwytywać zdarzenia generowane przez użytkownika za pośrednictwem takich kontrolerów jak ekran dotykowy czy akcelerometr. Szczególną rolę w

tej układance pełni klasa CCDirector ; pełni ona rolę reżysera – decyduje o tym, która scena ( CCScene ) będzie w danym momencie aktywna. Na początek przyjrzyjmy się, co oferuje klasa CCDirector . Po pierwsze, klasa ta jest tzw. singletonem (singleton jest to wzorzec projektowy, który zapewnia, że dana klasa posiada tylko jedną instancję). Fakt ten wydaje się być dość oczywisty (czy widziałeś kiedyś film lub przedstawienie, za które odpowiadało dwóch reżyserów?). Główne zadanie reżysera w bibliotece Cocos2D to zarządzanie scenami oraz przechowywanie danych konfiguracyjnych. W szczególności klasa CCDirector odpowiada za: startowanie sceny, zamianę bieżącej sceny, wkładanie (ang. pushing) nowej sceny na bieżącą, zdejmowanie (ang. popping) bieżącej sceny, gwarantowanie dostępu do bieżącej sceny, pauzowanie, kontynuowanie oraz kończenie gry, przechowywanie i gwarantowanie dostępu do globalnych danych konfiguracyjnych biblioteki Cocos2D, gwarantowanie dostępu do okna oraz widoku OpenGL, konwertowanie współrzędnych UIKit i OpenGL, kontrolowanie procesu uaktualniania stanu gry. Pracując z biblioteką Cocos2D, mamy do wyboru cztery typy reżyserów. Typy te implikują sposób kontrolowania procesu uaktualniania stanu gry i w rezultacie mają bardzo istotny wpływ na wydajność aplikacji: kCCDirectorTypeDisplayLink : gwarantuje najlepszą wydajność oraz płynność renderowania, dzięki zastosowaniu mechanizmu synchronizacji procesu uaktualniania ekranu z jego sprzętowym odświeżeniem; niestety – mechanizm ten dostępny jest od wersji 3.1 systemu iOS wzwyż, kCCDirectorTypeNSTimer : gwarantuje największą przenośność (będzie działać w każdej wersji systemu iOS), ale jest za to najwolniejszy, kCCDirectorTypeThreadMainLoop : szybki, ale sprawia problemy w sytuacji, kiedy chcemy używać z poziomu aplikacji Cocos2D widoków UIKit, KCCDirectorTypeMainLoop : jak wyżej. Domyślnie biblioteka Cocos2D korzysta z reżysera typu

kCCDirectorTypeDisplayLink , zaś w sytuacji gdy nie jest on dostępny (na dzień dzisiejszy bardzo mało prawdopodobna sytuacja), przełącza się na typ kCCDirectorTypeNSTimer . Jeśli chciałbyś sam określić typ reżysera, to musisz zmodyfikować następujący fragment kodu źródłowego w klasie reprezentującej delegata aplikacji: if ( ! [CCDirector setDirectorType: kCCDirectorTypeDisplayLink] ) [CCDirector setDirectorType: kCCDirectorTypeDefault]; Kolejną klasą z inwentarza biblioteki Cocos2D, z którą warto się szczegółowo zapoznać, jest CCScene . Obiekty tej klasy są zawsze korzeniami w grafie sceny. Rozwiązanie to wydaje się momentami odrobinę sztuczne (klasa CCScene w zasadzie nie rozszerza w żaden sposób funkcjonalności CCNode ), jednakże metody runWithScene , replaceScene czy pushScene z klasy CCDirector potrafią współpracować tylko z obiektami tego typu. Poza tym, sceny można opakować obiektami klas dziedziczących po klasie bazowej CCSceneTransition , co pozwala uzyskać miłe dla oka efekty przejść między scenami gry. Pracując z biblioteką Cocos2D, zazwyczaj przyjmuje się konwencję, że dziećmi sceny są jedynie warstwy (tj. obiekty wywodzące się z klasy CCLayer ), zaś one dopiero zawierają w sobie węzły reprezentujące konkretne obiekty występujące w grze. Bardzo przydatnym mechanizmem jest wkładanie (ang. pushing) oraz zdejmowanie (ang. popping) bieżącej sceny z poziomu reżysera. Operacje te niewątpliwie kojarzą się ze strukturą danych zwaną stosem. I rzeczywiście, myśląc o tym, w jaki sposób klasa CCDirector zarządza scenami, można posłużyć się modelem stosu. Oczywiście, wkładając nową scenę na stos, bieżąca nadal pozostaje widoczna. Mechanizm ten jest bardzo przydatny np. w sytuacji kiedy w trakcie gry chcemy wyświetlić okno dialogowe, bądź podręczne menu. W tej sytuacji wkładamy nową scenę na bieżącą, czekamy na działanie użytkownika, a w końcu

zdejmujemy bieżącą scenę ze stosu i automatycznie wracamy do przerwanej rozgrywki. Manipulując scenami, trzeba jednak uważać, aby nie przesadzić, gdyż za każdym razem kiedy Cocos2D podmienia sceny, nowa scena jest tworzona w pamięci, zaś stara zostaje usuwana. Jak przed momentem wspominałem, używanie scen pozwala uzyskiwać bardzo ciekawe efekty przejść (ang. transition) pomiędzy ekranami gry reprezentowanymi przez różne obiekty klasy CCScene . Służą ku temu klasy wywodzące się z klasy bazowej CCSceneTransition . Hierarchia tych klas przedstawiona jest na Rysunku 5. Na Listingu 2 pokazany jest przykład zastosowania jednego z przejść, tzw. zanikania (ang. fade). Rysunek 5. Hierarchia klas wywodzących się z CCSceneTransition (źródło: http://www.cocos2d-iphone.org/ ) Schemat w większej rozdzielczości. Listing 2. Przykład zastosowania efektu CCTransitionFade // Inicjalizacja obiektu reprezentującego efekt // przejścia. CCTransitionFade* transition = [CCTransitionFade transitionWithDuration:1 scene:[MyScene scene] withColor:ccBLACK]; // Zamiana scen z użyciem efektu przejścia. [[CCDirector sharedDirector] replaceScene:transition];

Wspomniany efekt polega na tym, że aktualna scena powoli blaknie, aż do momentu kiedy cały ekran stanie się czarny (lub inny; docelowy kolor można sobie dowolnie zdefiniować), po czym dzieje się efekt odwrotny, tyle że pojawia się nowa scena. Zaimplementowanie takiego efektu jest bardzo proste. W pierwszym kroku należy stworzyć obiekt reprezentujący efekt przejścia; w naszym przypadku będzie on typu CCTransitionFade . W konstruktorze możemy określić długość efektu (w sekundach), kolor oraz docelową scenę. Potem wystarczy tylko przekazać obiekt przejścia (na Listingu reprezentowany przez zmienną transition ) do odpowiedniej metody w klasie CCDirector . W tym przypadku należy pamiętać, iż przejścia będą działać ze wszystkimi metodami reżysera przewidzianymi do manipulacji scenami z jednym wyjątkiem, który stanowi funkcja popScene . Dlaczego? Powód jest prosty: popScene nie przyjmuje żadnych argumentów, po prostu zdejmuje ze stosu bieżącą scenę i nie ma możliwości opakowania tej sceny obiektem reprezentującym efekt przejścia. Cocos2D oferuje cały szereg efektów przechodzenia między scenami, informacje na ich temat można znaleźć w dokumentacji biblioteki. Korzystając z efektów przejść, warto pamiętać o jednej zasadzie, która brzmi: lepsze jest wrogiem dobrego. Przejścia między scenami wyglądają bardzo efektownie, jednakże jeśli przesadzimy z ich stosowaniem, to użytkownicy naszej gry dostaną białej gorączki (szczególną uwagę należy zwrócić na to, by czasy przejść między scenami nie były zbyt długie). Często zdarza się sytuacja kiedy warto podzielić scenę na warstwy. Wtedy można skorzystać z klasy CCLayer . Na Listingu 3 pokazane jest, jak można dodać kilka warstw do sceny. Listing 3. Dodawanie warstw do sceny CCScene* scene = [CCScene node]; CCLayer* backgroundLayer = [Background node]; [scene addChild: backgroundLayer]; CCLayer* spritesLayer = [Sprites node]; [scene addChild:spritesLayer]; CCLayer* hudLayer = [Hud node]; [scene addChild: hudLayer]; Tak zainicjowana scena posiada trzy warstwy, które będą (razem ze swoimi dziećmi) renderowane w kolejności ich dodawania do sceny. Zaraz, zaraz! Ale czy podobnego efektu nie można by uzyskać za pomocą zwykłych węzłów? Odpowiedź brzmi: owszem, można by. Co w takim razie odróżnia warstwy ( CCLayer ) od zwykłych węzłów (

CCNode )? Zasadnicza różnica polega na tym, że warstwy potrafią przechwytywać zdarzenia generowane przez ekran dotykowy. Ponieważ takie przechwytywanie wiąże się ze sporym narzutem wydajnościowym, domyślnie mechanizm ten jest wyłączony. Aby go włączyć, wystarczy odpowiednio ustawić właściwość isTouchEnabled dostępną w klasie CCLayer : self.isTouchEnabled = YES; Kiedy tylko do właściwości isTouchEnabled zostanie przypisana wartość YES , Cocos2D zacznie wywoływać na obiekcie reprezentującym warstwę cały szereg funkcji zwrotnych służących do obsługi zdarzeń generowanych przez ekran dotykowy: -(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event – ta funkcja jest wywoływana, kiedy palec dotknie ekranu dotykowego, -(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event – ta funkcja jest wywoływana, kiedy palec przesuwa się po ekranie, -(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event – ta funkcja jest wywoływana, kiedy palec odrywa się od ekranu, -(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event – ta funkcja jest wywoływana, kiedy proces przesuwania palca na ekranie zostaje przerwany (zdarzenie to występuje bardzo rzadko, powinno się je obsługiwać podobnie jak zdarzenie ccTouchesEnded ). DUSZKI Jak dotąd omawialiśmy niemalże same abstrakcyjne koncepcje: reżyser, graf sceny, warstwy, węzły... Wszystkie to jest oczywiście potrzebne, jednakże trudno byłoby zrealizować ciekawą gry składającą się jedynie z obiektów nie posiadających reprezentacji graficznej. Jak więc za pomocą Cocos2D wyświetlić zwykły obrazek? Odpowiedzią na to jest klasa CCSprite . Klasa ta reprezentuje tzw. duszka (ang. sprite). Duszek w nomenklaturze nazewnictwa używanego przez adeptów dziedziny wiedzy określanej jako grafika komputerowa oznacza obraz (teksturę) renderowaną bezpośrednio na ekran, w określonej pozycji. Termin ten

pojawił się już w latach 70 zeszłego stulecia. Wiele systemów komputerowych z tamtych oraz późniejszych lat używało duszków (których rysowanie często bywało wspierane sprzętowo) do tworzenia dwuwymiarowych gier. Nowoczesne duszki (chociażby takie, jakie oferuje Cocos2D) oprócz pozycji na ekranie pozwalają określać skalę (ang. scale) ich wielkości, kąt obrotu (ang. rotation angle), punkt zaczepienia (ang. anchor point), poziom przeźroczystości (ang. opacity level), efekt obrotu wokół osi (ang. flipping effect) czy ton koloru (ang. color tint). Wszystkie te właściwości duszków stanowią bazę dla uzyskiwania szeregów efektów, które na co dzień oglądamy w nowoczesnych grach 2D. Stworzenie duszka za pomocą biblioteki Cocos2D jest bardzo proste. Najłatwiej można to zrobić przez załadowanie obrazka do tekstury ( CCTexture2D ), która z kolei będzie przypisana do obiektu typu CCSprite . Z poziomu kodu źródłowego wygląda to następująco: CCSprite* heroSprite = [CCSprite spriteWithFile:@”hero.png”]; W tym momencie warto napisać kilka słów o tym, w jaki sposób Cocos2D pozycjonuje duszki. Tutaj mała zagadka: jak według Ciebie będzie wyglądał ekran po narysowaniu duszka załadowanego za pomocą przedstawionego wyżej fragmentu kodu (obrazek hero.png przedstawiony jest na Rysunku 6)? W ramach podpowiedzi dodam, że obiekty klasy CCSprite mają domyślnie ustawioną pozycję (0, 0). Rysunek 6. Przykładowa tekstura używana do wyświetlenia duszka (źródło: http://www.lostgarden.com) Jeśli chcesz poznać odpowiedź na to pytanie, spójrz proszę na Rysunek 7. Jak widać, duszek został wyświetlony tylko fragmentarycznie i na dodatek w lewym dolnym rogu ekranu. Czy aby wszystko jest w porządku? Jak najbardziej! Kwestia wyświetlenia duszka w lewym dolnym rogu wiąże się z tym, jak Cocos2D postrzega układ współrzędnych. Według konwencji przyjętej w tej bibliotece, punkt (0, 0) tego układu znajduje się właśnie w lewym dolnym rogu ekranu. Problem przycięcia duszka jest troszkę bardziej skomplikowany. Wiąże się on z tzw. punktem zaczepienia, o którym wspominałem kilka akapitów wyżej. Punkt zaczepienia jest punktem zdefiniowanym w układzie współrzędnych tekstury. Układ ten jest zorganizowany w dość specyficzny sposób. Punkty umieszczane w tym układzie mogą przyjmować wartości ze zmiennoprzecinkowego zakresu [0.0, 1.0]. Wartość 1.0 oznacza odpowiednio szerokość bądź wysokość tekstury. Punkt zaczepienia w bibliotece Cocos2D ustawiony jest domyślnie na wartość (0.5, 0.5), czyli środek tekstury. Gdybyśmy ustawili go na wartość (0.0, 0.0), to wskazywałby on na lewy-górny róg tekstury itd. Wracając do problemu przycięcia, cały haczyk polega na tym, że pozycja duszka na ekranie zawsze odnosi się do tego puntu zaczepiania. Innymi słowy, gdy ustawimy ekranową pozycję duszka na punkt (0, 0), zaś jego punkt

zaczepienia określony będzie jako (0.5, 0.5), to zostanie on narysowany w taki sposób, iż punkt centralny tekstury reprezentującej duszka pojawi się na układzie współrzędnych ekranu w punkcie (0, 0), czyli – jak wyżej pisałem, w lewym-dolnym rogu. Jeśli chcielibyśmy zmodyfikować pozycję naszego duszka na ekranie w taki sposób, aby był w całości wyświetlony w lewym-dolnym rogu, można ustawić jego punkt zaczepienia na wartość (0.0, 0.0), bądź zmodyfikować jego pozycję, przesuwając go w prawo i w górę o wartości odpowiadające połowie szerokości i wysokości reprezentującej go tekstury. Rysunek 7. Duszek wyświetlony na ekranie (pozycja domyślna) Z efektywnym korzystaniem z duszków wiąże się szereg technicznych ograniczeń, wynikających przede wszystkim z charakterystyki nowoczesnych układów wspomagających sprzętowo renderowanie grafiki. Cocos2D w miarę swoich możliwości ukrywa przed programistą techniczne detale, jednakże znajomość i świadomość istnienia ww. ograniczeń jest kluczem do tworzenia efektywnych i płynnie działających gier pod iOS. Niestety, od pewnych kwestii nie da się uciec. Pierwszy problem, którego należy być świadomym, to wymiarowość tekstur. Układy graficzne, z których korzysta system iOS, potrafią współpracować jedynie z teksturami o wielkościach będących potęgami dwójki z zakresu [2, 2048]. Ograniczenie to, połączone z niefrasobliwym podejściem do kwestii korzystania z tekstur może prowadzić do poważnych problemów związanych ze zużyciem pamięci. Dla przykładu, jeśli stworzymy za pomocą biblioteki Cocos2D duszka reprezentowanego przez obraz o rozmiarach 257 na 257 pikseli, to w celu jego załadowania silnik wygeneruje teksturę o rozmiarach 512 na 512 pikseli. Taka tekstura, przy założeniu, że korzystamy z 32-bitowego formatu piksela, może zająć około 1MB w pamięci karty graficznej. Dla nieświadomego tego problemu programisty może stanowić to niemały szok...

Inny ważny problem związany z korzystaniem z duszków wiąże się z przełączaniem tekstur. Rzecz polega na tym, że nowoczesne układy graficzne zaprojektowane zostały w taki sposób, iż nie potrafią współdziałać z dużą ilością tekstur w tym samym czasie. Typowy schemat współpracy układu graficznego z teksturami wygląda następująco: załaduj teksturę; rysuj korzystając z danych zawartych w aktualnie załadowanej teksturze; załaduj kolejną teksturę; itd. Haczyk w całej tej zabawie polega na tym, że operacja przeładowania tekstury jest bardzo czasochłonna. Tak bardzo, że niefrasobliwe jej używanie może literalnie zabić wydajność gry. Wyobraź sobie, że tworzysz dwuwymiarową grę typu shoot'em up, czyli klasyczną strzelaninę, w której Twój bohater pędząc w uzbrojonym pojeździe kosmicznym niszczy hordy Obcych próbujących podbić Ziemię. Gra oczywiście składa się z całego szeregu animacji opartych na duszkach. Pół biedy, jeśli wpadniesz na pomysł, aby klatki animacji pojazdu gracza i poszczególnych przeciwników umieścić w oddzielnych plikach. Gorzej, jeśli każdą klatkę zechcesz trzymać w osobnym obrazku. Jeśli tak zrobisz, gwarantuję Ci, że Twoja gra będzie (może) działać w granicach 2-3 FPS'ów, nawet na urządzeniach nowej generacji. Lekarstwem na opisane wyżej problemy jest stosowanie tzw. atlasów tekstur (ang. texture atlases). Są to po prostu obrazki o wymiarach kompatybilnych z wymiarami tekstur, do których upakowane są wszystkie elementy graficzne wykorzystywane w grze. Oprócz tego, w oddzielnym pliku umieszcza się informacje o położeniu oryginalnych obrazków na atlasie, dzięki czemu można się do nich odwoływać w trakcie działania gry. Na Rysunku 8 pokazany jest przykładowy atlas tekstur. Proces tworzenia takich atlasów jest zazwyczaj zautomatyzowany. Istnieją dedykowane narzędzia, korzystające w zaawansowanych algorytmów optymalizacyjnych służące po to aby składać dużą liczbę małych plików graficznych w jeden duży. Część z tych narzędzi przystosowana jest do współpracy z biblioteką Cocos2D. Jedną z ciekawszych opcji w tym zakresie jest TexturePacker (http://www.texturepacker.com/), który w podstawowej wersji dostępny jest za darmo.

Rysunek 8. Przykładowy atlas tekstur (źródło: http://pocketgod.wikia.com) Cocos2D oczywiście oferuje wsparcie dla atlasów tekstur, które obsługiwane są za pomocą klas CCSpriteFrameCache oraz CCSpriteFrame . Przedstawienie procesu tworzenia atlasu i obsługiwania go z poziomu silnika wykracza poza ramy niniejszego artykułu. Ważne jest, abyś był świadom istnienia tych klas, aczkolwiek jeśli planujesz pracować z Cocosem na poważnie, to prędzej czy później będziesz musiał zapoznać się z tym zagadnieniem. W razie czego ciekawy samouczek opisujący ten temat możesz znaleźć tutaj: http://www.raywenderlich.com/1271/how-to-use-animations-and- sprite-sheets-in-cocos2d. RENDEROWANIE TEKSTU Renderowanie napisów jest jednym z nieodzownych elementów każdej niemalże gry. W tej sytuacji nie do pomyślenia jest, aby Cocos2D nie oferował żadnych usprawnień w tym zakresie. Jak się zapewne domyślasz, usprawnienia takie istnieją i zamierzam je w tym podpunkcie zaprezentować. Najprostszym sposobem wyświetlenia tekstu przy pomocy biblioteki Cocos2D jest użycie obiektu klasy CCLabelTTF . Klasa ta, jak się łatwo domyśleć po jej nazwie, służy do wyświetlania napisów w oparciu o

czcionki TrueType. Podstawowe użycie tej klasy pokazane jest na Listingu 4. Listing 4. Proste użycie klasy CCLabelTTF CCLabelTTF* label = [CCLabelTTF labelWithString:@"Hello, Cocos2D!" fontName:@"Arial" fontSize:32]; Jak widać, rzecz jest bardzo prosta. Inna sprawa, że prosto nie zawsze oznacza wydajnie. Zanim zaczniesz używać klasy CCLabelTTF , musisz być świadom, że pod maską działa ona w ten sposób, iż w locie generuje teksturę i renderuje do niej żądany tekst, korzystając z odpowiedniej definicji czcionki TrueType. Takie renderowanie do pośredniej tekstury jest operacją dość zasobożerną i jeśli planujesz na bieżąco modyfikować zawartość tekstu reprezentowanego przez CCLabelTTF to licz się z potencjalnym spadkiem wydajności Twojej aplikacji. Drugi problem związany z używaniem czcionek TrueType jest tego rodzaju, że czasami trudno jest dobrać krój liter odpowiedni do Twojej gry. W praktyce często robi się tak, że grafik przygotowuje dedykowany zestaw znaków dla danej gry w postaci tzw. czcionki bitmapowej (ang. bitmap font). Czcionka taka zazwyczaj dostarczana jest w postaci dwóch plików: tekstury, na której umieszczone są poszczególne znaki, oraz metadanych (np. pliku w formacie XML, JSON czy plist), które definiują pozycje i atrybuty poszczególnych znaków na teksturze. Praktyka pokazuje, że czcionki bitmapowe są o wiele częściej używane w grach niż czcionki TrueType. Napis oparty na czcionce bitmapowej można też łatwiej i wydajniej renderować „w locie”, bez posiłkowania się oddzielną teksturą. Z tej racji, obiektów klasy CCLabelTTF w praktyce używa się głównie do wyświetlania informacji użytecznych przy debugowaniu aplikacji czy do szybkiego prototypownia. Cocos2D oczywiście wspiera czcionki bitmapowe. Do ich obsługi wykorzystuje się klasę CCLabelBMFont . Korzystanie z tej klasy jest równie proste jak korzystanie z CCLabelTTF (patrz Listing 5), ale... Analizując ten Listing, można zauważyć dwie istotne kwestie. Po pierwsze, aby wczytać pożądany font, musimy wskazać konkretny plik .fnt. Od razu pojawia się pytanie: skąd go wziąć? Po drugie, przy wczytywaniu fonta nie podajemy jego wielkości, co wydaje się zrozumiałe: przecież wymiar fonta bitmapowego zależy od tego, jak go sobie narysujemy. Te dwa spostrzeżenia wiążą się z podstawowymi problemami dotyczącymi stosowania fontów bitmapowych. Pierwsza kwestia w zasadzie nie jest problemem sensu stricto. To raczej konsekwencja decyzji używania takiego a nie innego formatu fonta. Po prostu w celu stworzenia pliku .fnt (a także towarzyszącego mu pliku .png) musimy skorzystać z narzędzia. Opcji jest kilka: Hiero (http://slick.cokeandcode.com/demos/hiero.jnlp): aplikacja webowa napisana w Javie; jej główną zaletą jest fakt, iż jest ona darmowa; niestety, poza tym stwarza sporo