Uzamčení ukazatele myši a ovládání first-person střílečky

Pomocí připravovaného API Pointer Lock může webová stránka uzamknout ukazatel uživatelovy myši. Primární použití se nabízí pro hry v prostředí webového prohlížeče. Na tom si dnes uzamčení ukazatele předvedeme.

Letos se u W3C objevila nová specifikace s názvem Pointer Lock (v překladu „uzamčení ukazatele“), která webovým stránkám umožní ovládat kurzor myši podobně, jak to již léta umí Adobe Flash, tj. skrýt kurzor myši v definované oblasti webové stránky, odchytávat jeho pohyb a případně implementovat si kurzor „po svém“.

Jedná se o akci potenciálně nebezpečnou, proto si k ní prohlížeč vyžádá uživatelovo povolení. První implementace již najdeme ve Firefoxu a Chrome. Specifikace se ale stále ještě vyvíjí.

Následuje překlad článku Pointer Lock and First Person Shooter Controlshtml5rocks.com, který uzamčení ukazatele představuje. Je zveřejněn pod licencí CC BY 3.0.

Úvod

Pointer Lock API pomůže v implementaci ovládání first-person stříleček do prohlížečů. Bez něj by uživatelův kurzor myši mohl kupříkladu narazit na pravý okraj obrazovky a další pohyb doprava by již nebyl možný – pohled by se již dále neposunoval doprava a hráč by nemohl pronásledovat zloduchy a pálit na ně ze svého kulometu. Místo toho by byl zastřelen a navíc ještě frustrován. S uzamčeným ukazatelem k ničemu takovému nemůže dojít.

Pointer Lock API vaší aplikaci umožní:

  • přímý přístup k datům myši včetně relativních pohybů myši
  • směrovat všechny události myši na specifický prvek

Vedlejším efektem je skrytí ukazatele myši, které vám umožní nakreslit si vlastní ukazatel vhodný pro vaši aplikaci nebo ukazatel kompletně ukrýt, takže uživatel může pomocí myši hýbat pohledem hráče. Relativní pohyby myši značí rozdíl současného umístění ukazatele oproti předchozímu umístění bez ohledu na jeho absolutní pozici. Pokud se ukazatel posune z (640, 480) do (520, 490), tak jeho relativní pohyb je (-120, 10). V prvním příkladu si budeme údaje o relativním pohybu zobrazovat.

Tento tutoriál pokrývá dvě témata: základy aktivace a zpracování událostí pro uzamčení ukazatele a dále implementaci základního ovládání  pro first-person střílečku. Až tento článek dočtete, budete umět používat uzamčení ukazatele a implementovat ovládání stylu Quake ve vaší vlastní prohlížečové hře.

Základy

Detekce

Zda uživatelův prohlížeč uzamčení ukazatele podporuje, zjistíte pomocí vlastnosti pointerLockElement (nebo analogické vlastnosti s vendor prefixem) v objektu document. Ukázka:

var havePointerLock = 'pointerLockElement' in document ||
    'mozPointerLockElement' in document ||
    'webkitPointerLockElement' in document;

Aktuálně uzamčení ukazatele podporuje jen Firefox a Chrome. V Opeře ani IE zatím podporu nenajdete.

Aktivace

Aktivace uzamčení ukazatele vyžaduje dva kroky. Nejprve vaše aplikace požádá o uzamčení ukazatele na konkrétním prvku. Až k tomu dá uživatel svolení, vyvolá se událost pointerlockchange. Uživatel může uzamčení ukazatele kdykoliv ukončit klávesou Escape. Také vaše aplikace může uzamčení ukazatele ukončit pomocí API. Když je ukazatel opět uvolněn, vyvolá se událost pointerlockchange.

element.requestPointerLock = element.requestPointerLock ||
                 element.mozRequestPointerLock ||
                 element.webkitRequestPointerLock;
// Požádej prohlížeč o uzamčení ukazatele
element.requestPointerLock();

// Požádej prohlížeč o uvolnění ukazatele
document.exitPointerLock = document.exitPointerLock ||
               document.mozExitPointerLock ||
               document.webkitExitPointerLock;
document.exitPointerLock();

A to je kompletní kód. Když prohlížeč uzamkne ukazatel myši, zobrazí se zpráva informující uživatele o uzamčení s tím, že uzamčení lze ukončit klávesou Escape.

Upozornění na uzamčení ukazatele v Chromu.

Obsluha událostí

Vaše aplikací musí obsluhovat dvě události. Tou první je pointerlockchange, která se vyvolá při změně stavu uzamčení ukazatele. Tou druhou je mousemove, která se vyvolá při jakémkoliv pohybu myši.

// Nastav obsluhu událostí uzamčení ukazatele
document.addEventListener('pointerlockchange', changeCallback, false);
document.addEventListener('mozpointerlockchange', changeCallback, false);
document.addEventListener('webkitpointerlockchange', changeCallback, false);

// Nastav obsluhu události pro pohyb myši
document.addEventListener("mousemove", this.moveCallback, false);

Pozn.: Nezapomeňte na prefixy prohlížečů, pokud chcete být kompatibilní napříč prohlížeči, musíte obsloužit události pointerlockchange, mozpointerlockchangewebkitpointerlockchange.

Uvnitř vašeho callbacku pro pointerlockchange musíte ověřit, zda byl ukazatel uzamčen nebo naopak uvolněn. Je to snadné: zkontrolujte, zda document.pointerLockElement odkazuje na prvek, na jehož uzamčení čekáte. Pokud ano, tak vaše aplikace ukazatel úspěšně uzamkla. Když ne, ukazatel byl buď uživatelem nebo kódem vaší aplikace uvolněn.

if (document.pointerLockElement === requestedElement ||
  document.mozPointerLockElement === requestedElement ||
  document.webkitPointerLockElement === requestedElement) {
  // Ukazatel byl právě uzamčen
  // Aktivujme obsluhu události mousemove
  document.addEventListener("mousemove", this.moveCallback, false);
} else {
  // Ukazatel byl právě uvolněn
  // Deaktivujme obsluhu události mousemove
  document.removeEventListener("mousemove", this.moveCallback, false);
  this.unlockHook(this.element);
}

Po uzamčení ukazatele zůstávají clientX, clientY, screenX a screenY konstantní. Naopak movementX a movementY obsahují počet pixelů, o které se ukazatel myši posunul od vyvolání předchozí události. V pseudo-kódu:

event.movementX = currentCursorPositionX - previousCursorPositionX;
event.movementY = currentCursorPositionY - previousCursorPositionY;

V callbacku pro mousemove získáme relativní pohyb ukazatele z vlastností movementX a movementY objektu události.

function moveCallback(e) {
  var movementX = e.movementX ||
      e.mozMovementX          ||
      e.webkitMovementX       ||
      0,
  movementY = e.movementY ||
      e.mozMovementY      ||
      e.webkitMovementY   ||
      0;
}

Pozn: Ano, i vlastnosti pro relativní pohyb ukazatele mají své vendor prefixy.

Interaktivní příklad

Je čas na interaktivní příklad. Když kliknete na box ‚Click me!‘, váš ukazatel myši bude uzamčen a uvidíte relativní souřadnice pohybu ukazatele myši. Data nejsou nijak dál zpracovávána, můžete si tak všimnout, že počátek souřadnic je vlevo nahoře a kladný pohyb směřuje doprava a dolů.

Interaktivní příklad ‚Click me!‘ najdete v originálním článku (hned NAD nadpisem Catching errors).

Odchytávání chyb

Pokud nastane chyba při uzamykání nebo uvolňování ukazatele, vyvolá se událost pointerlockerror. Tato událost neobdrží žádná data.

document.addEventListener('pointerlockerror', errorCallback, false);
document.addEventListener('mozpointerlockerror', errorCallback, false);
document.addEventListener('webkitpointerlockerror', errorCallback, false);

Je vyžadován celoobrazovkový režim?

Původně bylo uzamčení ukazatele svázáno s FullScreen API. Pokud měl být na prvku uzamknut ukazatel myši, musel se prvek nacházet v celoobrazovkovém režimu. To už ovšem neplatí a k uzamčení ukazatele může dojít na jakémkoliv prvku, ať již je v celoobrazovkovém režimu nebo není.

Ukázka ovládání first-person střílečky

Nyní, když máme ukazatel uzamčen a obsluhujeme potřebné události, nastal čas na praktickou ukázku. Zachtělo se vám někdy vědět, jak funguje ovládání v Quaku? Zapněte si pásy, a jdeme na to!

Ovládání first-person střílečky se skládá ze čtyř základních prvků:

  • Pohyb vpřed a vzad podél aktuálního vektoru
  • Pohyb vlevo a vpravo podél kolmého vektoru
  • Rotace vodorovného úhlu pohledu (doleva a doprava)
  • Rotace svislého úhlu pohledu (nahoru a dolů)

Hra implementující takové ovládání potřebuje pouze tři údaje: pozici kamery, vektor pohledu kamery a konstantní vektor směrem nahoru. Vektor směrem nahoru je vždy (0, 1, 0). Všechny čtyři zmíněné pohyby pracují s pozicí kamery a s vektorem pohledu kamery.

