I'm currently building a Tron canvas game based on this code.
it works kinda good, but if I restart the game, two values are getting passed via e.which.
Try to show main code:
function loadGame() {
var e;
document.onkeydown=function(event){e=event};
function intervall() {
function check() {
//Function for drawing Tron
}
var key = e.which;
if(key == 87) { //W and so one
check();
}
}
var timer = setInterval(intervall,900)
}
If I lose, and call loadGame() again, it works great.
It does not if I call loadGame() while Game is still running.
I uploaded a live demo here:
http://wernersbacher.de/tron.html
Thanks for any help!
You're getting two values because when you restart the game, the old interval loop is still running when you start a new one.
I see you're trying to stop the old loop here:
if(hard) {
clearInterval(timer);
}
However, because the variable 'timer' was declared in a different invocation of the loadGame function, this newly invoked loadGame function has no access to it.
You can see this in action by logging the variable to the console from the new loadGame invocation:
function loadGame(hard) {
if(hard) {
console.log(timer);
}
You'll see it returns undefined.
If any of this sounds confusing to you, you might want to learn more about Function scope.
A solution is to declare the 'time' variable outside the loadGame function, in the global scope:
var time;
function loadGame(hard) {
/* the rest of your code */
timer = setInterval(intervall,9)
}
That way, the new instance of the loadGame function which is called when you press the 'Load game' button has access to the old timer and can stop it before starting a new one.
Incidentally, completely reloading everything, including creating a new canvas element and destroying the old one, seems a bit unnecessary. Why not simply clear() the canvas and reset the player position to the start position? You don't even need to restart the timer using that method.
Related
I hope you are doing well.
I'm working on a minigame based on JS and want to prevent the user from shooting infinite bullets, so I created this condition. Which only works for the first time and then breaks for the next clicks
also to mention this whole section is inside the loop so shooting works well with or without setInterval
let shootController = true
canvas.addEventListener("click", () => {
if (shootController) {
//shooting code goes here
shootController = false;
}
});
if (shootController === false) {
setInterval(() => {
shootController = true;
}, 1000);
}
Thanks for your replies beforehand
Somehow, I defined the 'setInterval' before and outside the loop and then called that function below and outside. This one WORKS
But from curiosity, I didn't define it at the top as a function and only used it below the loop. This one didn't work.
The problem is solved, but I don't know why.
As part of my studies I build the game "Space Invaders" in javascript by using canvas.
Before the beginning of the game, the main page is loaded and waiting to click on new game button. When i clicked on this button, the game begins to run properly and there are no problems at all. (the game runs through a recursive function that call to functions update() and render() -see code-...)
The problem comes when during the run of the game i push this button again. What happens is that the game run faster and when i clicked on it several times the game run faster and faster...
i dont now if its because the cycle of interval was not clear itself or maybe its because run() function call itself again and cause to loop into the recursive functions.
thanks a lot.
//----------------------------------------//
//this function called when i pressed in newGame button
function startGame()
{
clearInterval(timeInterval);
run();
// run function - run the recursive function below
timeInterval = setInterval(randEnemyShots,frequencyShot);
// randEnemyShots function - choose one enemey from all enemies to shot
// frequencyShot variable - this is the frequqncy of shots
}
//----------------------------------------//
function run()
{
var loop=function(){
if(gameRuning)
{
update();
// update function - all the logic of game
render();
// render function - drawing the game
window.requestAnimationFrame(loop,canvas);
}
};
window.requestAnimationFrame(loop,canvas);
}
//-----------------------------------------------------------------------------------------------------------------------//
The problem is that when you click the 'Start' button you are calling the run() function again which is effectively doubling the speed of the game.
The run() function should be called only once in the game initialisation so that the game loop can run indefinitely.
function gameInit(){
//Initialisation code here
run(); //Start the game loop only once
}
function startGame() {
//Handle 'Start' button click
clearInterval(timeInterval);
timeInterval = setInterval(randEnemyShots,frequencyShot);
}
You can then use the gameRuning value to 'pause' the loop if you set to false.
It's better you used setTimeout (with checking) rather than setInterval. Since the behaviour and cost of setInterval usually weird.
I'm trying to get a javascript function to run only once. I've seen this question has been asked before, e.g. Function in javascript that can be called only once, but I can't get the solutions in here to work. I'm not sure if it's because I've got nested functions, or whether there's something I'm missing. Essentially, I'm trying to run a function which, when a webpage is scrolled, it:
- runs a little animation on a canvas in the header
- reduces the size of the header
- leaves it at that
But when there is any subsequent scrolling, the animation keeps re-running. Here's a summarised version of the non-working code:
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
},1000);
}
});
I've also tried declaring a global variable, setting it to "false" in a "window.onload" function, then set it to true in an if function that runs the animation (the if function running only if the variable is false), but that doesn't stop it either. Thoughts?
What you're looking for is something along the lines of listenToOnce where the listener fires the one time, but never again. This could be modified to a number of calls, but the logic is like so:
Register the listener.
Then once the listener fires, remove it.
See .off
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
// $(document).off('scroll'); // or here
},1000);
}
$(document).off('scroll'); // remove listener, you can place this in the setTimeout if you wish to make sure that the classes are added/removed
});
Don't use a time out. That is why you are getting in trouble. Declare a variable outside of your function using var, that will make it global. Your code should be inside of a check for that variable. Before executing your code the first time but inside of the check, change that variable so that the code will never run again.
Try avoid setTimeout. Almost all animation can be watched for end.
function doHeaderAnimation() {
return $('header').animate();
}
function makeHeaderSmall() {
$("header").removeClass("large").addClass("small");
}
function handleScroll(event) {
if ($(document).scrollTop() > 0) {
doHeaderAnimation().then(makeHeaderSmall);
$(document).off("scroll", handleScroll);
}
}
$(document).on("scroll", handleScroll);
I'm relatively new to javascript and having a couple of issues that I'm hoping you can help with. I'm utilizing the fabric library with canvas to create an animation of sorts.
I have a function that is called when the relevant button is pressed:
//for starting the animation
startSim.onclick = function(){
//obtain speed value from the control panel.
var speed = 10000 / (new Number(speedControl.value));
//stuff
//animation process
object.animate('top', '+='+canvas.height,{
duration: speed,
abort: function(){
var returnedBool;
//Pause is pressed, simulation pauses
if(pause){
//stuff
}
//restart required
if(restart){
//stuff
}
//option here to save data?
return returnedBool;
},//end abort callback
onChange: canvas.renderAll.bind(canvas),
onComplete: function(){
//stuff
}
});//end animate callback
};
What I'd like to do is call certain other functions while the above animation is active.
I know how I'd like to do this using Java or C++ but I can't seem to call the function.
I have a display panel that should output various information - as the animation completes variables will change and I'd like them to be output on the screen and be updated as they change.
I'm testing with a simple function:
function updateInfoPanel(testSpeed){
var test = document.getElementById('currentSpeed');
test.innerHTML = test.innerHTML + testSpeed;
}
I think I need to call this in the main animate loop, if I call it before it's fine but obviously only displays the variable once. Could someone tell me where I'm going wrong please?
I also have a basic collision detection function that works but not within the animate loop and I think these problems are linked.
Many thanks for any help.
I removed the unnecessary parts of the code to save space.
So I've just tried to use a function as the result of onChange:
onChange: function(){
canvas.renderAll.bind(canvas);
updateInfoPanel(speed);
},
This doesn't present syntax errors but the animation no lingers runs although the speed variable does update in the info panel very quickly. :(
You need to call renderAll(), bind is actually returning a function to be used as a handler. Just try this code:
onChange: function(){
canvas.renderAll();//.bind(canvas);
updateInfoPanel(speed);
},
And let me know if it helped?
I'm creating a content rotator in jQuery. 5 items total. Item 1 fades in, pauses 10 seconds, fades out, then item 2 fades in. Repeat.
Simple enough. Using setTimeout I can call a set of functions that create a loop and will repeat the process indefinitely.
I now want to add the ability to interrupt this rotator at any time by clicking on a navigation element to jump directly to one of the content items.
I originally started going down the path of pinging a variable constantly (say every half second) that would check to see if a navigation element was clicked and, if so, abandon the loop, then restart the loop based on the item that was clicked.
The challenge I ran into was how to actually ping a variable via a timer. The solution is to dive into JavaScript closures...which are a little over my head but definitely something I need to delve into more.
However, in the process of that, I came up with an alternative option that actually seems to be better performance-wise (theoretically, at least). I have a sample running here:
http://jsbin.com/uxupi/14
(It's using console.log so have fireBug running)
Sample script:
$(document).ready(function(){
var loopCount = 0;
$('p#hello').click(function(){
loopCount++;
doThatThing(loopCount);
})
function doThatOtherThing(currentLoopCount) {
console.log('doThatOtherThing-'+currentLoopCount);
if(currentLoopCount==loopCount){
setTimeout(function(){doThatThing(currentLoopCount)},5000)
}
}
function doThatThing(currentLoopCount) {
console.log('doThatThing-'+currentLoopCount);
if(currentLoopCount==loopCount){
setTimeout(function(){doThatOtherThing(currentLoopCount)},5000);
}
}
})
The logic being that every click of the trigger element will kick off the loop passing into itself a variable equal to the current value of the global variable. That variable gets passed back and forth between the functions in the loop.
Each click of the trigger also increments the global variable so that subsequent calls of the loop have a unique local variable.
Then, within the loop, before the next step of each loop is called, it checks to see if the variable it has still matches the global variable. If not, it knows that a new loop has already been activated so it just ends the existing loop.
Thoughts on this? Valid solution? Better options? Caveats? Dangers?
UPDATE:
I'm using John's suggestion below via the clearTimeout option.
However, I can't quite get it to work. The logic is as such:
var slideNumber = 0;
var timeout = null;
function startLoop(slideNumber) {
//... code is here to do stuff here to set up the slide based on slideNumber...
slideFadeIn()
}
function continueCheck() {
if (timeout != null) {
// cancel the scheduled task.
clearTimeout(timeout);
timeout = null;
return false;
} else {
return true;
}
};
function slideFadeIn() {
if (continueCheck){
// a new loop hasn't been called yet so proceed...
$mySlide.fadeIn(fade, function() {
timeout = setTimeout(slideFadeOut,display);
});
}
};
function slideFadeOut() {
if (continueCheck){
// a new loop hasn't been called yet so proceed...
slideNumber=slideNumber+1;
$mySlide.fadeOut(fade, function() {
//... code is here to check if I'm on the last slide and reset to #1...
timeout = setTimeout(function(){startLoop(slideNumber)},100);
});
}
};
startLoop(slideNumber);
The above kicks of the looping.
I then have navigation items that, when clicked, I want the above loop to stop, then restart with a new beginning slide:
$(myNav).click(function(){
clearTimeout(timeout);
timeout = null;
startLoop(thisItem);
})
If I comment out 'startLoop...' from the click event, it, indeed, stops the initial loop. However, if I leave that last line in, it doesn't actually stop the initial loop. Why? What happens is that both loops seem to run in parallel for a period.
So, when I click my navigation, clearTimeout is called, which clears it.
What you should do is save the handle returned by setTimeout and clear it with clearTimeout to interrupt the rotator.
var timeout = null;
function doThatThing() {
/* Do that thing. */
// Schedule next call.
timeout = setTimeout(doThatOtherThing, 5000);
}
function doThatOtherThing() {
/* Do that other thing. */
// Schedule next call.
timeout = setTimeout(doThatThing, 5000);
}
function interruptThings() {
if (timeout != null) {
// Never mind, cancel the scheduled task.
clearTimeout(timeout);
timeout = null;
}
}
When a navigation element is clicked simply call interruptThings(). The nice part is that it will take effect immediately and you don't need to do any polling or anything else complicated.