Intense.js – ciekawe elementy frameworka

Ponownie wracam do przeszłości, odkurzam swoją pracę i przedstawiam te moduły, które w mojej opinii są ciekawe i mogą być dla kogoś natchnieniem przy tworzeniu własnego frameworka czy też gry. Zapraszam!

Dla osób, które zaglądają tu pierwszy raz, przedstawiam listę artykułów dotyczących frameworka intense.js:

  1. Intense.js – framework dla gier HTML5
  2. Intense.js – encje
  3. Intense.js – komponenty
  4. Intense.js – systemy

Konstruktor gry

Konstruktor gry frameworka intense jest punktem wejścia aplikacji i zalecaną metodą na inicjalizację wszystkich modułów. Najważniejszym zadaniem konstruktora gry jest wywołanie procesu uruchomienia modułów i utworzenia wirtualnego płótna, na którym będzie rysowana gra. Konstruktor jest wywoływany poleceniem intense.game i przyjmuje w parametrze obiekt konfiguracyjny o następujących właściwościach:

  • server – obiekt konfiguracyjny dla części serwerowej, niezbędny gdy framework uruchamiany jest z poziomu środowiska node.js. Zawiera właściwość rate, określającą częstotliwość z jaką będą wysyłane dane do klientów.
  • preinit – funkcja zwrotna uruchamiana przed przystąpieniem do inicjowania modułów.
  • debug – uruchamia moduł debugowania.
  • element – pobrany za pomocą podstawowych funkcji drzewa DOM element, który będzie zawierał element canvas.
  • width – szerokość elementu zawierającego grę.
  • height – wysokość elementu zawierającego grę.
  • fullscreen – ustawia szerokość i wysokość elementu gry na cały ekran.
  • pooling – uruchamia pooling obiektów.
  • multiplayer – obiekt konfiguracji dla rozgrywki wieloosobowej.
  • fps – obiekt konfiguracji dla licznika wartości FPS (ang. frames per second).
  • preload – funkcja zwrotna uruchamiana przed wywołaniem managera pobierania zewnętrznych zasobów.
  • init – funkcja zwrotna uruchamiana po wczytaniu zasobów, przed przygotowaniem systemów.
  • load – funkcja zwrotna uruchamiana po całkowitej inicjalizacji frameworka.

Z pomocą funkcji preinit, preload, init i load, programista ma możliwość zautomatyzowania procesu wybranych funkcjonalności już na etapie uruchomienia modułów. Konstruktor gry został tak zaprojektowany, że nie wymaga podawania obiektu konfiguracyjnego. Zostaną wtedy zastosowane wartości domyślne dla poszczególnych elementów konfiguracji.

Zarządzanie pętlą gry

Mechanizm pętli gry jest ukryty przed użytkownikiem w module intense.engine i tylko odpowiednie metody tego modułu mogą się z tym mechanizmem komunikować. Główną metodą modułu jest metoda start. Uruchomienie przebiegu pętli odbywa się za pomocą funkcji animationFrame. Ukrywa ona implementację funkcji języka JavaScript requestAnimationFrame, która różni się pomiędzy przeglądarkami. Jeżeli metoda ta w ogóle nie istnieje w przeglądarce, to do przebiegu zostanie wykorzystana funkcja setInterval. Funkcja requestAnimationFrame umożliwia kontrolowanie tempa rysowania przez przeglądarkę i pozwala na zatrzymanie pętli gdy użytkownik przełączy kartę w przeglądarce. W przypadku środowiska node.js gdzie funkcja requestAnimationFrame nie występuje, za przebieg pętli będzie odpowiadała funkcja setInterval. Oprócz głównej pętli gry uruchamiana jest również pętla aktualizacji systemów typu „net”, jeżeli przekazano do konstruktora gry odpowiedni parametr.

Schemat pętli gry we frameworku intense

