A-Frame – szybki i przyjemny start z VR

Gdy po raz pierwszy zetknąłem się z elementem <canvas> w HTML5 byłem niezmiernie ciekaw dokąd to, że tak kolokwialnie powiem „zajdzie” i czy będzie to sposób na pobicie Flasha.
Dziś już wiem, że tak, a framework, który chcę w tym wpisie przedstawić tylko utwierdza mnie w tym przekonaniu.

Na potrzeby konkursu Daj się poznać 2017 postanowiłem, że stworzę prostą grę zrobioną w stylu strzelnicy w modnej ostatnimi czasy technologii VR. Jako iż na co dzień zajmuje się szeroko pojętym front-end’em to skierowałem swój wzrok ku najnowszym zabawkom jakie daje nam obecnie JavaScript i HTML5. Pomysł nie zrodził się tak zupełnie spontanicznie, ponieważ wiedziałem, że trwają zaawansowane prace nad standardem WebVR, który jest odpowiedzią na zapotrzebowanie na technologie VR w przeglądarkach. O samej specyfikacji tego standardu można poczytać tutaj, natomiast ja chciałbym się skupić nad jednym z frameworków, który wspomnianą technologię wykorzystuje. Warto jeszcze tylko wspomnieć, że jako iż jest to dość nowa rzecz to wsparcie przeglądarek jest jeszcze dość niewielkie, co można zobaczyć na tej stronie.



