Pembuatan game 3d berbasis browser dari awal dalam html, css, dan js murni. Bagian 1/2

Teknologi komputasi modern memungkinkan Anda membuat game komputer yang keren! Dan sekarang, game dengan grafik 3d cukup populer, karena memainkannya, Anda terjun ke dunia fiksi dan kehilangan semua koneksi dengan kenyataan. Perkembangan internet dan teknologi browser telah memungkinkan untuk menjalankan teka-teki dan penembak di Chrome favorit Anda, Mozilla atau sesuatu yang lain di sana (mari diam tentang Explorer) online, tanpa mengunduh. Jadi, di sini saya akan memberi tahu Anda cara membuat game browser tiga dimensi yang sederhana.



Pemilihan genre, plot, dan gaya game merupakan tugas yang cukup menarik, dan keberhasilan game mungkin bergantung pada solusi dari masalah ini. Selain itu, pilihan teknologi yang menjadi dasar produk akan dibuat juga menghadirkan nuansa tersendiri. Tujuan saya adalah untuk menunjukkan dasar dasar dari proses yang menyenangkan ini, jadi saya akan membuat labirin 3 dimensi dengan desain yang sederhana. Selain itu, saya akan melakukannya dalam kode murni tanpa menggunakan pustaka dan mesin, seperti three.js (meskipun lebih baik melakukan proyek besar di atasnya) untuk menunjukkan bagaimana Anda dapat membuat mesin untuk kebutuhan Anda. Gim yang sepenuhnya ditulis sendiri bisa jadi orisinal dan karenanya menarik. Secara umum, kedua pendekatan tersebut memiliki pro dan kontra.



Saya kira jika Anda membaca artikel ini, maka Anda tertarik dengan topik membuat game untuk Google Chrome, yang berarti Anda memahami cara kerja bundel html-css-javaScript, jadi saya tidak akan membahas dasar-dasarnya, tetapi akan segera mulai mengembangkan. Di html5 dan css3, yang didukung oleh semua browser modern (Explorer tidak dihitung), dimungkinkan untuk mengatur blok dalam ruang 3-dimensi. Ada juga elemen di mana Anda dapat menggambar garis dan grafik primitif. Sebagian besar mesin browser menggunakan <canvas> karena lebih banyak hal yang dapat dilakukan padanya, dan kinerjanya lebih baik. Tetapi untuk hal-hal sederhana, sangat mungkin untuk menggunakan metode transform-3d, yang akan memakan lebih sedikit kode.



1. Alat pengembangan



Saya hanya menggunakan 2 browser untuk memeriksa situs dan game: Chrome dan Mozilla. Semua browser lain (kecuali Explorer itu sendiri) dibangun di mesin pertama, jadi saya tidak mengerti gunanya menggunakannya, karena hasilnya persis sama dengan di Chrome. Notepad ++ cukup untuk menulis kode.



2. Bagaimana ruang 3D diimplementasikan dalam html?



Mari kita lihat sistem koordinat blok:







Secara default, blok anak memiliki koordinat (kiri dan atas) 0 piksel di x dan 0 piksel di y. Offset (terjemahkan), juga 0 piksel pada ketiga sumbu. Mari tunjukkan ini dengan sebuah contoh, di mana kita akan membuat folder baru. Di dalamnya, kita akan membuat file index.html, style.css dan script.js. Mari buka index.html dan tulis yang berikut ini di sana:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world">
        </div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>


Di file style.css, mari kita setel gaya untuk elemen "container" dan "world".



#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
}
#world{
	width:300px;
	height:300px;
        background-color:#C0FFFF;
}


Ayo selamatkan. Membuka index.html dengan Chrome, kita mendapatkan:







Mari mencoba menerapkan translate3d ke elemen "dunia":



#world{
	width:300px;
	height:300px;
        background-color:#C0FFFF;
        transform:translate3d(200px,100px,0px);
}






Seperti yang Anda pahami, saya beralih ke mode layar penuh. Sekarang mari kita setel Z offset:

transform: translate3d (200px, 100px, -1000px);



Jika Anda membuka file html di browser lagi, Anda tidak akan melihat perubahan apa pun. Untuk melihat perubahannya, Anda perlu menyetel perspektif untuk objek "container":



#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
	perspective:600px;
}


Akibatnya:







Alun - alun telah menjauh dari kami. Bagaimana cara kerja perspektif di html? Mari kita lihat gambarnya:







d adalah jarak dari pengguna ke objek, dan z adalah koordinatnya. Z negatif (dalam html ini adalah translateZ) berarti kita telah memindahkan objek, dan positif - sebaliknya. Nilai perspektif menentukan nilai d. Jika properti perspektif tidak disetel, maka nilai d dianggap tak terhingga, dan dalam hal ini objek tidak berubah secara visual untuk pengguna dengan perubahan z. Dalam kasus kami, kami menetapkan d = 600px. Secara default, sudut pandang perspektif berada di tengah elemen, namun, ini bisa diubah dengan menyetel properti perspektif-origin:.



Sekarang mari kita putar "dunia" di sekitar sumbu tertentu. Ada 2 cara rotasi yang bisa digunakan di css. Yang pertama adalah rotasi di sekitar sumbu x, y, dan z. Untuk melakukan ini, gunakan properti transformasi rotateX (), rotateY (), dan rotateZ (). Yang kedua adalah rotasi di sekitar sumbu tertentu menggunakan properti rotate3d (). Kami akan menggunakan metode pertama, karena lebih cocok untuk tugas kami. Perhatikan bahwa sumbu rotasi keluar dari tengah persegi panjang!







Titik di mana transformasi terjadi dapat diubah dengan menyetel properti translate-origin :. Jadi, mari kita setel rotasi "dunia" di sepanjang sumbu x:



#world{
	width:300px;
	height:300px;
background-color:#C0FFFF;
transform:translate3d(200px,100px,0px) rotateX(45deg);
}


Kami mendapatkan:







Offset terlihat berlawanan arah jarum jam. Jika kita menjumlahkan rotateY (), maka akan didapat perpindahan sepanjang sumbu Y. Perlu diperhatikan bahwa pada saat balok diputar, sumbu rotasinya juga ikut berputar. Anda juga dapat bereksperimen dengan nilai rotasi yang berbeda.

