Bagaimana Slow Roads menarik minat gamer dan developer, menyoroti kemampuan 3D yang luar biasa di browser

Temukan potensi WebGL dengan pemandangan tak terbatas dan yang dihasilkan secara prosedural dari game mengemudi kasual ini.

Slow Roads adalah game mengemudi kasual dengan penekanan pada pemandangan yang dihasilkan secara prosedural, semuanya dihosting di browser sebagai aplikasi WebGL. Bagi banyak orang, pengalaman yang intensif seperti itu mungkin tampak tidak sesuai dalam konteks browser yang terbatas—dan memang, memperbaiki sikap tersebut telah menjadi salah satu tujuan saya di project ini. Dalam artikel ini, saya akan menguraikan beberapa teknik yang digunakan untuk mengatasi rintangan performa dalam misi saya untuk menyoroti potensi 3D yang sering diabaikan di web.

Pengembangan 3D di browser

Setelah merilis Slow Roads, saya terus melihat komentar berulang kali di masukan: "Saya tidak tahu fitur ini mungkin ada di browser". Jika memiliki sentimen ini, Anda pasti bukan minoritas; menurut survei State of JS 2022, sekitar 80% developer belum bereksperimen dengan WebGL. Bagi saya, sangat memalukan karena begitu banyak potensi potensi yang terlewatkan, terutama ketika berkaitan dengan game berbasis browser. Dengan Slow Roads, saya berharap WebGL semakin menjadi pusat perhatian, dan mungkin mengurangi jumlah developer yang menolak menyebut frasa "game engine JavaScript berperforma tinggi".

WebGL mungkin tampak misterius dan rumit bagi banyak orang, tetapi dalam beberapa tahun terakhir ekosistem pengembangannya telah berkembang pesat menjadi alat dan library yang sangat mumpuni dan mudah digunakan. Kini developer front-end lebih mudah untuk menyertakan UX 3D ke dalam pekerjaan mereka, bahkan tanpa pengalaman sebelumnya di bidang grafis komputer. Three.js, library WebGL terkemuka, berfungsi sebagai dasar untuk banyak ekspansi, termasuk react-three-fiber yang menghadirkan komponen 3D ke dalam framework React. Kini ada juga editor game berbasis web yang komprehensif, seperti Babylon.js atau PlayCanvas yang menawarkan antarmuka yang sudah dikenal dan toolchain terintegrasi.

Namun, terlepas dari kegunaan library ini yang luar biasa, project yang ambisius pada akhirnya terikat oleh batasan teknis. Yang skeptis terhadap gagasan game berbasis browser mungkin menyoroti bahwa JavaScript adalah thread tunggal dan dibatasi resource. Namun, menavigasi batasan ini akan membuka nilai tersembunyi: tidak ada platform lain yang menawarkan aksesibilitas instan dan kompatibilitas massal yang sama seperti yang diaktifkan oleh browser. Pengguna di sistem apa pun yang mendukung browser dapat mulai bermain dengan sekali klik, tanpa perlu menginstal aplikasi dan tidak perlu login ke layanan. Selain itu, developer menikmati kenyamanan elegan karena memiliki framework front-end yang kuat yang tersedia untuk mem-build UI, atau menangani jaringan untuk mode multiplayer. Menurut saya, nilai-nilai ini membuat browser menjadi platform yang sangat baik bagi pemain dan developer—dan, seperti yang ditunjukkan oleh Slow Roads, batasan teknis mungkin sering dapat direduksi menjadi masalah desain.

Mencapai performa lancar di Jalan Lambat

Karena elemen inti dari Jalan Lambat melibatkan gerakan berkecepatan tinggi dan pembuatan pemandangan yang mahal, kebutuhan untuk performa yang lancar menggarisbawahi setiap keputusan desain saya. Strategi utama saya adalah memulai dengan desain gameplay sederhana yang memungkinkan pintasan kontekstual diterapkan dalam arsitektur mesin. Di sisi negatifnya, hal ini berarti mengorbankan beberapa fitur yang bagus untuk dimiliki sesuai dengan prinsip minimalis, tetapi menghasilkan sistem khusus yang sangat dioptimalkan dan berfungsi dengan baik di berbagai browser dan perangkat.

Berikut ini perincian komponen utama yang menjaga Jalan Lambat tetap ramping.

Membentuk mesin lingkungan di sekitar gameplay

Sebagai komponen utama dalam game, mesin pembuatan lingkungan tidak dapat dihindari mahal, dan ini memang mengambil proporsi anggaran terbesar untuk memori dan komputasi. Trik yang digunakan di sini adalah menjadwalkan dan mendistribusikan komputasi yang berat selama periode waktu tertentu, agar tidak mengganggu frekuensi gambar dengan lonjakan performa.

