Will Posted April 8, 2014 Share Posted April 8, 2014 Hey there, I searched about and couldn't find much about this, so here I am. With Pixi, how do you guys write your timestep (in specific the update/logic and physics part of it)? Any advice? I would prefer not to have a variable update loop, though of course I use requestAnimationFrame for the render loop. setTimeout seems to sometimes jump a bit, and isn't very smooth. Same thing if I multiply values by the delta time. Quote Link to comment Share on other sites More sharing options...
Rex Rhino Posted April 8, 2014 Share Posted April 8, 2014 I base mine entirely on delta time, and I have no problems with it. As long as your update requires less time than the requestAnimationFrame interval it should work fine. I would recommend using delta in most cases. But, if like you say, you don't want a variable update loop, I would still keep track of delta time, and I would sum it until you reach your desired minimum time step and then update (for example, you would update your game world only if more than 1/60th of a second has passed... essentially limiting it to 60 fps or fewer). I would never, ever, use setTimeout if requestAnimationFrame is available. Will 1 Quote Link to comment Share on other sites More sharing options...
xerver Posted April 8, 2014 Share Posted April 8, 2014 ... I would never, ever, use setTimeout if requestAnimationFrame is available. They both have use cases, setTimeout is good if you need your loop to continue running when the window is blurred. It is also fine if you are not syncing with the monitor refresh rate, or have a low fps need. Will 1 Quote Link to comment Share on other sites More sharing options...
Will Posted April 8, 2014 Author Share Posted April 8, 2014 I base mine entirely on delta time, and I have no problems with it. As long as your update requires less time than the requestAnimationFrame interval it should work fine. I would recommend using delta in most cases. But, if like you say, you don't want a variable update loop, I would still keep track of delta time, and I would sum it until you reach your desired minimum time step and then update (for example, you would update your game world only if more than 1/60th of a second has passed... essentially limiting it to 60 fps or fewer). I would never, ever, use setTimeout if requestAnimationFrame is available. Okay, though seeing as I use requestAnimationFrame anyway for the render loop, and very very few people have 120hz monitors, then surely it would be limited to 60fps or lower either way? I would prefer it to be a constant value. They both have use cases, setTimeout is good if you need your loop to continue running when the window is blurred. It is also fine if you are not syncing with the monitor refresh rate, or have a low fps need. I'm using node-webkit, so I guess the window blurring would be less likely to occur. Is it not possible to "smooth" the update? With setTimeout, there still appears to be small variations in the update speed (not my PC, it is fast ), resulting in a tiny jittering effect by the time rendering happens. Quote Link to comment Share on other sites More sharing options...
OadT Posted April 8, 2014 Share Posted April 8, 2014 Well, requestAnimationFrame is a kind of "smooth" update If you want to avoid problems caused by variations in update speed you need to make your game logic fps independent, even with setTimeout. Little tipp btw: call setTimeout at the beginning at your loop function (maybe you already know it, I just want to make sure ) Quote Link to comment Share on other sites More sharing options...
Rex Rhino Posted April 8, 2014 Share Posted April 8, 2014 Okay, though seeing as I use requestAnimationFrame anyway for the render loop, and very very few people have 120hz monitors, then surely it would be limited to 60fps or lower either way? I would prefer it to be a constant value. You need to make your calculations based on the delta, even if requestAnimationFrame will most likely be ~60fps, because not ALL devices are 60fps. And in the future, when all devices are 120hz, people might still be playing your game! The logic for frame limiting in requestAnimationFrame is as simple as this:elapsedTimeSinceUpdate = elapsedTimeSinceUpdate + deltaTime; // add the time since last refresh to the elapsed time since the last updateif (elapsedTimeSinceUpdate > 16.6666666) { //60fps = ~16.666666 milisecs update(elapsedTimeSinceUpdate); //update logic is called here elapsedTimeSinceUpdate = 0; // reset the time because we are calling the logic}render(deltaTime); // call the render code every frameYou will still need to use the delta/elapsedTime in your render/update calculations, because while this code guarantees that update will not be called faster than 60fps, it could be slower than 60fps. They both have use cases, setTimeout is good if you need your loop to continue running when the window is blurred. It is also fine if you are not syncing with the monitor refresh rate, or have a low fps need.Yeah, I guess that I am a little bit guilty of using hyperbole when saying never, never, never use setTimeout... But from what I understand about the question, I would say in this case it isn't a good idea. In fact, if he is using node-webkit, he could even use something like workers: http://www.w3schools.com/html/html5_webworkers.asp Quote Link to comment Share on other sites More sharing options...
xerver Posted April 8, 2014 Share Posted April 8, 2014 ... Is it not possible to "smooth" the update? With setTimeout, there still appears to be small variations in the update speed (not my PC, it is fast ), resulting in a tiny jittering effect by the time rendering happens. You should be basing updates on the delta time, linear interpolation is your friend here. Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Well, requestAnimationFrame is a kind of "smooth" update If you want to avoid problems caused by variations in update speed you need to make your game logic fps independent, even with setTimeout. Little tipp btw: call setTimeout at the beginning at your loop function (maybe you already know it, I just want to make sure ) Thanks, I have seen that before, but never really knew why. Care to enlighten me? You need to make your calculations based on the delta, even if requestAnimationFrame will most likely be ~60fps, because not ALL devices are 60fps. And in the future, when all devices are 120hz, people might still be playing your game! The logic for frame limiting in requestAnimationFrame is as simple as this:elapsedTimeSinceUpdate = elapsedTimeSinceUpdate + deltaTime; // add the time since last refresh to the elapsed time since the last updateif (elapsedTimeSinceUpdate > 16.6666666) { //60fps = ~16.666666 milisecs update(elapsedTimeSinceUpdate); //update logic is called here elapsedTimeSinceUpdate = 0; // reset the time because we are calling the logic}render(deltaTime); // call the render code every frameYou will still need to use the delta/elapsedTime in your render/update calculations, because while this code guarantees that update will not be called faster than 60fps, it could be slower than 60fps. Yeah, I guess that I am a little bit guilty of using hyperbole when saying never, never, never use setTimeout... But from what I understand about the question, I would say in this case it isn't a good idea. In fact, if he is using node-webkit, he could even use something like workers: http://www.w3schools.com/html/html5_webworkers.asp Thanks, that works! But, the jittering occurs as soon as I start multiplying values by delta time. You should be basing updates on the delta time, linear interpolation is your friend here. I know how to lerp, but what values should I lerp? And by how much? Quote Link to comment Share on other sites More sharing options...
Rex Rhino Posted April 9, 2014 Share Posted April 9, 2014 Thanks, that works! But, the jittering occurs as soon as I start multiplying values by delta time.Can you post some of the code where you are multiplying the delta time? Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Can you post some of the code where you are multiplying the delta time?core.logic = (function(){ var logic = {}; var target = 1000 / 60; logic.leftOver = 0; logic.buffer = 0; logic.tick = function(dt){ core.time.tick();//Update the delta time. requestAnimationFrame(function(){ core.logic.tick(core.time.delta); }); logic.buffer += dt; if(logic.buffer >= target){ logic.update(dt / 1000); logic.buffer = 0; } core.render(); }; logic.update = function(dt){ core.square.x += 75 * dt; }; logic.lerp = function(a, b, x){ return a + x * (b - a); }; return logic;})();It doesn't jitter by very much (at all), and perhaps animation would make it barely noticeable, but it annoys the hell out of me. (Though, my 11 year old brother noticed it without me pointing it out, so I do need to fix it really.) Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 9, 2014 Share Posted April 9, 2014 Just out of curiosity what browsers are you testing this in? Are you noticing the same issue in all of them? Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Just out of curiosity what browsers are you testing this in? Are you noticing the same issue in all of them? Node Webkit, happens in Chrome too. Though, I think I might have fixed it now!core.logic = (function(){ var logic = {}; var target = 1000 / 60; logic.leftOver = 0; logic.buffer = 0; logic.tick = function(dt){ core.time.tick();//Update the delta time. requestAnimationFrame(function(){ core.logic.tick(core.time.delta); }); if(1 / (core.time.delta / 1000) < 30){ console.warn("FPS DIP! " + 1 / (core.time.delta / 1000) + " " + Date() + " " + logic.buffer + " " + core.time.delta) } logic.buffer += dt; while(logic.buffer >= target){ logic.update(dt / 1000); logic.buffer -= target; } core.render(); }; logic.update = function(dt){ //core.square.rotation += 0.01; if(core.square.x < 1240){ core.square.x += 1; } else{ core.square.x = 0; } }; logic.lerp = function(a, b, x){ return a + x * (b - a); }; return logic;})();However, the framerate goes down a fair bit when the buffer gets to the point of having to update twice, though only every couple of minutes, also barely noticeable as it goes right up again. The motion of the square is silky smooth! Quote Link to comment Share on other sites More sharing options...
OadT Posted April 9, 2014 Share Posted April 9, 2014 Thanks, I have seen that before, but never really knew why. Care to enlighten me? Sure it's quite simple, just imagine following: Your loop-function needs 10ms to execute and you want it to run every 20ms. If you call setTimeout at the end of the function the function will start just every 30ms, because after you call the loop-function it will calculate for 10ms and after its finished it will call setTimeout (needs further 20ms). If you call setTimeout at the beginning the browser plans to start the loop 20ms later again and will calculate the function finishDidn't help you now, but now you are enlightened Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Sure it's quite simple, just imagine following: Your loop-function needs 10ms to execute and you want it to run every 20ms. If you call setTimeout at the end of the function the function will start just every 30ms, because after you call the loop-function it will calculate for 10ms and after its finished it will call setTimeout (needs further 20ms). If you call setTimeout at the beginning the browser plans to start the loop 20ms later again and will calculate the function finishDidn't help you now, but now you are enlightened I see, thanks! Have a cookie Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 9, 2014 Share Posted April 9, 2014 Few things to note from your code above.requestAnimationFrame(function(){ core.logic.tick(core.time.delta); });Every time your calling requestAnimationFrame your assigning it a new function, this will get garbage collected fast (probably the FPS drop your noticing every couple minutes). In your case you should be able to call it like this:requestAnimationFrame(core.logic.tick);I don't mess with pixi.js so I'm not sure how everything is set up, but just from your code I don't think you need to pass the core.time.delta since your calling the function to update it just prior. That variable should be available to anything that can access core.time. Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Few things to note from your code above.requestAnimationFrame(function(){ core.logic.tick(core.time.delta); });Every time your calling requestAnimationFrame your assigning it a new function, this will get garbage collected fast (probably the FPS drop your noticing every couple minutes). In your case you should be able to call it like this:requestAnimationFrame(core.logic.tick);I don't mess with pixi.js so I'm not sure how everything is set up, but just from your code I don't think you need to pass the core.time.delta since your calling the function to update it just prior. That variable should be available to anything that can access core.time. Thanks for that! The FPS dip does still happen - though I know it is when the leftover time in the buffer gets to the point where the while loop runs twice - though it also happens less often. Can you recommend anything for learning how to keep the garbage collector happy? Yeah, I was just doing that so I can refer to the delta time as "dt" instead of core.time.delta. It's no big deal. With Pixi, you have to write all the loop stuff, it doesn't appear to matter how, as long as core.renderer.render(core.stage);gets called to render all the stuff. Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 9, 2014 Share Posted April 9, 2014 Here are a few good links to look at. http://buildnewgames.com/garbage-collector-friendly-code/https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascripthttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Managementhttp://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/ The big thing to know with the garbage collector is that it will collect anything thats no longer being referenced. So functions in a callback are bad, but a reference to a function are good (the reference keeps it alive). Also know that passing in an object/array, simply passes in a reference/pointer to the object, but passing in a variable actually passes in the values which will then be garbage collected.var myObject = {x:0,y:0}; var updateXY = function(x,y){ var x = x+1; // var x will be collected var y = y+1; // var y will be collected return ([x,y]); // will be collected as a new array, very expensive.}var newXY = updateXY(myObject.x,myObject.y);// The above function creates a lot of garbage, and when run 1000 times, // that garbage will add up.var updateXY = function(a){ // a = a pointer to your object. a.x = a.x+1; // nothing to collect a.y = a.y+1; // nothing to collect // no need to return values, your code actually changed the referenced object.}updateXY(myObject);// The above actually uses the fact that JS only passes in references to objects to it's// advantage.My example code could probably be better, but hopefully it helps a little. Will and nacs 2 Quote Link to comment Share on other sites More sharing options...
Will Posted April 9, 2014 Author Share Posted April 9, 2014 Here are a few good links to look at. http://buildnewgames.com/garbage-collector-friendly-code/https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascripthttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Managementhttp://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/ The big thing to know with the garbage collector is that it will collect anything thats no longer being referenced. So functions in a callback are bad, but a reference to a function are good (the reference keeps it alive). Also know that passing in an object/array, simply passes in a reference/pointer to the object, but passing in a variable actually passes in the values which will then be garbage collected.var myObject = {x:0,y:0}; var updateXY = function(x,y){ var x = x+1; // var x will be collected var y = y+1; // var y will be collected return ([x,y]); // will be collected as a new array, very expensive.}var newXY = updateXY(myObject.x,myObject.y);// The above function creates a lot of garbage, and when run 1000 times, // that garbage will add up.var updateXY = function(a){ // a = a pointer to your object. a.x = a.x+1; // nothing to collect a.y = a.y+1; // nothing to collect // no need to return values, your code actually changed the referenced object.}updateXY(myObject);// The above actually uses the fact that JS only passes in references to objects to it's// advantage.My example code could probably be better, but hopefully it helps a little. Certainly does help! Thanks a bunch, I understand things a bit better now, though I will give those articles a more thorough read through when I'm less tired. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.