Sekarang, di dalam blok "dunia", kami akan membuat blok lain, untuk ini kami menambahkan tag ke file html:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world">
			<div id="square1"></div>
		</div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>


Tambahkan gaya ke blok ini di style.css:



#square1{
	position:absolute;
	width:200px;
	height:200px;
	background-color:#FF0000;
}


Kita mendapatkan:







Artinya, elemen di dalam blok "dunia" akan diubah sebagai bagian dari blok ini. Mari kita coba untuk memutar "square1" sepanjang sumbu y dengan menambahkan gaya rotasi padanya:

transform: rotateY (30deg);



Akhirnya:







"Dimana rotasinya?" - Anda bertanya? Sebenarnya, seperti inilah proyeksi balok "persegi1" pada bidang yang dibentuk oleh elemen "dunia". Tapi kita tidak butuh proyeksi, tapi rotasi yang nyata. Untuk membuat semua elemen di dalam "dunia" menjadi tiga dimensi, Anda perlu menerapkan properti transform-style: preserv-3d padanya. Setelah mengganti properti di dalam daftar gaya "dunia", periksa perubahannya:







Luar biasa! Setengah dari blok "persegi" tersembunyi di balik blok biru. Untuk menampilkannya sepenuhnya, hapus warna blok "dunia", yaitu, hapus garis warna latar belakang: # C0FFFF; Jika kita menambahkan lebih banyak persegi panjang di dalam blok "dunia", maka kita dapat membuat dunia 3D. Sekarang mari kita hapus offset "dunia" dengan menghapus baris properti transformasi dalam gaya untuk elemen ini.



3. Ciptakan gerakan dalam dunia tiga dimensi



Agar pengguna dapat bergerak di seluruh dunia ini, Anda perlu menentukan penangan untuk penekanan tombol dan gerakan mouse. Kontrolnya akan standar, yang ada di sebagian besar penembak 3D. Dengan tombol W, S, A, D, kita akan maju, mundur, kiri, kanan, dengan spasi kita akan melompat (dengan kata lain, naik), dan dengan mouse kita akan mengubah arah pandangan kita. Untuk melakukan ini, mari buka file script.js yang masih kosong. Pertama, mari tambahkan variabel berikut di sana:



//   ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;


Awalnya tidak ada tombol yang ditekan. Jika kita menekan sebuah tombol, nilai variabel tertentu akan berubah menjadi 1. Jika kita melepaskannya, itu akan menjadi 0. Kita akan menerapkannya dengan menambahkan penangan untuk menekan dan melepaskan kunci:



//   

document.addEventListener("keydown", (event) =>{
	if (event.key == "a"){
		PressLeft = 1;
	}
	if (event.key == "w"){
		PressForward = 1;
	}
	if (event.key == "d"){
		PressRight = 1;
	}
	if (event.key == "s"){
		PressBack = 1;
	}
	if (event.keyCode == 32 && onGround){
		PressUp = 1;
	}
});

//   

document.addEventListener("keyup", (event) =>{
	if (event.key == "a"){
		PressLeft = 0;
	}
	if (event.key == "w"){
		PressForward = 0;
	}
	if (event.key == "d"){
		PressRight = 0;
	}
	if (event.key == "s"){
		PressBack = 0;
	}
	if (event.keyCode == 32){
		PressUp = 0;
	}
});


Angka 32 adalah kode spasi. Seperti yang Anda lihat, ada variabel onGround yang menunjukkan apakah kita berada di lapangan. Untuk saat ini, mari kita izinkan gerakan ke atas dengan menambahkan variabel onGround setelah variabel tekan ...



//    ?

var onGround = true;


Jadi, kami telah menambahkan algoritma dorong dan tarik. Sekarang kita perlu menambahkan gerakan itu sendiri. Sebenarnya, apa yang kita pindahkan. Bayangkan kita memiliki sebuah benda yang sedang kita pindahkan. Sebut saja "pion". Seperti kebiasaan untuk developer normal, kami akan membuat kelas "Player" yang terpisah untuk itu. Kelas di javaScript dibuat, anehnya, menggunakan fungsi:



function player(x,y,z,rx,ry) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.rx = rx;
	this.ry = ry;
}


Mari tempel kode ini ke script.js di awal file. Di akhir file, mari buat objek jenis ini:



//   

var pawn = new player(0,0,0,0,0);


Mari kita tulis apa arti variabel-variabel ini. x, y, z adalah koordinat awal pemain, rx, ry adalah sudut rotasinya relatif terhadap sumbu x dan y dalam derajat. Baris terakhir yang ditulis berarti kita membuat objek "pion" dengan tipe "pemain" (Saya menulis tipe secara khusus, bukan kelas, karena kelas dalam javascript memiliki arti beberapa hal lain) dengan koordinat awal nol. Saat kita memindahkan objek, koordinat dunia seharusnya tidak berubah, tetapi koordinat "pion" harus berubah. Ini dalam istilah variabel. Dan dari sudut pandang pengguna, pemain berada di satu tempat, tetapi dunia sedang bergerak. Jadi, Anda perlu memaksa program untuk mengubah koordinat pemain, menangani perubahan ini dan, pada akhirnya, menggerakkan dunia. Nyatanya, ini lebih mudah daripada kedengarannya.



Jadi, setelah memuat dokumen ke browser, kita akan menjalankan fungsi yang menggambar ulang dunia. Mari menulis fungsi gambar ulang:



function update(){
	
	//  
	
	let dx = (PressRight - PressLeft);
	let dz = - (PressForward - PressBack);
	let dy = PressUp;
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	
	//    ( )
	
	world.style.transform = 
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";
	
};


Di browser baru, dunia akan mencocokkan elemen dengan id = "world", tetapi lebih aman untuk menetapkannya sebelum fungsi update () menggunakan konstruksi berikut:



var world = document.getElementById("world");


Kami akan mengubah posisi dunia setiap 10 md (100 pembaruan per detik), di mana kami akan memulai putaran tak terbatas:



TimerGame = setInterval(update,10);


