Zeigersperre und Ego-Shooter-Steuerung

John McCutchan
John McCutchan

Einführung

Die Pointer Lock API hilft dabei, die Steuerung von Ego-Shootern in einem Browserspiel richtig zu implementieren. Ohne relative Mausbewegung könnte der Cursor des Spielers beispielsweise den rechten Bildschirmrand erreichen und alle weiteren Bewegungen nach rechts würden ignoriert. Die Ansicht würde nicht weiter nach rechts schwenken und der Spieler könnte die Bösewichte nicht verfolgen und mit seinem Maschinengewehr beschießen. Der Spieler wird getötet und frustriert. Mit der Maussperre kann dieses suboptimale Verhalten nicht auftreten.

Mit der Pointer Lock API können Sie Folgendes in Ihrer Anwendung tun:

  • Zugriff auf Rohmausdaten, einschließlich relativer Mausbewegungen
  • Alle Mausereignisse an ein bestimmtes Element weiterleiten

Wenn Sie die Maussperre aktivieren, wird der Mauszeiger ausgeblendet. Sie können dann einen anwendungsspezifischen Mauszeiger zeichnen oder den Mauszeiger ausgeblendet lassen, damit der Nutzer den Frame mit der Maus verschieben kann. Die relative Mausbewegung ist die Deltaposition des Mauszeigers vom vorherigen Frame, unabhängig von der absoluten Position. Wenn sich der Mauszeiger beispielsweise von (640, 480) nach (520, 490) bewegt hat, betrug die relative Bewegung (-120, 10). Unten finden Sie ein interaktives Beispiel mit Rohmesswerten für die Mausposition.

In dieser Anleitung werden zwei Themen behandelt: die Aktivierung und Verarbeitung von Ereignissen für die Maussperre und die Implementierung des Steuerschemas für Ego-Shooter. Genau! Wenn Sie diesen Artikel gelesen haben, wissen Sie, wie Sie die Maussperre verwenden und Quake-ähnliche Steuerelemente für Ihr eigenes Browserspiel implementieren.

Browserkompatibilität

Unterstützte Browser

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

Quelle

Funktionsweise der Zeigersperre

Feature-Erkennung

Um festzustellen, ob der Browser des Nutzers die Maussperre unterstützt, müssen Sie im Dokumentobjekt nach pointerLockElement oder einer Version mit Anbieterpräfix suchen. Im Code:

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

Derzeit ist die Maussperre nur in Firefox und Chrome verfügbar. Opera und IE unterstützen sie noch nicht.

Wird aktiviert

Die Aktivierung der Maussperre erfolgt in zwei Schritten. Zuerst fordert Ihre Anwendung die Aktivierung der Maussperre für ein bestimmtes Element an. Unmittelbar nachdem der Nutzer die Berechtigung erteilt hat, wird ein pointerlockchange-Ereignis ausgelöst. Der Nutzer kann die Maussperre jederzeit durch Drücken der Esc-Taste aufheben. Ihre Anwendung kann die Maussperre auch programmatisch beenden. Wenn die Maussperre aufgehoben wird, wird ein pointerlockchange-Ereignis ausgelöst.

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();

Der Code oben reicht aus. Wenn der Browser den Cursor sperrt, wird eine Pop-up-Bubble angezeigt, in der der Nutzer darüber informiert wird, dass Ihre Anwendung den Cursor gesperrt hat, und dass er die Sperrung durch Drücken der Esc-Taste aufheben kann.

Infoleiste für die Maussperre in Chrome
Informationsleiste zur Maussperre in Chrome

Ereignisverarbeitung

Für zwei Ereignisse müssen Ihrer Anwendung Listener hinzugefügt werden. Das erste ist pointerlockchange, das ausgelöst wird, wenn sich der Status der Maussperre ändert. Das zweite ist mousemove, das ausgelöst wird, wenn die Maus bewegt wird.

// 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);

Im pointerlockchange-Callback musst du prüfen, ob der Cursor gerade gesperrt oder entsperrt wurde. Es ist ganz einfach zu ermitteln, ob die Maussperre aktiviert wurde: Prüfen Sie, ob document.pointerLockElement mit dem Element übereinstimmt, für das die Maussperre angefordert wurde. Wenn das der Fall ist, hat Ihre Anwendung den Cursor erfolgreich gesperrt. Andernfalls wurde der Cursor vom Nutzer oder Ihrem eigenen Code entsperrt.

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);
}

