簡介
Pointer Lock API 可協助您在瀏覽器遊戲中正確實作第一人稱射擊遊戲控制項。舉例來說,如果沒有相對滑鼠移動動作,玩家的游標就可能會撞到螢幕右側邊緣,而往右移動的任何動作都會被忽略,畫面不會繼續向右滑動,玩家也無法追捕壞人,並用機關槍掃射他們。玩家會被擊殺,並因此感到挫折。使用指標鎖定功能後,就不會發生這種不理想的行為。
Pointer Lock API 可讓應用程式執行下列操作:
- 取得原始滑鼠資料,包括相對滑鼠動作
- 將所有滑鼠事件轉送至特定元素
啟用游標鎖定功能後,滑鼠游標會隱藏起來,您可以視需要選擇繪製特定應用程式的游標,或是讓滑鼠游標保持隱藏狀態,讓使用者可以使用滑鼠移動影格。相對滑鼠動作是滑鼠游標相對於上一個影格的位置差異,不受絕對位置影響。舉例來說,如果滑鼠游標從 (640, 480) 移動到 (520, 490),相對移動量就是 (-120, 10)。請參閱下方的互動式範例,瞭解原始滑鼠位置差異。
本教學課程將介紹兩個主題:啟用及處理指標鎖定事件的細節,以及實作第一人稱射擊遊戲控制方案。沒錯,閱讀完本文後,您就會知道如何使用指標鎖定功能,並為自己的瀏覽器遊戲實作 Quake 風格的控制項!
瀏覽器相容性
指標鎖定機制
功能偵測
如要判斷使用者的瀏覽器是否支援指標鎖定功能,您需要在文件物件中檢查 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」鍵取消鎖定。
事件處理
應用程式必須為兩個事件新增監聽器。第一個是 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);
}
啟用游標鎖定功能後,clientX
、clientY
、screenX
和 screenY
會保持不變。movementX
和 movementY
會根據自上次事件傳送以來,游標移動的像素數量進行更新。在虛擬程式碼中:
event.movementX = currentCursorPositionX - previousCursorPositionX;
event.movementY = currentCursorPositionY - previousCursorPositionY;
在 mousemove
回呼中,您可以從事件的 movementX
和 movementY
欄位擷取相對滑鼠動作資料。
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 相關聯,也就是說,元素必須處於全螢幕模式,才能讓指標鎖定在該元素上。但現在已非如此,您可以在應用程式全螢幕模式或非全螢幕模式下,為任何元素使用指標鎖定功能。
第一人稱射擊遊戲控制項範例
我們已啟用指標鎖定功能並接收事件,現在來看看實際範例。您是否曾想過《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);
有了側移方向後,實作側移動作的做法就與前進或後退相同。
接下來,我們要旋轉檢視畫面。
偏轉
偏轉或攝影機檢視畫面的水平旋轉,只是繞著常數向上向量旋轉。以下是用來旋轉攝影機觀看向量 (look vector) 的一般程式碼,其運作方式是建構四元數,代表以 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);
音調
實作相機視圖的俯仰或垂直旋轉效果時,做法類似,但您不是以向上向量為旋轉軸,而是以側向向量為旋轉軸。第一步是計算側移向量,然後以該軸旋轉相機觀看向量。
摘要
您可以使用指標鎖定 API 控制滑鼠游標。如果您製作的是網路遊戲,玩家在興奮地將滑鼠移出視窗時,如果遊戲停止接收滑鼠更新,玩家就不會因為這樣而遭到攻擊,這會讓玩家感到開心。使用方式很簡單:
- 新增
pointerlockchange
事件監聽器,以追蹤指標鎖定的狀態 - 要求特定元素的指標鎖定
- 新增
mousemove
事件監聽器以接收更新