Ayo mulai permainannya. Hore, sekarang kita bisa bergerak! Namun, dunia merangkak di luar batas-batas elemen kontainer. Untuk mencegah hal ini terjadi, mari setel properti css untuknya di style.css. Tambahkan line overflow: hidden; dan lihat perubahannya. Dunia sekarang tetap berada di dalam wadah.



Ada kemungkinan Anda tidak selalu mengerti di mana Anda perlu menulis baris kode tertentu, jadi sekarang saya akan menyajikan kepada Anda file yang, saya yakin, harus Anda dapatkan:



index.html:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world">
			<div id="square1"></div>
		</div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>




style.css:

#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
	perspective:600px;
	overflow:hidden;
}
#world{
	position:absolute;
	width:300px;
	height:300px;
	transform-style:preserve-3d;
}
#square1{
	position:absolute;
	width:200px;
	height:200px;
	background-color:#FF0000;
	transform:rotateY(30deg);
}


script.js:



//  Pawn

function player(x,y,z,rx,ry) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.rx = rx;
	this.ry = ry;
}

//   ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;

//    ?

var onGround = true;

//   

document.addEventListener("keydown", (event) =>{
	if (event.key == "a"){
		PressLeft = 1;
	}
	if (event.key == "w"){
		PressForward = 1;
	}
	if (event.key == "d"){
		PressRight = 1;
	}
	if (event.key == "s"){
		PressBack = 1;
	}
	if (event.keyCode == 32 && onGround){
		PressUp = 1;
	}
});

//   

document.addEventListener("keyup", (event) =>{
	if (event.key == "a"){
		PressLeft = 0;
	}
	if (event.key == "w"){
		PressForward = 0;
	}
	if (event.key == "d"){
		PressRight = 0;
	}
	if (event.key == "s"){
		PressBack = 0;
	}
	if (event.keyCode == 32){
		PressUp = 0;
	}
});

//   

var pawn = new player(0,0,0,0,0);

//     world

var world = document.getElementById("world");

function update(){
	
	//    
	
	let dx = (PressRight - PressLeft);
	let dz = - (PressForward - PressBack);
	let dy = - PressUp;
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	
	//    ( )
	
	world.style.transform = 
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";
	
};

TimerGame = setInterval(update,10);


Jika Anda memiliki sesuatu yang berbeda, pastikan untuk memperbaikinya!



Kami telah belajar cara memindahkan karakter, tetapi kami belum tahu cara memutarnya! Perputaran karakter, tentunya akan dilakukan dengan mouse. Untuk mouse, kami akan menambahkan variabel status gerakan mouse ke variabel status tekan ... tombol:



//       ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;
var MouseX = 0;
var MouseY = 0;


Dan setelah penangan pelepas dorong, masukkan penangan gerak:



//   

document.addEventListener("mousemove", (event)=>{
	MouseX = event.movementX;
	MouseY = event.movementY;
});


Tambahkan rotasi ke fungsi pembaruan:



	//    
	
	let dx = (PressRight - PressLeft);
	let dz = - (PressForward - PressBack);
	let dy = - PressUp;
	let drx = MouseY;
	let dry = - MouseX;
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	pawn.rx = pawn.rx + drx;
	pawn.ry = pawn.ry + dry;


Perhatikan bahwa menggerakkan mouse di sepanjang sumbu y akan memutar bidak di sepanjang sumbu x dan sebaliknya. Jika kita melihat hasilnya, kita akan ngeri dengan apa yang kita lihat. Intinya adalah jika tidak ada offset, maka MouseX dan MouseY tetap sama, dan tidak sama dengan nol. Ini berarti bahwa setelah setiap iterasi pembaruan, offset misha harus disetel ulang ke nol:



//    
	
	let dx = (PressRight - PressLeft);
	let dz = - (PressForward - PressBack);
	let dy = - PressUp;
	let drx = MouseY;
	let dry = - MouseX;

//   :
	
	MouseX = MouseY = 0;

//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	pawn.rx = pawn.rx + drx;
	pawn.ry = pawn.ry + dry;


Bahkan lebih baik, kita menghilangkan inersia rotasi, tetapi rotasinya masih aneh! Untuk memahami apa yang terjadi, tambahkan div "pion" di dalam "container":



	<div id="container">
		<div id="world">
			<div id="square1"></div>
		</div>
		<div id="pawn"></div>
	</div>


Mari menatanya dengan style.css:



#pawn{
	position:absolute;
	width:100px;
	height:100px;
	top:400px;
	left:600px;
	transform:translate(-50%,-50%);
	background-color:#0000FF;
}


Mari kita periksa hasilnya. Sekarang semuanya lancar! Satu-satunya hal adalah kotak biru tetap di depan, tetapi untuk saat ini mari kita tinggalkan itu. Untuk membuat game sebagai orang pertama, dan bukan orang ketiga, Anda perlu membawa dunia lebih dekat dengan kita melalui nilai perspektif. Mari kita lakukan di script.js dalam fungsi update ():



world.style.transform = 
	"translateZ(600px)" +
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";


Sekarang Anda dapat membuat game dari orang pertama. Sembunyikan pion dengan menambahkan baris ke style.css:



#pawn{
	display:none;
	position:absolute;
	top:400px;
	left:600px;
	width:100px;
	height:100px;
	transform:translate(-50%,-50%);
	background-color:#0000FF;
}


Luar biasa. Saya harus segera mengatakan bahwa sangat sulit untuk menavigasi di dunia dengan satu kotak, jadi kami akan membuat situs. Mari tambahkan blok "square2" ke "dunia":



	<div id="world">
			<div id="square1"></div>
			<div id="square2"></div>
		</div>


Dan di style.css, tambahkan gaya untuk itu:



#square2{
	position:absolute;
	width:1000px;
	height:1000px;
	top:400px;
	left:600px;
	background-color:#00FF00;
	transform:translate(-50%,-50%) rotateX(90deg) translateZ(-100px);
}


Sekarang semuanya jelas. Yah, kurang tepat. Saat kami menekan tombol, kami bergerak dengan ketat di sepanjang sumbu X dan Z. Dan kami ingin melakukan gerakan ke arah tampilan. Mari lakukan hal berikut: di awal file script.js, tambahkan 2 variabel:



//  

