Why is my interval clearing only halfway? - javascript

I have been working on my first major programming project and so far it has been going pretty well. My goal is to have an animation run until either an event is triggered or 15 seconds have passed. After the animation times out I want to animation to repeat. My current solution to the plan is shown below.
var animationSpeed = 40;
var animationTimout = 375;
var testTime = 1;
//some more set up variables
function onClick() {
startDrive();
setTimeout(startDrive, 15000); //****
}
function startDrive() {
var Driving = setInterval(carDriving, aniSpeed);
}
function carDriving() {
testLocation();
drawCar();
angleCalc();
newLocation();
getInfo();
}
function testLocation() {
//this code gets information on whether or not the animation should be stopped
testTime = testTime + 1
if(a === 1 || testTime > animationTimeout) {
//a test to cancel the animation, the other variables test to
clearInterval(Driving);
}
}
function drawCar() {
//draws the car
}
function angleCalc() {
//gets some info on how to move the car
}
function newLocation() {
//decides on new coords for the car based on angleCalc();
}
function getInfo() {
//gets some info I plan to use later in the project
}
When I run the code without the starred line, the whole thing works. The car animates as I want it to and stops if the conditions for stopping are met. The car freezes where it was on the canvas, and it seems as if the interval was cleared. When I add the starred line of code, the animation seems to work, but it runs twice as fast as before. I am utterly lost and nothing I try works. Please help.

The problem is likely due to the local variable defined here:
function startDrive() {
var Driving = setInterval(carDriving, aniSpeed);
}
The variable Driving is only defined in the function startDrive, it's a local variable because you are using var to define it inside the function. So when you attempt to access it inside testLocation() you are not accessing the same variable. In fact when you do clearInterval(Driving) the variable Driving isn't defined. A simple solution would be to make Driving global by removing the var:
function startDrive() {
Driving = setInterval(carDriving, aniSpeed);
}
Or you can pass the timer as a parameter inside the testLocation function. This way you will be clearing the interval properly.

Related

callback function passed to setInterval is not being called when used in a loop

I keep trying to post a question and it keeps getting closed as a duplicate, the last time it was because I forgot to add some code. However, with a little more testing I discovered that the problem is not what I initially thought it was. Here is my code:
class GameTimer {
construtor() {
this.interval = undefined;
this.time = 0; // seconds
}
start(seconds) {
this.time = seconds;
this.interval = setInterval(this.tick.bind(this), 1000);
}
tick() {
this.time--;
// for testing
console.log(this.time);
}
getTime() {
return this.time;
}
stop() {
clearInterval(this.interval);
}
reset() {
this.time = 0;
}
}
// Test
var gameTimer = new GameTimer();
var currentTime = 10;
gameTimer.start(currentTime );
while (gameTimer.getTime() > 0) {
if (gameTimer.getTime() != currentTime) {
console.log(gameTimer.getTime());
currentTime = gameTimer.getTime();
}
// This is here only to see if I am going into the loop
console.log("In loop: " + gameTimer.getTime());
}
At first, I thought tick was not getting called because I was getting stuck in the loop. In my original code I did not have the .bind(this) on with this.tick in the class to setInterval. I added that and had the same problem. I tried to post that but was kicked out again because I forgot to add it to the code the I copied into the question (I copied the old code instead of the new). Anyway, with this change it still does not work. I placed console.log(this.time) and the time was never output, again making me think that tick was not getting called. However, just to see what would happen, I commented out the while loop and sure enough tick is getting called. So even though there was a problem with the binding, which I appreciate those who pointed this out, now there seems to be another problem. Why will it not work inside the loop?

SetTimeOut() in THREE JS

