I have a javascript game that will run really fast on some computers and really slow on others. I've been doing some research and I've found that I need to update my loop based on time, but I can't seem to find any good examples of this for javascript. Can someone point me to the right direction on how to get a game to run at 30fps on any hardware?
Thanks
Normally games work from a Delta Time, that is, the amount of time since the last frame was rendered.
Psuedocode (roughly C#):
DateTime lastFrameTimeStamp = DateTime.Now;
void Draw()
{
TimeSpan timeSinceLastFrame = DateTime.Now.Subtract(lastFrameTimeStamp);
float deltaTime = timeSinceLastFrame.TotalSeconds;
// Do all of your movement and other time-based math based on the deltaTime, Like:
float x = x + (MovementPerSecond * deltaTime);
lastFrameTimeStamp = DateTime.Now;
}
Using a Delta Time prevents all dependency on CPU power or how often frames get drawn.
You can't force a game to run at 30fps if the hardware is unable achieve it. If what it is doing is taking more than 1/30th of a second, you're out of luck.
You can use requestAnimationFrame to let it run as fast as it can though. See here: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
You could have a timer function which you can measure how long you are executing for, then call back to yourself at your ((required interval) - (execution time)), in pseudo code
function timer(){
var timeStart = new Date();
// Your stuff
setTimeout (timer, (1000/30) - (new Date() - timeStart));
}
What you are looking for is a simple implementation of delta timing in JavaScript. Implememting it in JavaScript is a relatively simple task. In fact it's so simple that it can be achieved in less than 25 lines of code (stripping out blank lines and comments):
function DeltaTimer(render, interval) {
var timeout;
var lastTime;
this.start = start;
this.stop = stop;
function start() {
timeout = setTimeout(loop, 0);
lastTime = Date.now();
return lastTime;
}
function stop() {
clearTimeout(timeout);
return lastTime;
}
function loop() {
var thisTime = Date.now();
var deltaTime = thisTime - lastTime;
var delay = Math.max(interval - deltaTime, 0);
timeout = setTimeout(loop, delay);
lastTime = thisTime + delay;
render(thisTime);
}
}
Using it is even more simple. Let's learn by example:
var timer = new DeltaTimer(render, 1000 / 30);
var start = timer.start();
var body = document.body;
var frame = 0;
function render(time) {
time -= start;
body.innerHTML += (frame++) + ". " + time + " ms<br/>";
if (time >= 1000) {
var stop = timer.stop() - start;
body.innerHTML += "<br/>" + stop + " ms (stopped)";
}
}
I think the code is pretty self explanatory. For the live demo click on this link.
Frameworks like EaselJS often have Tickers/Timers that run every x milliseconds.
http://easeljs.com/docs/Ticker.html
A list of JavaScript game frameworks:
https://gist.github.com/768272
The only timing mechanisms available to you in JavaScript are setTimeout and setInterval. However, there is no guarantee on the precision of the timers. In fact, since JavaScript in browsers is single threaded there is no guarantee that your timer will fire when you want it to if there is JS running elsewhere already.
Generally speaking, if you want something to occur regularly at a specified interval, you use setInterval. You have to make sure that your callback does not take longer to run than the duration of the interval, otherwise your next interval will fire late. And if that fires late, chances are the next will as well, and you'll just get further and further behind in your executions.
Many modern browsers do support Web Workers, which is essentially a background JS execution (in addition to the primary UI blocking thread we are all familiar with). I have not used Web Workers yet, so I cannot speak to them with any authority or expertise.
If this were me, I would take the following initial approach (http://jsfiddle.net/Ce3wq/):
var GameTimer = (function () {
var gameTimer = function (opts) {
var self = this;
opts = opts || {};
opts.stepInterval = opts.stepInterval || 30;
var callbacks = {};
var stepInterval= opts.stepInterval; // ms
this.domReady = function () {
setInterval(step, stepInterval);
};
this.registerService = function(callback){
callbacks[callback] = callback;
};
this.removeService = function(){
delete callbacks[callback];
};
var step = function () {
for(var id in callbacks){
callbacks[id]();
}
};
};
return new gameTimer;
})();
var eachTick = function(){
console.log(new Date().getTime());
};
GameTimer.registerService (eachTick);
jQuery(document).ready(GameTimer.domReady);
The only option you have with JavaScript is using setTimeout and setInterval to emulate game loops found in other languages.
You cannot force your game to run at the desired speed in each hardware (even if your game is really low on requisites, there will be always stuttering because of web browsers JavaScript engines), but you can still use Delta Timing to not waste time between each frame.
Here is a simple code I use for my JavaScript games. It's a function that gets called FPS times in a second. In the example is 30 times each second.
Related
The following snippet does it job, but it looks clumsy to me:
var lastFrame = performance.now();
function elapsedMillis(){
var now = performance.now();
var elapsedTime = lastFrame - now;
lastFrame = now;
return elapsedTime;
}
Every time it's called it computes the elapsed time since last call. performance.now() should be called only once, since it's the most expensive operation.
I got this one, but I think there might be a better solution out there:
var lastFrame = performance.now();
function elapsedMillis(){
var now = performance.now();
var elapsedTime;
return ([elapsedTime,lastFrame]=[now-lastFrame,now])[0];
}
elapsedTime is not really needed.
Is there a better way of writing such an assignment?
Edit:
I'm using destructuring assignment here, which is apparently only supported by firefox for the time being. I wasn't aware that I could also do something like this:
var lastFrame = performance.now();
function elapsedMillis(){
var now = performance.now();
return ([,lastFrame]=[now-lastFrame,now])[0];
}
the firstone I think, could be a way to control the elapsed time from the lastFrame assuming that performance is a Date...
The second example, instead, is totally wrong... because it ends with an invalid left-hand side in assignment and throws a Bad Assignment Error...
In stackoverflow are many topics on top of this error...
Here my way, assuming that is singletone:
var elapsedTime = (function(performance) {
var lastCall = performance.now();
console.log(lastCall);
return function() {
var now = performance.now();
var elapsed = now - lastCall;
// reassigning lastCall each time you call the function make the function work with the last invocation, if you want to calc the elapsed time from the first invocation you have to not-reassign the lastCall variable!
lastCall = now;
return elapsed;
};
})(window.Date);
Is there some way to make a function just like the setInterval but the timeout to be exactly the same each time.
In setInterval the timeout varies about the given timeout, a little bit more, a little bit less, but very rare exactly the same.
For example:
var before = new Date().getTime();
setInterval(function() {
var after = new Date().getTime();
newTab.window.location=someURL;
console.log((after - before));
before = after;
}, 50000);
prints 50000,50002, 50005, 50994, 50997, 49999, 50003, 49998 and so on.
I want to be printed always 50000
Javascript is executed in one flow only, so if there is another process doing something at the same time, there's always a chance you timer function will not be executed in time.
If you really need the exact time interval you can block the execution of any other process in advance and hope for the best:
function setExactInterval(handler, time) {
var startTime = Date.now();
setTimeout(function() {
while (true) {
var currentTime = Date.now();
var diff = currentTime - startTime;
if (diff >= time) {
setExactInterval(handler, time);
return handler();
}
}
}, time - 50);
}
It still will not be exact in case the process is blocked by OS though...
I have a function that should postpone the call for the next function :
function waitFunc( func, time )
{
var startTime = performance.now();
var currTime = performance.now();
while ( (currTime - startTime) <= (time / 1))
{
currTime = performance.now();
}
func();
}
Another two function that i have are supposed to change the content of the tag i have in the body:
function showPlus()
{
//display plus
pictureImg.src = plus;
//blank only the form that contains input
inputForm.style.display="none";
pictureImg.style.display="block";
//after timeout start "showPicture" function
waitFunc(showPicture, 250);
//setTimeout(showPicture, 250);
}
function showPicture()
{
//generate picture pass
imgName = "../images/Slide"+ i + ".PNG";
if (i < 100)
{
//increase variable i by 1
++i;
//blank only the form that contains input
inputForm.style.display="none";
pictureImg.style.display="block";
//display picture
pictureImg.src = imgName;
//after timeout start "showForm" function
waitFunc(showForm, 750);
//setTimeout(showForm, 750);
}
}
In the html:
<img src="../images/Blank.png" id="pictures" name="pictures"/>
Currently i am trying to call to waitFunc function from showPlus function and i expect that showPicture function would be called after the timeout. PROBLEM: When i use waitFunc function the content does not change as it suppose. However, when i use setTimeout function everything works just great.
Can some please assist me (i hope that this is not a stupid question)?
P.S. you can find the page here.
Thank you!
EDIT: You can also find a jsfiddle here. Notice that the fiddle is using setTimeout so you can see how it is supposed to work; if you comment the setTimeout and uncomment the waitFunc line next to it, then you will be able to see the issue.
There's a function called setTimeout() which does exactly the same functionality you need. Why don't you use it instead of building JavaScript all over again.
Also, performance.now(); isn't supported in all the browsers:
https://developer.mozilla.org/en-US/docs/Web/API/Performance.now
Also, your function shouldn't work at all:
var startTime = performance.now();
var currTime = performance.now();
Here you should have startTime == currTime because you're setting the same value in less than 1 millisecond.
while((currTime - startTime) <= (time / 1))
Here, (currTime - startTime) should be equal to zero, and (time / 1) is equal to time.
I don't understand what you're trying to do, but if you're looking to "pass a function as a parameter", your question is a duplicate: Pass a JavaScript function as parameter
You must know that javascript is excuted in a single thread. If you run a wait function implemented using a while statement the main thread of the page will be frozen.
The difference of you waitFunc implementation and the setTimeout of javascript is that the setTimeout function run in a separated thread. By other side your function is executed synchronous. By example the showPicture function will take 75000 ms on it run because the loop will wait on each waiFunc 750 ms.
I hope that this help.
This code works perfect. When the page load the main thread is busy for 3 seconds set the 3.jpg image and after 2 seconds the image return to be the first image again. This is using you function and the settimeout.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="js/lib/jquery-2.1.1.js"></script>
<script>
function waitFunc( func, time )
{
var startTime = performance.now();
var currTime = performance.now();
while ( (currTime - startTime) <= (time / 1))
{
currTime = performance.now();
}
func();
}
$(function(){
waitFunc(function(){
$('#dd').prop('src', '3.jpg');
}, 3000);
setTimeout(function(){
$('#dd').prop('src', 'A_Girl_In_The_Horizon_by_yeg0.jpg');
}, 2000)
});
</script>
</head>
<body>
<img id='dd' src="A_Girl_In_The_Horizon_by_yeg0.jpg">
</body>
</html>
I ran some tests and, as I mentioned in the question comment, the issue seems to be that the waitFunc that you use freezes the browser before it is able to load the picture and change the display.
I got something kind of working, but again, you may not like it: the solution would consist on using setTimeout (with a low milliseconds value: 1-20) to call waitFunc (with the value that you want minus the 1-20 milliseconds). Something like this:
setTimeout(waitAuxForm, 1);
And then you have the function:
function waitAuxForm() { waitFunc(showForm, 749); }
I have put it in a jsfiddle and you can see it working here.
This solution presents some issues too:
You have to rely on setTimeout for a low amount of time (you may not like this).
You may need more than 1-20 milliseconds to load the pictures (you may want to preload them and once cached, it would work even with a value of 1).
I hope it helps :)
Okay, so I was able to get this to work with the information you provided. Right out of the gates, looking at what you have, nothing is defined. However, assuming that you have inputForm and pictureImg defined correctly in both of the last functions, the only thing you're missing is to define i.
function showPicture()
{
var i = 0;
//generate picture pass
imgName = "../images/Slide"+ i + ".PNG";
[...]
}
I couldn't find anything wrong with your waitFunc function. If you substitute in a simpler function, it works as intended.
function waitFunc( func, time )
{
[...]
while ( (currTime - startTime) <= (time / 1))
{
currTime = performance.now();
console.log(time); // log so you can see, for testing
}
func();
}
waitFunc(function(){console.log("Hello, world!");}, 750); // wait 750 ms, then say "Hello, world!"
I am developing an application using Processing.js.
At each step in the drawing loop I increment the number of frames by one frame++ .
I want to know how much time has passed. Currently to get the time passed (knowing that my application is set to run on 60FPS) I do like so: time=frame/60 . But this only works if the application always run at exactly FPS and we all know that's not the case cause it depends on the users hardware.
I want the timer to be pretty accurate (having only like 0.0001s error).
Suggesting some javascript algorithm for calculating the difference between now() and start_time() is also welcome.
If you want accuracy, take a look into high resolution timers. However, this feature isn't available on all browsers.
Also, Processing.js has a built-in read only variable named frameCount. You can query that instead of counting the frames yourself.
You can store the start time at a variable.
Then create a new timer whenever you want and subtract the start time variable from it..
The result will be the difference in milliseconds..
You can also use the actual time divided by the frames counter to get an accurate average frame-rate of your application..
something like
var startTimer = new Date(); // at start (once)
and whenever you want to check
var passed = new Date() - startTimer; // in milliseconds
Demo at http://jsfiddle.net/gaby/CF4Ju/
I made up this class for a Game. It uses millis() and is independent of frameRate
class Timer{
boolean increment, inProgress;
int spawn, end;
int seconds, tm_limit;
Timer(int tm_limit){
this.tm_limit = tm_limit;
seconds = tm_limit;
increment = false;
inProgress = false;
}
Timer(){
seconds = 0;
increment = true;
inProgress = false;
}
void start(){
inProgress = true;
spawn = millis();
}
void stop(){
inProgress = false;
end = millis();
}
int getSeconds(){
if(inProgress){
if(increment){
seconds = int((millis() - spawn) / 1000);
}
else{
if(seconds - int((millis() - spawn) / 1000) != seconds){
seconds = seconds - int((millis() - spawn) / 1000);
if(seconds <= 0) { stop(); }
else spawn = millis();
}
}
}
return seconds;
}
void reset(){
if(!increment)
seconds = tm_limit;
else
seconds = 0;
inProgress = false;
}
}
If a Timer object is instantiated with a parameter, it is assumed that Timer should decrease. Otherwise, the exit condition can be checked by getting the value from getSeconds() method.
How could I accurately run a function when the minute changes? Using a setInterval could work if I trigger it right when the minute changes. But I'm worried setInterval could get disrupted by the event-loop in a long-running process and not stay in sync with the clock.
How can I run a function accurately when the minute changes?
First off, you should use setInterval for repeating timers, since it (tries to) guarantee periodic execution, i.e. any potential delays will not stack up as they will with repeated setTimeout calls. This will execute your function every minute:
var ONE_MINUTE = 60 * 1000;
function showTime() {
console.log(new Date());
}
setInterval(showTime, ONE_MINUTE);
Now, what we need to do is to start this at the exact right time:
function repeatEvery(func, interval) {
// Check current time and calculate the delay until next interval
var now = new Date(),
delay = interval - now % interval;
function start() {
// Execute function now...
func();
// ... and every interval
setInterval(func, interval);
}
// Delay execution until it's an even interval
setTimeout(start, delay);
}
repeatEvery(showTime, ONE_MINUTE);
This may be an idea. The maximum deviation should be 1 second. If you want it to be more precise, lower the milliseconds of setTimeout1.
setTimeout(checkMinutes,1000);
function checkMinutes(){
var now = new Date().getMinutes();
if (now > checkMinutes.prevTime){
// do something
console.log('nextminute arrived');
}
checkMinutes.prevTime = now;
setTimeout(checkChange,1000);
}
1 But, see also this question, about accuracy of timeouts in javascript
You can try to be as accurate as you can, setting a timeout each X milliseconds and check if the minute has passed and how much time has passed since the last invocation of the function, but that's about it.
You cannot be 100% sure that your function will trigger exactly after 1 minute, because there might be something blocking the event-loop then.
If it's something vital, I suggest using a cronjob or a separate Node.js process specifically for that (so you can make sure the event loop isn't blocked).
Resources:
http://www.sitepoint.com/creating-accurate-timers-in-javascript/
I've put up a possible solution for you:
/* Usage:
*
* coolerInterval( func, interval, triggerOnceEvery);
*
* - func : the function to trigger
* - interval : interval that will adjust itself overtime checking the clock time
* - triggerOnceEvery : trigger your function once after X adjustments (default to 1)
*/
var coolerInterval = function(func, interval, triggerOnceEvery) {
var startTime = new Date().getTime(),
nextTick = startTime,
count = 0;
triggerOnceEvery = triggerOnceEvery || 1;
var internalInterval = function() {
nextTick += interval;
count++;
if(count == triggerOnceEvery) {
func();
count = 0;
}
setTimeout(internalInterval, nextTick - new Date().getTime());
};
internalInterval();
};
The following is a sample usage that prints the timestamp once every minute, but the time drift is adjusted every second
coolerInterval(function() {
console.log( new Date().getTime() );
}, 1000, 60);
It's not perfect, but should be reliable enough.
Consider that the user could switch the tab on the browser, or your code could have some other blocking tasks running on the page, so a browser solution will never be perfect, it's up to you (and your requirements) to decide if it's reliable enough or not.
Tested in browser and node.js
sleeps until 2 seconds before minute change then waits for change
you can remove logging as it gets pretty cluttered in log otherwise
function onMinute(cb,init) {
if (typeof cb === 'function') {
var start_time=new Date(),timeslice = start_time.toString(),timeslices = timeslice.split(":"),start_minute=timeslices[1],last_minute=start_minute;
var seconds = 60 - Number(timeslices[2].substr(0,2));
var timer_id;
var spin = function (){
console.log("awake:ready..set..");
var spin_id = setInterval (function () {
var time=new Date(),timeslice = time.toString(),timeslices = timeslice.split(":"),minute=timeslices[1];
if (last_minute!==minute) {
console.log("go!");
clearInterval(spin_id);
last_minute=minute;
cb(timeslice.split(" ")[4],Number(minute),time,timeslice);
console.log("snoozing..");
setTimeout(spin,58000);
}
},100);
};
setTimeout(spin,(seconds-2)*1000);
if (init) {
cb(timeslice.split(" ")[4],Number(start_minute),start_time,timeslice,seconds);
}
}
}
onMinute(function (timestr,minute,time,timetext,seconds) {
if (seconds!==undefined) {
console.log("started waiting for minute changes at",timestr,seconds,"seconds till first epoch");
} else {
console.log("it's",timestr,"and all is well");
}
},true);
My first thought would be to use the Date object to get the current time. This would allow you to set your set interval on the minute with some simple math. Then since your worried about it getting off, every 5-10 min or whatever you think is appropriate, you could recheck the time using a new date object and readjust your set interval accordingly.
This is just my first thought though in the morning I can put up some code(its like 2am here).
This is a fairly straightforward solution ... the interval for the timeout is adjusted each time it's called so it doesn't drift, with a little 50ms safety in case it fires early.
function onTheMinute(callback) {
const remaining = 60000 - (Date.now() % 60000);
setTimeout(() => {
callback.call(null);
onTheMinute(callback);
}, remaining + (remaining < 50 ? 60000 : 0));
}
Here's yet another solution based on #Linus' post and #Brad's comment. The only difference is it's not working by calling the parent function recursively, but instead is just a combination of setInterval() and setTimeout():
function callEveryInterval(callback, callInterval){
// Initiate the callback function to be called every
// *callInterval* milliseconds.
setInterval(interval => {
// We don't know when exactly the program is going to starts
// running, initialize the setInterval() function and, from
// thereon, keep calling the callback function. So there's almost
// surely going to be an offset between the host's system
// clock's minute change and the setInterval()'s ticks.
// The *delay* variable defines the necessary delay for the
// actual callback via setTimeout().
let delay = interval - new Date()%interval
setTimeout(() => callback(), delay)
}, callInterval, callInterval)
}
Small, maybe interesting fact: the callback function only begins executing on the minute change after next.
The solution proposed by #Linus with setInterval is in general correct, but it will work only as long as between two minutes there are exactly 60 seconds. This seemingly obvious assumption breaks down in the presence of a leap second or, probably more frequently, if the code runs on a laptop that get suspended for a number of seconds.
If you need to handle such cases it is best to manually call setTimeout adjusting every time the interval. Something like the following should do the job:
function repeatEvery( func, interval ) {
function repeater() {
repeatEvery( func, interval);
func();
}
var now = new Date();
var delay = interval - now % interval;
setTimeout(repeater, delay);
}