Javascript setInterval: multiple intervals - javascript

I'm playing with animations. I'm trying to have two circles move a specified distance and then stop. The issue I'm having is with multiple intervals. I assign each interval to a unique variable, and clearInterval that variable, but the first interval continues on.
Here is the code:
function moveRight(player){
if(player==1){
currentLoc = p1Loc[0]; //global var with player's current location
intervalR1 = setInterval(function(){
p1Loc[0]+=1;
draw(); //function that redraws the canvas
if(p1Loc[0]>currentLoc+wu){ //wu is a single "width-unit"
clearInterval(intervalR1);
}
},5)
}
else{
currentLoc = p2Loc[0];
intervalR2 = setInterval(function(){
p2Loc[0]+=1;
draw();
if(p2Loc[0]>currentLoc+wu){
clearInterval(intervalR2);
}
},5)
}
}
Then, let's say I give the following instructions within a while loop:
instructions = ["moveRight(1)", "moveRight(2)"];
for(instruction in instructions){
eval(instructions[instruction]); //I know, eval(), ugh. Is just temporary
}
What ends up happening is, both players start moving right, but player 2 stops after a single wu, or width-unit while player one keeps going. If i change the instructions to only "moveRight(1);", then player one moves a single wu and stops.
What's going on here?

This is just a guess, since is a bit hard to tell what is going on with only part of the code, but could it be that you are assigning currnetLoc twice, and thus the first player's location will always be always be compared against p2Loc[0]?
So when you call moveRight(1) it sets currentLoc to p1Loc[0] then proceeds. Then immediately after you call moveRight(2) which sets currentLoc to p2Loc[0]. So now the comparison for the interval for player 1 is no longer p1Loc[0]>currentLoc+wu, but rather p2Loc[0]>currentLoc+wu. Depending on the value of p2Loc this may always be false.

This line:
currentLoc = p1Loc[0]; //global var with player's current location
is kinda scary. Don't use a global for this, especially when you re-assign its value on the second entry to the function:
currentLoc = p2Loc[0];
That's likely your problem.
If you need to keep track of a single player's position, create a player object and keep track of it in there, something like:
function Player(number) {
this.number = number
this.position = 0 //some default initial position
}
and pass this into moveRight()

Related

why does current tab pause every time when I switch tabs in browser?