var pi = 3.141592;
var deg = pi/180;


Derajat adalah pi / 180 dari satu radian. Kita harus menerapkan sinus dan cosinus, yang dihitung dari radian. Apa yang harus dilakukan? Perhatikan gambarnya:







Ketika pandangan kita diarahkan pada suatu sudut dan kita ingin maju, maka kedua koordinat akan berubah: X dan Z. Jika kita pindah ke samping, fungsi trigonometri hanya akan berpindah tempat, dan tanda di depan sinus yang dihasilkan akan berubah. Mari kita ubah persamaan offset di update ():



//    
	
	let dx = (PressRight - PressLeft)*Math.cos(pawn.ry*deg) - (PressForward - PressBack)*Math.sin(pawn.ry*deg);
	let dz = - (PressForward - PressBack)*Math.cos(pawn.ry*deg) - (PressRight - PressLeft)*Math.sin(pawn.ry*deg);	
	let dy = -PressUp;
	let drx = MouseY;
	let dry = - MouseX;


Tinjau semua file dengan cermat! Jika ada sesuatu yang salah dengan Anda, maka pasti akan ada kesalahan yang akan menghancurkan kepala Anda!



index.html:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world">
			<div id="square1"></div>
			<div id="square2"></div>
		</div>
		<div id="pawn"></div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>


style.css:



#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
	perspective:600px;
	overflow:hidden;
}
#world{
	position:absolute;
	width:inherit;
	height:inherit;
	transform-style:preserve-3d;
}
#square1{
	position:absolute;
	width:200px;
	height:200px;
	top:400px;
	left:600px;
	background-color:#FF0000;
	transform:translate(-50%,-50%) rotateY(30deg);
}
#square2{
	position:absolute;
	width:1000px;
	height:1000px;
	top:400px;
	left:600px;
	background-color:#00FF00;
	transform:translate(-50%,-50%) rotateX(90deg) translateZ(-100px);
}
#pawn{
	display:none;
	position:absolute;
	top:400px;
	left:600px;
	transform:translate(-50%,-50%);
	width:100px;
	height:100px;
	background-color:#0000FF;
}


script.js:



//  

var pi = 3.141592;
var deg = pi/180;

//  Pawn

function player(x,y,z,rx,ry) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.rx = rx;
	this.ry = ry;
}

//       ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;
var MouseX = 0;
var MouseY = 0;

//    ?

var onGround = true;

//   

document.addEventListener("keydown", (event) =>{
	if (event.key == "a"){
		PressLeft = 1;
	}
	if (event.key == "w"){
		PressForward = 1;
	}
	if (event.key == "d"){
		PressRight = 1;
	}
	if (event.key == "s"){
		PressBack = 1;
	}
	if (event.keyCode == 32 && onGround){
		PressUp = 1;
	}
});

//   

document.addEventListener("keyup", (event) =>{
	if (event.key == "a"){
		PressLeft = 0;
	}
	if (event.key == "w"){
		PressForward = 0;
	}
	if (event.key == "d"){
		PressRight = 0;
	}
	if (event.key == "s"){
		PressBack = 0;
	}
	if (event.keyCode == 32){
		PressUp = 0;
	}
});

//   

document.addEventListener("mousemove", (event)=>{
	MouseX = event.movementX;
	MouseY = event.movementY;
});


//     player

var pawn = new player(0,0,0,0,0);

//     world

var world = document.getElementById("world");

function update(){
	
	//    
	
	let dx = (PressRight - PressLeft)*Math.cos(pawn.ry*deg) - (PressForward - PressBack)*Math.sin(pawn.ry*deg);
	let dz = - (PressForward - PressBack)*Math.cos(pawn.ry*deg) - (PressRight - PressLeft)*Math.sin(pawn.ry*deg);
	let dy = - PressUp;
	let drx = MouseY;
	let dry = - MouseX;
	
	//   :
	
	MouseX = MouseY = 0;
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	pawn.rx = pawn.rx + drx;
	pawn.ry = pawn.ry + dry;

	
	//    ( )
	
	world.style.transform = 
	"translateZ(600px)" +
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";
	
};

TimerGame = setInterval(update,10);


Kami hampir menemukan gerakannya. Tapi ada ketidaknyamanan: kursor mouse hanya bisa bergerak di dalam layar. Dalam penembak tiga dimensi, Anda dapat memutar mouse selama dan sejauh yang Anda suka. Mari kita lakukan juga: saat kita mengklik layar permainan (pada "container"), kursor akan menghilang, dan kita akan dapat memutar mouse tanpa batasan pada ukuran layar. Kami mengaktifkan tangkapan mouse saat mengklik layar, yang kami letakkan penangan untuk mengklik mouse pada "wadah" di depan penangan penekanan tombol:



//     container

var container = document.getElementById("container");

//    

container.onclick = function(){
	container.requestPointerLock();
};


Sekarang ini masalah yang sama sekali berbeda. Namun, umumnya yang terbaik adalah membuat rotasi hanya terjadi saat kursor ditangkap. Mari kita perkenalkan variabel baru setelah pers ...



//    ?

var lock = false;


Tambahkan penangan untuk mengubah status penangkapan kursor (diambil atau tidak) sebelum penangan penangkap kursor (maaf untuk tautologi):



//     

document.addEventListener("pointerlockchange", (event)=>{
	lock = !lock;
});


Dan di update () tambahkan kondisi rotasi "pion":



//   ,  

	if (lock){
		pawn.rx = pawn.rx + drx;
		pawn.ry = pawn.ry + dry;
	};


Dan penangkapan mouse itu sendiri saat mengklik penampung hanya diperbolehkan jika kursor belum tertangkap:



//    

container.onclick = function(){
	if (!lock) container.requestPointerLock();
};


Kami telah sepenuhnya menangani gerakan ini. Mari kita lanjutkan untuk menghasilkan dunia



4. Memuat peta



