Javascript settimeouts на html ударить крот игры

Я пытался создать html-ударить кротовую игру, в которой крот имеет класс, добавленный к нему с определенным интервалом, затем запускается другая функция тайм-аута, дающая пользователю 3 секунды, чтобы щелкнуть крота и удалить класс, прежде чем будет проведена проверка, которая определяет, есть ли у этого крота еще класс, прикрепленный к нему.

вот jsfiddle моей игры:https://jsfiddle.net/gko9puqf/1/ и ниже мой javascript.

var score = 0;
var numberofpipes = 9;
var lastnum = 0;
var intervalseconds;
var interval;
var haslost = false;
var checkpipetimer;
var timeoutfunc;
var timeoutinit;
var timers = [];
var burstingpipes = {};
var timeoutinit = setTimeout(startaburst, 3000);
$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
  clearTimeout(timeoutinit);
  if (score < 10) {
    intervalseconds = 2;
  } else if (score >= 10 && score < 25) {
    intervalseconds = 1.5;
  } else if (score >= 25 && score < 40) {
    intervalseconds = 1;
  } else if (score >= 40 && score < 60) {
    intervalseconds = 0.5;
  } else if (score >= 60) {
    intervalseconds = 0.25;
  } else if (score > 100) {
    intervalseconds = 0.1;
  }
  interval = intervalseconds * 1000;
  burstingpipe();
  //creating a loop with the new timeout value as the game gets harder.
  //also assigning it to the timeoutfunc variable so i can cancel the loop later.
  timeoutfunc = setTimeout(startaburst, interval);
}

//adds the bursting pipe attributes to the pipe intersections
function burstingpipe() {
  randomnum = Math.floor(Math.random() * 9) + 1;
  //cant be the same twice in case of overlapping
  if ((randomnum == lastnum) || $("." + randomnum).hasClass("burstingpipe")) {
    //if the random num is still valid after -1, -1
    if (((randomnum - 1) >= 0) && !($("." + (randomnum - 1)).hasClass("burstingpipe"))) {
      randomnum = (randomnum - 1);
      //add one to the random number
    } else if (((randomnum + 1) <= (numberofpipes)) && !($("." + (randomnum + 1)).hasClass("burstingpipe"))) {
      randomnum = (randomnum + 1);
    } else {
      burstingpipe();
    }
  }
  //make the lastnum the current number so we dont get 2 in a row
  lastnum = randomnum;
  randomdiv = $("." + randomnum);
  console.log(randomdiv.hasClass("burstingpipe"));
  //adds shake animation and red glow
  console.log(randomnum);
  randomdiv.addClass("burstingpipe");

  //setting a timeout of 3 seconds, so th user has 3 seconds to press each 
  //bursting pipe before it bursts.
  checkpipetimer = setTimeout(haspipeburst.bind(this, randomdiv), 3000);
}

//function to check if the pipe has burst.
function haspipeburst(pipecheck) {
  console.log(pipecheck);
  console.log(pipecheck.hasClass("burstingpipe"));
  //checking to see if the pipe still has the class attached after 3 seconds
  //and if the user has already lost.
  if (pipecheck.hasClass("burstingpipe")) {
    //if the pipe still has the class attached - game over.
    haslost = true;
    $("#result").text("you have lost");
    //stopping the loop.
    clearTimeout(timeoutfunc);
    //changing the background color to make it look like the pipe has broken.
    //(will possibly change to image in future)
    //$(".hitpoint").removeClass("burstingpipe");
    $(pipecheck).css("background-color", "#49c1e2");
  }
}

//when the user clicks a hitpoint the class is removed and they gain a point.
$(document).on('click', '.hitpoint', function() {
  if ($(this).hasClass("burstingpipe") && haslost == false) {
    $(this).removeClass("burstingpipe");
    score++;
    $("#scorecontainer").text(score);
  }
});

он работает ожидалось до тех пор, пока тайм-аут не станет значительно короче (около 40 баллов), и родинки глючат, как будто тайм-аут был проигнорирован.

Я смотрел на код в течение нескольких часов и сделал небольшой прогресс, поэтому я надеюсь, что вы можете мне помочь! я считаю, что это связано с тем, что тайм-ауты не завершаются должным образом.

любая помощь очень ценится, спасибо.

2 ответов


немного позднее добавление, но немного работал над этим между другими задачами. Как уже говорилось, проблема с запуском нескольких таймеров заключается в том, что вам нужно помнить конкретные таймеры, а не только последний. В приведенном ниже коде это делается путем сохранения "разрывной трубы" внутри одного класса (функции) со своим собственным таймером.

возможно, я немного переборщил, но, как также заявили другие, мне понравилась игра, которую вы сделали :) одно из изменений не петляет по всем трубам, чтобы получить трубы, которые не разрываются, но удаляют трубу из доступных труб, как только она разрывается. Это также отрицает необходимость нумерации divs. Подробнее в коде-комментарии. Конечно, вы можете полностью игнорировать этот код, но так как я закончил его, я все равно отправлю его.

