I have a function that runs on a click event that uses javascript's setIterval for some of my animations (i'm doing a game) so the problem is that if a user clicks while the animation is still displaying (setInterval is still executing) the setInterval is stacking up in the event stack or that is what I found out thus either crushing my game or running twice as fast (the animation). My question is is there any way to prevent event stacking? I do not want the setInterval to stack up on the previous setInterval and so on. I know that I could use clearInterval function like so:
var timerInterval = setInterval(drawPlayerRunning, 50);
clearInterval(timerInterval);
but it does not really work as I want it to, because what if user clicks many times while the function is still is executing, the clearInterval will only get rid of last event of the event stack leaving all the previous ones still in the "game". Any idea how to prevent this event stack up, or at least removing them efficiently?
You can create a flag that monitors the interval state:
1)
var isIntervalInProgress = false;
setInterval(function()
{
if ( isIntervalInProgress )
return false;
isIntervalInProgress = true;
drawPlayerRunning();
isIntervalInProgress = false;
}, 50);
or just a timeout that will run itself once it's finished:
2)
var func = function()
{
setTimeout(function()
{
drawPlayerRunning();
func();
}, 50)
}
whichever you like
You want to use requestAnimationFrame. It is designed with games in mind, and if your code happens to be too slow, it will reduce your frame rate accordingly (from 60 fps to 30 fps for instance). But it won't stack-up events.
Edit: Sorry, I think I misunderstood your question. Let me try again.
You should have only one draw function which is called every few milliseconds (set the interval up with requestAnimationFrame(draw)).
A click should not add a new interval, but rather create a floatingAnimation object and add it to the list of objects to render. All animation objects will be rendered by the draw function everytime the browser calls draw. In the arguments passed to draw, there will be a timestamp. Use this timestamp minus the creation date of floatingAnimation to determine how to draw the floating thing above the character.
Related
I am trying to make a sleep function in Javascript.
The function drawLinesToHtmlCanvas() is meant to draw random lines to a HTML canvas and the user is meant to be able to see the lines being drawn in real time.
For this example I use a delay of 500ms, but would like to be able to go to 1ms (or even less than 1ms resolution in the future)
Originally I followed a answer from this post: What is the JavaScript version of sleep()?
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
await sleep(drawSpeed);
}
}
And that worked very well (above). It was efficient, did not slow the browser down at all and let me have some control over the timing.
The issue was that setTimeout() cannot seem to go down to 1ms precision and this is something I require for this function.
So instead I tried my own approach as follows:
function sleep(ms) {
ms = parseInt(ms);
var now = new Date();
nowMs = now.valueOf();
var endMs = nowMs + ms;
while (endMs > nowMs) {
nowMs = new Date().valueOf();
}
return true;
}
function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
while (!sleep(drawSpeed));
}
}
This one is very slow, the while loop waiting for the right time uses up all the browsers resources, it's completely unusable. In addition as the function drawLinesToHtmlCanvas() is running, the lines are not being updated to the canvas element.
The promise solution with the setTimeout() was fantastic, it is just not precise enough for my requirements.
Can I make a promise that works similar to the first example? But instead of using setTimeout() it uses a similar algorithm to my Date() now vs end ms comparison, as that would be much more accurate?
The lines need to be able to be drawn down to 1 ms for now and have real time updates, the user needs to be able to see the lines being drawn to the canvas.
Even if setTimeout did work on such extremely small time frames this would probably not have worked out. When you use callbacks and/or promises you rely of JS runtime's event loop. This event loop only executes your callback as fast as it can. The architecture is going to impose lags that will become visible when you go below 1ms. The callback in setTimeout is not exactly executed after N ms passes. After N ms passes it only becomes eligible to be executed. And it gets invoked finally only when its turn comes on another event loop tick.
As for your second approach it does not exactly "use up resources". The thing is you no longer use event loop. But you must remember that JS is single-threaded. And because of it when JS-code executes non-stop it will not let user interact with UI at all. User can do something only between event callback executions. So don't ever use long running whiles in JS in browser unless you want to ruin user experience. Maybe unless you use Web workers because they will let you create new threads, but then you wouldn't be able to draw anything from there.
In general your approach to animation as "drawing something and then sleeping" is rather naive. Performant and smooth animations are what the video cards are made for although writing it in browser to efficiently utilize video card may be tricky. If you want to make animation in browser then you have to find specific browser function calls made specifically for animation on a Canvas or WebGL.
Maybe start here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
Also think about if you actually need so may frames per second. Over 1000fps? Can the monitor make it? What about performance impact?
I am trying to use the dev console to give mario a mushroom every 5 seconds (in the browser game super mario html5)
I can give mario mushrooms manually by typing marioShroons(mario) but I would like to have it on loop so I don't have to pause the game every time I want a mushroom. I have tried a while loop and set timeout but I can't figure it out. The only coding languages I familiar with are c++ and html.
**
while(data.time.amount > 0) {
killOtherCharacters()
}
setTimeout(function() {
killOtherCharacters()
}, 1000);
I expected these lines of code to not give me a mushroom, but to automatically kill enemies. But on the first try (the while loop) it froze the tab and I had to reload the page.
With the set timeout, it didn't make any obvious results, it killed all near characters once and then stopped.
You tried using setTimeout, and it only worked once. This is to be expected, because:
Window.setTimeout() sets a timer which executes a function or specified piece of code once the timer expires
From MDN
What you need to do is use setInterval:
The setInterval() method...repeatedly calls a function or executes a code snippet, with a fixed time delay between each call.
From MDN
So in your console, you should write this:
setInterval(killOtherCharacters, 1000);
(I removed the anonymous function because it wasn't needed - you only need an anonymous function if you're passing parameters or doing multiple things. You do need to remove the () for this though).
And if you want to stop the function from executing, assign a variable to the interval:
var killCharacters = setInterval(killOtherCharacters, 1000);
Then call clearInterval upon this variable to clear the interval (stop the loop):
clearInterval(killCharacters);
The reason your while loop froze the page is because Javascript can only do one thing at a time and you told it to always run your while function, blocking all other Javascript from running on your site.
setTimeout is only run once after a set time (see documentation), if you want to run something every x miliseconds it's better to use setInterval instead.
var intervalID = window.setInterval(killOtherCharacters(), 500); //run this every 500 ms
Use setInterval if you want killOtherCharacters() to be called repeatedly.
const interval = setInterval(function() {killOtherCharacters() },1000);
Then when you want the function to stop being called:
clearInterval(interval);
My main goal is at 7:45 in the video to display a countdown timer from 20:00.
Here is my event called by the jwplayer I'm using:
onTime: function(event){showTimer(event.position);}
Here is my funciton showTimer:
function showTimer(video_position){
// Schedule the update to happen once every second
if(Math.round(video_position) == 465){
setInterval(updateTimer, 1000);
}
};
The Problem:
Because it onTime() isn't guaranteed to hit right at 465.0 I have to d a Math.round on the video position. If I do this, 4-10 onTime event handlers knock off within 465.0 - 466.0 so my setInterval() gets called several times and the timer counts down insanely fast.
Is there a way to make it hit the setInterval() once or maybe a global variable I can set the first time to let setInterval() know its already been knocked off?
I'd just use a boolean flag to check whether the event has fired yet.
var fired = false;
if(Math.round(video_position) == 465 && !fired){
fired = true;
setInterval(updateTimer, 1000);
}
That way it can only fire the event once.
The problem is as such:
In a js and asm.js based multiplayer game I've got two loops.
One handles the actual game ticks, like unit position, velocity and combat.
The other handles rendering of this world onto the canvas for the user to see.
What I'd like to happen is when the processor/GPU(they made those the same thing on some machines now, can't say I'm happy about that) gets encumbered too much the rendering loop should skip and thus stop changing the canvas. I.e. freezing the game screen in a lag pike.
Meanwhile the little processing power left is used to successfully complete the actual game tick preventing de-synchronisation with other game clients.
(It's an RTS-like game when it comes to load so the user input instead of positions of all objects are sent over the net).
Failing this the client would have to be kicked by the other clients or all clients would have to pause for him to reconnect and resync. i.e. bad bad bad!
A sloppy makeshift way to do this would probably be by using timestamps and terminate the graphic loop if it won't be complete by a certain time. One would presumably do this by determining max execution time for the packet types on the stack of the loop and immediately terminate the loop if the "time to execute value" of all packets together is too great to be dealt with within the resource capacity the timestamps are indicating by slowdown measurement. Hell, maybe that's radical but perhaps even skip-terminating the graphic loop when any slowdown is detected just to be sure to avoid desync.
So priorotizing one loop over another(both handling ticks) and making the second one skip if a shortage in resource is detected to ensure the first one always completes it's tick within each timeframe(10 ticks per second here).
Any possibilities or best practice methods you guys can inform me on?
EDIT: Please focus on the ability to measure availability of cpu resources and the skipping/termination for one tick of the graphic loop if these resources would not available enough to finish both loops (i.e. if the loops won't finish in the 100ms timeframe after which the next loop tick should already be firing, don't start/terminate the graphics loop).
One solution would be to use a web worker to do your world update loop and then the normal javascript loop to do the render. You would need to hand the state back and forthright o /from the web worker but the render loop would only draw on the updated data.
An advantage is that you could still have reactionary display code on the main ui loop.
This also have the advantage of the fact that the web worker could be using a different core and with multiple web workers you could use multiple extra cores
Fo the logical loop, i would take setInterval, and for the paint - requestAnimationFrame. And even more - the callback at requestAnimationFrame also receives a timestamp, so you can track timestamps and skip single frame if some lack appear.
the processor is able to handle other tasks while also rendering the animation
This statement is wrong - processor can handle only one task, and requestAnimationFrame is not actually the Rendering, it is your callback - generic javascript. You can think about it like a setTimeout. The only difference is, that it tries to run the callback on next free framerate's frame. That's why it is much better than setTimeout. So for the animations you must use the requestAnimationFrame. Other good part about it is, when the webpage is in background(other tab opened). Then the callback wont be called until it comes to the foreground. This saves processor time, as nothing is calculated in that callback.
Going back to your question: You now but, that only one callback can be processed in a time, so if the processor is in a particular time busy with the logical function, then the callback of the animation loop won't be fired. In that case it calls 'lag'. But as I understood, it is actually the desired behavior - to give the logical callback function more time. But there is other side. What if your animation function is busy, when the time for logical function came to be fired? In this case it will be fired only when animation function ends. There is nothing to do about it. If your animation function is 'heavy', you could only try to split it for 2 frames. One frame - prepare everything for render, the second one - render.
But anyway, you never become millisecond-perfect interval or timeout in javascript. As it want be called until event-loop is not free. To get the idea:
var seconds = 0;
setInterval(function(){ seconds++; var x = 10e8; while(--x); }, 1000);
Depends on you CPU, but after 10 seconds time, variable 'seconds' will be much less then 10.
And one more thing, if you really rely on time, then it is safer to use Date.now() to synchronize next logical tick:
var setLogicalLoop = (function(){
var _startedAt,
_stop,
_ms;
function frame(){
if (_stop === true)
return;
// calculations
var dt = Date.now() - _startedAt,
diff = dt % _ms;
setTimeout(frame, ms - diff);
};
return function (callback, ms){
_startedAt = Date.now();
_stop = false;
setTimeout(frame, ms);
return function(){
_stop = true;
};
};
});
// -> start
var stopLoop = setLogicalLoop(myFunction, ms);
// -> stop
stopLoop();
I am trying to create a scenario where I have two variables (or more) both assigned to their own setInterval object and each setInterval object has a different function and a different millisecond value. The goal is to create a rhythm between the two by launching functions that play two different audio files repeatedly.
The current problem I've come across is this:
If one set interval is set to 1000 and the other is set to 500 each one doesn't start immediately. The millisecond delay is part of the start time. I want to know how to omit the delay on the start time so that both functions launch immediately but then they each keep there respective interval times.
The "logic" I've come up with so far is something along the lines of creating a function that immediately plays audio with no millisecond value and then have another function that is assigned to setInterval which begins playing x number of millisecond earlier on the next "beat" to make up the difference. I'm curious if the logic seems sound or if this is a problem in which a "conventional" javascript solution exist that I don't know about.It seems like there is an easier way.
I haven't begun coding yet albeit I didn't want to dig myself a hole before I have to if someone already has a fix for this.
Thanks
I avoid setInterval. Instead, i use setTimeout with such a pattern:
function someThing() {
// do something ...
setTimeout(someThing, 2000);
// or do something after setting the timeout ...
}
// either delay the start:
// setTimeout(someThing, 1000);
// or start right away
someThing()
But if you really want to use setInterval then a similar pattern works:
function someThing(firstTime) {
if (firstTime === true) {
console.log('setting interval for someThing');
setInterval(someThing, 1000);
}
console.log('someThing called');
}
someThing(true);