Dunia dalam kasus kami paling mudah direpresentasikan sebagai sekumpulan persegi panjang dengan lokasi, rotasi, ukuran, dan warna yang berbeda. Tekstur juga bisa digunakan sebagai pengganti warna. Faktanya, semua dunia 3D modern dalam game adalah kumpulan segitiga dan persegi panjang yang disebut poligon. Di game-game keren, jumlahnya bisa mencapai puluhan ribu hanya dalam satu frame. Kami akan memiliki sekitar seratus, karena browser itu sendiri memiliki kinerja grafis yang rendah. Di paragraf sebelumnya, kami memasukkan blok "div" di dalam "dunia". Tetapi jika ada banyak blok seperti itu (ratusan), maka memasukkan masing-masing dari mereka ke dalam wadah sangat membosankan. Dan bisa ada banyak tingkatan. Jadi biarkan javaScript menyisipkan persegi panjang ini, bukan kita. Kami akan membuat array khusus untuk itu.



Mari buka index.html dan hapus semua blok internal dari blok "dunia":



<BODY>
	<div id="container">
		<div id="world"></div>
		<div id="pawn"></div>
	</div>
</BODY>


Seperti yang Anda lihat, tidak ada apa pun di "dunia" sekarang. Di style.css, hapus gaya untuk # square1 dan # square2 (hapus # square1 dan # square2 dari file ini sekaligus), dan sebagai gantinya buat gaya untuk kelas .square, yang akan umum untuk semua persegi panjang. Dan kami hanya akan menetapkan satu properti untuk itu:




.square{
	position:absolute;
}


Sekarang mari kita buat array persegi panjang (misalnya, kita akan mendorongnya di antara konstruktor pemain dan variabel pers ... di script.js):



//  

var map = [
		   [0,0,1000,0,180,0,2000,200,"#F0C0FF"],
		   [0,0,-1000,0,0,0,2000,200,"#F0C0FF"],
		   [1000,0,0,0,-90,0,2000,200,"#F0C0FF"],
		   [-1000,0,0,0,90,0,2000,200,"#F0C0FF"],
		   [0,100,0,90,0,0,2000,2000,"#666666"]
]


Hal ini dimungkinkan untuk melakukan ini dalam bentuk konstruktor, tetapi untuk saat ini kita akan mengelola dengan array murni, karena lebih mudah memulai siklus untuk menyusun persegi panjang melalui array, dan bukan melalui konstruktor. Saya akan menjelaskan arti angka-angka di dalamnya. Array peta berisi array satu dimensi dari 9 variabel: [,,,,,,,,]. Saya pikir Anda mengerti bahwa tiga angka pertama adalah koordinat dari pusat persegi panjang, tiga angka kedua adalah sudut rotasi dalam derajat (relatif terhadap pusat yang sama), lalu dua angka adalah dimensinya dan angka terakhir adalah latar belakang. Selain itu, latar belakang bisa berupa warna solid, gradien, atau foto. Yang terakhir ini sangat nyaman digunakan sebagai tekstur.



Kami telah menulis array, sekarang kami akan menulis fungsi yang akan mengubah array ini menjadi persegi panjang yang sebenarnya:



function CreateNewWorld(){
	for (let i = 0; i < map.length; i++){
		
		//      
		
		let newElement = document.createElement("div");
		newElement.className = "square";
		newElement.id = "square" + i;
		newElement.style.width = map[i][6] + "px";
		newElement.style.height = map[i][7] + "px";
		newElement.style.background = map[i][8];
		newElement.style.transform = "translate3d(" +
                (600 - map[i][6]/2 + map[i][0]) + "px," +
		(400 - map[i][7]/2 + map[i][1]) + "px," +
		(map[i][2]) + "px)" +
		"rotateX(" + map[i][3] + "deg)" +
		"rotateY(" + map[i][4] + "deg)" +
		"rotateZ(" + map[i][5] + "deg)";
		
		//    world
		
		world.append(newElement);
	}
}


Izinkan saya menjelaskan apa yang terjadi: kita membuat variabel baru yang menunjuk ke elemen yang baru kita buat. Kami menetapkannya sebagai id dan kelas css (inilah yang kami maksud dengan kelas kata dalam javaScript), atur lebar dengan tinggi, latar belakang dan transformasi. Patut dicatat bahwa dalam transformasi, selain koordinat pusat persegi panjang, kami menentukan offset 600 dan 400 dan setengah dimensi sehingga pusat persegi panjang tepat pada titik dengan koordinat yang diinginkan. Mari kita mulai generator dunia di depan pengatur waktu:



CreateNewWorld();
TimerGame = setInterval(update,10);


Kami sekarang melihat area dengan dinding merah muda dan lantai abu-abu. Seperti yang Anda lihat, membuat peta secara teknis tidak sulit untuk diterapkan. Hasilnya, kode Anda dalam tiga file akan terlihat seperti ini:



index.html:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world"></div>
		<div id="pawn"></div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>


style.css



#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
	perspective:600px;
	overflow:hidden;
}
#world{
	position:absolute;
	width:inherit;
	height:inherit;
	transform-style:preserve-3d;
}
.square{
	position:absolute;
}
#pawn{
	display:none;
	position:absolute;
	top:400px;
	left:600px;
	transform:translate(-50%,-50%);
	width:100px;
	height:100px;
}


script.js:



//  

var pi = 3.141592;
var deg = pi/180;

//  player

function player(x,y,z,rx,ry) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.rx = rx;
	this.ry = ry;
}

//  

var map = [
		   [0,0,1000,0,180,0,2000,200,"#F0C0FF"],
		   [0,0,-1000,0,0,0,2000,200,"#F0C0FF"],
		   [1000,0,0,0,-90,0,2000,200,"#F0C0FF"],
		   [-1000,0,0,0,90,0,2000,200,"#F0C0FF"],
		   [0,100,0,90,0,0,2000,2000,"#666666"]
]

//       ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;
var MouseX = 0;
var MouseY = 0;

//    ?

var lock = false;

//    ?

var onGround = true;

//     container

var container = document.getElementById("container");

//     

document.addEventListener("pointerlockchange", (event)=>{
	lock = !lock;
});

//    

container.onclick = function(){
	if (!lock) container.requestPointerLock();
};

//   

document.addEventListener("keydown", (event) =>{
	if (event.key == "a"){
		PressLeft = 1;
	}
	if (event.key == "w"){
		PressForward = 1;
	}
	if (event.key == "d"){
		PressRight = 1;
	}
	if (event.key == "s"){
		PressBack = 1;
	}
	if (event.keyCode == 32 && onGround){
		PressUp = 1;
	}
});

