I've been trying to create a html whack a mole game in which a mole has a class added to it at a certain interval, another timeout function is then triggered giving the user 3 seconds to click the mole and remove the class before a check is carried out which determines if that mole still has the class attached to it.
here is a jsfiddle of my game : https://jsfiddle.net/gko9puqf/1/ and below is my 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);
}
});
it works as expected up until the timeout gets significantly shorter (around a score of 40) and the moles glitch out as if the timeout was ignored.
I've been staring at the code for hours now and have made little progress so I am hoping you can help me out! i believe its something to do with the timeouts not being completed properly.
any help is greatly appreciated, thanks.
A bit of a late addition, but was working a bit on this in-between other tasks. As stated a problem with starting multiple timers is that you need to remember the specific timers and not only the last one. In the code below that is done by keeping a 'bursting pipe' inside a single class (function) with its own timer.
Perhaps I went a bit overboard, but as also stated by others, I liked the game you made :) One of the changes is not looping through all pipes to get a pipes that's not bursting, but remove the pipe from available pipes once it's bursting. This also negates the need for numbering the divs. More details in the code-comments. Of course you're free to ignore this code completely, but since I had it about finished, am posting it anyway.
Fiddle
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: </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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With