I realize a project for the master degree.
I must create a three js space invaders game.
The project is well underway but i have a problem. My aliens ( THREE.Mesh object ) must be able to fire randomly.
To carry out that, i've created a function which should draw a random number. This function works.
The problem comes the animate() function. In fact i can't put a SetTimeOut() the animate() function.
The SetTimeOut() works the first time animate() is called but after there is no timer. The code executing continually without waiting the timer.
Maybe the problem coming because animate is continually called by requestAnimationFrame();
My code :
Index.html =>
if (!init())animate();
function animate(){
requestAnimationFrame( animate );
level1.animate();
render();
}
Level.js =>
Level.prototype.animate = function()
{
//Timer doesn't work
var that = this;
//Just a test with a simple console.log test
setTimeout(function() { console.log("test"); },10000);*/
this.sky.rotation.x -=0.005;
this.spaceship.fire();
for (var i=0; i<this.ducks.length;i++)
{
this.ducks[i].move();
if (this.ducks[i].is_ready_to_fire())
this.ducks[i].fire_if_ready();
}
};
With this example the program will wait 10 seconds the first time before print "test" and after the first call, print "test" without waiting.
Have you any ideas ?
Thank you very much.
Sorry for my poor english.
The question if you need a timer for your purpose.
If I understand your problem correctly, you need the aliens to fire after a random amount of time.
If you don't care about the exact amount of time and only about the aliens shooting at random occasions, I'd use a counter on each alien to count the frames until it shoots.
So your code will look something like this:
var MAX_FRAMES_TO_WAIT = 600000;
Alien.prototype.init = function() {
this.framesUntilFire = Math.round(Math.random() * MAX_FRAMES_TO_WAIT);
}
Alien.prototype.fireWhenReady = function() {
if(--this.framesUntilFire === 0) {
this.fire();
this.framesUntilFire = Math.round(Math.random() * MAX_FRAMES_TO_WAIT);
}
}
function animate() {
requestAnimationFrame(animate);
/* ... */
for (var i=0; i<this.ducks.length;i++)
{
this.ducks[i].move();
this.ducks[i].fireWhenReady();
}
That should do the trick. Be aware that this will mean that the enemies fire quicker when the framerate is higher and slower when the framerate should drop.
You can counter that with counting the framerate as well and using it as a divider to level it out.
I hope that helped you a bit!
You can avoid setting a new timer every frame by simply resetting a new timer once the previous one has finished. A simple solution for this is recursive:
Level.prototype.fireDelayed = function() {
setTimeout(function() {
if (!this.currentLevel) {
return;
}
this.fireDelayed();
this.fire();
}.bind(this), Math.random() * 1000);
};
It simply stops firing if the level is no longer the currentLevel.
Does that make sense?

javascript sprite animation in a game

I am working on a simple animation of a sprite in a little Javascript game, a mosquito that is changing state while flying towards right. These states correspond to two different images in my tile sheet.
So far I have been doing this, but the animation is very irregular:
for (var i = 0; i < mosquitos.length; i++) {
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
mosquito.update();
// rest of code non-relevant to animation here...
and then, later:
function updateAnimation () {
next();
function next () {
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous () {
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}
The two states are of course FLYINGRIGHT and FLYINGRIGHT1...
problem is that mosquito starts animating very quickly and very irregularly. I would like it to change state, i.e. every half of a second. I tried with different time periods but it is always the same effect.
I can produce a jsfiddle of the whole thing, if what I am missing is not so obvious.
Thank you for any help and insights.
Here's the game in question, from my website:
http://www.retroinvaders.net/laurasworld/src/laurasTriviaLevels.html
I think you should rethink this part:
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
I suppose you want every new 'updateAnimation' get a different mosquito (mosquitos[0], mosquitos[1] .. mosquitos[i]). But what really happens is something different. Every time you get the same mosquito (mosquitos[i]).
This is because of asynchronous behaviour of setInterval() function.
function updateAnimation () {
next();
function next() {
// every time, mosquito is equal to mosquitos[i]
// and NOT mosquitos[0], then mosquitos[1] ...
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous()
{
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}

On Meteor, a function with $inc: is being called twice, when I am attempting to only call it once

I have a game in which two people play against each other. After the clock runs down I call the function below, which is supposed to increase the current question by 1. However, it increases it by 1 TWICE.
increaseQuestion: function() {
GameCollection.update({current:true}, { $inc: { currentQuestion: 1}});
},
Here is specifically the code where it is called:
Template.gamePage.clock = function () {
var game = GameCollection.findOne({current: true});
var currentQuestion = game.currentQuestion;
var question = game.gameQuestions[currentQuestion];
var clockQuestion = Clocks.findOne({gameId: game._id, questionId: question._id});
var clock = clockQuestion.clock;
if(clock === 0) {
Meteor.call('increaseQuestion');
} else {
Meteor.call('windDown', clockQuestion, clock);
}
// format into M:SS
var min = Math.floor(clock / 60);
var sec = clock % 60;
return min + ':' + (sec < 10 ? ('0' + sec) : sec);
};
Here is the method within the code above (which could be causing problems)
Meteor.methods({
windDown: function(clockQuestion, clock) {
var interval = Meteor.setInterval(function () {
clock -= 1;
Clocks.update(clockQuestion._id, {$set: {clock: clock}});
// end of game
if (clock === 0) {
// stop the clock
Meteor.clearInterval(interval);
// declare zero or more winners
}
}, 1000);
}
});
Why is the function being called twice? I tried moving the method from both client and server folder to a server only folder and it is still called twice.
I'm surprised it's only being called twice, to be honest.
The clock helper has a dependency on the ClockQuestion document which has the same gameId as the current game. However, when that helper is run, the windDown method is called, which is going to update that ClockQuestion document, which is going to cause the helper to be reactively rerun, which is going to call the windDown method again, etc...
To be honest, this sort of control logic really should not be included in a helper function for exactly this reason - it's going to be impossible to control. windDown needs to be called by whatever it is that's making the gamePage template render in the first place, or else a Deps.autorun block, the reactivity of which you can have much more control over, as opposed to a reactive UI element which is (by design) being managed by Meteor rather than your own app explicitly.
My suspicion is this: Template.gamePage.clock uses GameCollection, so when that collection is updated, then the function will be re-run reactively.
Since increaseQuestion doesn't depend on any arguments from the client, why not just move it to the // end of game if block in windDown?

Worn out getting animation to sequence

This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question

Categories