Wenn die Zeigersperre aktiviert ist, bleiben clientX, clientY, screenX und screenY konstant. movementX und movementY werden mit der Anzahl der Pixel aktualisiert, um die sich der Cursor seit dem letzten Ereignis bewegt hätte. In Pseudocode:

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

Im mousemove-Callback können relative Mausbewegungsdaten aus den Feldern movementX und movementY des Ereignisses extrahiert werden.

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

Fehler abfangen

Wenn beim Aktivieren oder Deaktivieren der Maussperre ein Fehler auftritt, wird das Ereignis pointerlockerror ausgelöst. Mit diesem Ereignis sind keine Daten verknüpft.

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

Ist der Vollbildmodus erforderlich?

Ursprünglich war die Maussperre an die FullScreen API gebunden. Das bedeutet, dass sich ein Element im Vollbildmodus befinden muss, damit der Cursor darauf fixiert werden kann. Das ist nicht mehr der Fall. Die Maussperre kann für jedes Element in Ihrer App verwendet werden, unabhängig davon, ob es sich um ein Vollbildelement handelt oder nicht.

Beispiel für die Steuerung eines Ego-Shooters

Jetzt, da die Maussperre aktiviert ist und wir Ereignisse empfangen, ist es an der Zeit für ein praktisches Beispiel. Haben Sie sich schon einmal gefragt, wie die Steuerung in Quake funktioniert? Ich erkläre sie gleich anhand von Code.

Die Steuerung von Ego-Shootern basiert auf vier Kernmechaniken:

  • Vor- und zurück entlang des aktuellen Blickvektors bewegen
  • Nach links und rechts entlang des aktuellen Seitwärtslaufvektors bewegen
  • Ansicht um den Nickwinkel drehen (nach links und rechts)
  • Ansicht neigen (hoch und runter)

Für ein Spiel, das dieses Steuerschema implementiert, sind nur drei Daten erforderlich: Kameraposition, Kamera-Suchvektor und ein konstanter Auf-Vektor. Der Aufwärtsvektor ist immer (0, 1, 0). Alle vier oben genannten Mechanismen bewirken nur, dass die Kameraposition und der Kamerablickvektor auf unterschiedliche Weise manipuliert werden.

Bewegung

Als Erstes geht es um Bewegung. In der Demo unten sind die Bewegungen den Standardtasten W, A, S und D zugeordnet. Mit den Tasten „W“ und „S“ bewegen Sie die Kamera vorwärts und rückwärts. Mit den Tasten A und D können Sie die Kamera nach links und rechts schwenken. So bewegen Sie die Kamera vor- und zurück:

// 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);

Für das seitliche Ausweichen nach links und rechts ist eine seitliche Ausweichrichtung erforderlich. Die Richtung der Seitwärtsbewegung kann mit dem Kreuzprodukt berechnet werden:

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

Sobald Sie die Richtung für die Seitwärtsbewegung festgelegt haben, ist die Implementierung der Seitwärtsbewegung identisch mit der Implementierung der Vor- und Rückwärtsbewegung.

Als Nächstes drehen wir die Ansicht.

Gierwinkel

Der Gierwinkel oder die horizontale Drehung der Kameraansicht ist nur eine Drehung um den konstanten Aufwärtsvektor. Unten finden Sie allgemeinen Code zum Drehen des Kamerablickvektors um eine beliebige Achse. Dazu wird ein Quaternion erstellt, der die Drehung um deltaAngle Grad um axis darstellt. Anschließend wird der Kamerablickvektor mithilfe des Quaternions gedreht:

// 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);

Pitchen

Die Implementierung des Neigungswinkels oder der vertikalen Drehung der Kameraansicht ist ähnlich, aber anstelle einer Drehung um den Aufwärtsvektor wenden Sie eine Drehung um den seitlichen Vektor an. Im ersten Schritt wird der Seitwärtsvektor berechnet und dann der Kamerablickvektor um diese Achse gedreht.

Zusammenfassung

Mit der Pointer Lock API können Sie den Mauszeiger steuern. Wenn Sie Webspiele entwickeln, werden Ihre Spieler es lieben, wenn sie nicht mehr getötet werden, weil sie aus Versehen die Maus aus dem Fenster bewegt haben und Ihr Spiel keine Mausaktualisierungen mehr erhält. Die Verwendung ist ganz einfach:

  • pointerlockchange-Ereignis-Listener hinzufügen, um den Status der Maussperre zu erfassen
  • Cursorsperre für ein bestimmtes Element anfordern
  • mousemove-Ereignis-Listener hinzufügen, um Updates zu erhalten

Externe Demos

Verweise