W wirtualną rzeczywistość dość solidnie zanurzyła się firma Mozilla tworząc framework A-Frame. Jak możemy przeczytać na oficjalnej stronie frameworka (https://aframe.io) – A-frame jest narzędziem, którzy wykorzystując HTML pozwala nam budować sceny w wirtualnej rzeczywistości. Pod maską wykorzystywany jest popularny framework Three.js, który to znowu ułatwia pracę z technologią WebGL do budowania trójwymiarowych scen, a całość wykorzystuje w moim mniemaniu, dość egzotyczny wzorzec projektowania o nazwie entity-component-system (ECS). Ze względu na to, że wpis ten jest jedynie wprowadzeniem do A-Frame, nie będę rozkładał go szczegółowo na czynniki pierwsze, ale z pewnością chciałbym chociaż wspomnieć o co w tym wzorcu chodzi. Na pierwszy rzut oka może kojarzyć się on z nurtem webcomponents, który wykorzystuje coraz więcej frameworków (tym bardziej, że sam A-Frame udostępnia własne znaczniki HTML-owe do budowania sceny), ale nic bardziej mylnego. Koncepcja wzorca ECS składa się z trzech elementów:

  • Encja (ang. entity) – to podstawowa jednostka (obiekt) utożsamiana z identyfikatorem. Encja sama w sobie nie posiada żadnych właściwości ani zachowania. Najprościej ją sobie wyobrazić jako wiersz tabeli w bazie danych, gdzie o jej unikalności będą świadczyć poszczególne wartości w kolumnach dostarczone przez komponenty.
  • Komponent (ang. component) – obiekt wielokrotnego użytku, który może dostarczać encjom zachowanie, wygląd i inne funkcjonalności. Możemy sobie wyobrazić encję z komponentem „car”, dzięki czemu encja stanie się obiektem reprezentującym samochód.
  • System (ang. system) – obiekt/funkcja operująca na encjach o określonych komponentach. Np. dla encji z komponentem „car” możemy wyobrazić sobie system o nazwie „carSystem”, który „wprawi w ruch” nasze pojazdy na scenie (to oczywiście duże uproszczenie, ale mam nadzieję, że dobrze zadziała na wyobraźnię).

Wzorzec ECS idealnie nadaje się do tworzenie gier, ale także i aplikacji w których jest potrzeba sporej manipulacji we właściwościach obiektu. Gry są świetnym tego typu przykładem gdzie dla zobrazowania problemu możemy wyobrazić sobie encję drzewa. Będzie to obiekt statyczny, ale co stoi na przeszkodzie w grach aby drzewo mogło się przemieszczać? 😉 Ciężko więc za pomocą samego programowania obiektowego zobrazować hierarchię dziedziczenia, w której obiekty dość dynamicznie mogą się zmieniać wraz z upływem czasu. ECS pozwala niejako na przecinanie drzewa dziedziczenia w poprzek i tym samym nie musimy się długo zastanawiać nad przyporządkowaniem obiektów do określonych grup. Jednym z przykładów frameworków wykorzystujących ECS jest CraftyJS, jednakże Crafty miksuje niejako system i komponent w jednym obiekcie.

Trochę odpłynąłem w kierunku wzorca ECS, a przecież nie on jest bohaterem tego wpisu. Wspomniałem już, że A-Frame wykorzystuje HTML do budowania sceny. Aby więc utworzyć najprostszą encję, która nie posiada żadnych właściwości, wystarczy wpisać:

<a-entity></a-entity>

A-Frame jest dostarczany z zestawem predefiniowanych encji, które możemy ujrzeć na scenie np. tag <a-sphere> reprezentujący kule. Dzięki komponentowej naturze dodawanie nowych elementów jest wręcz banalne, a rozszerzanie frameworka o nowe elementy jest dużo bardziej uproszczone. Manipulacja obiektami jest praktycznie identyczna jak innymi elementami HTMLa, ponieważ wykorzystano w tym celu standardowe DOM API.

Czy to oznacza, że można wykorzystać jQuery np. do obsługi zdarzeń?

Jak najbardziej 🙂 Wydaje mi się, że to jedna z mocniejszych stron A-Frame, ponieważ nie narzuca nam zbyt wiele API i nie narzuca także wzorca architektonicznego. Możemy więc wykorzystać Reacta i Redux’a, ale także i inne frameworki korzystające z DOM API. Krzywa uczenia powinna być więc w miarę prosta.

Ok to tyle gadania na chwilę obecną, a teraz pobrudźmy sobie dłonie w piaskownicy i zbudujmy standardowe Hello World w A-Frame… no może nie do końca standardowe, bo nie będzie to napis lecz trójwymiarowa kula 😉

Na początek musimy dołączyć skrypt biblioteki A-Frame do naszego projektu. Możemy to zrobić w dwojaki sposób: albo zainstalować za pomocą menadżera pakietów npm i wykorzystać narzędzie do zarządzania zależnościami np. Browserify, albo po prostu ściągając skrypt ze strony frameworka i dołączyć do do naszego pliku HTML. I ja na potrzeby tego przykładu skorzystam z tego drugiego sposobu:

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>A-Frame hello world</title>
    </head>
    <body>
        <script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
    </body>
</html>

Jeżeli do budowania sceny wykorzystywane są znaczniki HTML to miejsce ich prezentacji jest oczywiste i będzie to znacznik <body>:

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>A-Frame hello world</title>
    </head>
    <body>
        <a-scene>
        </a-scene>
        <script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
    </body>
</html>

Element <a-scene> reprezentuje scenę na której będą wyświetlane umieszczone na tej scenie obiekty. Aby umieścić jakiś obiekt na scenie, należy po prostu wpisać odpowiedni element we wnętrzu taga . Jest to analogiczne do np. rysowania elementów w SVG gdzie naszą sceną jest znacznik <svg> Proste prawda? 🙂 Zatem narysujmy kule:

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>A-Frame hello world</title>
    </head>
    <body>
        <a-scene>
            <a-sphere></a-sphere>
        </a-scene>
        <script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
    </body>
</html>

Skoro elementy na scenie są tagami HTML to posiadają także atrybuty, które wpływają na wygląd obiektu na scenie. I tak rzeczywiście jest. Aby nadać naszej kuli odpowiedni rozmiar, trzeba zdefiniować jej promień za pomocą atrybutu radius, a kolor nadajemy atrybutem color:

<a-sphere radus="100" color="#258bd6"></a-sphere>

Jest tylko jeden problem… gdzie jest ta kula? Jeżeli zaczniemy przemieszczać się z pomocą przycisku myszy to w końcu ją znajdziemy, ale nie jest to zbyt wygodne. Stwórzmy zatem element kamery, który będzie „spoglądał” na nasz obiekt:

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>A-Frame hello world</title>
    </head>
    <body>
        <a-scene>
            <a-sphere radius="100" color="#258bd6"></a-sphere>
            <a-camera position="0 0 500"></a-camera>
        </a-scene>
        <script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
    </body>
</html>

I to by było na tyle. Więcej o pozycjonowaniu elementów opowiem w kolejnych wpisach. Powyższy przykład możecie zobaczyć tutaj. Odpalając go na własnej maszynie należy pamiętać o tym żeby postawić sobie lokalny serwer WWW.