Lingkungan ini terdiri dari ubin geometri, dengan ukuran dan resolusi yang berbeda-beda (dikategorikan sebagai "tingkat detail" atau LoD) bergantung pada seberapa dekat mereka terlihat dengan kamera. Pada game standar dengan kamera free-roaming, LoD yang berbeda harus terus-menerus dimuat dan dihapus muatannya untuk memerinci lingkungan pemain di mana pun mereka dapat pergi. Hal ini dapat menjadi operasi yang mahal dan boros, terutama saat lingkungan itu sendiri dihasilkan secara dinamis. Untungnya, konvensi ini dapat sepenuhnya dibalikkan di Jalan Lambat berkat ekspektasi kontekstual bahwa pengguna harus tetap berada di jalan. Sebagai gantinya, geometri detail tinggi dapat dicadangkan untuk koridor sempit yang langsung mengapit rute.

Diagram yang menunjukkan bagaimana pembuatan jalan jauh lebih awal dapat memungkinkan penjadwalan proaktif dan penyimpanan dalam cache pembuatan lingkungan.
Tampilan geometri lingkungan di Jalan Lambat yang dirender sebagai wireframe, yang menunjukkan koridor geometri resolusi tinggi yang mengapit jalan. Bagian lingkungan yang jauh, yang tidak boleh dilihat dari dekat, dirender dengan resolusi yang jauh lebih rendah.

Garis tengah jalan itu sendiri dihasilkan jauh sebelum kedatangan pemain, sehingga memungkinkan prediksi yang akurat terkait kapan dan di mana detail lingkungan akan diperlukan. Hasilnya adalah sistem ramping yang dapat secara proaktif menjadwalkan pekerjaan mahal, hanya menghasilkan jumlah minimum yang dibutuhkan di setiap titik waktu, dan tanpa upaya sia-sia pada detail yang tidak akan terlihat. Teknik ini hanya dapat dilakukan karena jalan tersebut merupakan jalur tunggal tanpa cabang—contoh bagus dalam membuat trade-off gameplay yang mengakomodasi jalan pintas arsitektur.

Diagram yang menunjukkan bagaimana pembuatan jalan jauh lebih awal dapat memungkinkan penjadwalan proaktif dan penyimpanan dalam cache pembuatan lingkungan.
Dengan melihat jarak tertentu di sepanjang jalan, potongan lingkungan dapat dikurangi dan dibuat secara bertahap tepat sebelum diperlukan. Selain itu, setiap bagian yang akan ditinjau kembali dalam waktu dekat dapat diidentifikasi dan di-cache untuk menghindari pembuatan ulang yang tidak perlu.

Pilih-pilih rumusan hukum fisika

Yang kedua setelah permintaan komputasi mesin lingkungan adalah simulasi fisika. Slow Roads menggunakan mesin fisika minimal kustom yang mengambil setiap jalan pintas yang ada.

Penghematan utama di sini adalah menghindari simulasi terlalu banyak objek sejak awal, yakni condong ke konteks zen yang minimal dengan memberikan diskon pada hal-hal seperti tabrakan dinamis dan objek yang dapat diurai. Dengan asumsi bahwa kendaraan akan tetap berada di jalan, tabrakan dengan objek off-road dapat diabaikan secara wajar. Selain itu, encoding jalan sebagai garis tengah yang jarang memungkinkan trik elegan untuk mendeteksi tabrakan cepat dengan permukaan jalan dan rel pelindung, semuanya didasarkan pada pemeriksaan jarak ke pusat jalan. Mengemudi off-road kemudian menjadi lebih mahal, tetapi ini adalah contoh lain dari kompromi yang adil yang sesuai dengan konteks gameplay.

Mengelola jejak memori

Sebagai resource lain yang dibatasi browser, Anda harus mengelola memori dengan hati-hati, meskipun JavaScript tetap mengumpulkan sampah. Meskipun mungkin sering diabaikan, mendeklarasikan memori baru dalam jumlah kecil sekali pun dalam game loop dapat menjadi masalah yang signifikan saat dijalankan pada frekuensi 60 Hz. Selain menghabiskan resource pengguna dalam konteks yang kemungkinan melakukan multitasking, pembersihan sampah memori yang besar dapat membutuhkan beberapa frame untuk diselesaikan, sehingga menyebabkan ketersendatan yang terlihat. Untuk menghindarinya, memori loop dapat dialokasikan sebelumnya di variabel class saat inisialisasi dan didaur ulang di setiap frame.

