If a game is running at 177FPS or 22FPS, how is the player movements calculated? I'm trying to do this in Javascript:
setInterval(update, 0);
function update() {
player_x++;
}
Problem is if the player will be move faster/slower according to the frame rate. How would I solve this?
I highly recommend using requestAnimationFrame when available (which will get you the best frame rate possible in modern browsers) and setTimeout when not. Then base your players position on the time since the animation started:
// Anonymous function used to make variables declared inside it not global
// This avoids conflicts in case variables with the same names are used
// in other scripts
(function(){
// This checks for the requestAnimationFrame in each browser and store the first
// it finds into requestAnimationFrame. If none are found it uses setTimeout
// Attempting 60 frames per second.
// It's taken from Paul Irish's blog: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// and you can use it as is
var requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
setTimeout(function(){ callback((new Date).getTime()); }, 1000/60);
};
})();
var speed = 60; // pixels per second
// This is used so that we only divide by 1000 once instead of every frame
var calc = speed/1000;
// Store the current time in start_time
var start_time = (new Date).getTime();
// This function is self invoking. It is wrapped in parenthesis and called immediately
// time is passed in by requestAnimationFrame
(function draw(time){
// Calculate the new x position based on the current time
var pos = (time - start_time)*calc;
// Update the player's position
// In my JSFiddle I update the style.left of a div
player_x = pos;
// Now that this frame is done request another frame.
// draw will be called again before that frame is rendered
requestAnimationFrame(draw);
})();
})();
JSFiddle
Try using a timer function instead, so that the player moves based on time and not changes in frames...
The distance is equal to speed (constant) and time. Depending on the framerate (in Hz) the time t = 1 / framerate will vary.
To put this into code: measure the time between subsequent frames and use that time to calculate the distance.
Sometimes a better idea is to have a background thread (in JavaScript you can use setInterval() as suggested by CezarisLT and use constant time. However in practice you still need to measure the time between subsequent invocations because setInterval() is not guaranteed to run exactly at scheduled time (long running functions, high CPU usage).
BTW your code is overly verbose and won't compile:
setInterval(update, 0)
function update() {
player_x++;
}
Related
I am trying to animate a Sequence of JPG images using requestAnimationFrame , however I noticed that sometime it takes a bit longer on some frames.
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var start;
var i=0;
var animateLoop = function() {
if(i>500) {
return false;
}
i++;
requestAnimFrame(animateLoop);
var _start = start;
start = +new Date();
console.log("Iteration:"+i, "Milliseconds Diff: "+(start-_start));
}
animateLoop();
To elaborate, please have a look at this fiddle : https://jsfiddle.net/bhenqfbw/
If you run this while the console is open, you will see that the milliseconds difference between each call is not the same. and in my case where I change the image source in this function, the fluctuation is even higher.
is there a way I can make it constant or it just has to be this way ?
requestAnimationFrame is not time based. You should make your animation time based, not frame.
Basically if you want to change an element's position with a constant value and you don't have a fixed frame rate, you can use the time difference between frames to calculate the distance in each frame.
Here's the main idea: (check out console)
https://jsfiddle.net/bhenqfbw/1/
Well the point of requestAnimationFrame is for the browser to decide when to call that function again. It's optimized for that. Therefore that's the best option. Yet if you really want something aggressive that doesn't care whether the function finished being called setInterval is your friend (I mean enemy because it's pretty bad). Or:
function toCall(){
//do stuff
setTimeout(toCall); //Place this at the end of your function. And no second parameter needed. It'll be called whenever the stack is empty.
}
How to get a better animation, dinamically, even when browser is busy or idle, for different devices which have different hardware capacity.
I have tried many ways and still cannot find the right way to make the game to display a better animation.
This is what i tried:
var now;
var then = Date.now();
var delta;
window.gamedraw = function(){
now = Date.now();
delta = now - then;
if(delta > 18){
then = now - (delta % 18);
game_update();
}
}
window.gameloop = setInterval(window.gamedraw,1);
18 is the interval value to update the game, but when browser is busy this interval is not good, and it needs to lower. How to get a better animation dinamically, even when browser is idle or busy ?
I suppose that the interval value is the problem, because if interval is lower then game animation is very fast, if this value is 18 then game animation is good but not when browser is busy, and I do not have idea how to change it dinamically.
To get a smooth animation, you must :
• Synchronise on the screen.
• Compute the time elapsed within your game.
• Animate only using this game time.
Synchronizing with the screen is done by using requestAnimationFrame (rAF).
When you write :
requestAnimationFrame( myCalbBack ) ;
You are registering myCalbBack to be called once, the next time the screen is available to draw on.
( If you know about double buffering (canvas are always double-buffered), this time is the next time the GPU will swap the draw buffer with the display buffer. )
If, on the other hand, you don't use rAF but a interval/timeout to schedule the draws, what will happen is that the draws won't get actually displayed until next display refresh. Which might happen (on a 60Hz display) any time from right now to 16.6 ms later.
Below with a 20ms interval and a 16 ms screen, you can see that the images actually displayed will be alternatively 16ms away OR 2*16ms away - never 20, for sure-. You just can't know, from the interval callback, when the actual draw will show. Since both rAF and Intervals are not 100% accurate, you can even have a 3 frames delta.
So now that you are on sync with the screen, here's a bad news : the requestAnimationFrame does not tick exactly regularly. For various reasons the elapsed time in between two frames might change of 1ms or 2, or even more. So if you are using a fixed movement each frame, you'll move by the same distance during a different time : the speed is always changing.
(expl : +10 px on each rAF,
16.6 display -->> rAF time of 14, 15, 16 or 17 ms
--> the apparent speed varies from 0.58 to 0.71 px/ms. )
Answer is to measure time... And use it !
Hopefully requestAnimationFrame provides you the current time so you don't even have to use Date.now(). Secondary benefit is that this time will be very accurate on Browsers having an accurate timer (Chrome desktop).
The code below shows how you could know the time elapsed since last frame, and compute an application time :
function animate(time) {
// register to be called again on next frame.
requestAnimationFrame(animate);
// compute time elapsed since last frame.
var dt = time-lastTime;
if (dt<10) return;
if (dt >100) dt=16; // consider only 1 frame elapsed if unfocused.
lastTime=time;
applicationTime+=dt;
//
update(dt);
draw();
}
var lastTime = 0;
var applicationTime = 0;
requestAnimationFrame(animate);
Now last step is to always use time inside all you formulas. So instead of doing
x += xSpeed ;
you'll have to do :
x += dt * xSpeed ;
And now not only the small variations in between frames will be taken into account, but your game will run at the same apparent speed whatever the device's screen (20Hz, 50Hz, 60 Hz, ...).
I did a small demo where you can choose both the sync and if using fixed time, you'll be able to judge of the differences :
http://jsbin.com/wesaremune/1/
You should use requestAnimationFrame instead of setInterval.
Read this article or this one.
The latter one is in fact exactly discussing your approach (with the delta time), and then introduces the animation frame as a more reliable alternative.
The first article is really a great resource. For starters, with your interval of 18 ms you're apparently aiming for something close to 60 fps. This is in fact the default for requestAnimationFrame, so you don't need to write anything special:
function gamedraw() {
requestAnimationFrame(gamedraw); //self-reference
game_update(); //your update logic, propably needs to handle time intervals internally
}
gamedraw(); //this starts the animation
If you want to set the update interval explicitly, you can do so by wrapping the requestAnimationFrame inside a setInterval, like this:
var interval = 18;
function gamedraw() {
setTimeout(function() {
requestAnimationFrame(gamedraw);
game_update(); //must handle time difference internally
}, interval);
}
gamedraw();
Note that the game_update() function must keep track of when it was last called in order to e.g. move everything twice as far as normal in case a frame had to be skipped.
Actually, this means you could (and probably should) refactor your game_update() function to take the time that has actually passed as an argument instead of determining that internally. (There's no functional difference, it just is better, clearer code IMO because it doesn't hide the timing magic.)
var time;
function gamedraw() {
requestAnimationFrame(gamedraw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now; //reset the timer
game_update(dt); //update with explicit and variable time step
}
gamedraw();
(Here I dropped the explicit frames again.)
Still, I urge you to read the first article because it also deals with cross-browser issues that I haven't gotten into here.
You should use requestAnimationFrame. It will queue up a callback to run on the next time the browser renders a frame. To achieve constant updating, call the update function recursively.
var update = function(){
//Do stuff
requestAnimationFrame(update)
}
I start the loop
function gameLoop(){
update();
draw();
requestAnimFrame(gameLoop);
}
var requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 1);
};
I can't adjust the frame rate. It is always really fast. Why can't I change it to 1 frame a second. I want to do this just for testing purposes.
Do I have to clear the canvas each time? It seems to work good without clearing it.
Thanks.
Here is a link to a fiddle for the complete code:
complete code
Thanks
rAF is locked to monitor's sync, typically 60 Hz, so we can't adjust the FPS for it in itself (browser may reduce FPS when tab is inactive or on batteries).
Also, what you are trying to change is the fallback for the poly-fill; that is: if rAF is not supported in the browser it will instead use setTimeout. However, most browsers nowadays do support rAF (even un-prefixed) so the setTimeout will never be used.
You can do two things:
Replace rAF in your loop by using setTimeout directly (when testing)
Example:
var FPS = 1;
function testLoop() {
... ordinary code
setTimeout(testLoop, 1000/FPS);
}
Throttle rAF by using a counter:
Example:
var framesToSkip = 60,
counter = 0;
function loop() {
if (counter < framesToSkip) {
counter++;
requestAnimationFrame(loop);
return;
}
/// do regular stuff
counter = 0;
requestAnimationFrame(loop);
}
MODIFIED FIDDLE HERE
There are most likely better ways to implement throttling, but I am trying to just show the basic principle. This will still run at full speed, 60 FPS, but your code will do minimal of operations and only when the counter has reached its count will it execute the main code.
You do not need to clear the canvas each time if what you draw next will cover previously drawn content, or if you want to keep it of course. You can also clear a portion to optimize further, if needed.
A bit late to the party, but here's how to get the benefit of RAF while also controlling frames/second.
Note: requestAnimationFrame now has a better way of doing things than by using the code pattern in my original 3 year old original answer ... see my update below for the new and improved way.
[Update: requestAnimationFrame now has a better way of throttling]
The new version of requestAnimationFrame now automatically sends in a current timestamp that you can use to throttle your code execution.
Here is example code to execute your code every 1000ms:
var nextTime=0;
var delay=1000;
function gameLoop(currentTime){
if(currentTime<nextTime){requestAnimationFrame(gameLoop); return;}
nextTime=currentTime+delay;
// do stuff every 1000ms
requestAnimationFrame(looper);
}
}
You should look at this article which gives a proper treatment of the subject.
http://creativejs.com/resources/requestanimationframe/
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
Here is the code I think you want, but in the original article it said used requestAnimationFrame, but here I am using requestAnimFrame. I think maybe it changed and you're supposed to use requestAnimFrame now. requestAnimationFrame did not work for me while requestAnimFrame did.
The way browsers and javascript work makes it difficult to set up a fixed frame rate. Say you want to do something every one second, like updating and drawing. One way of doing that could be to call window.setTimeout() with a setting of one second. But the problem is that this is not that reliable, even if you configure a callback every second you can't be sure all callbacks will be in time. A high processor load, for example, could make the callbacks arrive much later than they should. And even if the callbacks would be on time, you have no control of when the actual drawing to the screen will happen.
A better way of handling it is to accept the fact that you can't get a very precise timing of your calls, and instead, whenever you get a call, you calculate how much time has passed and act according to that. This means you'll let the system decide the frame rate, and you just take care of updating your animation or game depending on how much time that has passed.
requestAnimationFrame is a newer functionality supported by most browsers by now that is especially useful for games. It will be called every time the browser is ready to draw, which is good. Then you will know that the updates and drawing you are doing will happen right before the actual frame is drawn to screen.
Here's an example on how you could update your gameLoop to take the time difference into account.
var lastTimestamp = +new Date;
function gameLoop(timestamp) {
var now = +new Date;
var dt = now - lastTimestamp;
// dt is the amount of time in ms that has passed since last call.
// update takes this time difference (in seconds) and can then perform its
// updates based on time passed.
update(dt / 1000);
draw();
lastTimestamp = now;
requestAnimationFrame(gameLoop);
}
That's how requestAnimationFrame works. If you want a specific framerate, use setTimeout only.
Usually you would take a parameter, which is the current time. Compare it to the last frame's time to find out how far along the animation should move.
Quite handy js library if you need to control Framrate in javascript
https://github.com/aaronGoshine/Javascript-OnEnterFrame-Event-Manager/blob/master/index.html
requestAnimationFrame will run with the maximum achievable frame rate (up to 60 fps). This is because it will always give you the next animation frame.
The parameter you adjusted is only for the polyfill, which will be active if your browser has no implementation of requestAnimationFrame.
If you want to try painting at one second for testing purposes, try setInterval instead.
I have a function that gets called with setInterval() like this:
canvasInterval = setInterval(updateCGoL, (1000/this.frameRate)|0);
I am allowing the user to specify the frames per second (with limitations, only non-NaN values after parseInt() as Math.max(Math.min( user input , 30), 1)). Even if it runs at 30 frames per second I am pretty sure it is completing its work before the next frame. My questions though are:
What happens if it does not finish its work within the amount of time
I gave it?
Is there a way to test if it did not finish its work before the next
frame if this is a problem?
Edit: (Copy / pasted from comments) If the limit of my function is 20 frames per second (to compute) but I have setInterval running at 30 frames per second will it instead run at 20? (As opposed to two functions running at the same time)
Javascript is single-threaded, so your calls to set interval will be added to a queue. They will execute sequentially, but if your functions take longer than your actual interval you will work beyond the expected finish time of your setInterval calls.
Use requestAnimationFrame instead..This will not hog your cpu.
In simple words,setInterval does not have the ability to interact with our cpu and unnecessarily it ends up making queues of your calls and wasting a lot of cpu cycles
RequestAnimationFrame works smartly and allows you to manipulate the frame rate without burdening yor browser.
I just answered a similar question.
LINK-->Replace setinterval by RAF
It has all the links a begineer should follow!!!
Instead of clearing the interval use cancelAnimationFrame
Just a snippet on how you should approach things.Definately a better solution.
// This makes sure that there is a method to request a callback to update the graphics for next frame
requestAnimationFrame =
window.requestAnimationFrame || // According to the standard
window.mozRequestAnimationFrame || // For mozilla
window.webkitRequestAnimationFrame || // For webkit
window.msRequestAnimationFrame || // For ie
function (f) { window.setTimeout(function () { f(Date.now()); }, 1000/60); }; // If everthing else fails
var cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;//for cancellation
// some code here
var progress = 0
function doSomething(){
if (progress != 100){
// do some thing here
var myAnimation = requestAnimationFrame(doSomething)
}else{
// dont use clearInterval(interval) instead when you know that animation is completed,use cancelAnimationFrame().
window.cancelAnimationFrame(myAnimation);
}
I don't know about what's your frameRate. If it is entered by user, do some validation there. so you have better value.
Better try this
var timeRate = this.frameRate? 1000/parseInt(this.frameRate) : 0;
canvasInterval = setInterval(updateCGoL, timeRate);
How to solve different FPS in requestAnimationFrame on different browsers?
I am making a 3D game using THREE.js that uses requestAnimationFrame and it is fast on Google Chrome 15.
However, it is really slow on Firefox 6 and really really slow (slower than Firefox) on IE9.
This is really a big problem and I am wondering if there is a solution to that.
Thanks.
The common thing to do is to create a deltaTime (dt) variable which is then be used as a parameter for every animation/update cycle.
Code is only for visualizing the problem/solution.
// ...
timer: function(){
var now = new Date().getTime(); // get current time
this.controls.dt = now - this.controls.time; // calculate time since last call
this.controls.time = now; // update the current application time
this.controls.frame++; // also we have a new frame
return this.controls.dt ;
}
for any call to the render function you then pass dt
// we call the update function with every request frame
update: function(){
var dt = this.timer();
_.each(this.activeViews, function(item){ item.update(dt); }); // this is underscore.js syntax
}
item.update(dt) looks like that
//...
var x = this.position.get(x);
x = x + (10*dt); // meaning: x increases 10 units every ms.
this.position.x = x;
As far as I know there's no way to really fix this, other than making your code less resource intensive.
Chrome seems to be the fastest browser, but usually FF is not far behind, but IE is still slow. Depending on the rendering methods, canvas, svg or webGL, it's also very dependent on your local hardware as it uses the clientside for most things, and complicated webGL renderings need a powerful GPU to achieve good framerates.
There are ways to measure the framerate on the fly, and change your animations accordingly.
Here's a very simple example that measures framerate.
function step(timestamp) {
var time2 = new Date;
var fps = 1000 / (time2 - time);
time = time2;
document.getElementById('test').innerHTML = fps;
window.requestAnimationFrame(step);
}
var time = new Date(), i = 0;
window.requestAnimationFrame(step);
<div id="test"></div>
This is just an instant measure that's not that accurate, you'd probably want something that measures over some time to get a more correct framerate for the browser being used.
Also note the timestamp argument, which in requestAnimationFrame is high-res timestamp with a minimal precision of 1 milliseconds, that can also be used to deterine the speed of the animation, and any browser lag.
On some browsers requestAnimationFrame works something like
setTimeout(callback, 1000 / (16 + N)
where N is time required for your code to execute. Which means it caps your FPS at 62Hz but if your code works slowly, it will cap at something way lower. It basically tries to make a 16ms gap between every gap. Of course, this is not true for all browsers and will probably change in the future anyway but it still may give you an idea how it works.
Even if it was implemented the same in every browser, there are many factors which affect the performance of your code and etc. You can never be sure your code will be running at a constant frequency.
The Crafty framework does something that's a bit different, but might work for some cases -- the number of game ticks per draws is not constant. Rather, it notices when the framerate is falling behind some ideal target, and will cycle through multiple game ticks before performing the draw step. You can see the step function on github.
This works well so long as the game would be running smoothly. But if you try something more processor intensive, it can tend to exacerbate the situation, as it will prioritize game logic over animation.
In any case, it'll only work if the game logic and render logic are somewhat decoupled. (If they were completely decoupled you might be able to put them in completely separate loops.)
As adeneo mentioned, the requestAnimationFrame callback is sent a timestamp argument. Here is a solution to measure the delta between requestAnimationFrame events using that timestamp argument instead of creating a separate variable using the Date() function (which performance.now() may be a better solution anyhow).
This solution also includes a Start/Stop option to show why I am using a separate function to initialize the previousTimestamp at each start, and why I am setting a reqID value.
var reqID, previousTimestamp, output;
const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
const caf = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
// This is run first to set the previousTimestamp variable with an initial value, and then call the rafLoop function.
const startStop = () => {
if ($('#start-stop').prop('checked')) {
reqID = raf(timestamp => {
previousTimestamp = timestamp;
reqID = raf(rafLoop);
});
}
else caf(reqID);
};
const rafLoop = timestamp => {
animation(timestamp - previousTimestamp);
previousTimestamp = timestamp;
reqID = raf(rafLoop);
};
// Create animation function based on delta timestamp between animation frames
const animation = millisesonds => {
output.html(millisesonds);
};
$(document).ready(() => {
output = $('#output');
$('#start-stop').change(startStop);
$('#start-stop').prop('checked', true).trigger('change');
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>requestAnimationFrame</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<label for="start-stop">Start/Stop: </label><input class="switch" type="checkbox" id="start-stop"><br>
<div id="output"></div>
</body>
See also https://codepen.io/sassano/pen/wvgxxMp for another sample with animation from which this snippet was derived.