Скрипка

var score = 24; //set higher for testing purposes
var pipes = $('.hitpoint').toArray() ,
	last = null,
  haslost = false,
	interval = 2, //start interval
	thresholds = {10: 1.5, 25: 1 , 40: 0.5, 60:0.25, 100 :1}; //interval thresholds
setTimeout(startaburst, 3000); //intial timeout (doesn't need to be cleared, because it's fired once)

$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
	if(haslost)return; //already lost
  
  if(pipes.length>0){ //pick a pipe to burst unless all pipes allready bursting
    var i;
    while(true){
      var p = pipes[i = Math.floor(Math.random() * pipes.length)]; //get random element from the available pipes
      if(p!==last || pipes.length === 1)break;
    }  
    pipes.splice(i,1); //remove pipe from available pipes
    last = p; //remember last to prevent reusing the same pipe twice
    new burstingPipe(p);
	}
  
  setTimeout(startaburst, interval * 1000); //wait until staring the new burst. interval is increased inside backInGame if the score increases
}

function burstingPipe(pipe){
	this.pipe = $(pipe);
  this.pipe.addClass("burstingpipe");  
  
  function checkBurst(){  
  	this.dispose();
  	if(haslost)return; //already lost on other pipe
    haslost = true;
    $("#result").text("you have lost");
    //changing the background color to make it look like the pipe has broken.
    //(will possibly change to image in future)    		
    this.pipe.css("background-color", "#49c1e2");
  };
  
  this.dispose=function(){
  	this.pipe.off('click'); //unbind click (no longer bursting or already burst)
  	this.pipe.removeClass("burstingpipe");    
  }
  
  function backInGame(){  	
  	clearTimeout(this.timer); //clear the burst timeout (specific for this pipe)
    this.dispose();
    pipes.push(this.pipe[0]); //make pipe available again (NB, because the array contains of DOM elements and not jquery objects, [0] is needed)
    var int = thresholds[++score]; //increase the score and check if interval should be increased for the new score
    if(int && int < interval){ 
    	//optional: some message or css that interval is increased
    	interval =int;
    }
    $("#scorecontainer").text(score);
  }
  
  this.pipe.click(backInGame.bind(this)); //bind the click
  this.timer =setTimeout(checkBurst.bind(this), 3000);
}
@keyframes shake {
  5%,
  15%,
  25%,
  35%,
  45%,
  55%,
  65%,
  75%,
  85%,
  95% {
    left: 0;
    right: 1vh;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
  10%,
  20%,
  30%,
  40%,
  50%,
  60%,
  70%,
  80%,
  90%,
  100% {
    left: 1vh;
    right: 0;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
}

@-webkit-keyframes shake {
  5%,
  15%,
  25%,
  35%,
  45%,
  55%,
  65%,
  75%,
  85%,
  95% {
    left: 0;
    right: 1vh;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
  10%,
  20%,
  30%,
  40%,
  50%,
  60%,
  70%,
  80%,
  90%,
  100% {
    left: 1vh;
    right: 0;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
}

@-moz-keyframes shake {
  5%,
  15%,
  25%,
  35%,
  45%,
  55%,
  65%,
  75%,
  85%,
  95% {
    left: 0;
    right: 1vh;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
  10%,
  20%,
  30%,
  40%,
  50%,
  60%,
  70%,
  80%,
  90%,
  100% {
    left: 1vh;
    right: 0;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
}

@-o-keyframes shake {
  5%,
  15%,
  25%,
  35%,
  45%,
  55%,
  65%,
  75%,
  85%,
  95% {
    left: 0;
    right: 1vh;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
  10%,
  20%,
  30%,
  40%,
  50%,
  60%,
  70%,
  80%,
  90%,
  100% {
    left: 1vh;
    right: 0;
    outline: none;
    border-color: red;
    box-shadow: 0 0 10px red;
  }
}

html {
  height: 100%;
  width: 100%;
}

* {
  margin: 0;
  padding: 0;
}

body {
  height: 100%;
  width: 100%;
}

#gamecontainer {
  height: 100%;
  width: 100%;
  background-color: #49c1e2;
}

#gameinformation {
  height: 10%;
  display: flex;
  flex-direction: row;
  align-items: center;
  padding-left: 10%;
}

#pipecontainer {
  height: 80%;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}

.pipe {
  height: 8vh;
  width: 100vw;
  background-color: #a5a5a5;
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}

.hitpoint {
  height: 10vh;
  width: 10vh;
  background-color: #6d6d6d;
  border-radius: 2vh;
  position: relative;
  bottom: 1vh;
  cursor: pointer;
}

#scoretext {
  color: #fff;
  font-size: 6vh;
}

#scorecontainer {
  color: #fff;
  font-size: 6vh;
}

#statusupdate {
  color: #fff;
  font-size: 6vh;
}

.burstingpipe {
  animation-name: shake;
  animation-duration: 3s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="gamecontainer">
  <div id="gameinformation">
    <p id="scoretext">Score:&nbsp;</p>
    <div id="scorecontainer">
    </div>
  </div>
  <div id="pipecontainer">
    <div class="pipe">
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
    </div>
    <div class="pipe">
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
    </div>
    <div class="pipe">
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
      <div class="hitpoint"></div>
    </div>
  </div>
  <div id="statusupdate">
    <p id="result"></p>
  </div>
</div>

мне понравилась ваша игра :)

и вот ваша проблема: когда счет увеличивается, вы уменьшаете интервал тайм-аута, чтобы иметь несколько зон шакинга одновременно. И чтобы остановить все таймер, если пользователь свободно, вы используете эту строку:

timeoutfunc = setTimeout(startaburst, интервал);

а то

clearTimeout(timeoutfunc);

это не будет работать, потому что timeoutfunc будет содержать только последний запущенный тайм-аут, а не весь запущенный тайм-аут (помните, что проверка выполняется каждые 3 секунды, тайм-аут встряхивания будет выполняться несколько раз). Поэтому вам нужен массив, чтобы сохранить все интервалы, а затем очистить их.

я обновил вашу скрипку (также удалил некоторые ненужные строки)

var score = 0;
var numberofpipes = 9;
var lastnum = 0;
var intervalseconds;
var interval;
var haslost = false;
var checkpipetimer;
var timeoutfunc = [];
var timers = [];
var burstingpipes = {};
setTimeout(startaburst, 3000);
$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
  if (score < 10) {
    intervalseconds = 2;
  } else if (score >= 10 && score < 25) {
    intervalseconds = 1.5;
  } else if (score >= 25 && score < 40) {
    intervalseconds = 1;
  } else if (score >= 40 && score < 60) {
    intervalseconds = 0.5;
  } else if (score >= 60) {
    intervalseconds = 0.25;
  } else if (score > 100) {
    intervalseconds = 0.1;
  }
  interval = intervalseconds * 1000;
  burstingpipe();
  //creating a loop with the new timeout value as the game gets harder.
  //also assigning it to the timeoutfunc variable so i can cancel the loop later.

  timeoutfunc.push(setTimeout(startaburst, interval));
}