Tampilan sebelum dan setelah profil memori selama pengoptimalan codebase Slow Roads, yang menunjukkan penghematan yang signifikan dan pengurangan tingkat pembersihan sampah memori.
Meskipun penggunaan memori secara keseluruhan hampir tidak berubah, memori loop pra-pengalokasian dan daur ulang dapat mengurangi dampak pembersihan sampah memori yang mahal.

Struktur data yang lebih berat, seperti geometri dan buffer data yang terkait, dikelola secara ekonomis. Dalam game yang dihasilkan tanpa batas seperti Slow Roads, sebagian besar geometri ada di semacam treadmill - saat benda tua berada di kejauhan, struktur datanya dapat disimpan dan didaur ulang lagi untuk bagian dunia yang akan datang, pola desain yang dikenal sebagai penggabungan objek.

Praktik ini membantu memprioritaskan eksekusi yang ramping, dengan mengorbankan beberapa kesederhanaan kode. Dalam konteks performa tinggi, penting untuk memperhatikan bagaimana fitur kemudahan terkadang meminjam dari klien untuk kepentingan developer. Misalnya, metode seperti Object.keys() atau Array.map() sangat berguna, tetapi mudah untuk mengabaikan bahwa setiap metode membuat array baru untuk nilai yang ditampilkannya. Memahami cara kerja bagian dalam dari kotak hitam semacam itu dapat membantu memperketat kode Anda dan menghindari klik kinerja yang tersembunyi.

Mengurangi waktu pemuatan dengan aset yang dihasilkan secara prosedural

Meskipun performa runtime harus menjadi perhatian utama bagi developer game, sumbu umum terkait waktu pemuatan halaman web awal tetap berlaku. Pengguna mungkin akan lebih mudah memakluminya ketika mengakses konten yang berat secara sengaja, tetapi waktu pemuatan yang lama masih dapat mengganggu pengalaman tersebut, jika tidak, retensi pengguna. Game sering kali memerlukan aset besar dalam bentuk tekstur, suara, dan model 3D, dan setidaknya harus dikompresi dengan cermat di mana pun detail tidak diperlukan.

Atau, menghasilkan aset sesuai prosedur pada klien dapat menghindari transfer yang lama sejak awal. Ini adalah manfaat besar bagi pengguna dengan koneksi lambat, dan memberi developer kontrol yang lebih langsung atas cara pembuatan game—tidak hanya untuk langkah pemuatan awal, tetapi juga saat mengadaptasi tingkat detail untuk berbagai setelan kualitas.

Perbandingan yang menggambarkan bagaimana kualitas geometri yang dihasilkan secara prosedural di Jalan Lambat dapat disesuaikan secara dinamis dengan kebutuhan performa pengguna.

Sebagian besar geometri di Jalan Lambat dihasilkan secara prosedural dan sederhana, dengan shader kustom yang menggabungkan beberapa tekstur untuk memberikan detail. Kelemahannya adalah tekstur ini dapat menjadi aset berat, meskipun ada peluang lebih lanjut untuk menghemat di sini, karena metode seperti tekstur stokastik dapat mencapai detail yang lebih besar dari tekstur sumber yang kecil. Dan pada tingkat yang ekstrem, Anda juga dapat membuat tekstur sepenuhnya pada klien dengan alat seperti texgen.js. Hal yang sama berlaku untuk audio, dengan Web Audio API memungkinkan pembuatan suara dengan node audio.

Dengan memanfaatkan aset sesuai prosedur, menghasilkan lingkungan awal rata-rata hanya memerlukan waktu 3,2 detik. Untuk memanfaatkan ukuran download di muka yang kecil dengan baik, layar pembuka yang sederhana menyambut pengunjung baru dan menunda inisialisasi scene yang mahal hingga setelah tombol afirmatif ditekan. Hal ini juga berfungsi sebagai buffer yang praktis untuk sesi pantulan, sehingga meminimalkan transfer aset yang dimuat secara dinamis secara dinamis.

Histogram waktu pemuatan menunjukkan puncak yang kuat dalam tiga detik pertama yang mencakup lebih dari 60% pengguna, diikuti oleh penurunan cepat. Histogram ini menunjukkan bahwa lebih dari 97% pengguna melihat waktu pemuatan kurang dari 10 detik.

Mengambil pendekatan yang fleksibel terhadap pengoptimalan yang terlambat

