Blocco del puntatore e controlli per sparatutto in prima persona

Mario Bianchi
John McCutchan

Introduzione

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

L'API Pointer Lock consente all'applicazione di:

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

Come effetto collaterale dell'attivazione del blocco del puntatore, il cursore del mouse viene nascosto e puoi scegliere di tracciare un puntatore specifico per l'applicazione, se lo desideri, oppure 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 spostava da (640, 480) a (520, 490) il movimento relativo era (-120, 10). Di seguito è riportato un esempio interattivo che mostra i delta della posizione del mouse non elaborati.

Questo tutorial tratta due argomenti: gli aspetti pratici dell'attivazione e dell'elaborazione degli eventi di blocco del puntatore e l'implementazione dello schema di controllo degli sparatutto in prima persona. Esatto, al termine della lettura di questo articolo saprai come usare il blocco del puntatore e implementare controlli in stile Quake per il tuo gioco personale su browser.

Compatibilità del browser

Supporto dei browser

  • 37
  • 13
  • 50
  • 10.1

Fonte

Meccanismi di blocco del puntatore

Rilevamento delle funzionalità

Per determinare se il browser dell'utente supporta il blocco del puntatore, devi verificare la presenza di pointerLockElement o di una versione con prefisso del fornitore nell'oggetto 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 progressivamente dal blocco del puntatore. Quando il blocco del puntatore viene annullato, si attiva 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();

Serve solo il codice riportato sopra. Quando il browser blocca il puntatore, viene visualizzato un popup che informa l'utente che l'applicazione ha bloccato il puntatore e viene chiesto di annullare l'operazione premendo il tasto "Esc".

Barra delle informazioni del Blocco puntatore in Chrome.
Barra delle informazioni per il Blocco 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 una modifica dello stato di blocco del puntatore. Il secondo è mousemove, che si attiva quando 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 pointerlockchange devi controllare se il puntatore è appena stato bloccato o sbloccato. Determinare se il blocco del puntatore era abilitato è semplice: verifica se document.pointerLockElement è uguale all'elemento per cui è stato richiesto il blocco del puntatore. In questo caso, l'applicazione ha bloccato correttamente 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 è attivato, i valori clientX, clientY, screenX e screenY rimangono invariati. I grafici movementX e movementY vengono aggiornati con il numero di pixel che il puntatore avrebbe spostato dalla pubblicazione dell'ultimo evento. In pseudocodice:

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

All'interno del callback mousemove, i dati relativi al movimento del mouse 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;
}

Rilevare errori

Se viene generato un errore entrando o uscendo dal puntatore, blocca l'evento pointerlockerror. Nessun dato associato all'evento.

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

Occorre la modalità a schermo intero?

Originariamente il blocco del puntatore era legato all'API FullScreen. Questo significa che un elemento deve essere in modalità a schermo intero prima di poter avere il puntatore bloccato. Questo non è più vero e il blocco del puntatore può essere utilizzato per qualsiasi elemento dell'applicazione a schermo intero o meno.

Esempio di controlli sparatutto in prima persona

Ora che il blocco del puntatore è attivato e la ricezione degli eventi, è il momento di fare un esempio pratico. Hai mai desiderato sapere come funzionano i controlli di Quake? Legati perché sto per spiegarli in codice!

I controlli degli sparatutto in prima persona si basano su quattro meccanismi principali:

  • Spostarsi avanti e indietro lungo il vettore del look corrente
  • Spostamento a sinistra e a destra lungo il vettore di navigazione corrente
  • Rotazione dello scarto visivo (a sinistra e a destra)
  • Rotazione dell'inclinazione del campo visivo (verso l'alto e verso il basso)

Un gioco che implementa questo schema di controllo richiede solo tre tipi di dati: posizione della videocamera, vettore di visualizzazione della videocamera e vettore costante costante. Il vettore up è sempre (0, 1, 0). Tutti e quattro i meccanismi descritti sopra manipolano semplicemente la posizione e il vettore dell'aspetto della videocamera in modi diversi.

Movimento

Prima di tutto c'è il movimento. Nella demo seguente il movimento è mappato ai tasti W, A, S e D standard. I tasti W e S guidano la fotocamera in 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 sinistra e a destra richiede una direzione di circonvenzione. La direzione della differenza può essere calcolata utilizzando il prodotto incrociato:

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

Una volta ottenuta la direzione di spostamento, implementare il movimento del mirino equivale a spostarsi in avanti o indietro.

Ora c'è la rotazione della visualizzazione.

Scarto

L'imbarazzo o la rotazione orizzontale dell'inquadratura della fotocamera è solo una rotazione attorno al vettore costante verso l'alto. Di seguito è riportato il codice generale per ruotare il vettore di aspetto della fotocamera attorno a un asse arbitrario. Funziona costruendo un quaternione che rappresenta la rotazione di deltaAngle radianti intorno a axis, quindi utilizza il quaternione per ruotare il vettore di aspetto della 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);

Avanza proposte

L'implementazione dell'inclinazione o della rotazione verticale dell'inquadratura della fotocamera è simile, ma invece di una rotazione attorno al vettore verso l'alto si applica una rotazione intorno al vettore della strategia. Il primo passaggio consiste nel calcolare il vettore della strategia e quindi ruotare il vettore di aspetto della fotocamera attorno a questo asse.

Riepilogo

L'API Pointer Lock ti consente di assumere il controllo del cursore del mouse. Se stai creando giochi web, i tuoi giocatori lo adoreranno quando smettono di essere frammentati perché hanno spostato con entusiasmo il mouse fuori dalla finestra e il tuo gioco ha smesso di ricevere aggiornamenti del mouse. È facile da usare:

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

Demo esterne

Riferimenti