I loaded navMesh and animal using three.js. Animal is moving on map. But when I changed the tab animal moving pause and if again open that tab animal moving again from pause point. How to play continue animation either tab changed or background of tab.
Jsfiddle
function animate() {
var currTime = window.performance.now();
var delta = (currTime - lastFrameTime) / 1000;
var dTime = Math.min(delta, maxFrameTime);
elapsedTime += delta;
lastFrameTime = currTime;
tick(dTime);
requestAnimationFrame( animate );
render();
}
This is a feature of setAnimationFrame at work. Browsers limit the processing on inactive tabs to save power consumption and CPU cycles. For more information: https://stackoverflow.com/a/5927432/7316502
It doesn't make sense to play an animation that people can't see. However, it might make sense to update the position of the creature in the background and display its current position when the user returns to the tab, given its target point and velocity.
If I understand correctly, the desired outcome is to click a point, have the monster start moving, and then when you tab away and tab back, it's as though the monster has moved the same amount as if you were watching.
You could use the Page Visibility API to help do this: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
On a visibilityChange, you can check to see if window.hidden===true. If so, pause the simulation and await another visibilityChange event for which window.hidden===false.
When focus returns to the tab, calculate the time difference between the current time and the time the window became hidden. Fast-forward your rendering to this time point. Then continue displaying animation.
Here is an example of what this might look like.
I placed this in your init() function.
function handleVisibilityChange() {
if (document.hidden) {
windowHidden=true;
hideTime=window.performance.now();
} else {
windowHidden=false;
var timeDiff=(window.performance.now()-hideTime)/1000;
console.log('Page invisible for ',timeDiff,' seconds, jump ahead.');
tick(timeDiff);
render();
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
Then I placed a check in your tick function to check the windowHidden flag:
if (!level||windowHidden) { return; }
(almost-complete) example: https://jsfiddle.net/thmsdnnr/thk80hsh/2/
Note that this does not quite work the way you'd intend: currently the monster just proceeds on the path toward the next node while the window is hidden. This is why when you tab back, the monster is off the path, proceeds back to the last node it was headed toward, and then continues.
Since the monster is following a path with multiple points and different directed segments, you'd need to refactor your tick function, or (better choice, I think) write another one entirely, that takes the time interval the window was hidden for and calculates the number of nodes on the path the monster would have passed given its velocity and time, removes those nodes, and renders the monster in the correct position.
requestAnimationFrame passes the current time to the function it calls, so you can declare function animate(currTime) {...} and then remove your own currTime. In this example, delta will be either the amount of time between frames, or the amount of time that passed between leaving and returning to the current tab.
function animate(currTime) { // <-- currTime is populated by requestAnimationFrame
var delta = (currTime - lastFrameTime) / 1000;
// I don't think you need dTime anymore
elapsedTime += delta;
lastFrameTime = currTime;
tick(delta);
requestAnimationFrame( animate );
render();
}

JS getting variable inside the function

I have a function to do something on onClick event.
function onDocumentMouseUp( event ) {
createsprite(clickedposition);
}
createsprite(clickedposition) {
var sprite = JSONSPITE // this add correctly sprite to my scene;
//// here timer function 1 sec
setTimeout(function(){ remove(sprite); }, 1000);
}
Everything works correctly.
But when i click more than once, i have still my variable sprite as a last added one. (yes i know that is correct). And logically remove() have affected only that last one.
What is the correct solution to handle work with unknown amount of same variables.
Purpose it too remove each sprite after one second after click one by one in the order of creation.
How can i reach only one variable in the same function, even when there are more variables with the same name on each function init.
You have defined sprite as a global variable. It will exist not only in that function, but is created on the window object.
If you want to remove only the sprite you created in that function, you should make it a variable
var sprite = JSONSPITE; // typo?
This way, the scope of the sprite variable is just that function.
EDIT: OP has changed their question, making the previous explanation seem obsolete
If you want to create multiple sprites, and store them somewhere so you can access them before they are deleted, you might want to create an array of sprites.
sprites = [];
function onDocumentMouseUp( event ) {
createsprite(clickedposition);
}
function createsprite(clickedposition) {
sprites.push(JSONSPITE); // Adds the current sprite to the end of the array
setTimeout(function(){
var firstSprite = sprites.shift(); // Takes the first sprite out of the array
remove(firstSprite);
}, 1000);
}

variable doesn't update when function restarts (javascript)

I'm going to try my best to ask this question without a huge wall of code. Basically I have written a very simple math quiz game. In this game you select a difficulty, the number of questions you want, and the game starts. It asks that number of questions and then you get a score and then the game is over. However, you can restart the game. When you restart, it simply returns you to the home screen, then you can select your options again. The only problem is, we need to keep track of the number of questions remaining in the quiz, the first time around, it works well. We pass numQuestions to the game function. The second time, however, even if I pass numQuestions=10, the value remains 0 from the first time I played the game. Here is the game function:
function startGame(difficulty,numQuestions,score){
// begins game, excluded that come its just some acsii art
// asks question
var q = new question(difficulty);
$("#gameInside").html(q.string);
$("#gameInside").data('answer',q.answer);
// gives answer options
for (var ii=0;ii<=3;ii++){
$("#answers").append("<button class='answerButton'>"+q.answerArray[ii]+"</button>")
}
// starts timer
var b = document.getElementById("timer");
timer = new stopWatch(b, {delay: 100});
timer.start();
},5500)
// when answer is clicked, go here
$("#gameScreen").on("click",".answerButton",function() {
// this seems to be the problem: on the second time I play the game, numQuestions remains the value from the first game and continues to go down (-1,-2) and since my selector is (>0), the else statement fires.
numQuestions--;
var time = parseFloat($("#timer span").html());
var correct = parseFloat($("#gameInside").data('answer'));
var userAnswer = parseFloat($(this).html());
if (correct==userAnswer)
tempScore = Math.round(calculateScore(time)*100)/100;
else
tempScore = 0;
score += tempScore;
$("#score").html(Math.round(score*100)/100);
if (numQuestions > 0) {
var q = new question(difficulty);
$("#gameInside").html(q.string);
$("#gameInside").data('answer',q.answer);
$("#answers").empty();
for (var ii=0;ii<=3;ii++){
$("#answers").append("<button class='answerButton'>"+q.answerArray[ii]+"</button>")
}
timer.reset();
} else {
$("#answers").empty();
$("#gameInside").html('Game Over! Thanks for Playing!');
timer.stop();
}
});
}
any ideas? Thanks
edit:
$(".numberQButton").click(function(){
numQuestions = parseFloat($(this).html());
$("#numQuestionsScreen").hide();
$("#gameScreen").show();
$("#score").html(0);
startGame(difficulty,numQuestions,score);
});
You're problem (I think) lies in closures. When you declare a function in javascript, any accessing of a variable from outside it's own scope generates what is called a closure. In this case, numQuestions is accessed from inside your event function, creating a closure. The effect of this is that any accessing of the variable numQuestions inside your event functions references the variable as it was from the moment your javascript interpreter came across the event function itself- ie, the first time you play your game.
Have a look at these to get an idea of where it's going wrong.
It sounds like you need to instantiate a new Game object when the game is restarted.
Where do you define the on click listener? If it triggers two times, it seems like you are defining the on click listener in such way, that after you completed one game and start all over, a new listener will be registered so you got 2 in the end.