Framework intense dzieli mechanizm zarządzający pętlą główną na dwie pętle:

  • Pętla gry – pętla podstawowa służąca do rysowania i aktualizacji fizyki w grze. Pętla jest wykonywana za pomocą funkcji requestAnimationFrame a o szybkości jej wykonywania decyduje proces przeglądarki, który wywołuje ją tak szybko jak to tylko możliwe dla środowiska. Aktualizowane są w ten sposób systemy typu „render”. Aby uzyskać stabilność części odpowiadającej za fizykę, stosowana jest tzw. metoda aktualizacji o stałym kroku (ang. fixed time step). Polega ona na umieszczeniu dodatkowej pętli aktualizującej stan fizyki. Pętla ta, wykonywana jest tyle razy ile czasu minęło od aktualizacji ostatniej klatki. Przekazywany jest do niej również stały krok aktualizacji. Symulacja stałokrokowa sprawia, że fizyka w grze jest deterministyczna i stabilna. W pętli stałokrokowej intense aktualizuje systemy typu „update”.
  • Pętla dla sieci – uruchamiana za pomocą funkcji setInterval o stałym kroku zdefiniowanym przez użytkownika. Aktualizuje systemy typu „update” i pozwala zachować determinizm wysyłanych wiadomości do serwera w przypadku klienta i wiadomości do klienta w przypadku serwera.

Pozostałe metody modułu intense.engine:

  • stop – zatrzymuje pętle frameworka.
  • pause – wstrzymuje przebieg pętli.
  • resume – wznawia przebieg pętli.
  • isPaused – sprawdza czy pętla jest w stanie wstrzymania.
  • showFPS – pokazuje wartość licznika FPS.
  • delta – właściwość przechowująca różnicę jaka nastąpiła pomiędzy klatkami.

Moduł zdarzeń

Moduł intense.eventManager odpowiada we frameworku za obsługę zdarzeń. Oprócz możliwości udostępniania rejestracji własnych zdarzeń, moduł ten obsługuje wszelkie wewnętrzne zdarzenia zachodzące podczas pracy z frameworkiem. Do implementacji tego modułu został wykorzystany wzorzec projektowy obserwator (ang. observer). Aby zarejestrować funkcję, która ma zostać uruchomiona w momencie zajścia zdarzenia, należy wykorzystać metodę listen. Metoda ta przyjmuje w parametrach nazwę zdarzenia, funkcję która ma zostać zarejestrowana i opcjonalnie kontekst w ramach którego zostanie wywołana funkcja. Uruchomienie funkcji odbywa się za pomocą metody notify, która rozgłasza nazwę zdarzenia i przekazuje opcjonalnie parametry dla zarejestrowanych funkcji. Oprócz wymienionych metod, moduł zdarzeń posiada dodatkowo następujące metody:

  • register – rejestruje funkcję dla zdarzenia, ale nie nasłuchuje.
  • registerListeners – wszystkie zarejestrowane funkcje zostają włączone do mechanizmu nasłuchiwania.
  • remove – usuwa nasłuchiwanie na konkretne zdarzenie.
  • removeByType – usuwa zdarzenia danego typu.
  • getTypesOfListeners – zwraca tablicę typów zarejestrowanych zdarzeń.

Zdarzenia są wykorzystywane we frameworku m.in. w zbiorach encji i w module debugowania.

Moduł wykrywania funkcjonalności

Framework intense posiada moduł do sprawdzania czy dane funkcjonalności są obsługiwane przez przeglądarkę. Wykorzystywana jest w tym celu przestrzeń intense.support, hermetyzująca listę wszystkich funkcjonalności. Moduł ten jest sam w sobie metodą, która w pierwszym parametrze przyjmuje nazwę funkcjonalności, natomiast drugi parametr jest opcjonalny i określa za pomocą wartości logicznej czy moduł ma zwrócić specyficzny obiekt dla przeglądarki. Przykładowe sprawdzenie funkcjonalności wygląda następująco:

intense.support("websocket", true);

Powyższy przykład pokazuje sprawdzenie obsługi protokołu WebSocket przez przeglądarkę. Dodatkowo zwrócona zostanie nazwa dostępnego konstruktora dla tej funkcjonalności. Jest to istotne w przypadku starszych przeglądarek Firefox, w których obiekt konstruktora WebSocket posiada nazwę MozWebSocket. Zbiór wszystkich funkcjonalności do sprawdzania jest duży i został opisany w kodzie źródłowym modułu.

Moduł rysowania

