I już połowa maja minęła, czas biegnie nieubłaganie, a ja akurat mam teraz milion rzeczy na głowie. Broni jednak nie składam i pokażę dziś jak zbudowałem sobie tarczę i jak rozwiązałem problem punktacji.
Do tej pory moim celem była encja kuli zawieszona w przestrzeni, animująca się z lewej do prawej i na odwrót. Animacje chciałbym zachować, natomiast kule muszę zastąpić jakimś sensownym obiektem, który chociaż trochę będzie przypominał normalną tarczę strzelniczą. Cel będzie grupą encji i będzie się składał z następujących elementów:
- Belka służąca jako „taśma” po której będzie poruszała się tarcza
- Belka „zwisająca” z taśmy, służąca do trzymania tarczy
- Białe płótno w postaci cienkiego prostopadłościanu imitujące ciało
- Białe płótno w postaci okręgu imitujące głowę
- Pierścienie z różnych kolorów szarości imitujące wartości na tarczy i umiejscowione na płótnach
I teraz w kolejności powyższe elementy będą w kodzie wyglądały następująco:
1. Taśma:
<Entity primitive="a-box" color="#575757" depth="1" height="0.5" width="25" position="0 4.75 -8"> </Entity>
Belka górna to po prostu prostopadłościan zbudowany z pomocą encji <a-box>
. Oprócz swojej wizualnej funkcji, będzie również spełniał rolę kontenera dla pozostałych encji. Pozycjonowanie elementów wewnętrznych odbywać się zatem będzie względem pozycji tej belki, nie względem środka sceny.
2. Encja animacyjna i belka dla tarczy:
<Entity position="10 -1.6 0" animation={{ property: "position", dir: "alternate", dur: 4000, easing: "easeInOutCubic", loop: true, to: "-10 -1.6 0" }}></Entity>
Powyższa encja nie ma żadnej geometrii poza pozycją, wyróżnia ją jednak komponent animacyjny. To właśnie ta encja przejmie animację po starym prototypie tarczy i również będzie grupą dla wewnętrznych obiektów. Dzięki temu będą się one poruszały wszystkie razem, nie trzeba będzie animować ich osobno (to nawet brzmi absurdalnie). Belka pionowa to z kolei taki prosty obiekt:
<Entity primitive="a-box" color="#333333" depth="0.1" height="2.5" width="0.2"></Entity> <Entity primitive="a-sphere" radius="0.4" color="#333333" position="0 1.5 0"></Entity>
Oprócz obiektu typu a-box, pojawia się tu też a-sphere, która „symuluje” uchwyt belki pionowej do poziomej. Taki tam smaczek graficzny ha!
3. Płótno główne tarczy:
<Entity primitive="a-box" color="#fafafa" depth="0.02" height="1.66" width="1.38" position="0 -0.9 0.08"></Entity>
I kolejny a-box. Tym razem będzie to właściwy element tarczy i przy okazji grupa dla pierścieni punktacyjnych.
4. Płótno imitujące głowę sylwetki na tarczy:
<Entity primitive="a-circle" radius="0.4" color="#fafafa" position="0 1.15 0"> </Entity>
Tym razem użyłem encji <a-circle>
, rysuje ona proste dwuwymiarowe koło na scenie. Encja ta została umieszczona wewnątrz encji sylwetki.
5. Pierścienie punktacyjne:
<Entity events={{ click: () => { console.log("25"); } }} primitive="a-ring" color="#666" radius-inner="0.19" radius-outer="0.3" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("50"); } }} primitive="a-circle" radius="0.08" color="#222" position="0 0 0.04"></Entity>
Powyższe dwie encje umieszczone zostały w elemencie głowy. Pierścień zewnętrzny zbudowany z pomocą <a-ring>
, który wypisuje na konsoli 25 przy kliknięciu w niego, oraz koło, które po kliknięciu wypisuje 50. Te liczby to oczywiście punktacja. Podobną rzecz wykonuję dla encji sylwetki:
<Entity primitive="a-circle" radius="0.6" color="#ccc" position="0 0 0.04"> <Entity events={{ click: () => { console.log("5"); } }} primitive="a-ring" color="#666" radius-inner="0.36" radius-outer="0.46" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("15"); } }} primitive="a-ring" color="#444" radius-inner="0.19" radius-outer="0.26" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("30"); } }} primitive="a-circle" radius="0.1" color="#222" position="0 0 0.04"></Entity> </Entity>
W powyższym przykładzie znajdują się dwa pierścienie z punktami 5 i 15, oraz środek sylwetki: koło z 30 punktami za strzał. I to by było na tyle jeżeli chodzi o tarczę! No dobrze, może nie do końca, bo przecież nikt nie chce patrzeć w konsole czy trafił. Zarządzaniem punktacją zajmę się w kolejnym wpisie.
Można się zastanawiać jak długo wybierałem wartości dla chociażby pozycji elementów. Otóż z A-Frame jest to bardzo proste, ponieważ dysponuje on wbudowanym A-Frame inspectorem. Za pomocą tego narzędzia można wizualnie przenosić obiekty encji w trzech płaszczyznach, zmieniać właściwości obiektów, a nawet dogrywać komponenty z A-Frame register. O to jak prezentuje się kawałek mojej sceny w A-Frame inspectorze:
A tak prezentuje się cały kod elementu tarczy:
import { Entity } from "aframe-react"; import React from "react"; const TargetComponent = () => ( <Entity primitive="a-box" color="#575757" depth="1" height="0.5" width="25" position="0 4.75 -8"> <Entity position="10 -1.6 0" animation={{ property: "position", dir: "alternate", dur: 4000, easing: "easeInOutCubic", loop: true, to: "-10 -1.6 0" }}> <Entity primitive="a-box" color="#333333" depth="0.1" height="2.5" width="0.2"></Entity> <Entity primitive="a-sphere" radius="0.4" color="#333333" position="0 1.5 0"></Entity> <Entity primitive="a-box" color="#fafafa" depth="0.02" height="1.66" width="1.38" position="0 -0.9 0.08"> <Entity primitive="a-circle" radius="0.4" color="#fafafa" position="0 1.15 0"> <Entity events={{ click: () => { console.log("25"); } }} primitive="a-ring" color="#666" radius-inner="0.19" radius-outer="0.3" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("50"); } }} primitive="a-circle" radius="0.08" color="#222" position="0 0 0.04"></Entity> </Entity> <Entity primitive="a-circle" radius="0.6" color="#ccc" position="0 0 0.04"> <Entity events={{ click: () => { console.log("5"); } }} primitive="a-ring" color="#666" radius-inner="0.36" radius-outer="0.46" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("15"); } }} primitive="a-ring" color="#444" radius-inner="0.19" radius-outer="0.26" position="0 0 0.04"></Entity> <Entity events={{ click: () => { console.log("30"); } }} primitive="a-circle" radius="0.1" color="#222" position="0 0 0.04"></Entity> </Entity> </Entity> </Entity> </Entity> ); export default TargetComponent;
Podsumowanie
Wpis nieco skromniejszy niż zwykle, ale mam już najważniejszy element na scenie. W kolejnym kroku zajmę się stanem gry i uzupełnieniem sceny o dodatkowe elementy.