Blokada wskaźnika i elementy sterujące strzelanki FPP

John McCutchan
John McCutchan

Wstęp

Interfejs Pointer Lock API pomaga prawidłowo wdrożyć elementy sterujące strzelaną pierwszoosobową w grze przeglądarkowej. Bez względnego ruchu myszy kursor gracza mógłby na przykład trafić w prawą krawędź ekranu, a wszelkie dalsze ruchy w prawo zostaną zniekształcone – widok nie byłby dalej przesuwany w prawo, a gracz nie byłby w stanie prześcignąć złoczyńców i opatrzyć ich karabinem maszynowym. Gracz będzie się zniechęcić i zniechęcić. W przypadku blokady wskaźnika takie nieoptymalne zachowanie nie jest możliwe.

Pointer Lock API umożliwia aplikacji te działania:

  • Dostęp do nieprzetworzonych danych myszy, w tym do względnych ruchów myszą
  • Kieruj wszystkie zdarzenia myszy do określonego elementu

Efektem ubocznym włączenia blokady wskaźnika jest ukrywanie kursora myszy. Możesz wtedy narysować wskaźnik specyficzny dla aplikacji lub pozostawić go ukrytego, aby użytkownik mógł przesunąć ramkę za pomocą myszy. Względny ruch myszy to różnica między położeniem wskaźnika myszy w stosunku do poprzedniej klatki niezależnie od pozycji bezwzględnej. Jeśli na przykład wskaźnik myszy został przesunięty z (640, 480) do (520, 490), ruch względny był (-120, 10). Poniżej znajdziesz interaktywny przykład pokazujący nieprzetworzone delta pozycji myszy.

Ten samouczek obejmuje 2 tematy: podstawowe informacje o aktywowaniu i przetwarzaniu zdarzeń blokady wskaźnika oraz wdrażanie schematu sterowania strzelanek pierwszoosobowych. Po przeczytaniu tego artykułu dowiesz się, jak używać blokady wskaźnika i wdrożyć elementy sterujące w stylu Quake w swojej grze przeglądarkowej.

Zgodność z przeglądarką

Obsługa przeglądarek

  • 37
  • 13
  • 50
  • 10.1

Źródło

Mechanizmy blokady wskaźnika

Wykrywanie cech

Aby ustalić, czy przeglądarka użytkownika obsługuje blokadę wskaźnika, musisz sprawdzić obiekt dokumentu pointerLockElement lub wersję z prefiksem dostawcy. W kodzie:

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

Obecnie blokada wskaźnika jest dostępna tylko w przeglądarkach Firefox i Chrome. Opera i IE nie obsługują jeszcze 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 tym, gdy użytkownik udzieli odpowiednich uprawnień, uruchamia się zdarzenie pointerlockchange. Użytkownik może w każdej chwili anulować blokadę wskaźnika, naciskając klawisz Escape. Aplikacja może również stopniowo wycofywać blokadę wskaźnika. Po anulowaniu blokady wskaźnika uruchamiane jest 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 powyższy kod. Kiedy przeglądarka zablokuje wskaźnik, pojawi się dymek z informacją, że aplikacja zablokowała wskaźnik, a także o możliwości anulowania tego działania przez naciśnięcie klawisza „Esc”.

Pasek informacji o blokadzie wskaźnika w Chrome.
Pasek informacji o blokadzie wskaźnika w Chrome.

Obsługa zdarzeń

Twoja aplikacja musi dodać detektory do 2 zdarzeń. Pierwszy to pointerlockchange, który uruchamia się przy każdej zmianie stanu blokady wskaźnika. Drugi to kod mousemove, który uruchamia się, gdy użytkownik się porusza.

// 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 musisz sprawdzić, czy wskaźnik został właśnie zablokowany lub odblokowany. Ustalenie, czy blokada wskaźnika jest włączona, jest proste: sprawdź, czy dokument document.pointerLockElement jest taki sam jak element, dla którego zażądano blokady wskaźnika. Jeśli tak, oznacza to, że aplikacja zablokowała wskaźnik, a jeśli tak nie jest, oznacza to, że został on 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);
}