//   

document.addEventListener("keyup", (event) =>{
	if (event.key == "a"){
		PressLeft = 0;
	}
	if (event.key == "w"){
		PressForward = 0;
	}
	if (event.key == "d"){
		PressRight = 0;
	}
	if (event.key == "s"){
		PressBack = 0;
	}
	if (event.keyCode == 32){
		PressUp = 0;
	}
});

//   

document.addEventListener("mousemove", (event)=>{
	MouseX = event.movementX;
	MouseY = event.movementY;
});

//   

var pawn = new player(0,0,0,0,0);

//     world

var world = document.getElementById("world");

function update(){
	
	//    
	
	let dx =   (PressRight - PressLeft)*Math.cos(pawn.ry*deg) - (PressForward - PressBack)*Math.sin(pawn.ry*deg);
	let dz = - (PressForward - PressBack)*Math.cos(pawn.ry*deg) - (PressRight - PressLeft)*Math.sin(pawn.ry*deg);
	let dy = - PressUp;
	let drx = MouseY;
	let dry = - MouseX;
	
	//   :
	
	MouseX = MouseY = 0;
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	
	//   ,  
	
	if (lock){
		pawn.rx = pawn.rx + drx;
		pawn.ry = pawn.ry + dry;
	};

	//    ( )
	
	world.style.transform = 
	"translateZ(" + (600 - 0) + "px)" +
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";
	
};

function CreateNewWorld(){
	for (let i = 0; i < map.length; i++){
		
		//      
		
		let newElement = document.createElement("div");
		newElement.className = "square";
		newElement.id = "square" + i;
		newElement.style.width = map[i][6] + "px";
		newElement.style.height = map[i][7] + "px";
		newElement.style.background = map[i][8];
		newElement.style.transform = "translate3d(" +
		(600 - map[i][6]/2 + map[i][0]) + "px," +
		(400 - map[i][7]/2 + map[i][1]) + "px," +
		                    (map[i][2]) + "px)" +
		"rotateX(" + map[i][3] + "deg)" +
		"rotateY(" + map[i][4] + "deg)" +
		"rotateZ(" + map[i][5] + "deg)";
		
		//    world
		
		world.append(newElement);
	}
}

CreateNewWorld();
TimerGame = setInterval(update,10);


Jika semuanya baik-baik saja, lanjutkan ke item berikutnya.



5. Tabrakan pemain dengan objek dunia



Kami telah menciptakan teknik gerak, generator dunia dari array. Kita bisa bergerak di dunia yang indah. Namun, pemain kami belum berinteraksi dengannya. Agar interaksi ini terjadi, kita perlu memeriksa apakah pemain bertabrakan dengan persegi panjang atau tidak? Artinya, kami akan memeriksa tabrakan. Pertama, mari masukkan fungsi kosong:



function collision(){
	
}


Dan kami akan menyebutnya di update ():



//   :
	
	MouseX = MouseY = 0;
	
	//    
	
	collision();


Bagaimana ini bisa terjadi? Bayangkan pemain tersebut adalah bola berjari-jari r. Dan bergerak menuju persegi panjang:







Jelas, jika jarak dari bola ke bidang persegi panjang lebih besar dari r, maka tabrakan pasti tidak terjadi. Untuk mengetahui jarak ini, Anda dapat menerjemahkan koordinat pemain ke sistem koordinat persegi panjang. Mari kita tulis fungsi pemindahan dari sistem dunia ke sistem persegi panjang:



function coorTransform(x0,y0,z0,rxc,ryc,rzc){
	let x1 =  x0;
	let y1 =  y0*Math.cos(rxc*deg) + z0*Math.sin(rxc*deg);
	let z1 = -y0*Math.sin(rxc*deg) + z0*Math.cos(rxc*deg);
	let x2 =  x1*Math.cos(ryc*deg) - z1*Math.sin(ryc*deg);
	let y2 =  y1;
	let z2 =  x1*Math.sin(ryc*deg) + z1*Math.cos(ryc*deg);
	let x3 =  x2*Math.cos(rzc*deg) + y2*Math.sin(rzc*deg);
 	let y3 = -x2*Math.sin(rzc*deg) + y2*Math.cos(rzc*deg);
	let z3 =  z2;
	return [x3,y3,z3];
}


Dan fungsi kebalikannya:



function coorReTransform (x3,y3,z3,rxc,ryc,rzc){
	let x2 =  x3*Math.cos(rzc*deg) - y3*Math.sin(rzc*deg);
	let y2 =  x3*Math.sin(rzc*deg) + y3*Math.cos(rzc*deg);
	let z2 =  z3
	let x1 =  x2*Math.cos(ryc*deg) + z2*Math.sin(ryc*deg);
	let y1 =  y2;
	let z1 = -x2*Math.sin(ryc*deg) + z2*Math.cos(ryc*deg);
	let x0 =  x1;
	let y0 =  y1*Math.cos(rxc*deg) - z1*Math.sin(rxc*deg);
	let z0 =  y1*Math.sin(rxc*deg) + z1*Math.cos(rxc*deg);
	return [x0,y0,z0];
}


Mari masukkan fungsi-fungsi ini setelah fungsi update (). Saya tidak akan menjelaskan cara kerjanya, karena saya tidak ingin memberikan kursus dalam geometri analitik. Saya akan mengatakan bahwa ada rumus untuk terjemahan koordinat selama rotasi dan kami baru saja menggunakannya. Dari sudut pandang persegi panjang, pemain kita diposisikan seperti ini:







Dalam hal ini, kondisi tumbukan menjadi sebagai berikut: jika, setelah menggeser bola dengan nilai v (v adalah vektor), koordinat z berada di antara –r dan r, dan koordinat x dan y berada di dalam persegi panjang atau dipisahkan darinya dengan jumlah yang tidak lebih besar dari r, maka tabrakan diumumkan. Dalam hal ini, koordinat z dari pemain setelah shift adalah r atau - r (tergantung dari sisi mana pemain tersebut berasal). Karenanya, offset pemain diubah. Kami secara khusus memanggil collision sebelum memperbarui () koordinat pemain untuk mengubah offset tepat waktu. Dengan demikian, bola tidak akan pernah berpotongan dengan persegi panjang, seperti yang terjadi pada algoritma tabrakan lainnya. Meski secara fisik pemain akan lebih seperti kubus, kami tidak akan memperhatikan hal ini. Jadi, mari kita terapkan ini di javaScript:



function collision(){
	for(let i = 0; i < map.length; i++){
		
		//       
		
		let x0 = (pawn.x - map[i][0]);
		let y0 = (pawn.y - map[i][1]);
		let z0 = (pawn.z - map[i][2]);
		
		let x1 = x0 + dx;
		let y1 = y0 + dy;
		let z1 = z0 + dz;
		
		let point0 = coorTransform(x0,y0,z0,map[i][3],map[i][4],map[i][5]);
		let point1 = coorTransform(x1,y1,z1,map[i][3],map[i][4],map[i][5]);
		let point2 = new Array();
		
		//      
		
		if (Math.abs(point1[0])<(map[i][6]+98)/2 && Math.abs(point1[1])<(map[i][7]+98)/2 && Math.abs(point1[2]) < 50){
			point1[2] = Math.sign(point0[2])*50;
			point2 = coorReTransform(point1[0],point1[1],point1[2],map[i][3],map[i][4],map[i][5]);
			dx = point2[0] - x0;
			dy = point2[1] - y0;
			dz = point2[2] - z0;
		}
	};
}


x0, y0 dan z0 adalah koordinat awal pemain dalam sistem koordinat persegi panjang (tanpa rotasi. x1, y1 dan z1 adalah koordinat pemain setelah perpindahan tanpa tabrakan. titik0, titik0, titik1 dan titik2 adalah vektor jari-jari awal, vektor jari-jari setelah perpindahan tanpa tabrakan dan vektor jari-jari dengan tumbukan, masing-masing. map [i] [3] dan lainnya, jika Anda ingat, ini adalah sudut rotasi persegi panjang. Perhatikan bahwa dalam kondisi kami menambahkan bukan 100 ke dimensi persegi panjang, tetapi 98. Ini kruk, mengapa, pikirkan Mulailah permainan dan Anda akan melihat beberapa tabrakan berkualitas tinggi.



Seperti yang Anda lihat, semua tindakan ini terjadi di loop for untuk semua persegi panjang. Dengan jumlah yang banyak, operasi semacam itu menjadi sangat mahal, karena sudah ada 3 panggilan ke fungsi transformasi koordinat, yang juga melakukan banyak operasi matematika. Jelas, jika persegi panjang sangat jauh dari pemain, maka tidak masuk akal menghitung tumbukan. Mari tambahkan kondisi ini:




if ((x0**2 + y0**2 + z0**2 + dx**2 + dy**2 + dz**2) < (map[i][1]**2 + map[i][2]**2)){
		
			let x1 = x0 + dx;
			let y1 = y0 + dy;
			let z1 = z0 + dz;
		
			let point0 = coorTransform(x0,y0,z0,map[i][3],map[i][4],map[i][5]);
			let point1 = coorTransform(x1,y1,z1,map[i][3],map[i][4],map[i][5]);
			let point2 = new Array();
		
			//      
		
			if (Math.abs(point1[0])<(map[i][6]+98)/2 && Math.abs(point1[1])<(map[i][7]+98)/2 && Math.abs(point1[2]) < 50){
				point1[2] = Math.sign(point0[2])*50;
				point2 = coorReTransform(point1[0],point1[1],point1[2],map[i][3],map[i][4],map[i][5]);
				dx = point2[0] - x0;
				dy = point2[1] - y0;
				dz = point2[2] - z0;
			}
			
		} 


Jadi, kami telah menangani tabrakan. Kita dapat dengan mudah memanjat permukaan miring, dan munculnya bug hanya mungkin terjadi pada sistem yang lambat, jika, tentu saja, memungkinkan. Faktanya, seluruh bagian teknis utama berakhir di sana. Kami hanya perlu menambahkan hal-hal pribadi seperti gravitasi, benda, menu, suara, grafik yang indah. Tapi ini cukup mudah dilakukan, dan tidak ada hubungannya dengan mesin yang baru saja kita buat. Oleh karena itu, saya akan membicarakan hal ini di bagian selanjutnya . Sekarang periksa apa yang Anda dapatkan dengan kode saya:



index.html:



<!DOCTYPE HTML>
<HTML>
<HEAD>
	<TITLE></TITLE>
	<LINK rel="stylesheet" href="style.css">
	<meta charset="utf-8">
</HEAD>
<BODY>
	<div id="container">
		<div id="world"></div>
		<div id="pawn"></div>
	</div>
</BODY>
</HTML>
<script src="script.js"></script>


style.css



#container{
	position:absolute;
	width:1200px;
	height:800px;
	border:2px solid #000000;
	perspective:600px;
	overflow:hidden;
}
#world{
	position:absolute;
	width:inherit;
	height:inherit;
	transform-style:preserve-3d;
}
.square{
	position:absolute;
}
#pawn{
	display:none;
	position:absolute;
	top:400px;
	left:600px;
	transform:translate(-50%,-50%);
	width:100px;
	height:100px;
}


script.js:



//  

var pi = 3.141592;
var deg = pi/180;

//  player

function player(x,y,z,rx,ry) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.rx = rx;
	this.ry = ry;
}

//  

var map = [
		   [0,0,1000,0,180,0,2000,200,"#F0C0FF"],
		   [0,0,-1000,0,0,0,2000,200,"#F0C0FF"],
		   [1000,0,0,0,-90,0,2000,200,"#F0C0FF"],
		   [-1000,0,0,0,90,0,2000,200,"#F0C0FF"],
		   [0,100,0,90,0,0,2000,2000,"#666666"]
];

//       ?

var PressBack = 0;
var PressForward = 0;
var PressLeft = 0;
var PressRight = 0;
var PressUp = 0;
var MouseX = 0;
var MouseY = 0;

//    ?

var lock = false;

//    ?

var onGround = true;

//     container

var container = document.getElementById("container");

//     