Moduł intense.drawing jest jedną z propozycji rozszerzenia funkcjonalności frameworka w przyszłości. Ideą powstania tego modułu było zautomatyzowanie rysowania podstawowych figur geometrycznych oraz innych, które są rysowane na elemencie canvas. Ze względu na dużą ilość pracy poświęconą innym modułom, autor zaimplementował w module jedną metodę o nazwie drawRotated. Metoda ta służy do rysowania obiektów pod wybranym kątem. Przyjmuje w parametrach obiekt obrazu, który zostanie narysowany a także współrzędne, kąt określony w stopniach i opcjonalny współczynnik skalowania.

Moduł sterowania

Aby użytkownik mógł wpływać na działania widoczne w grze, framework intense udostępnia moduł intense.controls za pomocą którego można w prosty sposób dokonywać sprawdzania zdarzeń wywołanych przez użytkownika. W przypadku komputerów osobistych, moduł nasłuchuje zdarzeń wywoływanych akcją wciśnięcia klawiszy na klawiaturze a także przycisków myszy. W przypadku urządzeń mobilnych, moduł sprawdza reakcję z ekranem dotykowym. W trakcie uruchamiania metody init frameworka, dla środowiska przeglądarki inicjalizowane są mechanizmy obsługujące poszczególne elementy sterujące. Bez względu na rodzaj urządzenia inicjalizowana jest obsługa klawiatury oraz myszy dlatego, że coraz więcej urządzeń mobilnych umożliwia podłączenie tych urządzeń. Następnie sprawdzana jest obsługa zdarzeń związanych z ekranami dotykowymi za pomocą modułu intense.support i jeżeli jest dostępna to inicjalizowane są zdarzenia dla tych ekranów. Podstawowym elementem obsługi gry jest klawiatura. Moduł sterowania posiada słownik najważniejszych klawiszy, które są mapowane na proste nazwy. Programista nie musi znać kodu ASCII danego klawisza aby go zidentyfikować. Moduł udostępnia metodę keyDown, która sprawdza czy podany przez użytkownika klawisz został wciśnięty. Przykładowe użycie metody keyDown:

if (intense.keyDown("D")) { 
    // wykonaj akcje 
}

W powyższym przykładzie następuje sprawdzenie czy użytkownik wcisnął na klawiaturze klawisz z literą D i jeżeli tak to zostaną wykonane określone akcje. Akcja zostanie wykonana bez względu na wielkość podanej litery. Jeżeli wymagane byłoby sprawdzenie obsługi wielkości liter, można wykorzystać klawisz shift w połączeniu z innym. Przykład zastosowania:

if (intense.keyDown("SHIFT") && intense.keyDown("D")) { 
    // wykonaj akcje 
}

Oprócz zwracania wartości logicznej, metoda keyDown umożliwia podanie jako drugiego parametru funkcji zwrotnej, która zostanie wywołana w momencie wciśnięcia danego klawisza. Kolejnym podstawowym urządzeniem wykorzystywanym w obsłudze gry jest mysz komputerowa. Moduł udostępnia do obsługi myszy metodę mouseDown, która działa analogicznie do metody keyDown. Zamiast klawiszy programista ma możliwość sprawdzenia, który z przycisków myszy został wciśnięty. Możliwymi opcjami są MOUSE_LEFT, MOUSE_RIGHT i MOUSE_MIDDLE. Dodatkowo dla myszy zapisywane są koordynaty kursora na ekranie w publicznych polach canvasX i canvasY.

W przypadku ekranów dotykowych, do sprawdzenia czy nastąpiła interakcja z ekranem wykorzystywana jest metoda isTouched. Aby zlokalizować miejsce dotyku na ekranie, wykorzystywane są te same pola koordynatów co w przypadku obsługi myszy. Pozostałe metody modułu sterownia:

  • keyReset – czyści obiekt wciśniętych klawiszy klawiatury.
  • mouseReset – czyści obiekt wciśniętych przycisków myszy.
  • setKey – pozwala ręcznie ustalić czy klawisz jest wciśnięty czy też nie.

Podsumowanie

Kolejna porcja modułów, a to jeszcze nie koniec! Został m.in. moduł komunikacji, o którym niebawem napiszę.