Sea, bajak laut - game online 3D di browser

Salam untuk pengguna Habr dan pembaca biasa. Ini adalah kisah pengembangan game online multi-pemain berbasis browser dengan grafis 3D poli rendah dan fisika 2D sederhana.



Ada banyak mini-game 2D berbasis browser di belakang, tetapi proyek seperti itu baru bagi saya. Di gamedev, memecahkan masalah yang belum Anda temui bisa sangat menarik dan menarik. Hal utama adalah jangan terjebak dengan menggerinda bagian dan memulai permainan yang bekerja sementara ada keinginan dan motivasi, jadi jangan buang waktu dan mulai berkembang!





Singkatnya permainan



Survival Fight adalah satu-satunya mode permainan saat ini. Pertempuran dari 2 hingga 6 kapal tanpa kelahiran kembali, di mana pemain yang bertahan terakhir dianggap sebagai pemenang dan menerima poin x3 dan emas.



Kontrol arcade : tombol W, A, D atau panah untuk bergerak, spasi untuk menembak kapal musuh. Anda tidak perlu membidik, jangan lewatkan, kerusakan tergantung pada keacakan dan sudut tembakan. Kerusakan yang lebih besar disertai dengan medali "tepat sasaran".



Kami mendapatkan emas dengan mengambil tempat pertama di peringkat pemain dalam 24 jam dan dalam 7 hari (reset pada pukul 00:00 waktu Moskow) dan dengan menyelesaikan tugas harian (satu dari tiga dikeluarkan untuk sehari, pada gilirannya). Ada juga emas untuk pertempuran, tetapi kurang.



Menghabiskan emasmengatur layar hitam di kapal Anda selama 24 jam. Rencana untuk menambahkan kemampuan untuk membangunkan Kraken, yang akan membawa bagian bawah kapal musuh apa pun tentakel raksasa nya :)



PVT atau pengecut zassal ? Fitur yang ingin saya terapkan bahkan sebelum memilih tema bajak laut adalah kemampuan untuk bertarung dengan teman dalam beberapa klik. Tanpa registrasi dan gerakan yang tidak perlu, Anda dapat mengirim tautan undangan ke teman-teman Anda dan menunggu sampai mereka memasuki permainan menggunakan tautan: ruang pribadi yang dapat dibuka untuk semua orang dibuat secara otomatis ketika seseorang mengikuti tautan, asalkan "penulis" tautan belum memulai yang lain pertarungan.



Tumpukan teknologi



Three.js adalah salah satu perpustakaan paling populer untuk bekerja dengan 3D di browser dengan dokumentasi yang baik dan banyak contoh berbeda. Juga, saya telah menggunakan Three.js sebelumnya - pilihannya jelas.



Kurangnya mesin permainan adalah karena kurangnya pengalaman yang relevan dan keinginan untuk belajar sesuatu yang tanpanya semuanya bekerja dengan baik :)



Node.js karena sederhana, cepat dan nyaman, walaupun saya tidak punya pengalaman di Node.js secara langsung. Saya menganggap Java sebagai alternatif, melakukan beberapa percobaan lokal, termasuk dengan soket web, tetapi tidak berani mengetahui apakah sulit untuk menjalankan Java pada VPS. Pilihan lain - Pergi, sintaksnya membuat saya berkecil hati - belum maju dalam studi sedikit pun.



Untuk soket web, gunakan modul ws di Node.js.



PHP dan MySQLpilihan kurang jelas, tetapi kriterianya masih sama - cepat dan mudah, karena ada pengalaman dalam teknologi ini.



Ternyata seperti ini:







PHP pertama-tama diperlukan untuk mengembalikan halaman web ke klien dan untuk permintaan AJAX yang langka, tetapi sebagian besar klien masih berkomunikasi dengan server game di Node.js melalui soket web.



Saya tidak ingin menghubungkan server game ke database sama sekali, jadi semuanya berjalan melalui PHP. Menurut pendapat saya, ada plus di sini, meskipun saya tidak yakin apakah itu signifikan. Misalnya, karena data yang sudah jadi datang ke Node.js dalam formulir yang diperlukan, Node.js tidak membuang waktu pemrosesan dan pertanyaan tambahan dalam database, tetapi menangani hal-hal yang lebih penting - itu "mencerna" tindakan para pemain dan mengubah keadaan dunia game di kamar.



Model dulu