document.addEventListener("pointerlockchange", (event)=>{
	lock = !lock;
});

//    

container.onclick = function(){
	if (!lock) container.requestPointerLock();
};

//   

document.addEventListener("keydown", (event) =>{
	if (event.key == "a"){
		PressLeft = 1;
	}
	if (event.key == "w"){
		PressForward = 1;
	}
	if (event.key == "d"){
		PressRight = 1;
	}
	if (event.key == "s"){
		PressBack = 1;
	}
	if (event.keyCode == 32 && onGround){
		PressUp = 1;
	}
});

//   

document.addEventListener("keyup", (event) =>{
	if (event.key == "a"){
		PressLeft = 0;
	}
	if (event.key == "w"){
		PressForward = 0;
	}
	if (event.key == "d"){
		PressRight = 0;
	}
	if (event.key == "s"){
		PressBack = 0;
	}
	if (event.keyCode == 32){
		PressUp = 0;
	}
});

//   

document.addEventListener("mousemove", (event)=>{
	MouseX = event.movementX;
	MouseY = event.movementY;
});

//   

var pawn = new player(-900,0,-900,0,0);

//     world

var world = document.getElementById("world");

function update(){
	
	//    
	
	dx =   (PressRight - PressLeft)*Math.cos(pawn.ry*deg) - (PressForward - PressBack)*Math.sin(pawn.ry*deg);
	dz = - (PressForward - PressBack)*Math.cos(pawn.ry*deg) - (PressRight - PressLeft)*Math.sin(pawn.ry*deg);
	dy = - PressUp;
	drx = MouseY;
	dry = - MouseX;
	
	//   :
	
	MouseX = MouseY = 0;
	
	//    
	
	collision();
	
	//    
	
	pawn.x = pawn.x + dx;
	pawn.y = pawn.y + dy;
	pawn.z = pawn.z + dz;
	console.log(pawn.x + ":" + pawn.y + ":" + pawn.z);
	
	//   ,  
	
	if (lock){
		pawn.rx = pawn.rx + drx;
		pawn.ry = pawn.ry + dry;
	};

	//    ( )
	
	world.style.transform = 
	"translateZ(" + (600 - 0) + "px)" +
	"rotateX(" + (-pawn.rx) + "deg)" +
	"rotateY(" + (-pawn.ry) + "deg)" +
	"translate3d(" + (-pawn.x) + "px," + (-pawn.y) + "px," + (-pawn.z) + "px)";
	
};

function CreateNewWorld(){
	for (let i = 0; i < map.length; i++){
		
		//      
		
		let newElement = document.createElement("div");
		newElement.className = "square";
		newElement.id = "square" + i;
		newElement.style.width = map[i][6] + "px";
		newElement.style.height = map[i][7] + "px";
		newElement.style.background = map[i][8];
		newElement.style.transform = "translate3d(" +
		(600 - map[i][6]/2 + map[i][0]) + "px," +
		(400 - map[i][7]/2 + map[i][1]) + "px," +
		(map[i][2]) + "px)" +
		"rotateX(" + map[i][3] + "deg)" +
		"rotateY(" + map[i][4] + "deg)" +
		"rotateZ(" + map[i][5] + "deg)";
		
		//    world
		
		world.append(newElement);
	}
}

function collision(){
	for(let i = 0; i < map.length; i++){
		
		//       
		
		let x0 = (pawn.x - map[i][0]);
		let y0 = (pawn.y - map[i][1]);
		let z0 = (pawn.z - map[i][2]);
		
		if ((x0**2 + y0**2 + z0**2 + dx**2 + dy**2 + dz**2) < (map[i][6]**2 + map[i][7]**2)){
		
			let x1 = x0 + dx;
			let y1 = y0 + dy;
			let z1 = z0 + dz;
		
			let point0 = coorTransform(x0,y0,z0,map[i][3],map[i][4],map[i][5]);
			let point1 = coorTransform(x1,y1,z1,map[i][3],map[i][4],map[i][5]);
			let point2 = new Array();
		
			//      
		
			if (Math.abs(point1[0])<(map[i][6]+98)/2 && Math.abs(point1[1])<(map[i][7]+98)/2 && Math.abs(point1[2]) < 50){
				point1[2] = Math.sign(point0[2])*50;
				point2 = coorReTransform(point1[0],point1[1],point1[2],map[i][3],map[i][4],map[i][5]);
				dx = point2[0] - x0;
				dy = point2[1] - y0;
				dz = point2[2] - z0;
			}
			
		}
	};
}

function coorTransform(x0,y0,z0,rxc,ryc,rzc){
	let x1 =  x0;
	let y1 =  y0*Math.cos(rxc*deg) + z0*Math.sin(rxc*deg);
	let z1 = -y0*Math.sin(rxc*deg) + z0*Math.cos(rxc*deg);
	let x2 =  x1*Math.cos(ryc*deg) - z1*Math.sin(ryc*deg);
	let y2 =  y1;
	let z2 =  x1*Math.sin(ryc*deg) + z1*Math.cos(ryc*deg);
	let x3 =  x2*Math.cos(rzc*deg) + y2*Math.sin(rzc*deg);
 	let y3 = -x2*Math.sin(rzc*deg) + y2*Math.cos(rzc*deg);
	let z3 =  z2;
	return [x3,y3,z3];
}

function coorReTransform(x3,y3,z3,rxc,ryc,rzc){
	let x2 =  x3*Math.cos(rzc*deg) - y3*Math.sin(rzc*deg);
	let y2 =  x3*Math.sin(rzc*deg) + y3*Math.cos(rzc*deg);
	let z2 =  z3
	let x1 =  x2*Math.cos(ryc*deg) + z2*Math.sin(ryc*deg);
	let y1 =  y2;
	let z1 = -x2*Math.sin(ryc*deg) + z2*Math.cos(ryc*deg);
	let x0 =  x1;
	let y0 =  y1*Math.cos(rxc*deg) - z1*Math.sin(rxc*deg);
	let z0 =  y1*Math.sin(rxc*deg) + z1*Math.cos(rxc*deg);
	return [x0,y0,z0];
}

CreateNewWorld();
TimerGame = setInterval(update,10);



All Articles