Blokada wskaźnika i elementy sterujące strzelanki FPP

John McCutchan
John McCutchan

Wprowadzenie

Pointer Lock API pomaga poprawnie wdrożyć elementy sterujące strzelankami FPP w grze przeglądarki. Bez względnego ruchu kursora kursor gracza mógłby na przykład trafić w prawą krawędź ekranu. Dalsze ruchy w prawo zostałyby obniżone – widok nie byłby dalej przesuwany w prawo, a gracz nie byłby w stanie wygonić ich z broni maszynowej. Gracz się rozrasta i się denerwuje. Dzięki blokowaniu kursora nie będzie dochodziło do takich nieoptymalnych zachowań.

Interfejs Pointer Lock API umożliwia aplikacji te czynności:

  • Uzyskiwanie dostępu do nieprzetworzonych danych myszy, w tym względnych ruchów kursora
  • Kierowanie wszystkich zdarzeń myszy do określonego elementu

Efektem ubocznym włączenia blokady wskaźnika jest fakt, że kursor myszy jest ukryty, dzięki czemu możesz narysować wskaźnik związany z aplikacją lub pozostawić go ukrytym, dzięki czemu użytkownik będzie mógł przesuwać ramkę za pomocą myszy. Względny ruch myszą to delta pozycji wskaźnika myszy względem poprzedniej klatki, niezależnie od pozycji bezwzględnej. Jeśli na przykład wskaźnik myszy przemieścił się z (640, 480) na (520, 490), ruch względny był (-120, 10). Poniżej znajdziesz interaktywny przykład przedstawiający delta nieprzetworzonych pozycji myszy.

Omawiamy w nim 2 tematy: śruby związane z aktywowaniem i przetwarzaniem zdarzeń blokowania wskaźnika oraz implementowanie schematu sterowania strzelankami FPP. Po przeczytaniu tego artykułu dowiesz się, jak używać blokady wskaźnika i wdrożyć elementy sterujące w stylu Quake w swojej przeglądarce.

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 37.
  • Krawędź: 13.
  • Firefox: 50.
  • Safari: 10.1

Źródło

Mechanika blokady wskaźnika

Wykrywanie cech

Aby określić, czy przeglądarka użytkownika obsługuje blokadę wskaźnika, sprawdź, czy w obiekcie dokumentu występuje pointerLockElement lub wersja z prefiksem producenta. W kodzie:

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

Obecnie blokada wskaźnika jest dostępna tylko w Firefoksie i Chrome. Opera i IE na razie nie obsługują tej funkcji.

Aktywuję

Aktywowanie blokady wskaźnika jest procesem dwuetapowym. Najpierw aplikacja prosi o włączenie blokady wskaźnika dla określonego elementu, a zaraz po udzieleniu przez użytkownika uprawnień uruchamia się zdarzenie pointerlockchange. Użytkownik może w dowolnym momencie anulować blokadę kursora, naciskając klawisz Escape. Aplikacja może też automatycznie wyjść z blokady wskaźnika. Po anulowaniu blokady wskaźnika uruchamia się zdarzenie 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();

Wystarczy użyć powyższego kodu. Gdy przeglądarka zablokuje wskaźnik, pojawi się dymek z informacją, że aplikacja zablokowała wskaźnik i informując go, że może anulować ten wskaźnik, naciskając Esc. .

Pasek informacyjny blokady wskaźnika w Chrome.
Pasek informacyjny blokady wskaźnika w Chrome

Obsługa zdarzeń

Aplikacja musi dodać detektory dla 2 zdarzeń. Pierwszy to pointerlockchange, który jest wywoływany, gdy nastąpi zmiana stanu blokady wskaźnika. Drugi to mousemove, który jest uruchamiany, gdy użytkownik porusza myszką.

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

W wywołaniu zwrotnym pointerlockchange sprawdź, czy wskaźnik został właśnie zablokowany lub odblokowany. Informacja, czy blokada wskaźnika jest włączona, jest prosta. Sprawdź, czy document.pointerLockElement jest równy elementowi, do którego zażądano blokady wskaźnika. Jeśli tak, oznacza to, że aplikacja zablokowała wskaźnik. Jeśli tak nie jest, wskaźnik został odblokowany przez użytkownika lub Twój kod.

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