Saya selalu menganggap codebase untuk Slow Roads sebagai eksperimental, sehingga saya mengambil pendekatan pengembangan yang sangat gesit. Saat bekerja dengan arsitektur sistem yang kompleks dan berkembang pesat, mungkin sulit untuk memprediksi di mana bottleneck penting yang mungkin terjadi. Fokusnya adalah menerapkan fitur yang diinginkan dengan cepat, bukan secara bersih, dan bekerja mundur untuk mengoptimalkan sistem yang benar-benar diperlukan. Profiler performa di Chrome DevTools sangat berharga untuk langkah ini, dan telah membantu saya mendiagnosis beberapa masalah utama pada versi game sebelumnya. Waktu Anda sebagai developer sangat berharga, jadi pastikan Anda tidak menghabiskan waktu untuk memikirkan masalah yang mungkin terbukti tidak signifikan atau berlebihan.

Memantau pengalaman pengguna

Saat menerapkan semua trik ini, penting untuk memastikan game bekerja seperti yang diharapkan di luar sana. Mengakomodasi berbagai kemampuan hardware merupakan aspek penting dari setiap pengembangan game, tetapi game web dapat menargetkan spektrum yang jauh lebih luas yang terdiri dari desktop kelas atas dan perangkat seluler berusia satu dekade sekaligus. Cara paling sederhana untuk menangani hal ini adalah dengan menawarkan setelan untuk menyesuaikan bottleneck yang paling mungkin dalam codebase Anda—untuk tugas yang menggunakan GPU dan CPU—seperti yang diungkapkan oleh profiler.

Namun, membuat profil di komputer Anda sendiri dapat mencakup sebanyak mungkin, jadi sebaiknya tutup feedback loop dengan pengguna Anda dengan cara tertentu. Untuk Jalan Lambat, saya menjalankan analisis sederhana yang melaporkan performa beserta faktor kontekstual seperti resolusi layar. Analisis ini dikirim ke backend Node dasar menggunakan socket.io, beserta masukan tertulis yang dikirimkan pengguna melalui formulir dalam game. Pada masa awal, analisis ini menemukan banyak masalah penting yang dapat dimitigasi dengan perubahan sederhana pada UX, seperti menyoroti menu setelan saat FPS rendah secara konsisten terdeteksi, atau memperingatkan pengguna mungkin perlu mengaktifkan akselerasi hardware jika performanya sangat buruk.

Jalan lambat di depan

Bahkan setelah melakukan semua langkah ini, masih ada sebagian besar basis pemain yang perlu bermain di setelan yang lebih rendah—terutama mereka yang menggunakan perangkat ringan yang tidak memiliki GPU. Meskipun rentang setelan kualitas yang tersedia menghasilkan distribusi performa yang cukup merata, hanya 52% pemain yang mencapai di atas 55 FPS.

Matriks yang ditentukan oleh setelan jarak tampilan terhadap setelan detail, yang menunjukkan rata-rata frame per detik yang dicapai pada pasangan yang berbeda. Distribusi tersebut tersebar cukup merata antara 45 dan 60, dengan 60 menjadi target untuk kinerja yang baik. Pengguna dengan setelan rendah cenderung melihat FPS yang lebih rendah daripada mereka yang menggunakan setelan tinggi, yang memperjelas perbedaan dalam kemampuan hardware klien.
Perhatikan bahwa data ini agak condong ke arah pengguna yang menjalankan browser dengan akselerasi hardware dinonaktifkan, dan sering kali menyebabkan penurunan performa dengan cara yang tidak semestinya.

Untungnya, masih ada banyak peluang untuk melakukan penghematan performa. Selain menambahkan trik rendering lebih lanjut untuk mengurangi permintaan GPU, saya berharap dapat bereksperimen dengan pekerja web dalam memparalelkan pembuatan lingkungan dalam waktu dekat, dan mungkin pada akhirnya akan diperlukan untuk memasukkan WASM atau WebGPU ke dalam codebase. Setiap kapasitas yang saya miliki akan memungkinkan lingkungan yang lebih kaya dan beragam, yang akan menjadi tujuan berkelanjutan untuk sisa proyek ini.

Seiring berjalannya project hobi, Slow Roads telah menjadi cara yang sangat memuaskan untuk menunjukkan betapa serunya game browser, dan game browser yang begitu rumit, berperforma tinggi. Jika saya telah berhasil menarik minat Anda pada WebGL, ketahuilah bahwa Jalan Lambat secara teknologi adalah contoh yang sedikit dangkal dari kemampuan penuhnya. Saya sangat menyarankan pembaca untuk menjelajahi galeri tiga.js, dan mereka yang tertarik dengan pengembangan game web secara khusus akan disambut baik di komunitas webgamedev.com.