C2 games and 120hz monitors (*dt problem?)

  • KaMiZoTo

    dt is the duration between two ticks, it is there because events don't occur on a fixed time like every 1/60 seconds.

    when you want something to move at a speed of 20 pixels per seconds, you will have to increase its position of 20 pixels each seconds at the end, so at 20*dt pixel each tick to achieve that.

    summing up dt every tick into a variable will make said variable increase by 1 each second (unless you have a timescale other than 1 or if the fps goes under 10, those are the two limitations).

    the solution?just do the calculation on a simple case with a fixed time and you will know:

    at 60 fps, I should be at the same position in one tick than at 120 fps in two ticks (as twice as more ticks will occur).

    in your case, you start up at position A, you increase it by 20*dt every tick, lets try on 1/60 of seconds of interval

    in one tick (1/60 of seconds) at 60 fps, the next tick it will be A+1/30

    at 120 fps (1/60 of seconds, correspond to two ticks), the next tick it will be A+1/60, the tick after that it will be A+1/60+1/60=A+2/60=A+1/30

    it is the same result, so you are good.

    heck, even more simple: imagine with 10 fps compared to 100 fps (ten times more ticks per seconds), for 1/10 of seconds passed

    in one tick at 10 fps, A+20/10=A+2

    at 100 fps, in ten ticks: A+20/100+20/100+20/100+20/100+20/100+20/100+20/100+20/100+20/100+20/100 = A+200/100 =A+2

    sqiddster "'every X seconds' should never be used with dt. It uses it internally."

    it is easier to understand as "you are acting on a fixed time, so no compensation is needed, every 1 seconds will be true every seconds, regardless of the framerate" (sure it can become false in extreme cases, but those cases are not something you should be designing for).

  • Aphrodite Thank you. I'm starting to understand.

    I'm a little narrow minded sometimes and like when things are simple. That would be great if that dt conversion was automatic...but it's probably utopic!

    (I like utopia)

    Well, let's check all my events/actions now. :*(

    Aurel If you use DT only for your lerp formulas, this is strange your game goes banana on 120 hz after what I understand from that threat. :/

    Perhaps it's related to EVERY TICK -> VALUE changes (with no lerp and no dt)

  • "That would be great if that dt conversion was automatic...but it's probably utopic!"

    well, it would be hard to know what needs dt and what needs NOT dt (if you move once an object of 200 pixels, dt is not something you might want).

    just have an event, compare situation A, and Situation B (the difference being a time between the two), for the same time, you should arrive at the same situation B in both cases in you calculs regardless of the framerate.

    btw, just for the "I said it" aspect of that (I know I am boring with that, but now that I can prove it once and for all, I won't let that chance go):

    A=lerp(A,B, 120*dt), for 1/30 seconds (2 frames at 60 fps, 4 frames at 120 fps)

    at 60 fps, A is first equal to 0, in one frame: A=lerp (0,B, 2)=2B, in two frames A=(2B, B, 2) =0 (if we continued, we would go 2B to 0 to 2B to 0, etc..)

    at 120 fps, A is first equal to 0, in one frame: A=lerp(0, B, 1)=B, in two frames, A=lerp(B, B, 1)= B+1*(B-B)=B

    in 3 frames, A=lerp(B, B, 1)= B, in 4 frames, A=lerp(B, B, 1)=B (if we continued, it would still be B).

    not the same result, so framerate independancy is not there, in short, never use dt in lerp like that, never use lerp like that for that matter, as it is not intended to be used like that!

    Aurel

    KaMiZoTo

  • Mathematically speaking dt has no relation to specific framerates. A game correctly using dt will work the same (or at least very similarly) at any framerate. Nothing in the C2 engine hard-codes an assumed 60 Hz rate. If your game works twice as fast (or even just differently) at 120 Hz, that strongly suggests you are not using dt when you should be!

    I'd point out 'Every X seconds' should not use dt in its parameter. This has exactly the opposite effect of what you want. It is already framerate independent, and adding dt makes it framerate dependent, since:

    Every 5 * dt seconds

    10 FPS, dt = 0.1, runs every 0.5 seconds

    100 FPS, dt = 0.01, runs every 0.05 seconds

    It's broken! It now depends on the framerate. On the other hand "Every 5 seconds" runs every 5 seconds at any framerate. So it's simply wrong to use dt in 'Every'.

    An easy way to test your game for correct use of dt is to set the timescale to 0.1 and look for anything that is still running at the normal rate. In other words everything should be super-slo-mo, but if you missed dt somewhere then something will be happening way too quickly. It's exactly the same principle that keeps the game running at the same rate at 120 FPS as well (or even higher).

    I think A = lerp(A, B, x*dt) is correct, even with A changing every frame. I vaguely remember an alternative from the Construct Classic days of A = lerp(A, B, 1 - x ^ dt), but I can't remember the maths behind it, and some quick experimenting shows that both ways appear to compensate correctly for changes in the framerate although they provide slightly different interpolation curves.

  • Ashley

    "I think A = lerp(A, B, x*dt) is correct, even with A changing every frame. I vaguely remember an alternative from the Construct Classic days of A = lerp(A, B, 1 - x ^ dt), but I can't remember the maths behind it, and some quick experimenting shows that both ways appear to compensate correctly for changes in the framerate although they provide slightly different interpolation curves."

    they don't give the same results on the same occuring times, that is enough to say that it is not framerate independant (being "fair enough at the end" is not something I would say correct due to the fact how it is evolving is important in that case, but again using lerp that way is asking for those issues as it is a really weird to do, I am wondering how to do an exponential interpolation to go to 95% of the road from a to be which would be framerate independant completely, will get back if I have the time to do it), did not tried with lerp(A, B, 1- x^dt) so I cannot tell.

    EDIT:

    Lets imagine

    experp(a, b, x), if x =0, returns a, if x =1, returns 0.95b+0.05a, evolution exponential between a and b when x increase linearly from 0 to 1, so you can make x increase of 20*dt for exemple and lock it at x=1, that may work similarly without breaking the framerate independancy.

    the function being roughly

    a+(1-exp(-3x))*(b-a)

    exp(c) being the contant e at the power c (e^c)

    if someone can try it on C2 to verify

  • Ashley / Aphrodite,

    The use of a = lerp(a, b, z * dt) to compensate for variations in delta time is correct. I've just crunched an example to illustrate this in LibreOffice Calc. The lines in the image equate the results from using lerp with dt for fps values of 60, 72, 120 and 144. As you can see, the end result after 1 second at each frame rate is the same (well, the same within a value of approx 0.002).

    The x axis is number of frames, so the right hand side of each drawn line equates to 1 second of time, the y axis is the result of the lerp using cell value for B3, for example =B2 + (1 - B2) / $B$1 where B1 contains the fps value. I am not a LibreOffice guru and couldn't figure how to stretch the x axis for each line so they would overlap....

  • Thanks for that chart, Colludium, Was getting confusing with the diverging statements. dt is still our safe haven for consistent results it seems.

  • Somebody - it has been an emotional 30 minutes! I went from total confidence in using dt to a horrific realization that I had to go through my game and edit my lerps all the way back to where I started.

  • they don't give the same results on the same occuring times

    What do you mean by that? What did you test and what results did you get?

    I am gradually remembering that A = lerp(A, B, 1 - x ^ dt) was in fact a correct form. I think the maths was something like:

    Instead of considering how far you've gone, think about how far you have left to go. So if you want to jump 10% of the way there, that's the same as saying there's 90% of the way left to go after the jump, which is the same as multiplying the distance remaining by 0.9. Let's call that 'm'.

    So if you make multiple jumps, that's the same as multiplying the remaining distance by m multiple times, which is m^n.

    Remember that multiplying powers adds the power, i.e. m^a * m^b = m^(a+b). If we raise m to the power dt, then whatever number of multiplications happen over one second (i.e. the number of frames) always add up to 1. E.g.:

    At 4 FPS, dt will be 0.25, so the distance remaining is m^0.25 * m^0.25 * m^0.25 * m^0.25 = m^1.

    At 10 FPS, dt will be 0.1, so the distance remaining is m^0.1 * m^0.1 * m^0.1 * ... (10 times) = m^1.

    Both times, despite there being a different number of frames (multiplications), the distance remaining was reduced by the same factor m after one second.

    Therefore a formula like A = lerp(A, B, 1 - 0.5^dt) means "cover half the remaining distance every 1 second regardless of the framerate".

    Without doing the actual maths to prove it, I think Colludium has shown A = lerp(A, B, n * dt) is also correct. But I think the raise-to-power way is more predictable - the example I gave demonstrates how you can easily set it to cover half the distance every 1 second, but with just using n * dt, how do you choose n such that it covers half the distance every 1 second?

  • Colludium my calculs shows that when z becomes larger, the error becomes more important, and with values really high, it can become quite big, which value of z did you tried? (as I am curious to see, on my side, there is a value limit where the result becomes completely unstable, and can go far in variations with time), like if z is equal to fps, you have the direct value of b, stable as it is lerp (a,b,1), but ifyou have z being twice the value of fps, you have the recurring unstability, where you over around b but never come near to it (the difference a-b is always the same absolute value), and over that, well...

    (I cannot use C2 until the next week so I could be wrong)

    Ashley I did not said lerp(a,b, 1-x^dt) was a bad idea (I did not calculate it), but that lerp(a, b, x times dt) was, as the product x*dt can go potentially over 1 with the fps variations (for x being 60 you can have issues as soon as the fps goes under 60) and even without that issue, the evolution is prone to errors with that calcul as with an higher x, the errors are higher too for the same time value, and I still have trouble with why this is even used at all too.

    Actually, the way it goes reminds me of my studies, basically:

    SP being b, PV being a, temps being the time, the blue one is when x*dt is between 1 and 2, the red one when x*dt is under 1 (which should tick something in your head, what if the framerate drastically go down for one frame or two due to something that may happen)

    PS: this graph is not related to lerp, just the allure is the exact same (the maths behind a P regulation being similar to a a=lerp(a, b, x*dt) )

    in short, due to how C2 works, you will be safe as long as x is under 10 for a timescale of 1.

    EDIT: well actually... why I am even talking about this, I am going far off topic, we will see what were the issues of the people that have trouble.

  • I don't know how to compensate the variation of Dt, but I know how to compensate that high level math discussion : http://bit.ly/1B44qVT

  • I'm pretty sure the dt in C2 is based on the assumption that whatever the framerate, it'll sum to a value of 1 after a second. I don't see why it should go crazy at 120 fps unless you've double counted dt somewhere, or just plain used it wrong

  • You have to change the timescale depending on target vsync i believe.

    dt adapts itself to the fps value and as far as I saw on my own, that applies even when the V-sync changes (if the refresh rate was under 10 hz, you would have to, but that is not the case).

    which is normal, as there is no way in C2 to check the refresh rate and the a multiplatform engine should at least support different refresh rate by itself.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Thanks a billion for all your answers. This is really kind of you all to take the time to have a look at this.

    Like Kamizoto, you got me lost pretty fast, but I understand the most important:

    • the game goes crazy only because I used *dt in events where I shouldn't, and I couldn't notice it before because 30-60 fps is a low variation compared to 60-120 tick per second.
    • You can't code a game if you suck at mathematics, even with C2. I have to learn what I skipped at school as I was making fun of the geeks. Payback time, I guess!

    I don't know if it makes sense, but here's a sincere feedback from a regular C2 user who sucks at math.

    The dt guide is very well written, and when you read it, you think(*dt) you understand it. I've read it, I don't know, maybe 50 or 60 times.

    But when you have to write tons of events, it would be so nice to have charts you can quickly reference to. Here on the forum, or even better inside C2.

    Something like this:

    lerp to an angle, use this:

    lerp to a position, use this:

    lerp to speed, use this:

    lerip to scale, use this:

    lerp to variable value, use this:

    every tick events: don't use if this and this. Use if this and this.

    every X second: don't use.

    I must sound super lazy to most of you, and I definitely understand this, but for average users it would be a freaking life saver.

  • DT is calculated by the C2 runtime every game tick by simply doing something like this: dt = time_now - last_tick_time

    Construct users do not have to worry about factoring in the v-sync rate in their calculations. As I mentioned earlier, the underlying Construct 2 engine uses something called "requestAnimationFrame" which asks the browser to run the next game tick according to the refresh rate of the local display to smooth out animation.

    Regardless of how often the game tick is run (whether its running 60 ticks or 120 ticks every second), dt will adjust accordingly.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)