Pengembangan dimulai dengan hal yang sederhana dan paling penting - model tertentu dari dunia game, menggambarkan pertempuran laut dari sudut pandang server. Kanvas 2D polos sangat ideal untuk tampilan skematis model pada layar.







Awalnya, saya menetapkan fisika "verlet" yang normal, dan memperhitungkan hambatan yang berbeda terhadap pergerakan kapal dalam arah yang berbeda relatif terhadap arah lambung kapal. Tetapi mengkhawatirkan kinerja server, saya mengganti fisika normal dengan yang paling sederhana, di mana garis besar kapal tetap hanya dalam visual, sedangkan secara fisik kapal adalah benda bulat yang bahkan tidak memiliki kelembaman. Alih-alih inersia, ada akselerasi maju terbatas.



Tembakan dan tembakan dikurangi menjadi operasi sederhana dengan vektor arah kapal dan arah tembakan. Tidak ada kerang di sini. Jika produk titik dari vektor yang dinormalisasi cocok dengan nilai yang dapat diterima dengan mempertimbangkan jarak ke target, maka akan ada tembakan dan pukulan jika pemain menekan tombol.



JavaScript sisi klien untuk rendering model dunia game, menangani pergerakan kapal dan tembakan, saya porting ke server Node.js hampir tidak berubah.



Server game



Server WebSocket Node.js hanya terdiri dari 3 skrip:



  • main.js - skrip utama yang menerima pesan WS dari pemain, membuat kamar dan membuat roda mesin berputar
  • room.js - skrip yang bertanggung jawab atas gameplay di dalam ruangan: memperbarui dunia game, mengirim pembaruan ke para pemain di dalam ruangan
  • funcs.js - termasuk kelas untuk bekerja dengan vektor, beberapa fungsi pembantu dan kelas yang mengimplementasikan daftar yang ditautkan dua kali lipat


Seiring perkembangan yang berkembang, kelas-kelas baru ditambahkan - hampir semuanya berhubungan langsung dengan gameplay dan berakhir di file room.js. Kadang-kadang nyaman untuk bekerja dengan kelas secara terpisah (dalam file terpisah), tetapi opsi semua dalam satu juga tidak buruk, asalkan tidak ada terlalu banyak kelas (lebih mudah untuk menggulir ke atas dan mengingat parameter apa yang diambil oleh metode kelas lain).



Daftar kelas server gim saat ini:



  • WaitRoom - ruang di mana pemain menunggu dimulainya pertempuran, ia memiliki metode centang sendiri yang mengirimkan pembaruan dan memulai pembuatan ruang permainan ketika lebih dari setengah pemain siap untuk berperang
  • Room โ€” , : /, ,
  • Player โ€” ยซยป :
  • Ship โ€” : , , ,
  • PhysicsEngine โ€” ,
  • PhysicsBody โ€”


Room
let upd = {p: [], t: this.gamet};
let t = Date.now();
let dt = t - this.lt;
let nalive = 0;

for (let i in this.players) {
	this.players[i].tick(t, dt);
}

this.physics.run(dt);

for (let i in this.players) {
	upd.p.push(this.players[i].getUpd());
}

this.chronology.addLast(clone(upd));
if (this.chronology.n > 30) this.chronology.remFirst();

let updjson = JSON.stringify(upd);

for (let i in this.players) {
	let pl = this.players[i];
	if (pl.ship.health > 0) nalive++;
	if (pl.deadLeave) continue;
	pl.cl.ws.send(updjson);
}

this.lt = t;
this.gamet += dt;

if (nalive <= 1) return false;
return true;




Selain kelas, ada fungsi seperti mendapatkan data pengguna, memperbarui tugas sehari-hari, mendapatkan hadiah, membeli kulit. Fungsi-fungsi ini pada dasarnya mengirim permintaan https ke PHP, yang mengeksekusi satu atau lebih query MySQL dan mengembalikan hasilnya.



Keterlambatan jaringan



Kompensasi latensi jaringan adalah bagian penting dari pengembangan game online. Pada topik ini, saya telah berulang kali membaca kembali serangkaian artikel di Habrรฉ . Dalam kasus pertempuran kapal layar, keterlambatan kompensasi bisa sederhana, tetapi Anda masih harus membuat kompromi.



