Three.js, zobrazování HUD prvků

Lišta kompasu

Vzhled a chování

Lišta kompasu je umístěna ve středu horní části okna. Na horní části lišty se nachází znaky N, S, W a E. Tyto znaky reprezentují světové strany. Mezi těmito znaky je lišta dále rozdělena na dílky, které jsou označeny pomocí čísel pod nimi. Tato čísla slouží pro přesnější určení směru pohledu hráče a nabývají hodnot od 0 do 360, kdy hodnoty 0, 90, 180 a 270 jsou zastoupeny zmíněnými znaky světových stran. Hodnoty jsou stupňovány po 15. V nejvrchnější části lišty se nachází trojúhelníkový ukazatel indikující aktuální směr pohledu. Na liště jsou dále umístěny dvě červené značky, které zobrazují aktuální polohu červených krychlí ve scéně. Při pohybu kamerou značka ukazuje vždy směr, ve kterém se nepřátelé (krychle) nachází. Pokud dojde k situaci, kdy je nepřítel mimo pohled kamery, jeho značka zůstává v rohu lišty tak, aby uživatel měl povědomí o poloze nepřítele.

See the Pen HUD_Compass_Bar_Enemies by Filip Mička (@Filda360) on CodePen.

Ukázka výsledného HUD prvku zobrazující lištu kompasu v CodePen

Popis implementace

Lišta kompasu byla realizována pomocí objektu kamery ve scéně v kombinaci s geometrickým útvarem kružnice, která je umístěna nad kamerou. Tato kružnice je dále doplněna o úsečky, které znázorňují stupně.

Nejprve byla vytvořena samotná kružnice. Velikost kružnice je dána proměnnou circleRadius. Při běžném vytvoření kružnice pomocí CircleGeometry je uvnitř kružnice vidět její segmentace. V tomto případě se hodí spíše kružnice bez viditelné segmentace, proto je nutné ji vytvořit pomocí bodů této kružnice, ze kterých je následně vytvořena křivka dané kružnice.

Pomocí funkce createLineOnCompass byly vytvořeny hlavní zelené úsečky na kružnici. Následně v cyklu byly vytvořeny úsečky vedlejší segmentace, které jsou zbarveny bíle. V cyklu je zahrnuta podmínka, aby nedošlo k vytvoření této vedlejší segmentace kružnice přes hlavní segmentaci. Poloha úsečky je určena pomocí výpočtu polohy bodu na kružnici (obrázek níže)

Výpočet polohy bodu na kružnici

S těmito úsečkami jsou dále na kružnici vytvářeny nápisy v podobě číslic označující stupně a také zkratky světových stran (N, S, E, W). Nápisy jsou realizovány pomocí objektů třídy Sprite (popsáno v kapitole o kontextových informacích) a vytvářeny ve funkci createTextSprite.

Vše je následně předáno metodou add objektu kamery a umístěno do pozice nad kameru.

Pomocí funkce createTextSprite je také objektu kamery předán znak Δ, který je otočen o 180 stupňů, aby působil jako šipka ukazující na hodnoty stupňů na liště.

Ve funkci animate je při každém renderování snímku nastavena kružnici rotace dle aktuálního směru pohledu kamery. Princip funkce calculateAngle je vysvětlen v kapitole o kompasu, kde bylo využito stejné funkce. Rozdíl je pouze v jednotkách, kdy zde pro rotaci kružnice (objekt finalCircle) jsou vyžadovány radiány (bez vynásobení 180/π) a směr rotace je opačný, proto není u návratové hodnoty funkce záporné znaménko.

Funkce drawEnemyMarks slouží k prvotnímu vykreslení značek nepřátel. Ve funkci je v cyklu pro každý objekt ze skupiny enemies volána funkce createEnemyMark. Zde je vytvořena samotná značka nepřítele. Značky nepřátel jsou přidány do skupiny enemyMarks a ta je následně předána objektu kružnice.

Pomocí funkce refreshEnemyMarks jsou aktualizovány polohy ukazatelů nepřátel. Funkce je volána ve funkci animate při obnově snímku. Ve funkci je nejprve pomocí metody getWorldDirection vypočítán vektor směru pohledu kamery a tento vektor je uložen do proměnné vectorCamera. Dále je v cyklu pro každý objekt ze skupiny enemies vypočítán vektor směřující od kamery k objektu. Tento vektor je uložen do proměnné vectorEnemyToCamera. Vektory jsou reprezentovány třídou Vector3. Oba tyto vektory mají souřadnici Y nastavenou na hodnotu 0. Tato hodnota je pro výpočet nepodstatná, jelikož je počítán úhel mezi vektory v rovině osy X a Z. Funkcí angleTo je vypočítán úhel mezi těmito dvěmi vektory. Pomocí tohoto samotného údaje by nebylo možné určit, zda objekt leží vlevo, či vpravo od kamery.

Nákres použitých vektorů

Jestli objekt leží vlevo, či vpravo od kamery je možné zjistit vektorovým součinem. Tento údaj je ve funkci uložen do proměnné direction a v podmínce je podle této proměnné rozhodnuto, zda bude hodnota úhlu kladná nebo záporná. V dalších podmínkách je následně kontrolováno, zda absolutní hodnota úhlu nepřesáhla 60 stupňů. Pokud hodnota přesáhne 60 stupňů, je hodnota úhlu změněna na 60 případně -60 pro opačný směr. Tato korekce je nutná, aby byl ukazatel nepřátel vždy viditelný i pokud nepřítel opustí zorné pole kamery. Výsledná poloha značky nepřítele na kružnici je vypočítána odečtením vypočítaného úhlu mezi vektory od úhlu směru pohledu kamery. Tato hodnota je uložena do proměnné finalAngleOnCircle. Nakonec je pomocí tohoto finálního úhlu vypočítána poloha bodu na kružnici a do této polohy je přemístěna značka nepřítele. Nalezení konkrétní značky nepřítele je provedeno v cyklu dle vlastnosti name.

Použité zdroje

[1] Zdrojový kód pro vykreslení textu pomocí třídy Sprite (funkce createTextSprite): https://jsfiddle.net/jj26qz2L/15/

[2] Zdrojový kód pro vytvoření kružnice bez segmentace, upraveno autorem: https://discourse.threejs.org/t/shift-vertices-of-circle-geometry-not-working/26664