Gdy blokada wskaźnika jest włączona, clientX, clientY, screenX i screenY pozostają bez zmian. Wartości movementX i movementY są aktualizowane o liczbę pikseli, o jaką wskaźnik przesunąłby się od czasu wywołania ostatniego zdarzenia. W pseudokodzie:

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

Z pól movementX i movementY zdarzenia można wyodrębnić dane względnego ruchu myszy w elemencie mousemove.

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

Wykrywanie błędów

Jeśli podczas włączania lub wyłączania blokady kursora wystąpi błąd, zostanie wywołane zdarzenie pointerlockerror. Do tego zdarzenia nie są dołączone żadne dane.

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

Czy wymagany jest tryb pełnoekranowy?

Początkowo blokada wskaźnika była powiązana z interfejsem FullScreen API. Oznacza to, że element musi być w trybie pełnoekranowym, aby można było zablokować na nim wskaźnik. To już nie jest prawda. Blokady wskaźnika można używać w przypadku dowolnego elementu aplikacji na pełnym ekranie.

Przykład elementów sterujących strzelanki FPP

Po włączeniu blokady wskaźnika i odbieraniu zdarzeń możemy przejść na praktyczny przykład. Chcesz wiedzieć, jak działają elementy sterujące w Quake? Dołącz, bo zaraz opowiem o nich za pomocą kodu.

Sterowanie w strzelankach FPP opiera się na 4 głównych elementach mechaniki:

  • Poruszanie się do przodu i do tyłu wzdłuż bieżącego wektora wyglądu
  • Ruch w lewo i w prawo wzdłuż bieżącego wektora strafe
  • Obracanie odchylenia widoku (w lewo i w prawo)
  • obracanie widoku (w górę i w dół),

Gra wykorzystująca ten schemat sterowania potrzebuje tylko 3 elementów danych: pozycji kamery, wektora kierunku kamery i stałego wektora w górę. Wektor w górę to zawsze (0, 1, 0). Wszystkie 4 powyższe mechanizmy manipulują tylko pozycją kamery i jej wektorem na różne sposoby.

Ruch

Pierwszy krok to ruch. W poniższej wersji demonstracyjnej ruch jest mapowany na standardowe klawisze W, A, S i D. Klawisze W i S przesuwają kamerę do przodu i do tyłu. Klawisze A i D przesuwają kamerę w lewo i w prawo. Obracanie kamery do przodu i do tyłu jest proste:

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

Aby poruszać się w lewo i w prawo, musisz wybrać kierunek. Kierunek ostrza można obliczyć za pomocą iloczynu krzyżowego:

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

Gdy określisz kierunek ruchu bocznego, implementacja ruchu bocznego będzie taka sama jak ruch do przodu lub do tyłu.

Kolejny krok to obracanie widoku.

Yaw

Odchylenie, czyli obrót w poziomie widoku kamery, to po prostu obrót wokół stałego wektora. Poniżej znajduje się ogólny kod służący do obracania wektora kierunku kamery wokół dowolnej osi. Działanie tej metody opiera się na skonstruowaniu kwartionu reprezentującego obrót o radianów o deltaAngle wokół wartości axis, a następnie z wykorzystaniem kwartionu do obracania wektora obrazu kamery:

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

Ton

Stosowanie tonu lub obracania widoku kamery w pionie jest podobne, ale zamiast obracać się wokół górnego wektora, wykonujesz obrót wokół wektora strave'a. Pierwszym krokiem jest obliczenie wektora strafe'a, a następnie obrót wektora obrazu wokół tej osi.

Podsumowanie

Interfejs Pointer Lock API umożliwia przejęcie kontroli nad kursorem myszy. Jeśli tworzysz gry internetowe, Twoi gracze pokochają je, gdy przestają się rozbijać, ponieważ podekscytowani wysunęli mysz z okna i gra przestała otrzymywać aktualizacje. Korzystanie jest proste:

  • Dodaj detektor zdarzeń pointerlockchange, aby śledzić stan blokady wskaźnika
  • Żądanie blokady wskaźnika dla konkretnego elementu
  • Dodaj detektor zdarzenia mousemove, aby otrzymywać powiadomienia

Zewnętrzne wersje demonstracyjne

Pliki referencyjne