Przy włączonej blokadzie wskaźnika clientX, wartości clientY, screenX i screenY pozostają bez zmian. Zdarzenia movementX i movementY są aktualizowane o liczbę pikseli, o którą wskaźnik przesunąłby się od czasu wykonania ostatniego zdarzenia. W pseudokodzie:

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

W obrębie pola movementX i movementY zdarzenia można pobrać dane względnego ruchu myszy z wywołania zwrotnego mousemove.

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

Wyłapywanie błędów

W przypadku wystąpienia błędu przy wpisywaniu lub wyjściu ze wskaźnika uruchamia się 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);

Wymagany pełny ekran?

Początkowo blokada wskaźnika była powiązana z interfejsem FullScreen API. Oznacza to, że aby można było zablokować element, musi on być w trybie pełnoekranowym. To już nieprawda. Blokada wskaźnika może być używana w przypadku dowolnego elementu aplikacji w trybie pełnoekranowym lub nie.

Przykład ustawienia strzelanki FPP

Po włączeniu blokady wskaźnika i odbieraniu zdarzeń czas na praktyczny przykład. Chcesz wiedzieć, jak działają elementy sterujące w Quake? Uwaga, za chwilę wszystko wyjaśnię za pomocą kodu.

Sterowanie strzelankami pierwszoosobową opiera się na 4 głównych elementach:

  • Poruszanie się do przodu i do tyłu po bieżącym wektorze ujęcia
  • Przesuwam w lewo i w prawo wzdłuż bieżącego wektora strafeum
  • Obracanie odchylenia widoku (w lewo i w prawo)
  • Obracanie kąta widoku (w górę i w dół)

Gra implementująca ten schemat sterowania potrzebuje tylko 3 rodzajów danych: pozycji kamery, wektora obrazu i wektora stałego w górę. Wektor w górę ma zawsze wartość (0, 1, 0). Wszystkie 4 powyższe działania manipulują położeniem kamery i jej wektorem na różne sposoby.

Nurt

Pierwszym z nich jest ruch. W poniższej demonstracji 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. Przesunięcie 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);

Struganie w lewo i w prawo wymaga określenia kierunku. Kierunek strafeum można obliczyć, używając iloczynu wektorowego:

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

Gdy ustalisz kierunek paska, ruch paska będzie taki sam jak ruch do przodu lub do tyłu.

Kolejnym krokiem jest obrócenie widoku.

Odchylenie

Odchylenie, czyli odchylenie widoku kamery w poziomie, to po prostu obrót wokół wektora stałej w górę. Poniżej znajduje się ogólny kod obracania wektora obrazu kamery wokół dowolnej osi. Jej działanie opiera się na skonstruowaniu kwartionu reprezentującego obrót radianów o deltaAngle wokół axis, a następnie za pomocą kwartionu obraca wektor 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);

Sugerowanie treści

Sposób przedstawiania nachylenia i pionowego obrotu widoku kamery jest podobny, ale zamiast obrotu wokół wektora w górę odbywa się obrót wokół wektora paska. Pierwszym krokiem jest obliczenie wektora słupka, a następnie obrócenie wektora obrazu wokół tej osi.

Podsumowanie

Interfejs Pointer Lock API umożliwia sterowanie kursorem myszy. Jeśli tworzysz gry internetowe, gracze pokochają je, gdy przestają się bawić, bo podekscytowani wysunęli mysz z okna, więc gra przestała otrzymywać aktualizacje. Korzystanie z tej funkcji jest proste:

  • Dodaj detektor zdarzeń pointerlockchange, aby śledzić stan blokady wskaźnika
  • Żądanie blokady wskaźnika dla określonego elementu
  • Dodaj detektor zdarzeń mousemove, aby otrzymywać aktualizacje

Zewnętrzne wersje demonstracyjne

Odniesienia