Interpolasi terus dilakukan pada klien - perhitungan kondisi dunia game antara dua saat dalam waktu, data yang telah diperoleh. Ada sedikit margin waktu, yang mengurangi kemungkinan lompatan tiba-tiba, dan dengan penundaan jaringan yang signifikan dan tidak adanya data baru, interpolasi digantikan oleh ekstrapolasi. Ekstrapolasi memberikan hasil yang tidak terlalu tepat, tetapi murah untuk prosesor dan tidak tergantung pada bagaimana pergerakan kapal diimplementasikan di server, dan tentu saja, kadang-kadang dapat menyelamatkan situasi.



Saat memecahkan masalah kelambatan, banyak hal bergantung pada permainan dan kecepatannya. Saya mengorbankan respons cepat terhadap tindakan pemain demi animasi yang halus dan korespondensi yang tepat dari gambar dengan keadaan dunia game pada titik waktu tertentu. Satu-satunya pengecualian adalah salvo meriam dimainkan segera dengan menekan sebuah tombol. Sisanya dapat dikaitkan dengan hukum alam semesta dan surplus rum dari awak kapal :)



Paling depan



Sayangnya, tidak ada struktur atau hierarki kelas dan metode yang jelas. Semua JS dibagi menjadi objek dengan fungsi mereka sendiri, yang dalam arti sama. Hampir semua proyek saya sebelumnya lebih logis daripada yang ini. Ini sebagian karena tujuan pertama adalah untuk men-debug model dunia game di server dan interaksi jaringan tanpa memperhatikan antarmuka dan komponen visual dari permainan. Ketika tiba saatnya untuk menambahkan 3D, saya benar-benar menambahkannya ke versi tes yang ada, secara kasar, saya mengganti fungsi drawShip 2D dengan persis sama, tetapi 3D, meskipun dengan cara yang bersahabat itu layak untuk merevisi seluruh struktur dan menyiapkan dasar untuk perubahan di masa depan.



Kapal 3D



Three.js mendukung penggunaan model 3D siap pakai dalam berbagai format. Saya memilih format GLTF / GLB untuk diri saya sendiri, di mana tekstur dan animasi dapat disematkan, mis. pengembang seharusnya tidak bertanya-tanya "sudahkah semua tekstur dimuat?"



Saya belum pernah berurusan dengan editor 3D sebelumnya. Langkah logis adalah menghubungi spesialis pada pertukaran freelance dengan tugas menciptakan model 3D dari kapal layar dengan animasi yang tertanam dari meriam salvo. Tapi saya tidak bisa menahan perubahan kecil pada model spesialis jadi saya sendiri, dan berakhir dengan fakta bahwa saya membuat model saya dari awal di Blender. Untuk membuat model low-poly dengan hampir tanpa tekstur sederhana, sulit tanpa model siap pakai dari spesialis untuk belajar di editor 3D apa yang diperlukan untuk tugas tertentu (setidaknya secara moral :).







Shader kepada dewa shader



Alasan utama mengapa saya memerlukan shader saya adalah kemampuan untuk memanipulasi geometri objek pada kartu video selama rendering, yang memiliki kinerja yang baik. Three.js tidak hanya memungkinkan Anda untuk membuat shader Anda sendiri, tetapi juga dapat mengambil beberapa pekerjaan.



Mekanisme atau metode yang saya gunakan saat membuat sistem partikel untuk menjiwai kerusakan pada kapal, permukaan air yang dinamis atau dasar laut statis adalah sama: ShaderMaterial khusus menyediakan antarmuka yang disederhanakan untuk menggunakan shadernya (kode GLSLnya), BufferGeometry memungkinkan Anda membuat geometri dari data sewenang-wenang ...



Kosong kosong, struktur kode yang nyaman bagi saya untuk menyalin, menambah, dan memodifikasi untuk membuat objek 3D saya dengan cara yang sama:



Tampilkan Kode
let vs = `
	attribute vec4 color;
	varying vec4 vColor;

	void main(){
		vColor = color;
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
		// gl_PointSize = 5.0; // for particles
	}
`;
let fs = `
	uniform float opacity;
	varying vec4 vColor;

	void main() {
		gl_FragColor = vec4(vColor.xyz, vColor.w * opacity);
	}
`;

let material = new THREE.ShaderMaterial( {
	uniforms: {
		opacity: {value: 0.5}
	},
	vertexShader: vs,
	fragmentShader: fs,
	transparent: true
});

let geometry = new THREE.BufferGeometry();

//let indices = [];
let vertices = [];
let colors = [];

/* ... */

//geometry.setIndex( indices );
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 4 ) );

let mesh = new THREE.Mesh(geometry, material);




Kerusakan kapal



