指標鎖定和第一人稱射擊控制項

John McCutchan
John McCutchan

簡介

Pointer Lock API 可協助在瀏覽器遊戲中妥善實作第一人稱射擊遊戲。例如,如果沒有相對的滑鼠移動,玩家的遊標可能會直接移動至畫面右側邊緣,而繼續往右移動的幅度沒有折扣,因為畫面無法繼續向右平移,而玩家也無法追上壞人,陷入他們的機器槍戰。玩家會陷入困境並造成挫折。使用指標鎖定時,無法達到這種不理想的行為。

Pointer Lock API 可讓應用程式執行下列操作:

  • 取得原始滑鼠資料 (包括相對滑鼠移動)
  • 將所有滑鼠事件轉送至特定元素

滑鼠遊標鎖定是啟用指標鎖定的副作用,讓您可以選擇是否繪製特定的應用程式指標,或保持隱藏滑鼠遊標,讓使用者可用滑鼠移動頁框。相對滑鼠移動是指滑鼠指標位置與前一個影格的位置差異 (無論絕對位置為何)。例如,如果滑鼠遊標從 (640, 480) 移動到 (520, 490),相對移動動作是 (-120, 10)。請見下方互動式範例,瞭解原始滑鼠位置差異值。

本教學課程涵蓋兩個主題:啟用和處理指標鎖定事件的注意事項,以及如何實施第一人稱射擊控制機制。沒錯,閱讀完本文後,您將瞭解如何使用指標鎖定,以及為自己的瀏覽器遊戲實作 Quake 樣式的控制項!

瀏覽器相容性

瀏覽器支援

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

資料來源

指標鎖定機制

特徵偵測

如要判斷使用者的瀏覽器是否支援指標鎖定,您需要檢查文件物件中是否有 pointerLockElement 或廠商前置字串的版本。在程式碼中:

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

遊標鎖定功能目前僅適用於 Firefox 和 Chrome。Opera 和 IE 目前尚不支援。

啟用中

啟用指標鎖定程序包含兩個步驟。首先,應用程式要求特定元素啟用指標鎖定,然後在使用者授予權限後立即觸發 pointerlockchange 事件。使用者隨時可以按下 Esc 鍵取消指標鎖定。應用程式也可以明顯結束指標鎖定。取消指標鎖定時,系統會觸發 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();

上述程式碼全都沒問題。瀏覽器鎖定指標時,系統會彈出說明圖示,讓使用者知道應用程式已鎖定指標,並指示使用者按下 Esc 鍵可取消該指標鍵。

Chrome 的指標鎖定資訊列。
Chrome 的遊標鎖定資訊列。

事件處理

應用程式有兩個事件必須新增事件監聽器。第一項是 pointerlockchange,每當指標鎖定狀態發生變更時就會觸發。第二個是 mousemove,每當滑鼠移動時觸發。

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

pointerlockchange 回呼中,您必須檢查指標是否已鎖定或解鎖。判斷指標鎖定是否已啟用非常簡單:檢查 document.pointerLockElement 是否等於要求指標鎖定的元素。如果是,表示您的應用程式已成功鎖定指標;如果不是,表示使用者或您的程式碼已解鎖指標。

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

啟用指標鎖定時,clientXclientYscreenXscreenY 都會維持不變。movementXmovementY 均更新為指標在上次提供事件後移動的像素數量。在虛擬程式碼中:

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

mousemove 回呼中,相對滑鼠動作資料可以從事件的 movementXmovementY 欄位擷取。

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

擷取錯誤

如果輸入或離開指標引發錯誤,會觸發 pointerlockerror 事件。這個活動未附加任何資料。

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

需要全螢幕嗎?

最初的指標鎖定功能和 FullScreen API 相連結。表示元素必須先進入全螢幕模式,才能鎖定該元素。這點不再是 True,而且應用程式中的任何元素都可使用指標鎖定 (不論是否以全螢幕顯示)。

第一人稱射擊控制項範例

現在,我們已啟用指標鎖定並接收事件,接著要參考實際範例。你是否想知道 Quake 的控制項如何運作?我正要透過程式碼說明他們吧!

第一人稱射擊遊戲的控制機制圍繞著四種核心機制:

  • 在目前的外觀向量上前後移動
  • 沿著目前的平流向量左右移動
  • 旋轉視角 (左右)
  • 旋轉視角 (向上和向下)

實作這個控製配置的遊戲只需要三種資料:相機位置、相機外觀向量和不斷上升的向量。上向量一律為 (0、1、0)。上述四項機制都是以不同的方式操控相機位置和相機外觀向量。

活動

第一是活動在下面的示範中,我們對應到標準的 W、A、S 和 D 按鍵。W 和 S 鍵可向前和向後推動相機。A 和 D 鍵可向左和向右移動攝影機。前後移動攝影機的方式非常簡單:

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

往左和右往左或往右移動需要小數點。可使用交叉乘積計算高度方向:

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

取得簡易方向後,實施輕微移動與向前或向後移動的效果相同。

接下來是旋轉視圖。

Yaw

相機檢視畫面的偏轉或水平旋轉,只會以固定向量為中心旋轉。下方是沿著任意軸旋轉相機外觀向量的一般程式碼。方法是建構代表 axis 周圍 deltaAngle 弧度的「四元組」,然後使用四元數旋轉相機外觀向量:

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

音調

為相機視角的垂直傾斜或垂直旋轉的做法與此相似,但並沒有針對上向量旋轉。第一步是計算特張向量,然後沿著該軸旋轉相機的視覺向量。

摘要

Pointer Lock API 可讓您控制滑鼠遊標。如果您要製作網頁遊戲,玩家會愛上遊戲,因為他們久未將滑鼠移到視窗外,遊戲也停止接收滑鼠更新。使用方式很簡單:

  • 新增 pointerlockchange 事件監聽器,用於追蹤指標鎖定狀態
  • 特定元素的要求指標鎖定
  • 新增 mousemove 事件監聽器以取得更新資訊

外部示範

參考資料