//adds the bursting pipe attributes to the pipe intersections
function burstingpipe() {
  randomnum = Math.floor(Math.random() * 9) + 1;
  //cant be the same twice in case of overlapping
  if ((randomnum == lastnum) || $("." + randomnum).hasClass("burstingpipe")) {
    //if the random num is still valid after -1, -1
    if (((randomnum - 1) >= 0) && !($("." + (randomnum - 1)).hasClass("burstingpipe"))) {
      randomnum = (randomnum - 1);
      //add one to the random number
    } else if (((randomnum + 1) <= (numberofpipes)) && !($("." + (randomnum + 1)).hasClass("burstingpipe"))) {
      randomnum = (randomnum + 1);
    } else {
      burstingpipe();
    }
  }
  //make the lastnum the current number so we dont get 2 in a row
  lastnum = randomnum;
  randomdiv = $("." + randomnum);
  console.log(randomdiv.hasClass("burstingpipe"));
  //adds shake animation and red glow
  console.log(randomnum);
  randomdiv.addClass("burstingpipe");

  //setting a timeout of 3 seconds, so th user has 3 seconds to press each 
  //bursting pipe before it bursts.
  checkpipetimer = setTimeout(haspipeburst.bind(this, randomdiv), 3000);
}

//function to check if the pipe has burst.
function haspipeburst(pipecheck) {
  console.log(pipecheck);
  console.log(pipecheck.hasClass("burstingpipe"));
  //checking to see if the pipe still has the class attached after 3 seconds
  //and if the user has already lost.
  if (pipecheck.hasClass("burstingpipe")) {
    //if the pipe still has the class attached - game over.
    haslost = true;
    $("#result").text("you have lost");
    //stopping the loop.
    for (var i = timeoutfunc.length - 1; i >= 0; i--) {
       clearTimeout(timeoutfunc[i]);
    }
    //changing the background color to make it look like the pipe has broken.
    //(will possibly change to image in future)
    //$(".hitpoint").removeClass("burstingpipe");
    $(pipecheck).css("background-color", "#49c1e2");
  }
}

//when the user clicks a hitpoint the class is removed and they gain a point.
$(document).on('click', '.hitpoint', function() {
  if ($(this).hasClass("burstingpipe") && haslost == false) {
    $(this).removeClass("burstingpipe");
    score++;
    $("#scorecontainer").text(score);
  }
});