Animasi kerusakan kapal adalah partikel bergerak yang mengubah ukuran dan warnanya, perilaku yang ditentukan oleh atributnya dan kode shader GLSL. Generasi partikel (geometri dan material) terjadi di muka, lalu untuk setiap kapal dibuat instance (Mesh) partikel kerusakan sendiri (geometri adalah umum untuk semua, material dikloning). Ada beberapa atribut partikel, tetapi shader yang dibuat secara simultan mengimplementasikan awan debu besar yang bergerak lambat, dan puing-puing yang beterbangan dengan cepat, dan partikel api, yang aktivitasnya tergantung pada tingkat kerusakan kapal.







Laut



Laut juga diimplementasikan menggunakan ShaderMaterial. Setiap vertex bergerak dalam 3 arah sepanjang sinusoid, membentuk gelombang acak. Atribut menentukan amplitudo untuk setiap arah gerakan dan fase sinusoid.



Untuk mendiversifikasi warna pada air dan membuat permainan lebih menarik dan menyenangkan mata, diputuskan untuk menambahkan bagian bawah dan pulau-pulau. Warna dasar tergantung pada ketinggian / kedalaman dan bersinar melalui permukaan air menciptakan area gelap dan terang.



Dasar laut dibuat dari peta ketinggian, yang dibuat dalam 2 tahap: pertama, bagian bawah tanpa pulau dibuat dalam editor grafis (dalam kasus saya, alat dibuat -> awan dan Gaussian blur), kemudian pulau ditambahkan secara acak menggunakan Canvas JS online di jsFiddle menggambar lingkaran dan kabur. Beberapa pulau rendah, melalui mereka Anda dapat menembak lawan, yang lain memiliki ketinggian tertentu, tembakan tidak melewati mereka. Selain peta ketinggian itu sendiri, pada output saya menerima data dalam format json tentang pulau-pulau (posisi dan ukurannya) untuk fisika di server.







Apa berikutnya?



Ada banyak rencana untuk pengembangan game. Yang utama adalah mode permainan baru. Yang lebih kecil - muncul dengan bayangan / refleksi di atas air, dengan mempertimbangkan keterbatasan kinerja WebGL dan JS. Saya telah menyebutkan kesempatan untuk membangunkan Kraken :) Penyatuan pemain ke kamar berdasarkan akumulasi pengalaman mereka belum diimplementasikan. Peningkatan prioritas yang jelas, tetapi tidak terlalu tinggi adalah untuk membuat beberapa peta dasar laut dan pulau-pulau dan memilih salah satu dari mereka secara acak untuk pertempuran baru.



Anda dapat membuat banyak efek visual dengan berulang kali menggambar adegan "ke dalam memori" dan kemudian menggabungkan semua data dalam satu gambar (pada kenyataannya, itu bisa disebut post-processing), tetapi tangan saya tidak naik untuk menambah beban pada klien dengan cara ini, karena klien masih browser daripada aplikasi asli. Mungkin suatu hari saya akan memutuskan langkah ini.



Ada juga pertanyaan yang sekarang saya rasa sulit untuk dijawab: berapa banyak pemain online yang dapat bertahan dari server virtual yang murah, apakah mungkin untuk mengumpulkan setidaknya sejumlah pemain yang tertarik dan bagaimana melakukannya.



telur Paskah



Siapa yang tidak suka mengingat game komputer lama yang memberi begitu banyak emosi? Saya suka memutar ulang game Corsairs 2 (alias Sea Dogs 2) berulang kali sejauh ini. Saya tidak bisa membantu tetapi menambahkan rahasia ke permainan saya dan secara eksplisit dan tidak langsung mengingatkan pada "Corsairs 2". Saya tidak akan mengungkapkan semua kartu, tetapi saya akan memberikan petunjuk: telur Paskah saya adalah objek tertentu yang dapat Anda temukan saat menjelajahi laut (Anda tidak perlu berlayar jauh melintasi laut yang tak berujung, objek itu masuk akal, tetapi masih kemungkinan menemukan itu tidak tinggi). Telur Paskah benar-benar memperbaiki kapal yang rusak.



Apa yang terjadi



Video menit (uji dari 2 perangkat):





Tautan ke permainan: https://sailfire.pw



Ada juga formulir kontak, pesan dikirimkan kepada saya melalui telegram: https://sailfire.pw/feedback/

Tautan bagi mereka yang ingin mengikuti berita dan pembaruan terbaru: VK Public , saluran Telegram



All Articles