Um tipógrafo vulnerável, estúpido e sonhador decidiu se tornar um programador e nada disso resultou ... Mas ele não desistiu da programação e decidiu começar com pequenos programas ...Essa é a melhor descrição que eu poderia apresentar. Foi com esse objetivo que comecei a escrever programas simples para aprimorar minhas habilidades, familiarizar-me com novos designs em minha linguagem usual e, para ser sincero, isso até me trouxe prazer.Se você tiver pouca experiência em desenvolvimento, o artigo será útil e, se você já tiver experiência em desenvolvimento, dedique algum tempo a algo mais valioso.Isto não é treinamento. Mais como um blog.O objetivo era fazer 3 versões do jogo tic tac toe.1 - O mais simples ( sem um visual bonito, usando o DOM )2 - Dê a oportunidade de jogar juntos (um computador )3 - Transfira tudo isso para o canvasNão vou descrever o jogo da velha, espero que todos saibam o princípio do jogo. Todos os links úteis (repositório, documentação) estarão no final do artigo.O que aconteceu com isso? Hum ...Primeira versão
Este é o mais simples. Para ser sincero, as versões subseqüentes não são difíceis ...Precisamos de um layout do contêiner no qual precisaremos colocar nosso campo de jogo. Eu adicionei item de dados a cada elemento desde Eu pensei que precisaria de um identificador, mas não o usei.<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>
Quero avisar você imediatamente! Este código não deve ser considerado o único verdadeiro, e escrever de outra forma é considerado um erro. Esta é a minha solução e nada mais.Assim. Primeiro, precisamos vincular um clique na célula. Durante o clique, vamos (o bot também, mas por sua vez) e verificamos a célula.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);
}
}
});
}
O bot caminha aleatoriamente.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));
}
Cell Checkfunction 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}
}
Aqui você pode escrever tudo através de loops. Eu escolhi uma maneira mais simples. Meu campo é sempre estático. Portanto, uma simples verificação das células. Vale ressaltar que eu retorno o objeto para verificar no futuro quem venceu. No objeto, val e win properties. Val é responsável por terminar o jogo.Fim do jogo.
function exit(obj) {
alert(obj.win + " - game over");
location.reload();
};
Durante o clique, temos uma verificação para ver se checkMap retornou val: true. Nesse caso, complete o jogo.Segunda versão
Dois jogadores no mesmo computador.Participei da lógica do manipulador de cliques em uma função separada e passei o contexto de chamada para a função, porque precisamos determinar qual botão foi clicado.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);
}
}
});
}
Dividi em duas funções, mas elas têm duplicação de código. Idealmente, divida por 3. Um principal e dois trabalhando com o contexto.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;
}
Terceira versão
Talvez este seja o ponto mais interessante porque Agora, o jogo realmente parece um jogo, não a interação dos elementos DOM.Eu escolhi trabalhar no PixiJS. Não posso dizer nada sobre o + e - desta biblioteca, mas observei um exemplo em que havia 60.000 elementos e todos eram animados. A animação é simples, mas o FPS ficou em 50-60. Gostei e comecei a ler a documentação. Devo dizer imediatamente que meu conhecimento do idioma inglês é mínimo, foi difícil, mas há muito poucos artigos em russo (ou pareci mal). Eu tive que cutucar e com a ajuda de um tradutor do google para superar os espinhos.Eu assisti apenas um relatório sobre este tópico por Yulia Pucnina "Animação gorda com Pixi js" .O relatório é de 2014 e você precisa entender que a API pode mudar. Um olho na documentação e o segundo no vídeo. Então eu estudei. Demorou 4 horas para escrever um protótipo tão simples. Mais perto do código.Tornamos pixi de inicialização padrãoconst app = new PIXI.Application({
width: 720,
height: 390,
resolution: window.devicePixelRation || 1,
});
document.body.appendChild(app.view);
e também criar um invólucro (o contêiner principal com células) e colocá-lo em nossa telalet wrapper = new PIXI.Container();
app.stage.addChild(wrapper);
No loop, criamos nossas células, definimos os tamanhos, coordenadas e também adicionamos o valor padrão à célula como uma string vazia, pois isso será útil no futuro e travará manipuladores nas células, depois de ativar o sinalizador de interatividade do contêiner.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 é responsável pelo progresso de cada jogador. Não encontrei uma maneira melhor do que declarar meus próprios estilos para cada texto. Aí a cor muda, mas eu não entendi como mudar a cor. Cada vez que novos estilos precisam ser atribuídos ao texto. Também há uma verificação de células.function addValueInBlock(that) {
if(firstPlayer) {
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 {
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();
}
Em relação à verificação em si. checkMap. Pelo que entendi, o pixiJS não pode acessar um elemento por nome ou ID. Temos que classificar a coleção inteira no contêiner por causa disso, o código parece complicado. A função não é diferente das anteriores, exceto pelos parâmetros que ela retorna.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};
}
Bem, as duas últimas funções são responsáveis por terminar o jogo e limpar a tela. Parece-me que a explicação aqui é supérflua.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;
}
}
Para resumir, foi interessante realizar o desenvolvimento em várias etapas. Não é um ciclo de desenvolvimento ideal, mas com o que eu precisava para começar.Obrigado pela leitura e até breve.Referências
Relatório do Githubde. Site PixiJS