How to trace in the console how many intervals are being executed - JavaScript

I have a problem trying to know if a setInterval() is taking place or it was killed.
I am creating an interval and saving it into a variable:
interval = setInterval('rotate()',3000);
Then on click to an element I stop the interval and wait 10 seconds before starting a new one, by the way the variable interval is global:
$(elm).click(function(){
clearInterval(interval);
position = index;
$('#banner div').animate({
'margin-left':position*(-img_width)
});
setTimeout('startItnerval()',10000);
});
function startItnerval(){
interval = setInterval('rotate()',3000);
}
It seems to work but eventually I can realize that there are intervals still being in place, everytime I start a new interval it is saved in the interval variable, which is global, so in theory even if I start 100 intervals they are all saved in the same variable replacing the previous interval right? So I should only have one instance of interval; then on clearInterval(interval); it should stop any instance.
After looking at the results, apparently even if it is saved in the same variable, they are all separate instances and need to be killed individually.
How can I trace how many intervals are being executed, and if possible identify them one by one? even if I am able to solve the problem I really would like to know if there is a way to count or show in the console how many intervals are being executed?
thanks
jsFiddle Demo
As pointed out in comments, the id's constantly increase as timers are added to a page. As a result, it may be possible to clear all timers running on a page like this:
function clearTimers(){
var t = window.setTimeout(function(){
var idMax = t;
for( var i = 0; i < idMax; i++ ){
window.clearInterval(i);
window.clearTimeout(i);
}
},4);
}
The reason that you can only see one interval is because every time you start a new interval, you overwrite the value in interval. This causes the previous intervals to be lost but still active.
A suggestion would be to just control access to your variable. Clearly there is an issue where the start function is called too often
clearInterval(interval);//when you clear it, null it
interval = null;
and then take advantage of that later
if( interval != null ){
interval = setInterval('rotate()',3000);
}
Also, as Pointy noted in a comment, using a string to call a function is not best practice. What it basically does is converts it into a Function expression which is similar to using eval. You should probably either use the function name as a callback
setInterval(rotate,3000);
or have an anonymous function issue the callback
setInterval(function(){ rotate(); },3000);
setInterval returns an Id, not the actual object, so no, no interval will be overriden if you repeat the line
var xy = setInterval(function() {...}, 1000);
If you want to stop the interval you have to clear it:
clearInterval(xy);
And if your startInterval can be called multiple times in a row, but you don't want to create multiple intervals, just clear the inverval before you start a new one:
function startInterval(){
clearInterval(interval);
interval = setInterval('rotate()',3000);
}
If you have to create multiple intervals, you could save the ids in an array to keep track of them:
var arr = [];
//set the interval
arr.push(setInterval(...));
//get number of currently running intervals
var count = arr.length //gives you the number of currently running intervals
//clear the interval with index i
clearInterval(arr[i]);
arr.splice(i, 1);

Javascript, object with set lifetime?

I have run in to a problem I do not know how to code in JavaScript really. The thing is I would like to be able to create a lot of objects added to an Array. when objects are created to be added to this array they will have a "lifetime". When this lifetime runs out this object should be removed from the array.
What Im trying to build here is a particle system where particles will vanish from being rendered after the particles lifetime in question have expired.
Anyone who have a good idea or example for this?
I have thought about using setTimeout, setInterval and clearInterval but not sure how this would be most effective.
Something like this?
Update for Felix Kling:
var a = [], next = function() {
a = a.slice(0,-1);
document.body.innerHTML += a.length + "<br />";
if (a.length != 0)
setTimeout(next, 100);
};
for (var i = 0; i < 100; i++) {
a.push({hi: 1});
}
setTimeout(next, 100);​
You can use the code sample of micha. On every call of "next" function you can update the state of you particles (position, velocity, etc). Also you can track the time of the creation of the particles and on every "next" call check if the current time minus the creation time exceeds certain constant and if it does then remove the particles. Depending on the required quality of the animation you may want to reduce the time between timeouts, e.g. setTimeout(next, 25);
Good luck :)

Categories

Resources