Tic Tac Toe (PixiJS)

gambar

Salah satu penata huruf yang rentan, bodoh, dan melamun memutuskan untuk menjadi seorang programmer dan tidak ada yang muncul darinya ... Tetapi dia tidak menyerah pemrograman dan memutuskan untuk memulai dengan program kecil ...

Ini adalah deskripsi terbaik yang bisa saya buat. Untuk tujuan inilah saya mulai menulis program-program sederhana untuk mengasah keterampilan saya, berkenalan dengan desain-desain baru dalam bahasa saya yang biasa, dan sejujurnya, itu bahkan mulai memberi saya kesenangan.

Jika Anda memiliki sedikit pengalaman pengembangan, maka artikel tersebut akan bermanfaat, dan jika Anda sudah memiliki pengalaman pengembangan, maka habiskan waktu untuk sesuatu yang lebih berharga.

Ini bukan pelatihan. Lebih mirip blog.

Tujuannya adalah membuat 3 versi game tic tac toe.

1 - Yang paling sederhana ( tanpa visual yang indah, menggunakan DOM )
2 - Berikan kesempatan untuk bermain bersama (satu komputer )
3 - Transfer semua ini ke kanvas

saya tidak akan menjelaskan tic-tac-toe, saya harap semua orang tahu prinsip permainan. Semua tautan yang bermanfaat (repositori, dokumentasi) akan ada di akhir artikel.

Apa yang terjadi dengan ini? Hm ...

Versi pertama


gambar

Ini yang paling sederhana. Sejujurnya, versi berikutnya tidak sulit ...

Kami membutuhkan tata letak dari wadah di mana kami perlu menempatkan lapangan bermain kami. Saya menambahkan data-item ke setiap elemen sejak itu Saya pikir saya akan membutuhkan pengenal, tetapi saya tidak menggunakannya.

<div class="app">
	<div class="app_block" data-item="0"></div>
	<div class="app_block" data-item="1"></div>
	<div class="app_block" data-item="2"></div>
	<div class="app_block" data-item="3"></div>
	<div class="app_block" data-item="4"></div>
	<div class="app_block" data-item="5"></div>
	<div class="app_block" data-item="6"></div>
	<div class="app_block" data-item="7"></div>
	<div class="app_block" data-item="8"></div>
</div>

Saya ingin segera memperingatkan Anda! Kode ini tidak boleh dianggap sebagai satu-satunya yang benar dan penulisan sebaliknya dianggap sebagai kesalahan. Ini solusi saya dan tidak lebih.

Begitu. Pertama, kita perlu mengikat klik pada sel. Selama klik kita pergi (bot juga, tetapi pada gilirannya) dan memeriksa sel.

var items = document.getElementsByClassName("app_block"); //  
var movePlayer = true; //  
var game = true;//  

//         .
for (var i = 0; i < items.length; i++) {
	items[i].addEventListener("click", function() {
		var collecion = document.querySelectorAll(".app_block:not(.active)");

		//   
		if(collecion.length == 1) {
			exit({win: "other"});
		}

		//     
		if( !this.classList.contains("active") ){

			//   
			if( movePlayer) {

				//   
				if(this.innerHTML == "") {
					//  
					this.classList.add("active");
					this.classList.add("active_x");
					this.innerHTML = "x"
				}
				//    
				var result = checkMap();
				if( result.val) {
					game = false;
					setTimeout(function() {
						exit(result);
					}, 10);
				}

				movePlayer = !movePlayer;
			}
			
			//    ,   
			if(game) {
				setTimeout(function() {
					botMove();
				}, 200);
			}
		}
	});
}

Bot berjalan secara acak.

function botMove() {
	var items = document.querySelectorAll(".app_block:not(.active)");

	var step = getRandomInt(items.length);

	items[ step ].innerHTML = "0";
	items[ step ].classList.add("active");
	items[ step ].classList.add("active_o");

	var result = checkMap();
	if( result.val) {
		setTimeout(function() {
			exit(result);
		}, 1);
	}

	movePlayer = !movePlayer;
}

function getRandomInt(max) {
	return Math.floor(Math.random() * Math.floor(max));
}

Pemeriksaan Sel

function checkMap() {
	var block = document.querySelectorAll(".app_block");
	var items = [];
	for (var i = 0; i < block.length; i++) { 
		items.push(block[i].innerHTML);
	}

	if ( items[0] == "x" && items[1] == 'x' && items[2] == 'x' ||
		 items[3] == "x" && items[4] == 'x' && items[5] == 'x' ||
		 items[6] == "x" && items[7] == 'x' && items[8] == 'x' ||
		 items[0] == "x" && items[3] == 'x' && items[6] == 'x' ||
		 items[1] == "x" && items[4] == 'x' && items[7] == 'x' ||
		 items[2] == "x" && items[5] == 'x' && items[8] == 'x' ||
		 items[0] == "x" && items[4] == 'x' && items[8] == 'x' ||
		 items[6] == "x" && items[4] == 'x' && items[2] == 'x' )
		return { val: true, win: "player"}
	if ( items[0] == "0" && items[1] == '0' && items[2] == '0' ||
		 items[3] == "0" && items[4] == '0' && items[5] == '0' ||
		 items[6] == "0" && items[7] == '0' && items[8] == '0' ||
		 items[0] == "0" && items[3] == '0' && items[6] == '0' ||
		 items[1] == "0" && items[4] == '0' && items[7] == '0' ||
		 items[2] == "0" && items[5] == '0' && items[8] == '0' ||
		 items[0] == "0" && items[4] == '0' && items[8] == '0' ||
		 items[6] == "0" && items[4] == '0' && items[2] == '0' )
		return { val: true, win: "bot"}

	return {val: false}
}

Di sini Anda bisa menulis semuanya melalui loop. Saya memilih cara yang lebih sederhana. Bidang saya selalu statis. Karena itu, periksa sel secara sederhana. Perlu dicatat bahwa saya mengembalikan objek untuk memeriksa di masa depan siapa yang menang. Di objek, val dan menangkan properti. Val bertanggung jawab untuk mengakhiri permainan.

Akhir permainan.

// /
function exit(obj) {
	alert(obj.win + " - game over");
	location.reload();
};

Selama klik, kami memiliki pemeriksaan untuk melihat apakah checkMap mengembalikan val: true. Jika demikian, selesaikan game.

Versi kedua


Dua pemain di komputer yang sama.

Saya mengambil bagian dari logika dari pengendali klik ke fungsi yang terpisah dan meneruskan konteks panggilan ke fungsi, karena kita perlu menentukan tombol mana yang diklik.

var items = document.getElementsByClassName("app_block");
var movePlayer = true;
var game = true;

for (var i = 0; i < items.length; i++) {
	items[i].addEventListener("click", function() {
		var collecion = document.querySelectorAll(".app_block:not(.active)");
		if(collecion.length == 1) {
			exit({win: "other"});
		}

		if( !this.classList.contains("active") ){
			if( movePlayer) {
				firstPlayer(this);
			} else {
				secondPlayer(this);
			}
		}
	});
}


Saya dibagi menjadi dua fungsi, tetapi mereka memiliki duplikasi kode. Idealnya, bagi dengan 3. Satu utama, dan dua bekerja dengan konteks.

function firstPlayer(that) {
	if(that.innerHTML == "") {
		that.classList.add("active");
		that.classList.add("active_x");
		that.innerHTML = "x"
	}

	var result = checkMap();
	if( result.val) {
		game = false;
		setTimeout(function() {
			exit(result);
		}, 10);
	}

	movePlayer = !movePlayer;
}

function secondPlayer(that) {
	if(that.innerHTML == "") {
		that.classList.add("active");
		that.classList.add("active_o");
		that.innerHTML = "0"
	}

	var result = checkMap();
	if( result.val) {
		game = false;
		setTimeout(function() {
			exit(result);
		}, 10);
	}

	movePlayer = !movePlayer;
}

Versi ketiga


Mungkin ini yang paling menarik karena Sekarang gim ini benar-benar terlihat seperti gim, bukan interaksi elemen DOM.

Saya memilih untuk bekerja PixiJS. Saya tidak bisa mengatakan apa pun tentang tanda + dan - di perpustakaan ini, tetapi saya melihat satu contoh di mana ada 60.000 elemen dan semuanya dianimasikan. Animasi ini sederhana, tetapi FPS tetap pada 50-60. Saya menyukainya dan saya mulai membaca dokumentasinya. Saya harus segera mengatakan bahwa pengetahuan saya tentang bahasa Inggris minimal, itu sulit, tetapi ada sangat sedikit artikel dalam bahasa Rusia (atau saya sudah kelihatan buruk). Saya harus menyodok dan dengan bantuan penerjemah google untuk melewati duri.

Saya hanya menonton satu laporan tentang topik ini oleh Yulia Pucnina "Animasi gemuk dengan Pixi js" .

Laporan ini berasal dari 2014 dan Anda perlu memahami bahwa API dapat berubah. Satu mata pada dokumentasi, dan yang kedua pada video. Jadi saya belajar. Butuh 4 jam untuk menulis prototipe sederhana. Lebih dekat ke kode.

Kami membuat inisialisasi default pixi

const app = new PIXI.Application({
	width: 720,
	height: 390,
	resolution: window.devicePixelRation || 1,
});
document.body.appendChild(app.view);

dan juga membuat pembungkus (wadah utama dengan sel) dan menaruhnya di kanvas kami

let wrapper = new PIXI.Container();
app.stage.addChild(wrapper);

Dalam loop, kami membuat sel kami, mengaturnya ukuran yang diperlukan, koordinat, dan juga menambahkan nilai default ke sel sebagai string kosong karena ini akan sangat berguna di masa depan dan menggantung penangan pada sel, setelah mengaktifkan bendera interaktivitas wadah.

for (let i = 0; i < 9; i++) {
    let container = new PIXI.Container();
	let block = new PIXI.TilingSprite( PIXI.Texture.from("images/bg.png") , 240, 130);
    
	container.x = (i % 3) * 240;
    container.y = Math.floor(i / 3) * 130;
    container.addChild(block);
    
    let text = new PIXI.Text("");
    text.anchor.set(0.5);
    text.x = container.width / 2;
    text.y = container.height / 2;
    container.addChild(text);
    
    container.interactive = true;    
    container.on("mousedown", function () {
        addValueInBlock(this);
    });
    
    wrapper.addChild(container);
}

addValueInBlock bertanggung jawab atas kemajuan setiap pemain. Saya tidak menemukan cara yang lebih baik daripada menyatakan gaya saya sendiri untuk setiap teks. Di sana warnanya berubah, tetapi saya tidak mengerti bagaimana mengubah warnanya. Setiap kali gaya baru harus ditetapkan ke teks. Juga ada pemeriksaan sel.

function addValueInBlock(that) {
    if(firstPlayer) {
        //    - X
        if( that.children[1].text == " " ) {
            that.children[1].style = {
                fill: "#d64c42",
                fontFamily: "Arial",
                fontSize: 32,
                fontWeight: "bold",
            };
            that.children[1].text = "x"
            
            firstPlayer = !firstPlayer;
        }
        
    } else {
        //    - 0
        
        if( that.children[1].text == " " ) {
            that.children[1].style = {
                fill: "#e2e3e8",
                fontFamily: "Arial",
                fontSize: 32,
                fontWeight: "bold",
            };
            that.children[1].text = "0"
            
             firstPlayer = !firstPlayer;
        }
    }
    endGame();
}

Mengenai verifikasi itu sendiri. checkMap. Seperti yang saya pahami, pixiJS tidak dapat mengakses elemen dengan nama atau id. Kami harus memilah-milah seluruh koleksi dalam wadah karena ini, kode terlihat rumit. Fungsi ini tidak berbeda dari yang sebelumnya, kecuali untuk parameter yang dikembalikannya.

function checkMap() {
    let items = wrapper.children;
    
	if ( items[0].children[1].text == "x" && items[1].children[1].text == 'x' && items[2].children[1].text == 'x' ||
		 items[3].children[1].text == "x" && items[4].children[1].text == 'x' && items[5].children[1].text == 'x' ||
		 items[6].children[1].text == "x" && items[7].children[1].text == 'x' && items[8].children[1].text == 'x' ||
		 items[0].children[1].text == "x" && items[3].children[1].text == 'x' && items[6].children[1].text == 'x' ||
		 items[1].children[1].text == "x" && items[4].children[1].text == 'x' && items[7].children[1].text == 'x' ||
		 items[2].children[1].text == "x" && items[5].children[1].text == 'x' && items[8].children[1].text == 'x' ||
		 items[0].children[1].text == "x" && items[4].children[1].text == 'x' && items[8].children[1].text == 'x' ||
		 items[6].children[1].text == "x" && items[4].children[1].text == 'x' && items[2].children[1].text == 'x' ) {
        return {active: true, win: "player 1"};
    }
		
	if ( items[0].children[1].text == "0" && items[1].children[1].text == '0' && items[2].children[1].text == '0' ||
		 items[3].children[1].text == "0" && items[4].children[1].text == '0' && items[5].children[1].text == '0' ||
		 items[6].children[1].text == "0" && items[7].children[1].text == '0' && items[8].children[1].text == '0' ||
		 items[0].children[1].text == "0" && items[3].children[1].text == '0' && items[6].children[1].text == '0' ||
		 items[1].children[1].text == "0" && items[4].children[1].text == '0' && items[7].children[1].text == '0' ||
		 items[2].children[1].text == "0" && items[5].children[1].text == '0' && items[8].children[1].text == '0' ||
		 items[0].children[1].text == "0" && items[4].children[1].text == '0' && items[8].children[1].text == '0' ||
		 items[6].children[1].text == "0" && items[4].children[1].text == '0' && items[2].children[1].text == '0' ) {
        return {active: true, win: "player 2"};
    }
    
	return {active: false};
}

Nah, dua fungsi terakhir bertanggung jawab untuk mengakhiri permainan dan membersihkan kanvas. Menurut saya penjelasan di sini berlebihan.

function endGame() {
    var result = checkMap();
    console.log(result);
    if( result.active ) {
        setTimeout(function() {
            alert(result.win + " - win");
            clearMap();
        }, 100);
    }
}

function clearMap() {
    console.log("sdf");
    let items = wrapper.children;
    
    for(let i = 0; i < items.length; i++) {
        console.log( items[i].children[1].text );
        items[i].children[1].text = "";
        firstPlayer = true;
    }
}

Ringkasnya, menarik untuk melakukan pengembangan dalam beberapa tahap. Ini bukan siklus pengembangan yang ideal, tetapi dengan apa yang saya butuhkan untuk memulai.

Terima kasih telah membaca dan melihat Anda segera.

Referensi



Laporan Github
Dari. Situs web PixiJS

All Articles