Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00295 005957 14496963 na godz. na dobę w sumie
Tworzenie izometrycznych gier społecznościowych w HTML5, CSS3 i JavaScript - książka
Tworzenie izometrycznych gier społecznościowych w HTML5, CSS3 i JavaScript - książka
Autor: Liczba stron: 136
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-3888-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> gry >> programowanie gier
Porównaj ceny (książka, ebook, audiobook).

Masz konto na Facebooku? Pewnie, że tak - wszyscy mają! Musiałeś więc zauważyć gry dostępne w tym serwisie. Może nie oszałamiają fotorealistyczną grafiką i superdynamiczną akcją, mają jednak to coś. Nazywa się to grywalność. Frajda ze współzawodniczenia nie da się niczym zastąpić. Popularność tego typu gier gwałtownie rośnie, a firma, która wyprodukowała prawdopodobnie najbardziej znaną z nich - Farmville - właśnie wchodzi na giełdę!

Dzięki tej książce również i Ty będziesz mógł spróbować szczęścia. W trakcie lektury dowiesz się, jak wykorzystać nowości HTML5 i CSS3 do osiągnięcia interesujących efektów, takich jak rzut izometryczny. Ponadto zobaczysz, jak wzbogacić aplikację o efekty dźwiękowe oraz przygotować atrakcyjny interfejs użytkownika. Wiedza tutaj zawarta pozwoli Ci na przygotowanie kompletnej gry z pomocą HTML5, CSS3 i JavaScriptu, a następnie zintegrowanie jej z Facebookiem. Teraz to, czy zdobędziesz popularność i osiągniesz sukces finansowy, zależy tylko i wyłącznie od Twojej pomysłowości oraz kreatywności!

Przygotuj grę, która będzie:

Zaistniej na rynku gier komputerowych!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Making Isometric Social Real-Time Games with HTML5, CSS3, and JavaScript Tłumaczenie: Aleksander Lamża ISBN: 978-83-246-3888-8 © 2012 HELION S.A. Authorized Polish translation of the English edition of Making Isometric Social Real-Time Games with HTML5, CSS3, and JavaScript, 1st edition 9781449304751 © 2011 Mario Andrés Pagella. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/twoizo.zip Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/twoizo Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treļci Wstýp ........................................................................................................................................7 1. Podstawy grafiki: pĥótno i duszki ............................................................................... 13 13 20 23 28 37 Praca z obiektem canvas Tworzenie päynnych animacji Praca z duszkami Manipulowanie pikselami Wybór metody renderowania grafiki 2. Zmiana perspektywy ................................................................................................... 51 3. Interfejs uŜytkownika .................................................................................................67 67 Graficzny interfejs uĔytkownika i interakcje w grach internetowych Implementowanie graficznego interfejsu uĔytkownika 69 4. DŚwiýki w HTML5 i optymalizacja przetwarzania ....................................................83 83 92 99 Dodawanie dĒwiöku za pomocñ znacznika audio Wykonywanie wymagajñcych zadaþ w wñtkach roboczych Skäadowanie danych: localStorage i sessionStorage 5. Niech ļwiat pozna Twojé grý! ................................................................................... 103 103 108 118 125 Zabezpieczenie przed oszustwami i operacje na serwerze Ostatnia prosta Ostatni szlif Gra trafia do spoäecznoĈci — integracja z Facebookiem 5 6 _ Spis treļci ROZDZIAĤ 5. Niech ļwiat pozna Twojé grý! StworzyäeĈ atrakcyjnñ grö z interaktywnñ grafikñ oraz muzykñ. Teraz wystarczy pokazaè jñ Ĉwiatu. Musisz wiöc umieĈciè logikö na serwerze, tak by uniemoĔliwiè „grzebanie” w niej i uchroniè siö przed spowodowanymi tym uszkodzeniami, a nastöpnie poäñczyè grö z miej- scem, gdzie przebywa duĔo ludzi, czyli Facebookiem. Zabezpieczenie przed oszustwami i operacje na serwerze Jednym z gäównych problemów zwiñzanych z grami dziaäajñcymi online jest skuteczna ochro- na przed oszustwami. Podobnie jak w przypadku zwykäych witryn internetowych, równieĔ tu nie moĔemy ufaè wszystkim uĔytkownikom, wiöc zabezpieczenie aplikacji przed szkodli- wymi dziaäaniami oraz wäaĈciwa obsäuga niewäaĈciwych danych wejĈciowych i zwracanych wartoĈci powinny mieè najwyĔszy priorytet. W grach rozpowszechnianych na zasadach open source ryzyko jest nawet wiöksze, zwäaszcza jeĈli zostaäy utworzone w technologiach takich jak JavaScript i HTML, poniewaĔ äatwo w nich manipulowaè zmiennymi (oraz Ĕñdaniami POST i GET), a nawet modyfikowaè kod w kliencie w czasie dziaäania aplikacji. Niestety nie ma idealnego sposobu na wszystkie problemy — w kaĔdej grze i aplikacji sprawa wyglñda trochö inaczej. MoĔna jednak wyróĔniè dwa kluczowe (ale zwykle maäo skuteczne) rozwiñzania, które stosuje siö we wstöpnej fazie projektowania oraz póĒniej, w trakcie progra- mowania: x Postaraj siö juĔ w projekcie zminimalizowaè ryzyko popeänienia oszustwa na poziomie klienta (przeglñdarki internetowej). x Sprawdzaj wszystkie dane trafiajñce do serwera. W przypadku naszej gry, jak w wiökszoĈci spoäecznoĈciowych gier strategicznych, musimy pamiötaè o kilku sprawach: x Stan konta w grze kaĔdego uĔytkownika powinien byè zapisany w bazie danych (w polu lub osobnej tabeli, w zaleĔnoĈci od tego, czy chcemy przechowywaè informacje o pojedyn- czych transakcjach), a kaĔda operacja sprzedaĔy i kupna powinna aktualizowaè tö wartoĈè. x Musimy regularnie synchronizowaè czas miödzy serwerem i klientem. 103 x Byè moĔe najistotniejsze (i najskuteczniejsze w walce z oszustwami) jest takie zaprojekto- wanie gry, by w kaĔdej chwili niezawodnie przewidywaè punktacjö uĔytkownika na ser- werze, bez koniecznoĈci kontaktowania siö z klientem. Jak to osiñgnñè? Przyjrzyjmy siö poniĔszemu scenariuszowi: Poczñtkowy stan posiadania uĔytkownika wynosi 2000 zäotych monet, 0 budynków, czas utworzenia konta równy 1293861600 (co zgodnie z uniksowym czasem odpowiada dacie 1 stycznia 2011 roku i godzinie 00:00:00), a czas ostatniej modyfikacji to 1293861600 (czyli taki sam jak wyĔej). UĔytkownik ma do wyboru trzy budynki: x budkö z lodami za 250 zäotych monet, która co 30 minut przynosi 5 zäotych monet zysku, x hotel za 1000 zäotych monet przynoszñcy zysk 30 zäotych monet co godzinö, x kino za 500 zäotych monet, które przynosi zysk 12 zäotych monet co 30 minut. UĔytkownik zbudowaä budkö z lodami w chwili 1293861660 (1 stycznia 2011 o godzinie 00:01:00, czyli minutö po utworzeniu konta). Kolejnym posuniöciem byäo zbudowanie hotelu (1294084800 — 3 stycznia 2011 o godzinie 14:00:00). W chwili 1294120800 (4 stycznia 2011 o godzinie 00:00:00) uĔytkownik zbudowaä kino. W chwili 1294639200 (10 stycznia 2011 o godzinie 00:00:00) uĔytkownik powróciä do gry i po- stanowiä sprawdziè stan konta. Jednym z moĔliwych rozwiñzaþ mogäoby byè zapisywanie informacji o wszystkich budynkach stawianych przez uĔytkownika i dodanie do stanu konta pola przechowujñcego dane ostatniej modyfikacji, w którym zapisany byäby teĔ czas operacji przeglñdania stanu konta albo kupna bñdĒ sprzedaĔy budynku. Dziöki temu w sytuacji, gdy uĔytkownik chce zobaczyè stan konta albo kupiä bñdĒ sprzedaä jakiĈ budynek, moĔna wykonaè poniĔszy algorytm: 1. Uaktualnij pole ostatniej modyfikacji bieĔñcym czasem. 2. Rozpocznij przeglñdanie wszystkich budynków uĔytkownika. 3. Przelicz róĔnicö (w sekundach) miödzy bieĔñcym czasem a czasem ostatniej modyfikacji zapisanym w polu uĔytkownika. 4. Wynik podziel przez liczbö sekund przypadajñcñ na päatnoĈci, a wynik dzielenia zaokrñ- glij w dóä. 5. Wynik pomnóĔ przez liczbö zäotych monet otrzymywanych w kaĔdej jednostce czasu. 6. Uaktualnij stan konta, dodajñc wynik powyĔszych operacji. 7. W zaleĔnoĈci od wykonywanej operacji (wyĈwietlanie, budowanie lub sprzedawanie) wy- Ĉwietl stan konta, dodaj zysk z budynku albo odejmij jego wartoĈè. 8. JeĈli budynek byä sprzedawany, usuþ powiñzanie. Po zastosowaniu tego algorytmu do powyĔszego scenariusza uzyskamy nastöpujñce efekty: x W chwili 1293861660 uĔytkownik postanowiä zbudowaè budkö z lodami. PoniewaĔ nie ma innych budynków, wystarczy, Ĕe uaktualnimy konto i czas ostatniej modyfikacji oraz utworzymy powiñzanie. Czas ostatniej modyfikacji bödzie równy 1293861660. 104 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! BieĔñcy stan konta uĔytkownika wynosi 1750 (2000 – 250). x W chwili 1294084800 uĔytkownik zbudowaä hotel. Zanim utworzymy nowe powiñzanie dla hotelu, musimy przeliczyè stan konta. Wiemy o budowie budki z lodami w chwili 1293861660, wiöc uaktualniamy pole ostatniej modyfikacji na wartoĈè 1294084800. Aby obliczyè dotychczasowy przychód z budki z lodami, wykonujemy nastöpujñcñ operacjö: róšnicaCzasu = 1294084800 1293861660 = 223140 sekund Budka generuje zysk 5 zäotych monet co 30 minut (1800 sekund), wiöc musimy obliczyè: wynik = róšnicaCzasu / 1800 sekund = 123.97 czyli tylko 123, poniewaĔ zaokrñglamy w dóä. Nastöpnie wykonujemy operacjö: wynik = wynik * 5 = 615 zĪotych monet Uaktualniamy stan konta do wartoĈci 1750 + 615 = 2365, tworzymy powiñzanie dla hote- lu (czas ostatniej modyfikacji to 1294084800) i pobieramy naleĔnoĈè za hotel. Po tych ope- racjach stan konta uĔytkownika bödzie wynosiä 1365 (2365 – 1000). x W chwili 1294120800 uĔytkownik kupuje hotel, wiöc wykonujemy te same operacje co wczeĈniej (tyle Ĕe dla dwóch budynków: budki z lodami i hotelu): róšnicaCzasu = 1294120800 – 1294084800 = 36000 sekund wynikBudkaZLodami = róšnicaCzasu / 1800 = 20 wynikBudkaZLodami = wynikBudkaZLodami * 5 zĪotych monet = 100 zĪotych monet wynikHotel = róšnicaCzasu / 3600 sekund (jedna godzina) = 10 wynikHotel = wynikHotel * 30 zĪotych monet = 300 zĪotych monet stanKonta = stanKonta + wynikHotel + wynikBudkaZLodami = 1765 stanKonta = stanKonta - 500 zĪotych monet (opĪata za kino) BieĔñcy stan konta uĔytkownika wynosi teraz 1265. x W chwili 1294639200 uĔytkownik sprawdza stan konta: róšnicaCzasu = 1294639200 - 1294120800 = 518400 sekund wynikBudkaZLodami = róšnicaCzasu / 1800 = 288 wynikBudkaZLodami = wynikBudkaZLodami * 5 = 1440 zĪotych monet wynikHotel = róšnicaCzasu / 3600 sekund (jedna godzina) = 144 wynikHotel = wynikHotel * 30 = 4320 zĪotych monet kinoWynik = róšnicaCzasu / 1800 = 288 kinoWynik = kinoWynik * 12 = 3456 zĪotych monet stanKonta = stanKonta + kinoWynik + wynikHotel + wynikBudkaZLodami Oznacza to, Ĕe przewidywany stan konta uĔytkownika 10 stycznia 2011 roku o godzinie 00:00:00 wynosi 10 481. ChociaĔ zabezpieczenie kodu aplikacji po stronie klienta przed modyfikacjñ jest bardzo trudne, o ile wröcz nie niemoĔliwe, zastosowanie opisanej wyĔej metody pomaga ustrzec siö przed szkodliwym dziaäaniem uĔytkowników. Efekt wszelkich zmian jest jedynie lokalny i nie ma wpäywu na serwer. Kolejnym przydatnym rozwiñzaniem, które moĔemy zastosowaè, jest me- chanizm uĔywany przez firmö Zynga w niektórych grach — zamiast automatycznie genero- waè zyski, moĔna zmusiè uĔytkownika do röcznego „zbierania” zysków. JeĈli na przykäad budynek generuje 500 monet zysku co 30 minut, a uĔytkownik nie graä przez trzy dni, gdy w koþcu zbierze zysk, otrzyma jedynie 500 monet. Aby zaimplementowaè ten mechanizm, musimy tylko sprawdziè, czy róĔnica czasu jest wiöksza niĔ zysk generowany przez budynek. Zabezpieczenie przed oszustwami i operacje na serwerze _ 105 JeĈli tak, ustawiamy znacznik na wartoĈè true. Po röcznym zebraniu zysku przez uĔytkowni- ka znacznik musimy ustawiè na false. Zrealizowanie oryginalnej metody opisanej wyĔej wymaga zastosowania modelu danych przed- stawionego na rysunku 5.1. Rysunek 5.1. Model danych äñczñcy uĔytkowników z budynkami Z modelu danych przedstawionego na rysunku 5.1 wynika, Ĕe: x KaĔdy uĔytkownik (tabela USER) moĔe mieè zero lub wiele egzemplarzy budynków (tabe- la BUILDING_INSTANCE). x KaĔdy egzemplarz budynku musi mieè dokäadnie jednego uĔytkownika. x KaĔdy budynek (tabela BUILDING) moĔe mieè zero lub wiele egzemplarzy. x KaĔdy egzemplarz budynku musi byè powiñzany z tylko jednym budynkiem. W naszej grze zaimplementujemy ten model danych oraz logikö za niego odpowiedzialnñ za pomocñ bazy danych MySQL i jözyka PHP. W tym celu na swoim komputerze musisz przy- gotowaè odpowiednie Ĉrodowisko1, skäadajñce siö z: x Bazy danych MySQL (http://dev.mysql.com/usingmysql/get_started.html). x Jözyka PHP (http://us.php.net/manual/en/install.php). Aby moĔna byäo korzystaè z PHP, trzeba jeszcze zainstalowaè serwer WWW, taki jak Apache, Lighttpd czy nginx. Instrukcjö insta- lacji i konfiguracji moĔesz znaleĒè na stronie z opisem jözyka PHP. Po zainstalowaniu i skonfigurowaniu skäadników Ĉrodowiska (wäñcznie z ustaleniem hasäa dla uĔytkownika root) moĔesz siö poäñczyè z serwerem bazy danych. W tym celu otwórz okno terminala i wykonaj polecenie: mysql –hlocalhost –uroot -phasĪo JeĈli hasäo uĔytkownika root nie zostaäo ustalone, naleĔy wykonaè polecenie: mysql -hlocalhost -uroot Po poäñczeniu z bazñ danych pojawi siö znak zachöty mysql: $ mysql -uroot -hlocalhost Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 816 1 Najszybszym i najbardziej niezawodnym sposobem zainstalowania oraz skonfigurowania takiego Ĉrodowiska jest skorzystanie z gotowych „paczek” zawierajñcych wszystkie niezbödne skäadniki. Dostöpnych jest wiele te- go typu rozwiñzaþ, np. wieloplatformowy XAMPP (http://www.apachefriends.org/en/xampp.html) — przyp. täum. 106 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Server version: 5.1.45 MySQL Community Server (GPL) Type help; or \h for help. Type \c to clear the current input statement. mysql Na Twoim komputerze moĔe byè zainstalowana inna wersja serwera MySQL. Skoro jesteĈmy juĔ poäñczeni z serwerem, moĔemy utworzyè bazö danych dla naszej gry. Wy- konujemy polecenie: CREATE DATABASE mygame; JeĈli wszystko zadziaäa prawidäowo, zostanie wyĈwietlony komunikat: mysql CREATE DATABASE mygame; Query OK, 1 row affected (0.00 sec) Przed utworzeniem tabel w bazie danych mygame musimy wskazaè tö bazö jako aktywnñ: USE mygame; JeĈli nie pojawiñ siö Ĕadne bäödy, zostanie wyĈwietlony komunikat: mysql USE mygame; Database changed W kodzie doäñczonym do ksiñĔki w katalogu server znajdziesz dwa pliki SQL: model-empty.sql i model-filled.sql. Drugi z nich zapisz w wybranym miejscu na komputerze i przejdĒ z powrotem do okna terminala. Wykonaj poniĔsze polecenie, podajñc peänñ ĈcieĔkö do zapisanego pliku: source Łcieška_do_pliku_SQL; JeĈli wszystko zadziaäa prawidäowo, zostanie wyĈwietlony komunikat: mysql source ~/Projekty/Gra/server/model-filled.sql Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 4 rows affected (0.05 sec) Query OK, 0 rows affected (0.05 sec) Query OK, 0 rows affected (0.07 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.10 sec) Query OK, 0 rows affected (0.09 sec) mysql W bazie danych zostanñ utworzone trzy tabele: users, buildings i building_instances. Ta- bela buildings zostanie równieĔ wypeäniona danymi czterech budynków (z których bödzie- my korzystaè z grze): budki z lodami, hotelu, kina i drzewa. Aby móc korzystaè z bazy mygame z poziomu skryptów PHP, musimy utworzyè uĔytkownika bazy MySQL, nadajñc mu prawa do wykonywania operacji SELECT, INSERT, UPDATE i DELETE. W tym celu wykonujemy polecenia: CREATE USER mygameuser @ localhost IDENTIFIED BY game123 ; GRANT SELECT, INSERT, UPDATE, DELETE ON mygame.* TO mygameuser @ localhost ; Zabezpieczenie przed oszustwami i operacje na serwerze _ 107 Podobnie jak poprzednio, teraz takĔe nie powinien siö pojawiè Ĕaden komunikat o bäödzie: mysql CREATE USER mygameuser @ localhost IDENTIFIED BY game123 ; Query OK, 0 rows affected (0.09 sec) mysql GRANT SELECT, INSERT, UPDATE, DELETE ON mygame.* TO mygameuser @ localhost ; Query OK, 0 rows affected (0.07 sec) mysql Wiöcej informacji na temat korzystania z bazy danych MySQL moĔesz znaleĒè na stronie http://dev.mysql.com. W katalogu server w kodzie doäñczonym do ksiñĔki znajdziesz jeszcze kilka innych katalogów i plików: x config.php — zawiera dane wymagane do poäñczenia z bazñ danych oraz rozmiar siatki. x classes/class.dbutil.php — jest to prosta klasa narzödziowa dla bazy MySQL. x classes/class.users.php — odpowiada za obsäugö operacji zwiñzanych z uĔytkownikami. x classes/class.buildings.php — odpowiada za obsäugö operacji zwiñzanych z budynkami. x classes/class.operations.php — odpowiada za tworzenie egzemplarzy budynków i ich po- bieranie. x classes/class.user.php — zawiera klasö user. x classes/class.building.php — zawiera klasö building. x classes/class.buildingInstance.php — zawiera klasö buildingInstance. x test-database.php — jest to przydatny skrypt äñczñcy siö z bazñ danych i testujñcy moĔliwo- Ĉci zapisywania, pobierania i usuwania danych. x registration.php — jest to skrypt ilustrujñcy sposób rejestracji nowych uĔytkowników z wy- korzystaniem wczeĈniej wymienionych klas. x authentication.php — jest to skrypt ilustrujñcy sposób uwierzytelniania uĔytkowników oraz inicjowania sesji z wykorzystaniem wczeĈniej wymienionych klas. Ostatnia prosta W poprzednim podrozdziale omówiliĈmy sprawy zwiñzane z implementacjñ skryptów dziaäa- jñcych po stronie serwera oraz ze strukturñ bazy danych. Teraz postaramy siö poäñczyè wstöp- nie opracowanñ grö ze skryptami na serwerze, tak by byäa moĔliwa rejestracja uĔytkowników, ich uwierzytelnianie, a takĔe by wartoĈci wyĈwietlane w panelu ze stanem posiadania uĔyt- kownika odpowiadaäy rzeczywistym danym zapisanym w bazie danych. Struktura plików i katalogów caäej aplikacji wyglñda nastöpujñco: x index.php — zawiera gäównñ stronö; obsäuguje uwierzytelnianie i rejestracjö uĔytkowników. x game.php — zawiera skrypt gry. JeĈli sesja nie jest aktywna, uĔytkownik zostanie przekie- rowany z powrotem na gäównñ stronö w celu rejestracji lub uwierzytelnienia. Kod z tego pliku jest podobny do tego z przykäadu 3.1, ale zawiera modyfikacje zwiñzane z wyĈwie- 108 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! tlaniem rzeczywistych wartoĈci w panelu; dodatkowo kod JavaScript jest podzielony na kilka plików, aby äatwiej nim zarzñdzaè. x async/ — zawiera skrypty PHP odpowiedzialne za obsäugö asynchronicznych wywoäaþ ge- nerowanych z poziomu kodu JavaScript za pomocñ XMLHttpRequest (technologia AJAX). x css/ — zawiera pliki CSS: site.css (wykorzystywany w pliku index.php) oraz ui-style.css (uĔy- wany w grze). x js/ — zawiera wszystkie pliki JavaScript wykorzystywane w grze. Skrypt game.php, poza przypisywaniem rzeczywistych wartoĈci do wszystkich pól, wykonu- je jeszcze dodatkowe operacje wiñĔñce egzemplarze budynków z bieĔñcym uĔytkownikiem oraz automatycznie wypeänia macierz tileMap budynkami naleĔñcymi do uĔytkownika. Plik index.php zostaä uzupeäniony o kod umieszczajñcy na siatce drzewa w losowym ukäadzie (10 pól siatki zostaje wypeänionych drzewami). Za tworzenie egzemplarzy budynków na siatce odpowiada funkcja initializeGrid(), która korzysta z tablicy PHP zawierajñcej egzemplarze budynków oraz macierzy tileMap znajdu- jñcej siö w kodzie JavaScript. Takie rozwiñzanie jest dobre, pod warunkiem Ĕe uĔytkownik nie zapeäni wszystkich pól siatki budynkami (co jest raczej niemoĔliwe, biorñc pod uwagö fakt, Ĕe siatka ma 62 500 pól). Alternatywnym rozwiñzaniem byäoby utworzenie identycznej macie- rzy tileMap w PHP, zakodowanie jej do postaci JSON, a nastöpnie wypeänienie oryginalnej macierzy tileMap (w kodzie JavaScript) zdekodowanymi danymi z JSON. Takie rozwiñzanie ma jednak pewnñ wadö — jest maäo wydajne, poniewaĔ dekodowanie duĔych obiektów JSON w skrypcie JavaScript mocno obciñĔa przeglñdarkö. JeĈli w swoim projekcie chcesz wykorzystaè wiökszñ siatkö, moĔesz zmodyfikowaè procedurö äadujñcñ, tak by pobieraäa w danej chwili tylko wybrany fragment siatki, a pozostaäe fragmenty pobieraäa dynamicznie tylko wtedy, gdy sñ potrzebne (czyli podczas przewijania). W omawianym tu przykäadzie nie zastosowaliĈmy takiego roz- wiñzania, poniewaĔ urzñdzenia przenoĈne charakteryzujñ siö zwykle wysokñ prze- pustowoĈciñ, ale bardzo duĔymi opóĒnieniami (zwäaszcza w przypadku poäñczeþ 3G). Wynika stñd, Ĕe lepiej pobraè wszystko za jednym razem, a nie wykonywaè wiele Ĕñ- daþ pobierajñcych maäe fragmenty danych. Zmodyfikujemy równieĔ nieco klasö Game, aby siatka nie byäa wyĈwietlana bezpoĈrednio po jej utworzeniu. W tym celu trzeba usunñè dwa ostatnie wiersze konstruktora klasy Game: this.doResize(); this.draw(); Kolejnñ sprawñ, którñ siö zajmiemy, jest ekran tytuäowy z jednego z pierwszych przykäadów. Zmodyfikujemy go tak, by wyĈwietlaä siö podczas äadowania strony game.php. Dodanie tego ekranu wymaga uĔycia obiektu, który bödzie przechowywaä informacjö o bieĔñcym stanie gry (LOADING, LOADED, PLAYING). Z tego wzglödu musimy utworzyè obiekt GameState (dostöpny globalnie): var GameState = { _current: null, LOADING: 0, LOADED: 1, TITLESCREEN: 2, PLAYING: 3 } Ostatnia prosta _ 109 Dziöki temu, w zaleĔnoĈci od bieĔñcego stanu gry ustawionego w polu GameState._current, niektóre obiekty i zdarzenia bödñ dziaäaäy w róĔny sposób. Na przykäad klikniöcie tytuäowe- go ekranu, gdy pole GameState._current jest ustawione na GameState.LOADING, nie powinno wygenerowaè Ĕadnego zdarzenia. JeĈli jednak stan gry jest ustawiony na GameState.TITLE- SCREEN, klikniöcie okna powinno spowodowaè rozpoczöcie gry. Ekran tytuäowy bödzie wyĈwietlany podczas wstöpnego äadowania w tle zasobów gry, takich jak obrazki i dĒwiöki. W tym celu skorzystamy z obiektu ResourceLoader, który bödzie odpo- wiadaä za pobranie wszystkich plików przed ich uĔyciem. Kod klasy ResourceLoader znajduje siö w pliku resourceLoader.js: // Klasa ResourceLoader var ResourceType = { IMAGE: 0, SOUND: 1 } function ResourceLoader(onPartial, onComplete) { this.resources = []; this.resourcesLoaded = 0; if (onPartial !== undefined typeof(onPartial) === function ) { this.onPartial = onPartial; } if (onComplete !== undefined typeof(onComplete) === function ) { this.onComplete = onComplete; } } ResourceLoader.prototype.addResource = function(filePath, fileType, resourceType) { var res = { filePath: filePath, fileType: fileType, resourceType: resourceType }; this.resources.push(res); } ResourceLoader.prototype.startPreloading = function() { for (var i = 0, len = this.resources.length; i len; i++) { switch(this.resources[i].resourceType) { case ResourceType.IMAGE: var img = new Image(); var rl = this; img.src = this.resources[i].filePath; img.addEventListener( load , function() { rl.onResourceLoaded(); }, false); break; case ResourceType.SOUND: var a = new Audio(); // Pobiera tylko te pliki dĨwiĊkowe, które moĪna odtworzyü. if (a.canPlayType(this.resources[i].fileType) === maybe || a.canPlayType(this.resources[i].fileType) === probably ) { a.src = this.resources[i].filePath; a.type = this.resources[i].fileType; 110 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! var rl = this; a.addEventListener( canplaythrough , function() { a.removeEventListener( canplaythrough , arguments.callee, false); rl.onResourceLoaded(); }, false); } else { // Nie moĪna odtworzyü dĨwiĊku. Zakáadamy, Īe zasób zostaá pobrany. this.onResourceLoaded(); } break; } } } ResourceLoader.prototype.onResourceLoaded = function() { this.resourcesLoaded++; if (this.onPartial != undefined) { this.onPartial(); } if (this.resourcesLoaded == this.resources.length) { if (this.onComplete != undefined) { this.onComplete(); } } return; } ResourceLoader.prototype.isLoadComplete = function() { if (this.resources.length == this.resourcesLoaded) { return true; } return false; } Z klasy ResourceLoader moĔemy skorzystaè w nastöpujñcy sposób: var rl = new ResourceLoader(); rl.addResource( image1.png , null, ResourceType.IMAGE); rl.addResource( image2.jpg , null, ResourceType.IMAGE); rl.addResource( image3.gif , null, ResourceType.IMAGE); rl.addResource( sound.ogg , audio/ogg , ResourceType.SOUND); rl.addResource( sound.mp3 , audio/mp3 , ResourceType.SOUND); rl.startPreloading(); Po wywoäaniu metody startPreloading() klasy ResourceLoader mamy dostöp do dwóch bardzo uĔytecznych wäaĈciwoĈci: x instancja_klasy_ResourceLoader.resources.length — zwraca liczbö dodanych za- sobów. x instancja_klasy_ResourceLoader.resourcesLoaded — okreĈla liczbö juĔ pobranych zasobów. Wykorzystujñc obie wäaĈciwoĈci, moĔemy obliczyè stan pobierania zasobów wyraĔony w pro- centach: Ostatnia prosta _ 111 var percentLoaded = Math.floor((instancja_klasy_ResourceLoader.resourcesLoaded * 100) / instancja_klasy_ResourceLoader.resources.length); Klasa ResourceLoader umoĔliwia równieĔ zdefiniowanie dwóch funkcji zwrotnych: x onPartial — funkcja zwrotna jest wywoäywana po pobraniu kaĔdego pojedynczego zasobu. x onComplete — funkcja jest wywoäywana po pobraniu wszystkich zasobów. Dziöki tym funkcjom zwrotnym moĔliwe jest wyĈwietlenie komunikatów o bieĔñcym statusie procesu pobierania zasobów (patrz rysunek 5.2). Rysunek 5.2. Ekran tytuäowy z paskiem postöpu pobierania Gra moĔe siö znaleĒè w róĔnych stanach (zapisanych w obiekcie GameState). W zaleĔnoĈci od bieĔñcego stanu wykonywany jest odpowiedni proces. Za przejĈcia miödzy stanami gry bödzie odpowiadaäa funkcja handleGameState(), która zostanie wywoäana na przykäad po pobraniu dokumentu. Funkcja ta korzysta z obiektu GameState opisanego wczeĈniej, a jej kod jest nastöpujñcy: function handleGameState(nextState) { if (nextState !== undefined) { GameState._current = nextState; } switch(GameState._current) { case GameState.LOADING: // ... break; 112 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! case GameState.LOADED: // ... break; case GameState.TITLESCREEN: // ... break; case GameState.PLAYING: // ... break; default: // ... break; } } Aby zmieniè bieĔñcy stan gry, wystarczy przekazaè funkcji jednñ z wartoĈci zdefiniowanych w klasie GameState. MoĔemy teĔ w prosty sposób dodaè nowy stan, na przykäad GameState. PAUSED, który wstrzymuje wykonywanie gry po wciĈniöciu jakiegoĈ klawisza, np. Esc. Po po- nownym jego wciĈniöciu wystarczy wywoäaè funkcjö handleGameState(GameState.PLAYING), aby wznowiè dziaäanie gry. W stanie GameState.LOADING wywoäamy funkcjö preloadResources(), która tworzy obiekt klasy ResourceLoader odpowiedzialny za wstöpne zaäadowanie wszystkich wymaganych za- sobów (obrazków i dĒwiöków). Kod funkcji wyglñda nastöpujñco: function preloadResources(canvas, callback) { var c = canvas.getContext( 2d ); var rl = new ResourceLoader(printProgressBar, callback); rl.addResource( ../img/tile.png , null, ResourceType.IMAGE); rl.addResource( ../img/ui-icons.png , null, ResourceType.IMAGE); rl.addResource( ../img/spritesheet.png , null, ResourceType.IMAGE); rl.addResource( ../sounds/title.ogg , audio/ogg , ResourceType.SOUND); rl.addResource( ../sounds/title.mp3 , audio/mp3 , ResourceType.SOUND); rl.startPreloading(); printProgressBar(); function printProgressBar() { var percent = Math.floor((rl.resourcesLoaded * 100) / rl.resources.length); var cwidth = Math.floor((percent * (canvas.width - 1)) / 100); c.fillStyle = #000000 ; c.fillRect(0, canvas.height - 30, canvas.width, canvas.height); c.fillStyle = #FFFFFF ; c.fillRect(1, canvas.height - 28, cwidth, canvas.height - 6); } } Funkcja wyĈwietla równieĔ pasek postöpu, który widaè na rysunku 5.2. Zwróè uwagö, Ĕe nie pobieramy plików takich jak cinema.png czy tree.png, ale jeden plik o na- zwie spritesheet.png. Aby zmniejszyè liczbö wysyäanych Ĕñdaþ (co jest jednym ze skuteczniej- szych sposobów optymalizacji aplikacji internetowych), nie pobieramy kaĔdego obrazka z osobna, ale tworzymy z nich arkusz zapisany w jednym pliku, który przekaĔemy obiektowi Sprite opisanemu w jednym z poprzednich rozdziaäów. Ostatnia prosta _ 113 Obiektowi klasy ResourceLoader przekazujemy równieĔ parametr callback, który jest po prostu wywoäaniem funkcji handleGameState(GameState.LOADED). PosäuĔy ona do przejĈcia do nastöpnego stanu po zakoþczeniu pobierania zasobów. Stan GameState.LOADED bödzie odpowiedzialny za utworzenie obiektu klasy Game oraz wypeä- nienie siatki budynkami, które posiada dany uĔytkownik. Po zaäadowaniu zawartoĈci siatki ponownie zostanie wywoäana funkcja handleGameState() z parametrem GameState.TITLE- SCREEN, co spowoduje przejĈcie do kolejnego stanu gry. W stanie GameState.TITLESCREEN bödzie wyĈwietlany ekran tytuäowy (na który w naszym przypadku skäada siö logo gry i tekst „Kliknij lub dotknij ekran, aby rozpoczñè grö”). W tym samym czasie zostanie dodana funkcja obsäugujñca zdarzenie klikniöcia (jest ona dezaktywo- wana po klikniöciu ekranu). Po klikniöciu lub dotkniöciu dowolnego obszaru ekranu zostanie wykonana seria wygaszeþ obrazu do koloru biaäego i wyĈwietlenie tekstu „To Twoje dzieäo!”. Po odtworzeniu animacji jeszcze raz zostanie wywoäana funkcja handleGameState(), by zmie- niè stan na GameState.PLAYING, co spowoduje wyĈwietlenie interfejsu uĔytkownika gry i siatki ze wszystkimi budynkami oraz aktywowanie funkcji nasäuchujñcych zdarzeþ. Po zgäoszeniu Ĕñdania do pliku game.php, który zawiera gäówny kod gry, serwer automatycz- nie wypeäni panel dostöpnymi budynkami wraz z okreĈleniem ich kosztu, generowanych zy- sków, czasu itd. Wyglñd wypeänionego panelu znajduje siö na rysunku 5.3. Rysunek 5.3. MoĔliwoĈci oferowane przez panel budynków Teraz, kiedy sñ juĔ aktywne funkcje nasäuchujñce zdarzeþ wejĈciowych (takich jak przewija- nie, wciskanie przycisku myszy i klawiszy), musimy skorzystaè z obiektu podobnego do Ga- meState, który bödzie przechowywaä informacjö o wybranym narzödziu, by prawidäowo ob- säugiwaè zdarzenie klikniöcia: var Tools = { current: 4, // DomyĞlne narzĊdzie MOVE: 0, ZOOM_IN: 1, ZOOM_OUT: 2, 114 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! DEMOLISH: 3, SELECT: 4, BUILD: 5 } Dziöki temu po wykryciu klikniöcia w obröbie siatki poniĔsza funkcja obsäugi zdarzenia wy- kona odpowiednie dziaäania: Game.prototype.handleMouseDown = function(e) { e.preventDefault(); switch (Tools.current) { case Tools.BUILD: // ... break; // Pozostaáe narzĊdzia... } } Niektóre akcje, jak budowanie czy burzenie, wymagajñ dostöpu do bazy danych, by uaktual- niè jej stan na serwerze. Obsäuga komunikacji z serwerem jest realizowana za pomocñ funkcji zawartych w pliku comm.js: var SERVER_PATH_URL = http://localhost:8080/ ; function request(url, callback) { var req = false; if (window.XMLHttpRequest) { try { req = new XMLHttpRequest(); } catch(e) { // Nic nie rób } } if (req) { req.open( GET , url, true); req.send(null); req.onreadystatechange = function() { switch(req.readyState) { case 2: if (req.status !== 200) { callback( ERROR ); return; } break; case 4: callback (req.responseText); break; } } } else { // Brak wsparcia dla XMLHttpRequest callback( ERROR ); } } // Budowanie function purchase(buildingId, col, row, callback) { var url = SERVER_PATH_URL + purchase.php ; url += ?buildingId= + buildingId; url += x= + col; Ostatnia prosta _ 115 url += y= + row; request(url, callback); } // Burzenie function demolish(col, row) { var url = SERVER_PATH_URL + demolish.php ; url += ?x= + col; url += y= + row; request(url, callback); } // Synchronizacja function sync() { var url = SERVER_PATH_URL + sync.php ; request(url, callback); } Korzystanie z tych funkcji jest bardzo proste. Aby postawiè drzewo (buildingId = 4) w czwar- tym wierszu i piñtej kolumnie, wystarczy wywoäaè kod: purchase(4, 5, 4, function(resp) { if (resp.substr(0, 3) == OK: ) { var buildingInstanceId = resp.substr(3, resp.length); alert( Budowa zakoĬczona powodzeniem. Identyfikator egzemplarza: + buildingInstanceId); } else { alert( Podczas tworzenia budynku wystîpiĪ bĪîd! ) } }); Jednak przed umoĔliwieniem postawienia budynku musimy sprawdziè, czy na klikniötym polu siatki (i wszystkich sñsiednich polach) jest wolne miejsce, a wiöc czy nie znajduje siö tam inny budynek lub jego czöĈè (BuildPortion). Za tö operacjö odpowiada metoda checkIfTile- IsBusy() klasy Game: Game.prototype.checkIfTileIsBusy = function(obj, col, row) { for (var i = (col + 1) - obj.tileWidth; i = col; i++) { for (var j = (row + 1) - obj.tileHeight; j = row; j++) { if (this.tileMap[i] != undefined this.tileMap[i][j] != null) { return true; } } } return false; } Teraz mamy juĔ wszystkie niezbödne elementy obsäugujñce zdarzenia, wiöc pozostaäo jeszcze uzupeänienie implementacji metody Game.prototype.handleMouseDown: Game.prototype.handleMouseDown = function(e) { e.preventDefault(); switch (Tools.current) { case Tools.BUILD: if (this.buildHelper.current != null) { var pos = this.translatePixelsToMatrix(e.clientX, e.clientY); 116 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! // Czy w siatce moĪemy umieĞciü element? if (!this.checkIfTileIsBusy(this.buildHelper.current, pos.col, pos.row)) { var obj = this.buildHelper.current; var t = this; var processResponse = function(resp) { if (resp.substr(0, 3) == OK: ) { var buildingInstanceId = resp.substr(3, resp.length); for (var i = (pos.col + 1) - obj.tileWidth; i = pos.col; i++) { for (var j = (pos.row + 1) - obj.tileHeight; j = pos.row; j++) { t.tileMap[i] = (t.tileMap[i] == undefined) ? [] : t.tileMap[i]; t.tileMap[i][j] = (i === pos.col j === pos.row) ? obj : new BuildingPortion(obj.buildingTypeId, i, j); } } } else { alert( Podczas tworzenia budynku wystîpiĪ bĪîd! ) } t.draw(); } purchase(obj.buildingTypeId, pos.col, pos.row, processResponse); } else { alert( W tej lokalizacji nie mošna umieŁcið budynku ); } } break; case Tools.MOVE: this.dragHelper.active = true; this.dragHelper.x = e.clientX; this.dragHelper.y = e.clientY; break; case Tools.ZOOM_IN: this.zoomIn(); break; case Tools.ZOOM_OUT: this.zoomOut(); break; case Tools.DEMOLISH: var pos = this.translatePixelsToMatrix(e.clientX, e.clientY); if (this.tileMap[pos.col] != undefined this.tileMap[pos.col][pos.row] != undefined) { var obj = this.tileMap[pos.col][pos.row]; // To nie jest budynek, tylko jego czĊĞü. Pobierz referencjĊ do oryginalnego budynku. if (obj instanceof BuildingPortion) { pos.col += obj.x; pos.row += obj.y; obj = this.tileMap[pos.col][pos.row]; } var t = this; var processResponse = function(resp) { if (resp.substr(0, 2) == OK ) { // SprawdĨ sąsiednie pola i usuĔ równieĪ czĊĞci budynku. for (var i = (pos.col + 1) - obj.tileWidth; i = pos.col; i++) { for (var j = (pos.row + 1) - obj.tileHeight; j = pos.row; j++) { t.tileMap[i][j] = null; Ostatnia prosta _ 117 } } } else { alert( Podczas usuwania budynku wystîpiĪ problem! ); } t.draw(); } demolish(pos.col, pos.row, processResponse); } break; } this.draw(); } Mimo Ĕe moĔemy juĔ budowaè i burzyè budynki (co jest realizowane poprzez dodawanie lub usuwanie egzemplarzy budynku w bazie danych), stan konta w ogóle siö nie zmienia. Aby rozwiñzaè ten problem, zaimplementujemy funkcjö refresh(), która bödzie wywoäywana co 15 sekund, a jej zadaniem bödzie aktualizowanie stanu konta. Plik sync.php po stronie serwe- ra bödzie równieĔ odpowiadaä za obliczanie bieĔñcego zysku generowanego przez budynki: function refresh() { var processResponse = function(resp) { if (resp.substr(0, 5) == ERROR ) { alert( Podczas próby zsynchronizowania serwisu wystîpiĪ bĪîd. ); } else { var balanceContainer = document.getElementById( balance ); var currBalance = parseInt(balanceContainer.innerHTML); var balance = parseInt(resp.substr(3, resp.length)); balanceContainer.innerHTML = balance; } } sync(processResponse); setTimeout(function() { refresh(ui); }, 15000); } Aktualny wyglñd ekranu gry zostaä przedstawiony na rysunku 5.4. Ostatni szlif Dziöki kosmetycznym poprawkom produkt (w naszym przypadku gra) zyskuje swój niepo- wtarzalny charakter. ChociaĔ w tej chwili gra jest w peäni funkcjonalna, nie wyróĔnia siö ni- czym szczególnym, moĔe byè nuĔñca i nieciekawa. Jednym z moĔliwych rozwiñzaþ tego problemu jest sprawienie, by gra miaäa w sobie wiöcej Ĕycia, poprzez wprowadzenie dynamicznych obiektów poruszajñcych siö po mieĈcie lub po- nad nim, jak na przykäad chmur. Do ich wyĈwietlenia zamiast obiektu canvas (co wiñzaäoby siö z koniecznoĈciñ ciñgäego przerysowywania caäej planszy przy kaĔdym ruchu takiego obiek- tu) uĔyjemy moĔliwoĈci oferowanych przez animacje CSS3. 118 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Rysunek 5.4. Dziaäajñca gra Animacje CSS3 pozwalajñ na modyfikowanie jednej lub wielu wäaĈciwoĈci CSS przez okreĈlo- nñ liczbö klatek kluczowych: @moveToRight { 0 { left: 0px } 50 { left: 100px } 100 { left: 200px } } Po zdefiniowaniu klatek kluczowych animacji (w tym przypadku animowany element rozpo- czyna ruch w pozycji left: 0px i dociera do pozycji left: 200px) musimy zajñè siö innymi wäaĈciwoĈciami, takimi jak: x animation-timing-function — steruje sposobem przechodzenia do nastöpnej klatki klu- czowej. Dostöpne wartoĈci to: ease, linear, ease-in, ease-out, ease-in-out, cubic- -bezier(liczba, liczba, liczba, liczba). x animation-name — okreĈla nazwö animacji (w tym przypadku bödzie to moveToRight). x animation-duration — definiuje däugoĈè trwania caäej animacji. Ostatni szlif _ 119 x animation-iteration-count — okreĈla liczbö powtórzeþ animacji; jako wartoĈè przyj- muje liczbö caäkowitñ lub staäñ infinite. x animation-direction — pozwala na zdefiniowanie „kierunku” animacji. Dopuszczalne wartoĈci to: normal (animuje od klatki kluczowej 0 do 100) i alternate (animuje od klat- ki kluczowej 100 do 0). Peänñ listö wäaĈciwoĈci moĔesz znaleĒè w roboczej wersji specyfikacji W3C pod adresem http:// www.w3.org/TR/css3-animations/. W naszym przypadku animacja zawsze bödzie wyĈwietlana w tym samym poäoĔeniu. Ten sam efekt moĔna w prosty sposób uzyskaè za pomocñ skryptu JavaScript i zmiennych o losowych wartoĈciach lub wartoĈciach uwzglödniajñcych bieĔñce poäoĔenie siatki: @-webkit-keyframes moveFromLeftToRight { 0 { -webkit-transform: translateX(-5000px) translateY(50px) translateZ(0px); } 50 { -webkit-transform: translateX(0px) translateY(50px) translateZ(0px); } 100 { -webkit-transform: translateX(5000px) translateY(50px) translateZ(0px); } } @-moz-keyframes moveFromLeftToRight { 0 { -moz-transform: translateX(-5000px) translateY(50px); } 50 { -moz-transform: translateX(0px) translateY(50px); } 100 { -moz-transform: translateX(5000px) translateY(50px); } } @-ms-keyframes moveFromLeftToRight { 0 { -ms-transform: translateX(-5000px) translateY(50px); } 50 { -ms-transform: translateX(0px) translateY(50px); } 100 { -ms-transform: translateX(5000px) translateY(50px); } } @-o-keyframes moveFromLeftToRight { 0 { -o-transform: translateX(-5000px) translateY(50px); } 50 { -o-transform: translateX(0px) translateY(50px); } 100 { -o-transform: translateX(5000px) translateY(50px); } } 120 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! @keyframes moveFromLeftToRight { 0 { transform: translateX(-5000px) translateY(50px); } 50 { transform: translateX(0px) translateY(50px); } 100 { transform: translateX(5000px) translateY(50px); } } div.cloud { position: absolute; top: 0px; left: 0px; z-index: 500; background: transparent url(../../img/spritesheet.png) no-repeat 10px 257px; width: 566px; height: 243px; pointer-events: none; -webkit-animation-timing-function: linear; -webkit-animation-name: moveFromLeftToRight; -webkit-animation-duration: 2s; -webkit-animation-iteration-count: infinite; -webkit-animation-direction: alternate; -moz-animation-timing-function: linear; -moz-animation-name: moveFromLeftToRight; -moz-animation-duration: 60s; -moz-animation-iteration-count: infinite; -moz-animation-direction: alternate; -ms-animation-timing-function: linear; -ms-animation-name: moveFromLeftToRight; -ms-animation-duration: 60s; -ms-animation-iteration-count: infinite; -ms-animation-direction: alternate; -o-animation-timing-function: linear; -o-animation-name: moveFromLeftToRight; -o-animation-duration: 60s; -o-animation-iteration-count: infinite; -o-animation-direction: alternate; animation-timing-function: linear; animation-name: moveFromLeftToRight; animation-duration: 60s; animation-iteration-count: infinite; animation-direction: alternate; } Osiñga siö dziöki temu efekt przedstawiony na rysunku 5.5. Kolejnym efektownym dodatkiem oĔywiajñcym grö moĔe byè dodanie cieni. Istnieje kilka tech- nik i algorytmów generujñcych cienie na dwuwymiarowej scenie. W wiökszoĈci wymagajñ one jednego lub kilku Ēródeä Ĉwiatäa, a to wiñĔe siö z wykonaniem wielu obliczeþ, zwäaszcza jeĈli mamy do czynienia z wieloma obiektami (a tak jest w naszym przypadku). PoniewaĔ obiekty przez wiökszoĈè czasu sñ nieruchome, moĔemy zastosowaè jedno z dwóch poniĔszych rozwiñzaþ: Ostatni szlif _ 121 Rysunek 5.5. Chmury przelatujñce nad miastem x rysowanie tekstury cienia dla kaĔdego budynku z osobna, x dynamiczne i samoczynne generowanie cienia. Zastosujemy drugie podejĈcie, poniewaĔ pozwala na zdefiniowanie „udawanego” Ēródäa Ĉwia- täa oraz dynamiczne sterowanie intensywnoĈciñ cienia. Ta metoda polega na narysowaniu zarysu oryginalnego budynku, wykrzywieniu go i obróce- niu oraz zredukowaniu stopnia krycia (czyli zwiökszeniu przezroczystoĈci). JeĔeli rysujemy tekstury przy Ĉwietle padajñcym na przykäad z poäudniowego wschodu, cienie powinny byè rzucane w kierunku póänocnego zachodu. JeĈli Ĉwiatäo pada z poäudniowego zachodu, cienie sñ rzucane w kierunku póänocnego wschodu itd. Rysunek 5.6 powinien wiele w tym wzglö- dzie wyjaĈniè. Aby zaimplementowaè ten mechanizm, musimy skorzystaè z dwóch funkcji obiektu canvas, z którymi siö juĔ zetknöliĈmy: getImageData() i putImageData(). Caäñ funkcjonalnoĈè za- mkniemy w klasie Sprite. Musimy zaczñè od zmodyfikowania klasy Sprite i dodania pustej wäaĈciwoĈci: this.shadow = null; Nastöpnie zmodyfikujemy metodö draw(), by przyjmowaäa opcjonalny parametr drawShadow, czyli wartoĈè logicznñ okreĈlajñcñ, czy do rysowanego wäaĈnie duszka dodaè cieþ. Niewielka dodatkowa procedura sprawdzi, czy this.shadow jest null, i wykona wszystkie niezbödne 122 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Rysunek 5.6. Dodawanie cienia do budynku operacje przeksztaäcajñce obraz na tablicö obrazów, które zostanñ przekazane metodzie put- ImageData(). Wynik jest zapisywany we wäaĈciwoĈci this.shadow, tak by nastöpnym razem nie trzeba byäo powtórnie przeprowadzaè wszystkich operacji zwiñzanych z przygotowaniem obrazu. Takie podejĈcie moĔe nie jest tak efektywne jak przygotowanie gotowych obrazów cieni, jednak daje znacznie wiökszñ kontrolö i jest bardziej elastyczne: if (this.shown) { if (drawShadow !== undefined drawShadow) { if (this.shadow === null) { // CieĔ nie zostaá jeszcze utworzony var sCnv = document.createElement( canvas ); var sCtx = sCnv.getContext( 2d ); sCnv.width = this.width; sCnv.height = this.height; sCtx.drawImage(this.spritesheet, this.offsetX, this.offsetY, this.width, this.height, 0, 0, this.width * this.zoomLevel, this.height * this.zoomLevel); var idata = sCtx.getImageData(0, 0, sCnv.width, sCnv.height); for (var i = 0, len = idata.data.length; i len; i += 4) { idata.data[i] = 0; // R idata.data[i + 1] = 0; // G idata.data[i + 2] = 0; // B Ostatni szlif _ 123 } sCtx.clearRect(0, 0, sCnv.width, sCnv.height); sCtx.putImageData(idata, 0, 0); this.shadow = sCtx; } c.save(); c.globalAlpha = 0.1; var sw = this.width * this.zoomLevel; var sh = this.height * this.zoomLevel; c.drawImage(this.shadow.canvas, this.posX, this.posY - sh, sw, sh * 2); c.restore(); } c.drawImage(this.spritesheet, this.offsetX, this.offsetY, this.width, this.height, this.posX, this.posY, this.width * this.zoomLevel, this.height * this.zoomLevel); } Efekt koþcowy zostaä przedstawiony na rysunku 5.7. Rysunek 5.7. Drzewa rzucajñ cieþ 124 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Gra trafia do spoĥecznoļci — integracja z Facebookiem Integracja gry z Facebookiem jest prosta — wystarczy wypeäniè kilka formularzy i dodaè tro- chö kodu. Przede wszystkim musisz utworzyè nowñ aplikacjö w serwisie Facebook. W tym celu przejdĒ na stronö http://www.facebook.com/developers/ i kliknij przycisk Utwórz aplikacjö2. Zostanie wyĈwietlony kreator Nowa aplikacja, za pomocñ którego moĔesz zintegrowaè grö z Facebookiem. W pierwszym oknie kreatora (patrz rysunek 5.8) w polu App Display Name (wyĈwietlana na- zwa aplikacji) musisz podaè nazwö aplikacji (moĔe byè dowolna, ale weĒ pod uwagö, Ĕe to ona bödzie wyĈwietlana uĔytkownikom), zgodziè siö z warunkami korzystania z platformy i kliknñè przycisk Kontynuuj3. Ja nazwaäem aplikacjö Tourist Resort. MoĔesz jñ nazwaè tak sa- mo, bo nazwa aplikacji nie musi byè unikalna. Rysunek 5.8. Tworzenie aplikacji w Facebooku Kolejnym krokiem (przedstawionym na rysunku 5.9) jest weryfikacja za pomocñ mechanizmu zabezpieczajñcego CAPTCHA. Rysunek 5.9. Kolejny krok — CAPTCHA 2 Aby utworzyè aplikacjö, trzeba oczywiĈcie mieè aktywne konto na Facebooku — przyp. täum. 3 W oknie kreatora znajduje siö jeszcze jedno pole — App Namespace. Jego znacznie zostanie wyjaĈnione w dal- szej czöĈci rozdziaäu — przyp. täum. Gra trafia do spoĥecznoļci — integracja z Facebookiem _ 125 Po klikniöciu przycisku WyĈlij, w zaleĔnoĈci od ustawieþ konta i lokalizacji, moĔe byè wyma- gane przeprowadzenie dodatkowej weryfikacji konta za pomocñ numeru telefonu lub karty kredytowej. Po zakoþczeniu tego etapu zostanie wyĈwietlona strona zarzñdzania aplikacjñ, na której mo- Ĕesz zmodyfikowaè informacje kontaktowe, nazwö aplikacji, jej opis, logo, jözyk i wiele innych szczegóäów. Aplikacjö moĔemy zintegrowaè z platformñ Facebook, wyĈwietlajñc jñ na stronie serwisu lub tworzñc odröbnñ aplikacjö stosujñcñ schemat uwierzytelniania Facebook Connect. Omówienie wszystkich dostöpnych sposobów uwierzytelniania wykracza poza ramy ksiñĔki, wiöc skupi- my siö na sposobie wyĈwietlania gry w obröbie strony Facebooka. Umieszczenie zewnötrznej strony wewnñtrz serwisu jest moĔliwe dziöki stosowanemu w Fa- cebooku elementowi canvas (który nie ma nic wspólnego z päótnem z HTML5). Jest to po pro- stu päywajñca ramka (iframe) o szerokoĈci 765 pikseli, do której jest äadowana strona aplika- cji wraz z parametrami umoĔliwiajñcymi zainicjowanie schematu uwierzytelniania OAuth 2.0, dostöpnego z poziomu serwera lub klienta (za pomocñ JavaScript i XBFML). Skupiamy siö tutaj na rozwiñzaniu realizowanym po stronie serwera. Wiöcej na te- mat róĔnych sposobów uwierzytelniania dostöpnych na platformie Facebook moĔna znaleĒè na stronie http://developers.facebook.com/docs/authentication/. Aby zaimplementowaè stosowany przez nas schemat uwierzytelniania po stronie serwera, na stronie zarzñdzania aplikacjñ musisz kliknñè link Basic (podstawowe) znajdujñcy siö w menu Ustawienia. Na górze strony sñ wyĈwietlane informacje przedstawione na rysunku 5.10. Rysunek 5.10. Kluczowe informacje dla aplikacji Zanotuj dane z pól App ID (identyfikator) oraz App Secret (tajny kod; na rysunku jest czöĈcio- wo zamazany) i pobierz oficjalne SDK Facebooka dla PHP: https://github.com/facebook/php-sdk/. Na stronie podstawowych ustawieþ aplikacji (Ustawienia/Basic) musimy wypeäniè dwa dodat- kowe pola: x App Namespace (przestrzeþ nazwy aplikacji) — adres w ramach serwisu Facebook, pod którym uĔytkownicy bödñ widzieè aplikacjö. Ustalony tu adres jest wyĈwietlany w innych miejscach jako Canvas Page (strona päótna). x Canvas URL (URL päótna) i Secure Canvas URL (bezpieczny URL päótna)4 w sekcji App on Facebook (aplikacja na Facebooku) — rzeczywisty adres, pod którym znajduje siö aplikacja. Panel z przykäadowymi danymi zostaä przedstawiony na rysunku 5.11. 4 Od 1 paĒdziernika 2011 roku trzeba podaè adres bezpiecznej strony HTTPS — przyp. täum. 126 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Rysunek 5.11. Definiowanie elementu canvas W skrócie — gdy uĔytkownik przejdzie na stronö http://apps.facebook.com/tourist-resort, Face- book automatycznie wywoäa adres podany w polu Canvas URL i przekaĔe kilka parametrów uäatwiajñcych uwierzytelnianie. Nastöpnie musimy doäñczyè pliki z API Facebooka oraz utworzyè nowy obiekt Facebook, ko- rzystajñc z przypisanego do aplikacji identyfikatora i tajnego kodu. Dziöki temu uzyskamy dostöp do informacji o uĔytkowniku: ?php require facebook/src/facebook.php ; $fb = new Facebook(array( appId = identyfikator_aplikacji , secret = tajny_kod_aplikacji , )); $user = $fb- getUser(); echo pre ; print_r($user); echo /pre ; ? JeĈli uĔytkownik nie jest zalogowany lub nie zostaä jeszcze uwierzytelniony w aplikacji, war- toĈè zmiennej $value jest równa 0. Lepszym sposobem wykrycia tej sytuacji jest zastosowa- nie podejĈcia, które moĔna znaleĒè w przykäadowym pliku example.php doäñczonym do SDK Facebooka dla PHP: ?php if ($user) { try { $user_profile = $fb- api( /me ); } catch (FacebookApiException $e) { $user = null; } } ? W bloku try do zmiennej $user_profile jest przypisywany wynik wywoäania $fb- api( /me ), który wysyäa zapytanie o wäasne konto uĔytkownika do Graph API Facebooka (wiöcej na ten temat moĔesz znaleĒè na stronie http://developers.facebook.com/docs/reference/api/). JeĈli operacja siö nie powiedzie, wyrzucany jest wyjñtek. Aby umoĔliwiè zarejestrowanie siö uĔytkownika w aplikacji, musisz dodaè poniĔszy kod: ?php $loginUrl = $fb- getLoginUrl(); ? a href= ?=$loginUrl? Zarejestruj siĂ /a Gra trafia do spoĥecznoļci — integracja z Facebookiem _ 127 Po klikniöciu linku Zarejestruj siö zostanie wyĈwietlone okno autoryzacji Facebooka, przedsta- wione na rysunku 5.12. Rysunek 5.12. WyraĔenie zgody na dostöp do danych JeĈli uĔytkownik zgodzi siö na udostöpnienie danych, strona zostanie przeäadowana i tym razem w zmiennej $user znajdzie siö identyfikator uĔytkownika Facebooka, a w zmiennej $user_profile — odpowiedĒ na zapytanie o wäasne konto (/me) skierowane do Graph API, zwrócona w postaci obiektu JSON. Facebook domyĈlnie pyta jedynie o „podstawowe informacje” o uĔytkowniku, do których za- liczajñ siö: imiö, nazwisko, identyfikator, lokalizacja, zainteresowania, päeè itd. JeĈli chcemy poprosiè o dodatkowe zezwolenia, by na przykäad mieè dostöp do adresu e-mail i znajomych oraz móc publikowaè komunikaty, musimy zmodyfikowaè wywoäanie $fb- getLoginUrl() w nastöpujñcy sposób: ?php $loginUrl = $fb- getLoginUrl(array( scope = email, publish_stream, friends_about_me )); ? Peänñ listö zezwoleþ moĔesz znaleĒè pod adresem http://developers.facebook.com/docs/ authentication/permissions/. Po zmodyfikowaniu zezwoleþ okno uwierzytelniania wyglñda jak na rysunku 5.13. JeĈli uĔytkownik zgodzi siö na udostöpnienie tych danych, zmienna $user_profile bödzie za- wieraäa równieĔ adres e-mail, z którego moĔemy skorzystaè do automatycznego rejestrowania uĔytkownika we wäasnej bazie danych. Skoro mamy dostöp do wszystkich informacji, moĔemy w bazie danych naszej gry zastoso- waè ten sam identyfikator uĔytkownika co na Facebooku. Dziöki temu moĔemy automatycz- nie zalogowaè uĔytkowników przechodzñcych do naszej gry bezpoĈrednio z Facebooka. 128 _ Rozdziaĥ 5. Niech ļwiat pozna Twojé grý! Rysunek 5.13. WyraĔenie zgody na dostöp do dodatkowych danych Wiöcej informacji na temat tworzenia aplikacji dla Facebooka moĔesz znaleĒè na stronie http:// developers.facebook.com/docs/guides/canvas/. Gra trafia do spoĥecznoļci — integracja z Facebookiem _ 129
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Tworzenie izometrycznych gier społecznościowych w HTML5, CSS3 i JavaScript
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: