Blocco del puntatore e controlli per sparatutto in prima persona

John McCutchan
John McCutchan

Introduzione

L'API Pointer Lock aiuta a implementare correttamente i controlli sparatutto in prima persona in un gioco per browser. Senza un movimento relativo del mouse, il cursore del giocatore potrebbe, ad esempio, colpire il bordo destro dello schermo e qualsiasi ulteriore movimento verso destra verrebbe scontato: la vista non avrebbe continuato a spostarsi verso destra e il giocatore non sarebbe in grado di inseguire i malintenzionati e colpirli con la sua mitragliatrice. Il giocatore si rompa e si sentirà frustrato. Con il blocco del puntatore, questo comportamento non ottimale non può verificarsi.

L'API Pointer Lock consente alla tua applicazione di:

  • Ottieni l'accesso a dati non elaborati del mouse, inclusi i movimenti relativi del mouse
  • Indirizza tutti gli eventi del mouse a un elemento specifico

A causa dell'attivazione del blocco del puntatore, il cursore del mouse viene nascosto per consentirti di scegliere se tracciare un puntatore specifico per l'applicazione o di lasciarlo nascosto in modo che l'utente possa spostare il frame con il mouse. Il movimento relativo del mouse è il delta della posizione del puntatore del mouse rispetto al frame precedente, indipendentemente dalla posizione assoluta. Ad esempio, se il puntatore del mouse si sposta da (640, 480) a (520, 490), lo spostamento relativo è stato (-120, 10). Di seguito è riportato un esempio interattivo che mostra i delta non elaborati della posizione del mouse.

Questo tutorial tratta due argomenti: le nozioni di base per attivare ed elaborare gli eventi di blocco del puntatore e implementare lo schema di controllo dello sparatutto in prima persona. Proprio così. Quando avrai finito di leggere questo articolo saprai come usare il blocco del puntatore e implementare i controlli in stile Quake per il tuo gioco per browser.

Compatibilità del browser

Supporto dei browser

  • Chrome: 37.
  • Edge: 13.
  • Firefox: 50.
  • Safari: 10.1.

Origine

Meccanismi di blocco del puntatore

Rilevamento delle caratteristiche

Per determinare se il browser dell'utente supporta il blocco del puntatore, devi controllare pointerLockElement o una versione con prefisso del fornitore nell'oggetto del documento. Nel codice:

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

Al momento il blocco del puntatore è disponibile solo in Firefox e Chrome. Opera e IE non lo supportano ancora.

In fase di attivazione

L'attivazione del blocco del puntatore è un processo in due passaggi. Innanzitutto l'applicazione richiede l'attivazione del blocco del puntatore per un elemento specifico e, subito dopo l'autorizzazione dell'utente, viene attivato un evento pointerlockchange. L'utente può annullare il blocco del puntatore in qualsiasi momento premendo il tasto Esc. L'applicazione può anche uscire in modo graduale dal blocco del puntatore. Quando il blocco del puntatore viene annullato, viene attivato un evento pointerlockchange.

element.requestPointerLock = element.requestPointerLock ||
                 element.mozRequestPointerLock ||
                 element.webkitRequestPointerLock;
// Ask the browser to lock the pointer
element.requestPointerLock();

// Ask the browser to release the pointer
document.exitPointerLock = document.exitPointerLock ||
               document.mozExitPointerLock ||
               document.webkitExitPointerLock;
document.exitPointerLock();

È sufficiente il codice riportato sopra. Quando il browser blocca il puntatore, viene visualizzato un fumetto che informa l'utente che l'applicazione ha bloccato il puntatore e che informa che è possibile annullare il puntatore premendo "Esc". chiave.

Barra delle informazioni del blocco del puntatore in Chrome.
Barra delle informazioni del blocco del puntatore in Chrome.

Gestione degli eventi

L'applicazione deve aggiungere listener per due eventi. Il primo è pointerlockchange, che si attiva ogni volta che si verifica un cambiamento nello stato di blocco del puntatore. Il secondo è mousemove, che si attiva ogni volta che il mouse viene spostato.

// Hook pointer lock state change events
document.addEventListener('pointerlockchange', changeCallback, false);
document.addEventListener('mozpointerlockchange', changeCallback, false);
document.addEventListener('webkitpointerlockchange', changeCallback, false);

// Hook mouse move events
document.addEventListener("mousemove", this.moveCallback, false);

All'interno del callback di pointerlockchange, devi controllare se il puntatore è appena stato bloccato o sbloccato. Determinare se il blocco del puntatore è stato abilitato è semplice: verifica se document.pointerLockElement è uguale all'elemento per cui è stato richiesto il blocco del puntatore. In questo caso, l'applicazione ha bloccato il puntatore. In caso contrario, il puntatore è stato sbloccato dall'utente o dal tuo codice.

if (document.pointerLockElement === requestedElement ||
  document.mozPointerLockElement === requestedElement ||
  document.webkitPointerLockElement === requestedElement) {
  // Pointer was just locked
  // Enable the mousemove listener
  document.addEventListener("mousemove", this.moveCallback, false);
} else {
  // Pointer was just unlocked
  // Disable the mousemove listener
  document.removeEventListener("mousemove", this.moveCallback, false);
  this.unlockHook(this.element);
}

Quando il blocco del puntatore è attivo, clientX, clientY, screenX e screenY rimangono invariati. movementX e movementY vengono aggiornati con il numero di pixel che il puntatore avrebbe spostato dalla pubblicazione dell'ultimo evento. Nello pseudocodice:

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

All'interno del callback mousemove, i dati sul movimento relativo del mouse del callback possono essere estratti dai campi movementX e movementY dell'evento.

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

Individuare gli errori

Se viene generato un errore entrando o uscendo dal puntatore, viene attivato l'evento pointerlockerror. Nessun dato allegato a questo evento.

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

Schermo intero necessario?

In origine, il blocco del puntatore era legato all'API FullScreen. Ciò significa che un elemento deve essere in modalità a schermo intero prima di poter bloccare il puntatore. Questo non è più vero e il blocco del puntatore può essere utilizzato o meno per qualsiasi elemento della tua applicazione a schermo intero.

Esempio di controlli per gli sparatutto in prima persona

Ora che abbiamo abilitato il blocco del puntatore e stiamo ricevendo eventi, possiamo fare un esempio pratico. Hai mai desiderato sapere come funzionano i controlli di Quake? Allacciati le cinture perché sto per spiegarglielo con il codice!

I controlli degli sparatutto in prima persona si basano su quattro meccaniche fondamentali:

  • Spostarsi avanti e indietro lungo il vettore look corrente
  • Spostamento a sinistra e a destra lungo il vettore di corda corrente
  • Rotazione dello scarto della vista (sinistra e destra)
  • Rotazione del tono della vista (verso l'alto e verso il basso)

Un gioco che implementa questo schema di controllo richiede solo tre dati: la posizione della videocamera, il vettore dell'aspetto della videocamera e il vettore costante in alto. Il vettore verso l'alto è sempre (0, 1, 0). Tutte e quattro le meccaniche precedenti si limitano a modificare la posizione e il vettore dell'aspetto della fotocamera in modi diversi.

Movimento

Il primo è il movimento. Nella demo seguente il movimento è mappato ai tasti W, A, S e D standard. I tasti W e S spostano la fotocamera avanti e indietro. mentre i tasti A e D guidano la fotocamera verso sinistra e verso destra. Spostare la fotocamera avanti e indietro è semplice:

// Forward direction
var forwardDirection = vec3.create(cameraLookVector);
// Speed
var forwardSpeed = dt * cameraSpeed;
// Forward or backward depending on keys held
var forwardScale = 0.0;
forwardScale += keyState.W ? 1.0 : 0.0;
forwardScale -= keyState.S ? 1.0 : 0.0;
// Scale movement
vec3.scale(forwardDirection, forwardScale * forwardSpeed);
// Add scaled movement to camera position
vec3.add(cameraPosition, forwardDirection);

Il mirino a destra e a sinistra richiede la direzione del mirino. La direzione del mitra può essere calcolata utilizzando il prodotto incrociato:

// Strafe direction
var strafeDirection = vec3.create();
vec3.cross(cameraLookVector, cameraUpVector, strafeDirection);

Una volta individuata la direzione del mirino, implementare il movimento del mirino equivale a spostarlo avanti o indietro.

Ora devi ruotare la visualizzazione.

Yaw

Lo scarto, o la rotazione orizzontale della visualizzazione della videocamera, è semplicemente una rotazione intorno al vettore verso l'alto. Di seguito è riportato il codice generale per ruotare il vettore di effetto della fotocamera attorno a un asse arbitrario. Funziona costruendo un quaternione che rappresenta la rotazione dei radianti deltaAngle intorno a axis e poi utilizza il quaternione per ruotare il vettore di tipo fotocamera:

// Extract camera look vector
var frontDirection = vec3.create();
vec3.subtract(this.lookAtPoint, this.eyePoint, frontDirection);
vec3.normalize(frontDirection);
var q = quat4.create();
// Construct quaternion
quat4.fromAngleAxis(deltaAngle, axis, q);
// Rotate camera look vector
quat4.multiplyVec3(q, frontDirection);
// Update camera look vector
this.lookAtPoint = vec3.create(this.eyePoint);
vec3.add(this.lookAtPoint, frontDirection);

Tono

L'implementazione della rotazione verticale o verticale della visualizzazione della videocamera è simile, ma invece di una rotazione attorno al vettore verso l'alto si applica una rotazione intorno al vettore di corda. Il primo passaggio consiste nel calcolare il vettore di mitraglie e poi ruotare il vettore dell'aspetto della fotocamera attorno a quell'asse.

Riepilogo

L'API Pointer Lock ti consente di assumere il controllo del cursore del mouse. Se stai realizzando giochi web, i giocatori apprezzeranno quando smettono di agitare, perché hanno spostato con entusiasmo il mouse fuori dalla finestra e il tuo gioco ha smesso di ricevere aggiornamenti del mouse. L'utilizzo è semplice:

  • Aggiungi il listener di eventi pointerlockchange per monitorare lo stato del blocco del puntatore
  • Richiedere il blocco del puntatore per un elemento specifico
  • Aggiungi listener di eventi mousemove per ricevere aggiornamenti

Demo esterne

Riferimenti