Pengantar
Pointer Lock API membantu menerapkan kontrol first-person shooter dengan benar dalam game browser. Tanpa gerakan mouse relatif, kursor pemain dapat, misalnya, menyentuh tepi kanan layar dan gerakan lebih lanjut ke kanan akan didiskon - tampilan tidak akan terus digeser ke kanan, dan pemain tidak akan dapat mengejar penjahat dan menembak mereka dengan senapan mesinnya. Pemain akan mati dan merasa frustrasi. Dengan kunci pointer, perilaku yang tidak optimal ini tidak dapat terjadi.
Pointer Lock API memungkinkan aplikasi Anda melakukan hal berikut:
- Mendapatkan akses ke data mouse mentah termasuk gerakan mouse relatif
- Mengarahkan semua peristiwa mouse ke elemen tertentu
Sebagai efek samping dari mengaktifkan kunci kursor, kursor mouse akan disembunyikan sehingga Anda dapat memilih untuk menggambar kursor khusus aplikasi jika diinginkan, atau membiarkan kursor mouse disembunyikan sehingga pengguna dapat memindahkan bingkai dengan mouse. Gerakan mouse relatif adalah delta posisi pointer mouse dari frame sebelumnya, terlepas dari posisi absolut. Misalnya, jika kursor mouse dipindahkan dari (640, 480) ke (520, 490), gerakan relatifnya adalah (-120, 10). Lihat di bawah untuk contoh interaktif yang menampilkan delta posisi mouse mentah.
Tutorial ini membahas dua topik: dasar-dasar pengaktifan dan pemrosesan peristiwa kunci pointer, serta penerapan skema kontrol game tembak-menembak orang pertama. Benar, setelah selesai membaca artikel ini, Anda akan tahu cara menggunakan kunci pointer dan menerapkan kontrol bergaya Quake untuk game browser Anda sendiri.
Kompatibilitas browser
Mekanika Kunci Kursor
Deteksi Fitur
Untuk menentukan apakah browser pengguna mendukung kunci pointer, Anda perlu memeriksa pointerLockElement
atau versi dengan awalan vendor di objek dokumen. Dalam kode:
var havePointerLock = 'pointerLockElement' in document ||
'mozPointerLockElement' in document ||
'webkitPointerLockElement' in document;
Saat ini, kunci kursor hanya tersedia di Firefox dan Chrome. Opera dan IE belum mendukungnya.
Mengaktifkan
Mengaktifkan kunci kursor adalah proses dua langkah. Pertama, aplikasi Anda meminta kunci pointer diaktifkan untuk elemen tertentu, dan segera setelah pengguna memberikan izin, peristiwa pointerlockchange
akan diaktifkan. Pengguna dapat membatalkan kunci pointer kapan saja dengan menekan tombol escape. Aplikasi Anda juga dapat keluar dari kunci pointer secara terprogram. Saat kunci pointer dibatalkan, peristiwa pointerlockchange
akan diaktifkan.
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();
Kode di atas adalah yang diperlukan. Saat browser mengunci pointer, balon akan muncul untuk memberi tahu pengguna bahwa aplikasi Anda telah mengunci pointer dan memberi tahu mereka bahwa mereka dapat membatalkannya dengan menekan tombol 'Esc'.
Penanganan Peristiwa
Ada dua peristiwa yang harus ditambahkan pemrosesnya oleh aplikasi Anda. Yang pertama adalah pointerlockchange
, yang diaktifkan setiap kali terjadi perubahan status kunci pointer. Yang kedua adalah mousemove
yang diaktifkan setiap kali mouse bergerak.
// 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);
Di dalam callback pointerlockchange
, Anda harus memeriksa apakah pointer baru saja dikunci atau tidak. Menentukan apakah penguncian pointer diaktifkan itu mudah: periksa apakah document.pointerLockElement sama dengan elemen yang diminta penguncian pointernya. Jika ya, aplikasi Anda berhasil mengunci pointer dan jika tidak, pointer dibuka kuncinya oleh pengguna atau kode Anda sendiri.
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);
}
Jika kunci pointer diaktifkan, clientX
, clientY
, screenX
, dan screenY
akan tetap konstan. movementX
dan movementY
diperbarui dengan jumlah piksel yang akan dipindahkan pointer sejak peristiwa terakhir dikirim. Dalam kode semu:
event.movementX = currentCursorPositionX - previousCursorPositionX;
event.movementY = currentCursorPositionY - previousCursorPositionY;
Di dalam callback mousemove
, data gerakan mouse relatif dapat diekstrak dari kolom movementX
dan movementY
peristiwa.
function moveCallback(e) {
var movementX = e.movementX ||
e.mozMovementX ||
e.webkitMovementX ||
0,
movementY = e.movementY ||
e.mozMovementY ||
e.webkitMovementY ||
0;
}
Menangkap error
Jika error muncul dengan memasuki atau keluar dari kunci pointer, peristiwa pointerlockerror
akan diaktifkan. Tidak ada data yang dilampirkan ke peristiwa ini.
document.addEventListener('pointerlockerror', errorCallback, false);
document.addEventListener('mozpointerlockerror', errorCallback, false);
document.addEventListener('webkitpointerlockerror', errorCallback, false);
Apakah Layar Penuh Diperlukan?
Awalnya, kunci pointer terikat dengan FullScreen API. Artinya, elemen harus dalam mode layar penuh sebelum pointer dapat dikunci ke elemen tersebut. Hal itu tidak lagi berlaku dan kunci pointer dapat digunakan untuk elemen apa pun di aplikasi Anda, baik dalam layar penuh maupun tidak.
Contoh Kontrol First-Person Shooter
Setelah kita mengaktifkan kunci pointer dan menerima peristiwa, saatnya untuk contoh praktis. Pernahkah Anda ingin mengetahui cara kerja kontrol di Quake? Siapkan diri Anda karena saya akan menjelaskannya dengan kode.
Kontrol game first-person shooter dibuat berdasarkan empat mekanisme inti:
- Bergerak maju dan mundur di sepanjang vektor tampilan saat ini
- Berpindah ke kiri dan kanan di sepanjang vektor strafe saat ini
- Memutar yaw tampilan (kiri dan kanan)
- Memutar pitch tampilan (naik dan turun)
Game yang menerapkan skema kontrol ini hanya memerlukan tiga bagian data: posisi kamera, vektor tampilan kamera, dan vektor atas konstan. Vektor atas selalu (0, 1, 0). Keempat mekanisme di atas hanya memanipulasi posisi kamera dan vektor tampilan kamera dengan cara yang berbeda.
Gerakan
Yang pertama adalah gerakan. Dalam demo di bawah, gerakan dipetakan ke tombol W, A, S, dan D standar. Tombol W dan S menggerakkan kamera maju dan mundur. Sementara tombol A dan D menggerakkan kamera ke kiri dan kanan. Cara menggerakkan kamera maju dan mundur sangat mudah:
// 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);
Untuk bergerak ke kiri dan kanan, Anda memerlukan arah strafe. Arah strafe dapat dihitung menggunakan produk silang:
// Strafe direction
var strafeDirection = vec3.create();
vec3.cross(cameraLookVector, cameraUpVector, strafeDirection);
Setelah Anda memiliki arah strafe, menerapkan gerakan strafe sama dengan bergerak maju atau mundur.
Selanjutnya adalah memutar tampilan.
Yaw
Yaw atau rotasi horizontal tampilan kamera hanyalah rotasi di sekitar vektor atas konstan. Berikut adalah kode umum untuk memutar vektor tampilan kamera di sekitar sumbu arbitrer. Cara kerjanya adalah dengan membuat kuaternion yang mewakili rotasi deltaAngle
radian di sekitar axis
, lalu menggunakan kuarternion untuk memutar vektor tampilan kamera:
// 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);
Promosi
Menerapkan pitch atau rotasi vertikal tampilan kamera serupa, tetapi Anda menerapkan rotasi di sekitar vektor strafe, bukan rotasi di sekitar vektor atas. Langkah pertama adalah menghitung vektor strafe, lalu memutar vektor tampilan kamera di sekitar sumbu tersebut.
Ringkasan
Pointer Lock API memungkinkan Anda mengontrol kursor mouse. Jika Anda membuat game web, pemain akan senang jika mereka berhenti terbunuh karena mereka dengan antusias memindahkan mouse keluar dari jendela dan game Anda berhenti mendapatkan update mouse. Penggunaannya sederhana:
- Menambahkan pemroses peristiwa
pointerlockchange
untuk melacak status kunci pointer - Meminta kunci pointer untuk elemen tertentu
- Menambahkan pemroses peristiwa
mousemove
untuk mendapatkan update