Hoje vou explicar como criar a
lista de tarefas mais simples,
o jogo IDLE incremental mais simples em JavaScript, depois de passar menos de um dia de férias anuais. Para fazer isso, sugiro escolher um jogo de
coruja mais fácil e não pule etapas entre a
coruja oval e a coruja finalizada, um projeto vazio e o jogo finalizado.
As pessoas que sabem como fazer esses jogos ficam entediadas; pessoas que conhecem JS, recomendo olhar o código com os dedos (para evitar lesões) e ler apenas sobre mecânica. Sob o corte, um guia consistente para iniciantes.
Jogos incrementais são jogos baseados no ciclo principal [sem fim], consistindo no acúmulo de recursos, em seus gastos constantes e na aceleração da renda. Sua principal característica é o constante crescimento de números. As instruções mostrarão como criar um jogo IDLE (preguiçoso) no qual os recursos são reabastecidos com um timer, em vez de um clique ativo.
Para começar, crie um arquivo de texto chamado index.html e escreva o seguinte:
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title> </title> <style> </style> </head> <body> : 0<br> </body> </html>
No momento, ainda não há jogo, mas há apenas uma inscrição estática.
Link para o jogo no estado atual:
0df7a27 .
Todos os jogos deste gênero são baseados em dois ciclos de jogo condicionalmente infinitos.
O primeiro ciclo é o acúmulo de recursos.
O segundo ciclo é um desperdício de recursos.
Vamos implementar o primeiro ciclo.
: <span id="spnCoppersValue">0</span><br> <script> let coppers = 0; let copperGrowth = 1; myTimer = setInterval(endOfTurnCalc, 2000); function endOfTurnCalc() { coppers = coppers+copperGrowth; document.getElementById("spnCoppersValue").innerHTML = coppers; } </script>
Primeira linha
<span id="spnCoppersValue">0</span><br>
nos fornece a capacidade de acessá-lo por seu ID e alterar o conteúdo dentro das tags, no nosso caso, o valor das moedas em dinheiro.
Em segundo lugar, um script apareceu.
Criamos a variável coppers e definimos seu escopo com a palavra-chave let. Semelhante ao anúncio do copperGrowth, que será responsável pela taxa de crescimento das moedas de cobre.
Em seguida, crie um objeto no qual colocamos nosso timer, que puxará a função endOfTurnCalc a cada 2000 ms (2 segundos). A função fornece um aumento nas moedas de cobre e uma atualização da interface. Viva, metade do jogo está terminado: temos um ciclo de acumulação de recursos. A próxima tarefa é aprender como gastá-lo.
Link para o jogo no estado atual:
e5d96e1 .
Vamos adicionar um botão que nos permita gastar moedas de cobre para melhorar a mina de cobre, sugiro colocá-lo na frente da nossa linha com moedas. Um botão, seu nome, a função que o botão chama, o nome do botão é o conteúdo dentro das tags.
<button id="btnUpgCopperMine" onclick="upgCopperMine()"> , 10 </button><br>
Adicione um código que permita que o botão de atualização funcione:
let coppersUpgCost = 10; let coppersUpgLevel = 1; function upgCopperMine() { if (coppers>coppersUpgCost){ coppers = coppers-coppersUpgCost; coppersUpgLevel = coppersUpgLevel + 1; coppersUpgCost = coppersUpgCost*2; document.getElementById("spnCoppersValue").innerHTML = coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; } }
e um código que afetará a taxa de extração de novas moedas:
function endOfTurnCalc() { coppers = coppers+copperGrowth*coppersUpgLevel;; document.getElementById("spnCoppersValue").innerHTML = coppers; }
Definimos o preço para melhorar a mina, definimos o nível padrão da mina e escrevemos na função a verificação da suficiência de moedas para a atualização da mina.
Se houver dinheiro suficiente, baixe o preço da atualização, aumente o nível atual de minas, calcule o preço para a próxima melhoria; exibir os valores atuais das moedas de cobre e o custo da próxima melhoria.
Link para o jogo no estado atual:
c731ec5 .
Bem, você já pode jogar - você pode economizar e, mais agradavelmente, gastar o acumulado. Mas precisamos reforçar o sucesso - o jogador não apenas calcula o aumento da taxa de acumulação de moedas, subtraindo o novo valor do antigo, mas, de uma maneira boa, vê imediatamente a atual taxa de acumulação de moedas. Vai fazer?
Adicione mais uma linha à interface:
: <span id="spnCoppersRate">1</span> 2 <br>
Agora nossa interface é descrita pelas seguintes linhas:
<button id="btnUpgCopperMine" onclick="upgCopperMine()" style="width: 240px;"> , 10 </button><br> : <span id="spnCoppersValue">0</span><br> : <span id="spnCoppersRate">1</span> 2 <br>
Nós fazemos uma alteração no script, na função upgCopperMine ():
if (coppers>coppersUpgCost){ coppers = coppers-coppersUpgCost; coppersUpgLevel = coppersUpgLevel + 1; coppersUpgCost = coppersUpgCost*2; document.getElementById("spnCoppersValue").innerHTML = coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = copperGrowth*coppersUpgLevel; }
Link para o jogo no estado atual:
3ac06b6 .
Ótimo! Temos um jogo condicionalmente infinito. Agora precisamos parar por um segundo e pensar sobre isso - algumas pessoas gostam quando não há um objetivo final e você pode jogar até se cansar, a outra parte acredita que as condições de finitude e acessibilidade do jogo devem ser. Já fizemos um jogo para o primeiro, mas o que nos impede de fazer uma pequena alteração para que o jogo tenha um objetivo e uma condição de vitória? Vamos fazer isso.
let win_condition = 50; myTimer = setInterval(endOfTurnCalc, 2000); function endOfTurnCalc() { if (coppers < win_condition) { coppers = coppers+copperGrowth*coppersUpgLevel; document.getElementById("spnCoppersValue").innerHTML = coppers; } else { clearTimeout(myTimer); alert(" ! "+win_condition.toString()); } }
Adicionamos uma variável na qual colocamos o valor que precisamos para atingir e alteramos a função do loop, adicionando uma verificação para atingir a meta. Ao atingir a meta, limpamos o intervalo do nosso objeto com um timer e exibimos uma mensagem pop-up no navegador.
Há um pequeno ponto: alguns antivírus não gostam de alertas e bloqueiam a página para uso.
Link para o jogo no estado atual:
8fa4041 .
A próxima funcionalidade que as pessoas esperam de um jogo por mais de cinco minutos é a capacidade de salvar e carregar o jogo. Vamos dar a ela!
Adicionamos duas linhas à interface, não esquecendo de adicionar a tag de feed de linha à linha anterior:
<button id="btnSaveGame" onclick="saveGame()" style="width: 240px;"> </button><br> <button id="btnLoadGame" onclick="loadGame()" style="width: 240px;"> </button><br>
e agora vamos expandir nossos scripts para que os botões funcionem:
function saveGame() { localStorage.setItem('coppers', coppers); localStorage.setItem('coppersUpgCost', coppersUpgCost); localStorage.setItem('coppersUpgLevel', coppersUpgLevel); } function loadGame() { coppers = parseInt(localStorage.getItem('coppers')); coppersUpgCost = parseInt(localStorage.getItem('coppersUpgCost')); coppersUpgLevel = parseInt(localStorage.getItem('coppersUpgLevel')); document.getElementById("spnCoppersValue").innerHTML = coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = copperGrowth*coppersUpgLevel; }
Colocamos todos os recursos mutáveis no armazenamento local do navegador ao salvar e durante a leitura, lemos novamente e atualizamos a interface.
Link para o jogo no estado atual:
54b1ea0 .
Tudo, a parte principal do jogo está pronta.
Até o momento, revisamos:
- criação de um recurso;
- extração de recursos;
- desperdício de recursos em uma atualização do seu sistema de produção;
- reflexão da velocidade de extração do recurso;
- condição de vitória;
- escreva e leia salve o jogo no armazenamento local do navegador.
Continuar? Antes de prosseguir para o próximo tópico, ou seja, adicionar um segundo recurso ao nosso sistema (prateado), sugiro realizar uma pequena refatoração do código atual.
O que precisa ser feito como parte da refatoração?
Primeiro, vamos criar um objeto de jogo e colocar moedas de cobre, o nível das minas de cobre e assim por diante, dentro das propriedades desse objeto. Isso ajudará bastante no futuro, à medida que expandimos o armazenamento e o carregamento para novas entidades do jogo.
Em segundo lugar, tornaremos o cálculo do preço de atualização dinâmico.
Em terceiro lugar, nos livramos da duplicação de código, responsável pela atualização da interface.
O código terá o seguinte formato:
let game = { coppers: 1, copperGrowth: 1, coppersUpgCost: 10, coppersUpgLevel: 1, } let win_condition = 50; myTimer = setInterval(endOfTurnCalc, 2000); function endOfTurnCalc() { if (game.coppers < win_condition) { game.coppers = game.coppers+game.copperGrowth*game.coppersUpgLevel; document.getElementById("spnCoppersValue").innerHTML = game.coppers; } else { clearTimeout(myTimer); alert(" ! "+win_condition.toString()); } } function upgCopperMine() { if (game.coppers>game.coppersUpgCost){ game.coppers = game.coppers-game.coppersUpgCost; game.coppersUpgLevel = game.coppersUpgLevel + 1; game.coppersUpgCost = game.coppersUpgCost*2; document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += game.coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; } } function saveGame() { localStorage.setItem('coppers', game.coppers); localStorage.setItem('coppersUpgCost', game.coppersUpgCost); localStorage.setItem('coppersUpgLevel', game.coppersUpgLevel); } function loadGame() { game.coppers = parseInt(localStorage.getItem('coppers')); game.coppersUpgCost = parseInt(localStorage.getItem('coppersUpgCost')); game.coppersUpgLevel = parseInt(localStorage.getItem('coppersUpgLevel')); document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += game.coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; } </script>
É óbvio que criamos o objeto do jogo e, dentro do objeto, registramos suas propriedades e valores de propriedade. Além disso, onde quer que costumávamos acessar variáveis diretamente, agora nos referimos às mesmas propriedades do objeto do jogo.
Link para o jogo no estado atual:
8a07f4d .
Vamos agora atualizar o sistema de salvar e carregar.
function saveGame() { localStorage.setItem('gameTutorial', JSON.stringify(game)); } function loadGame() { game = JSON.parse(localStorage.getItem('gameTutorial')); document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += game.coppersUpgCost.toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; }
Agora, em vez de salvar propriedades individuais, salvamos o objeto inteiro como um todo. Mas tenha cuidado: se você adicionar métodos ao objeto, eles não serão salvos dessa maneira e a reescrita subsequente do objeto a partir do salvamento excluirá todos os métodos ...
Link para o jogo no estado atual:
8eba059 .
Vamos remover a propriedade desnecessária - o preço da atualização da mina e criar uma função para calcular a atualização, e nós a chamaremos nos locais necessários.
function coppersUpgCost() { return game.coppersUpgLevel*10; } function upgCopperMine() { if (game.coppers>=coppersUpgCost()){ game.coppers = game.coppers-coppersUpgCost(); game.coppersUpgLevel = game.coppersUpgLevel + 1; document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost().toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; } } function saveGame() { localStorage.setItem('gameTutorial', JSON.stringify(game)); } function loadGame() { game = JSON.parse(localStorage.getItem('gameTutorial')); document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost().toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; }
Link para o jogo no estado atual:
4007924 .
Finalmente, colocamos as partes repetidas do código de atualização da interface em uma função separada.
function updateUI() { document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost().toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; }
Por sua vez, em todos os outros lugares em que acessamos certos objetos DOM, chamaremos a função updateUI ():
function endOfTurnCalc() { if (game.coppers < win_condition) { game.coppers = game.coppers+game.copperGrowth*game.coppersUpgLevel; updateUI(); } else { clearTimeout(myTimer); alert(" ! "+win_condition.toString()); } } function upgCopperMine() { if (game.coppers>=coppersUpgCost()){ game.coppers = game.coppers-coppersUpgCost(); game.coppersUpgLevel = game.coppersUpgLevel + 1; updateUI(); } } function loadGame() { game = JSON.parse(localStorage.getItem('gameTutorial')); updateUI(); }
Link para o jogo no estado atual:
2245f97 .
Agora, proponho a introdução de um segundo recurso: prata, construção e aprimoramento de minas de prata.
<button id="btnUpgCopperMine" onclick="upgCopperMine()" style="width: 240px;"> , 10 </button><br> : <span id="spnCoppersValue">0</span><br> : <span id="spnCoppersRate">1</span> 2 <br> <button id="btnUpgSilverMine" onclick="upgSilverMine()" style="width: 240px;"> , 50 </button><br> : <span id="spnSilversValue">0</span><br> : <span id="spnSilversRate">1</span> 2 <br> <button id="btnSaveGame" onclick="saveGame()" style="width: 240px;"> </button><br> <button id="btnLoadGame" onclick="loadGame()" style="width: 240px;"> </button><br> <script> let game = { coppers: 1, copperGrowth: 1, coppersUpgLevel: 1, silvers: 0, silverGrowth: 1, silversUpgLevel: 0, } let win_condition = 50; let silverMineBasePriceCoppers = 100; myTimer = setInterval(endOfTurnCalc, 2000); function endOfTurnCalc() { if (game.silvers < win_condition) { game.coppers = game.coppers+game.copperGrowth*game.coppersUpgLevel; game.silvers = game.silvers+game.silverGrowth*game.silversUpgLevel; updateUI(); } else { clearTimeout(myTimer); alert(" ! "+win_condition.toString()); } } function coppersUpgCost() { return game.coppersUpgLevel*10+5; } function silversUpgCost() { return game.silversUpgLevel*10+5; } function upgCopperMine() { if (game.coppers>=coppersUpgCost()){ game.coppers = game.coppers-coppersUpgCost(); game.coppersUpgLevel = game.coppersUpgLevel + 1; updateUI(); } } function upgSilverMine() { if (game.silversUpgLevel===0){ if (game.coppers>=silverMineBasePriceCoppers){ game.coppers = game.coppers-silverMineBasePriceCoppers; game.silversUpgLevel = 1; updateUI(); } } else { if (game.silvers>=silversUpgCost()){ game.silvers = game.silvers-silversUpgCost(); game.silversUpgLevel = game.silversUpgLevel + 1; updateUI(); } } } function updateUI() { document.getElementById("spnCoppersValue").innerHTML = game.coppers; document.getElementById("btnUpgCopperMine").innerHTML = " , "; document.getElementById("btnUpgCopperMine").innerHTML += coppersUpgCost().toString(); document.getElementById("btnUpgCopperMine").innerHTML += " "; document.getElementById("spnCoppersRate").innerHTML = game.copperGrowth*game.coppersUpgLevel; document.getElementById("spnSilversValue").innerHTML = game.silvers; if (game.silversUpgLevel===0) { document.getElementById("btnUpgSilverMine").innerHTML = " , "; document.getElementById("btnUpgSilverMine").innerHTML += silverMineBasePriceCoppers.toString(); document.getElementById("btnUpgSilverMine").innerHTML += " "; } else { document.getElementById("btnUpgSilverMine").innerHTML = " , "; document.getElementById("btnUpgSilverMine").innerHTML += silversUpgCost().toString(); document.getElementById("btnUpgSilverMine").innerHTML += " "; } document.getElementById("spnSilversRate").innerHTML = game.silverGrowth*game.silversUpgLevel; } function saveGame() { localStorage.setItem('gameTutorial', JSON.stringify(game)); } function loadGame() { game = JSON.parse(localStorage.getItem('gameTutorial')); updateUI(); } </script>
O que está feito? Campos para exibir moedas de prata, a velocidade de sua extração, um botão para construir ou melhorar uma mina de prata foram adicionados à interface.
As propriedades silvers, silverGrowth, silversUpgLevel são duplicadas dentro do objeto do jogo, mas o último é definido como 0, porque não temos uma mina de prata por padrão.
Adicionada a variável silverMineBasePriceCoppers, que refletirá o preço da construção de uma mina de prata em moedas de cobre (porque não podemos pagar por uma mina de prata com prata, que ainda não possuímos).
A função coppersUpgCost foi modificada e duplicada como silversUpgCost para que o preço da atualização seja diferente de zero se o nível atual da mina for 0.
Mudamos a função de calcular o final do turno, adicionando o cálculo da receita da mina de prata de lá e também alterando a condição de vitória - agora precisamos comparar não o número de moedas de cobre, mas o número de moedas de prata.
Criamos a função upgSilverMine, na qual refletimos a lógica de debitar fundos (se ainda não existe uma mina, baixamos as moedas de cobre, se a mina já estiver construída, então deduzimos a prata para melhorar a mina).
As linhas necessárias para o silver foram adicionadas à função updateUI, bem como a lógica da saída de texto foi dividida em um botão para melhorar a mina de prata; portanto, um botão funciona tanto na construção quanto na melhoria da mina.
As funções de salvar e carregar permaneceram inalteradas.
Link para o jogo no estado atual:
03eb0eb .
Pode acontecer que haja muito mais moedas de cobre para acumular do que o necessário, enquanto a prata ainda precisa ser conquistada. Para isso, em tais jogos, via de regra, existe a possibilidade de trocar recursos diferentes entre si.
Vamos adicionar alguns botões à interface:
<button id="btnUpgCopperMine" onclick="upgCopperMine()" style="width: 240px;"> , 15 </button><br> : <span id="spnCoppersValue">0</span><br> : <span id="spnCoppersRate">1</span> 2 <br> <button id="btnBuySilver" onclick="buySilver()" style="width: 240px;"> 1 100 </button><br> <hr> <button id="btnUpgSilverMine" onclick="upgSilverMine()" style="width: 240px;"> , 50 </button><br> : <span id="spnSilversValue">0</span><br> : <span id="spnSilversRate">0</span> 2 <br> <button id="btnBuySilver" onclick="buyCoppers()" style="width: 240px;"> 100 1 </button><br> <hr> <button id="btnSaveGame" onclick="saveGame()" style="width: 240px;"> </button><br> <button id="btnLoadGame" onclick="loadGame()" style="width: 240px;"> </button><br>
E adicione algumas funções a esses botões:
function buySilver() { if (game.coppers>=100) { game.coppers = game.coppers - 100; game.silvers = game.silvers + 1; updateUI(); } } function buyCoppers() { if (game.silvers>=1) { game.coppers = game.coppers + 100; game.silvers = game.silvers - 1; updateUI(); } }
Link para o jogo no estado atual:
92219b2 .
Então, o que mais pode ser adicionado ao jogo incremental? Prestige Gear! Claro, agora isso é mais que um excesso, mas se o jogo se desenvolver mais, será útil para você. Os jogadores o amam!
Em jogos diferentes, isso é feito de maneira diferente, mas em geral - o mecanismo concede pontos de Prestígio após a conclusão (aprovação) do jogo ou é desbloqueado durante o jogo após atingir um determinado limite de objetivo.
Com a ajuda de pontos Prestige, o jogador tem a oportunidade de começar rapidamente ou um bônus permanente no mesmo jogo (é possível que na próxima missão, se o jogo for dividido naqueles), para que ele possa tentar outras táticas, mas gaste menos tempo re-passando . Assim, é possível estudar vários garfos, testando soluções diferentes e gastando menos tempo com isso do que se não houvesse esse mecanismo.
Do que precisamos para isso? Precisa de um mecanismo de reinicialização do jogo com valores padrão. Como bônus permanente, adicionaremos pontos de prestígio às propriedades anteriormente inalteradas de copperGrowth e silverGrowth.
Adicione as seguintes funções winGame, restartGameDialog, restartGame e também altere endOfTurnCalc para chamar novas funções e atualizar updateUI:
function endOfTurnCalc() { if (game.silvers < win_condition) { game.coppers = game.coppers+game.copperGrowth*game.coppersUpgLevel; game.silvers = game.silvers+game.silverGrowth*game.silversUpgLevel; updateUI(); } else { winGame(); } } function winGame() { clearTimeout(myTimer); alert(" ! "+win_condition.toString()); myRestartTimer = setInterval(restartGameDialog, 2000); } function restartGameDialog() { if (confirm(' ?')) { restartGame(); } else { clearTimeout(myRestartTimer); } } function restartGame() { game.coppers = 1; game.copperGrowth = game.copperGrowth+1; game.coppersUpgLevel = 1; game.silvers = 0; game.silverGrowth = game.silverGrowth+1; game.silversUpgLevel = 0; clearTimeout(myRestartTimer); myTimer = setInterval(endOfTurnCalc, 2000); updateUI(); } function updateUI() { ... if (game.copperGrowth!==1) { document.getElementById("divLblPrestige").innerHTML = " "+game.copperGrowth.toString(); document.getElementById("divLblPrestige").style.display = "block"; } else { document.getElementById("divLblPrestige").style.display = "none"; } }
A função winGame inicia um temporizador, após o qual é chamada uma caixa de diálogo de confirmação. No caso de confirmação, a função de reinicialização é chamada, que redefine todos os valores para o padrão, mas aumenta as propriedades do crescimento básico de moedas.
Em geral, o jogo está pronto:
- criação de um recurso;
- extração de recursos;
- desperdício de recursos em uma atualização do seu sistema de produção;
- reflexão da velocidade de extração do recurso;
- condição de vitória;
- escreva e leia salve o jogo no armazenamento local do navegador;
- adicionar mais um recurso à produção e atualização de sua produção;
- intercâmbio de recursos;
- mecanismo de reinicialização do jogo e mecanismo de prestígio.
Link para o jogo no estado atual:
92219b2 .
Finalmente, a última coisa neste artigo que quero mostrar é um mecanismo de armazenamento diferente, porque o mecanismo atual possui um, mas um problema significativo.
Imagine que você está desenvolvendo um jogo, publicando-o em um servidor da web (possivelmente nas páginas do github) e as pessoas estão jogando. Com o tempo, você faz alterações no jogo que adicionam novas propriedades ao objeto do jogo.
Agora observe suas mãos.
O jogador entra com o antigo save, que tem todas as propriedades de cobre e prata, mas não, digamos, a propriedade gold. Nosso objeto de jogo é carregado e substitui o que foi criado quando a página da web foi carregada, e agora dentro do objeto existem apenas as propriedades que foram salvas. E nós já atualizamos o código! E esse código se refere a propriedades atualizadas que não estão lá. Assim, o código começa a receber vários erros, até a completa inoperabilidade do jogo. Surpreendentemente, para corrigir esse problema, é necessário reescrever apenas duas linhas na função de download do jogo: function loadGame() { gameTemp = JSON.parse(localStorage.getItem('gameTutorial')); for (var propertyName in gameTemp) { game[propertyName] = gameTemp[propertyName]; } updateUI(); }
Agora, se você adicionar game = {gold: 1,} e inicializar com seu antigo save, onde ainda não havia ouro, o ouro permanecerá no objeto e a lógica do jogo não será interrompida em lugar algum.Link para o jogo no estado atual: 83c258d .Mais uma coisa. Transferindo salvamentos entre navegadores, vamos salvá-lo também. <hr> <button id="btnSaveGame" onclick="saveGame()" style="width: 240px;"> </button><br> <br> <button id="btnExportGame" onclick="exportGame()" style="width: 240px;"> </button><br> <div id="divLblExport" style="display: none"></div> <br> <hr> <button id="btnLoadGame" onclick="loadGame()" style="width: 240px;"> </button><br> <br> <button id="btnImportGame" onclick="importGame()" style="width: 240px;"> </button><br>
Atualize a interface. let countdown = 30; let showExport = 0; function updateUI() { ... if (showExport===1){ document.getElementById("divLblExport").style.display = "block"; } else { document.getElementById("divLblExport").style.display = "none"; } } function exportGame() { exportTimer = setInterval(exportCountdown, 1000); document.getElementById("divLblExport").innerHTML = btoa(JSON.stringify(game)); showExport = 1; updateUI(); } function exportCountdown() { if (countdown > 0) { countdown = countdown - 1; } else { clearTimeout(exportTimer); countdown = 30; showExport = 0; updateUI(); } } function importGame() { let importString = prompt(' '); gameTemp = JSON.parse(atob(importString)); for (var propertyName in gameTemp) { game[propertyName] = gameTemp[propertyName]; } updateUI(); }
Adicionamos duas variáveis de utilitário, updateUI () atualizado, gravamos três funções - export, import e uma função que altera o sinalizador de exibição da exportação, para que a exportação "antiga" fique oculta 30 segundos após a sua formação.Isso é tudo por hoje.