Pohyb

Začneme pohybem. V našem demu je pohyb mapován na klávesy W, A, S a D. Klávesy W a S pohybují kamerou vpřed a vzad a klávesy A a D pohybují kamerou doleva a doprava. Pohyb kamery vpřed a vzad je jednoduchý:

// Směr vpřed
var forwardDirection = vec3.create(cameraLookVector);
// Rychlost
var forwardSpeed = dt * cameraSpeed;
// Vpřed nebo vzad dle stisknuté klávesy
var forwardScale = 0.0;
forwardScale += keyState.W ? 1.0 : 0.0;
forwardScale -= keyState.S ? 1.0 : 0.0;
// Velikost pohybu
vec3.scale(forwardDirection, forwardScale * forwardSpeed);
// Přičti ho k pozici kamery
vec3.add(cameraPosition, forwardDirection);

Úkroky vlevo a vpravo vyžadují kolmý směr. Ten spočítáme takhle:

// Směr úkroku
var strafeDirection = vec3.create();
vec3.cross(cameraLookVector, cameraUpVector, strafeDirection);

Jakmile máte směr úkroku, implementace pohybu vlevo a vpravo je již stejná, jako pohyb vpřed a vzad.

V prvním demu je implementován pouze pohyb (interaktivní demo najdete ho v původním článku), kompletní demo přijde později.

Pokračujme rotací pohledu kamery.

Yaw

Yaw nebo také vodorovná rotace pohledu kamery je rotací okolo konstantního vektoru nahoru. Níže najdete obecný kód pro rotaci pohledu kamery okolo libovolné osy. Vytvoří se v něm kvaternion reprezentující rotaci o deltaAngle  radiánů okolo axis a ten je použit k rotaci vektoru s pohledem kamery:

// Získej vektor pohledu kamery
var frontDirection = vec3.create();
vec3.subtract(this.lookAtPoint, this.eyePoint, frontDirection);
vec3.normalize(frontDirection);
var q = quat4.create();
// Vytvoř kvaternion
quat4.fromAngleAxis(deltaAngle, axis, q);
// Rotuj vektorem pohledu kamery
quat4.multiplyVec3(q, frontDirection);
// Aktualizuj vektor s pohledem kamery
this.lookAtPoint = vec3.create(this.eyePoint);
vec3.add(this.lookAtPoint, frontDirection);

Viz demo, ve kterém funguje pouze vodorovná rotace.

Pitch

Pitch neboli svislá rotace pohledu kamery se implementuje podobně, ovšem rotace neprobíhá okolo konstantního vektoru nahoru, ale okolo kolmého vektoru, který jsme použili pro úkroky vlevo vpravo. Nejprve tedy spočteme tento vektor a následně kolem něj otočíme vektor pohledu kamery.

Viz demo, ve kterém funguje pouze svislá rotace.

A nyní všechno dohromady

kompletním demu funguje uzamčení kurzoru spolu s ovládáním first-person střílečky. Kliknutím ho aktivujete.

Shrnutí

Pointer Lock API vám umožní ovládat kurzor myši. Pokud vytváříte hry pro webový prohlížeč, vaši uživatelé to budou milovat, jelikož už nebudou zbytečně zastřeleni, jen protože v zápalu nadšení ujeli myší příliš daleko a hra přestala reagovat. Používání je snadné:

  • Odchytávejte událost pointerlockchange a sledujte tak stav uzamčení ukazatele
  • Vyžádejte si uzamčení ukazatele na konkrétním prvku
  • Odchytávejte událost mousemove

Další dema

Odkazy

Martin Hassman založil a vede magazín Zdroják. Absolvoval VŠCHT Praha. Byl u založení projektu CZilla (dnes už nepamatujete, nevadí). Stavěl mosty a metal cestu pro HTML5 (to tu ještě máme). V GUG.cz organizoval akce pro vývojáře (a jestli neumřeli, kódují si dodnes…).

Komentáře: 8

Přehled komentářů

https://filip-jirsak.mojeid.cz/#hgWiJuhCVe Aktivace v Chrome
Martin Hassman Re: Aktivace v Chrome
https://filip-jirsak.mojeid.cz/#hgWiJuhCVe Re: Aktivace v Chrome
Martin Hassman Re: Aktivace v Chrome
Xjmeno363-MEGATROLL špatné
Kokeš Re: špatné
Martin Hassman Re: špatné
robot Re: špatné
Zdroj: https://www.zdrojak.cz/?p=3709