0 Favourites

Progress in Framerate-Independent Platforming

  • A while back, I stumbled upon a thread where some game developers noticed pixel-perfect platformers had different jump heights depending on the framerate. I’m pleased to announce I have a solution. Assuming your game doesn’t require changing gravity angles, add the following two events to your event sheet:

    EDIT: I originally thought subtracting a value would correct your position. Adding is actually what we want, and I've changed the events accordingly.

    Player Platform is falling

    System Player.Platform.VectorY < Player.Platform.MaxFallSpeed

    Player Set Y to Self.Y + ((0.5*Self.Platform.Gravity)*dt)*dt

    (Sorry, I had a little trouble inserting the Screenshot)

    I’ve nicknamed this trick “Platform Minus” (because "Platform Plus" is taken). C2’s built-in platform behavior makes an imperfect approximation of the physics equations behind projectile motion. This is usually close enough that the difference doesn’t matter, but it does flatten the top of the jump arc and reduce the jump height by a few pixels, and changing framerates makes the problem more pronounced. This quick fix moves the arc closer to the exact equation and is less sensitive to framerates. I built a test capx to illustrate my point:

    Jump Height Testing

    In the debug window, look at the array's third value after you jump and land. With the 650 px/sec jump strength and 1000 px/(sec^2) gravity, the jump height should actually be 211.25 px. Press the "0" and "2" keys to switch modes.

    Platform movement in the X-direction and 8-Direction movement have the same problem, but it’s much less noticeable because max speeds are usually relatively low, and it’s a little harder to fix because the accelerations aren’t constant. I hope I can get around to modifying the original plugins, but I’m not an expert coder, and I wouldn’t complain if someone beat me to it.

    Platform Minus is a simple step, but an important one, towards even better platforming with Construct 2. We’re that much closer to perfectly framerate-independent game design.

    Edit: Introducing "8 Direction Minus!" This slight change to the 8 Direction behavior makes top-down movement a little smoother and a bit less-sensitive to framerate changes:

    8 Direction Minus(Once you get to Google Drive, right-click the name of the whole folder and click "download" to download the whole folder)

    [NOTE: If you downloaded 8 direction minus before 13 August on 5:26 PM, please download it again, as the first version actually made a worse approximation of physics than the built-in behavior.]

    An additional. known error: When a sprite with 8 Direction Minus is moving in only one direction at max speed, speed in the other direction becomes a very, very small number close to zero. However, this doesn't actually move the sprite in the second direction at all.

  • Wow that's a pretty nice effort, and a lot of data analysis...

    Good job!

    It's always going to be difficult to get exact frame rate independence with behaviours - since by default the behaviours use some built-in form of 'dt'.

    I think the only REAL way to do this is to create a COMPLETELY custom movement system (moving, jumping, crouching - everything) without using ANY behaviours at all. You then have complete control over your fps independence.

    ~Sol

  • Double-Wow...

    That is one hell of an analysis you've done there! Sir, I applaud you with both hands.

    Perhaps the proper equation will be corrected in a future C2 version? (If my understanding of the thing is that it's "simply" maths that can be adjusted.

    Also, kinda related, something awesome I discovered a few days ago:

    Wish I could do like in Matrix and speed-learn all of it...

  • For the jump height issue, wouldn't it be easier to simply, when the sprite is starting to fall for the first time, set it's Y Position to the jump height desired (which can be calculated) unless it hitted a roof?

    Jump Height = (Jump Strength)*(Jump Strength)/(2*gravity)

    Also there already is a platform + plugin existing so you might consider a different name

    EDIT: Ahh I see what you have done (wasn't quite awake...), it might work actually but unsure it would work with obstacles.

  • It looks like your solution is just to subtract off the delta-time calculation C2 does, effectively removing dt from the formula.

    There's a better way: just set a minimum framerate of 200. Then dt is always fixed so stepping will be deterministic. You'll have to adjust your speeds and accelerations though.

  • Ashley

    There’s actually an error in the math for the Platform and 8-Direction behaviors.

    The velocity calculates right:

    V = V(old) + a*dt

    But the position calculates as:

    Y = Y(old) + V*dt

    So when you substitute the velocity equation in, it comes out:

    Y = Y(old) + (V(old) + a*dt)*dt

    Y = Y(old) + V(old)*dt + a*(dt)^2

    But since the actual physics equation is:

    Y = Y(old) + V(old)*dt + (1/2)*a*(dt)^2

    Subtracting out (1/2)*a*(dt)^2 puts the position to what the physics equation predicts.

    Also, I realize the classic physics equation depends on the total time from t = 0, but it works out if you think of starting a new equation every tick with the last tick’s positions and velocities as the new Y0 and V0. Or, you can think of it as position at time t and time t+dt, then subtract the two from each other to get the change in position.

    My change isn’t truly framerate-independent, but it loses accuracy more slowly as the framerate drops.

    Aphrodite Is "Platform Minus" taken? It kind of works, since I'm subtracting something from the current equations. Also, I made a demo game with the new events, and it looks like the behavior handles collisions, clipping, etc. just like it normally would.

  • calebbennetts - this is pretty confusing, but I don't think it's the case... it's not that C2 uses the wrong formula, it's that it treats acceleration differently to the kinematic equations.

    The kinematic equation (d = v * t + 1/2 * a * t^2) assumes the acceleration keeps increasing throughout the given time t. So for example if you have a car that starts at a given speed and keeps a steady acceleration, this will tell you how far it will travel in say 30 seconds.

    Construct 2 treats movement as a sequence of very small discrete steps that happen once per frame. Given a frame is typically just 16ms, I never thought to take in to account the effect of the continuing acceleration over that time. So C2 actually assumes the velocity is constant (not accelerating) for the duration of delta-time. However since it updates the velocity according to the acceleration after every step, you still get the effect of acceleration over time.

    Should Construct 2 take in to account the acceleration during delta-time? I guess that would be more correct. However there are two issues: we probably can't change this in the engine itself without breaking loads of existing games (which will all become slightly off), and the acceleration can actually change every tick as well, for example alternating between 1000p/s/s and 0p/s/s every tick. In that case which acceleration should be used for each step, given that there is a different start and finish acceleration and the engine doesn't necessarily know what the finish acceleration is? I think strictly speaking if I were starting from scratch I'd take in to account the starting acceleration over the whole step, but we can't change this now, and I think this is one of those things where as the time step tends to zero it becomes closer to correct, so this is another one of those "discrete steps don't work quite like the continuous real world" quirks.

    Anyway, this doesn't quite solve the stated problem: the jump height can vary depending on the framerate because the calculations take in to account delta-time, which is based off an imperfectly accurate timer in an imperfectly scheduled operating system, so it tends to have small random variations every step. Given the movement over time is a non-linear summation, the small random variations can accumulate and it can change by a couple of pixels by the crest of a jump. Your altered formula is still susceptible to the same problem, because it still uses a randomly-varying dt, although I can see it would reduce the accumulated error assuming a steady acceleration. My solution fixes the value of dt every step so there is no random variation and you are guaranteed identical results every time, which you would still need to do with your altered formula to guarantee identical results as well.

    Still, I'm wondering if a setting to use more accurate acceleration calculations would be worthwhile...

    tl;dr: the problem is a randomly-varying dt, but your formula only changes whether acceleration is taken in to account during the step or not.

  • The kinematic equation (d = v * t + 1/2 * a * t^2) assumes the acceleration keeps increasing throughout the given time t. So for example if you have a car that starts at a given speed and keeps a steady acceleration, this will tell you how far it will travel in say 30 seconds.

    Using this analogy I some quick maths (using Excel) to see which equation would be most reliable. For a car accelerating at 2 m/s^2 from an initial speed of zero for 30 secs, with a 1.0 sec sample (dt) period, the final displacements are:

    Using Platform Equation: 930m

    Using Incremental Newtonian Equation: 900m

    If the sample period is changed to 0.5 sec (ie analogous to using a 120 Hz monitor instead of a 60 Hz monitor) then we get the following answers:

    Using Platform Equation: 915m

    Using Incremental Newtonian Equation: 900m

    If the dt is changed to 2 sec (ie analogous to using a 30 Hz refresh rate) then we get the following answers:

    Using Platform Equation: 960m

    Using Incremental Newtonian Equation: 900m

    Conclusion (Revised) -

    The Newtonian equations work, whereas the equation used in the Platform Behaviour produces inconsistent results.

    Edited to correct some poor public maths...

  • Ashley calebbennetts,

    I updated my post above - I corrected my calculations and have to conclude that Newtonian equations should be used if consistent results are to be experienced across different browser refresh rates and with variations in dt....

    For anyone who's interested - here's a copy of the spreadsheet.

  • Colludium Thanks for your data. We definitely want to get as close to Newton as possible.

    However, neither my originally-proposed method nor the built-in calculation method is correct. We actually need to add (1/2)*g*t^2.

    I made a mistake in my calculations. Due to the discrete/continuous difference Ashley mentioned, I mismatched position and velocity by one tick. When I fixed the error, Platform Minus gave a worse error than the built-in Platform behavior.

    However, ADDING (1/2)*g*t^2 seemed to give a smaller error than the built-in calculations. I built a test program in Construct 2 that measures the unchanged, added, and subtracted jump heights.

    Thanks to Aphrodite for the max height equation, the actual maximum height for a 650 jump strength and 1000 gravity should be 211.25 pixels.

    The Platform behavior yields a 213.958 px jump height.

    Subtracting (1/2)*g*t^2 gives a less-accurate jump height: 216.667 px.

    But adding (1/2)*g*t^2 gives a jump height very close to the expected value: 211.285 px.

    You can find the test .capx here.

    To use it, go to debug and select the array. After you jump and land, the third array value will be your jump height. Press 0, 1, and 2 to toggle between the unchanged, subtracting, and adding calculation methods, respectively.

  • calebbennetts also with the Plus method I saw that the time to go from the beginning of the jump to the top (in fact half the time to do the jump completely) was 0.64999999...

    with the minus it was 0.65833333...

    and with the null it was 0.65416666...

    it should be JumpStrength / gravity aka 0.650, so it is also nearer newton in that department it seems.

    I edited my original maths topic to have all the equations between jumpstrength, gravity, height, and time from beginning to top

    PS: all times are in seconds

  • Construct 3

    Buy Construct 3

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

    Buy Now Construct 3 users don't see these ads
  • calebbennetts, the acceleration factor should be added iaw s = ut + 0.5 * a * t^2.

    I think that a capx example using platform motion does not provide a representative example because the equation to modify the sprite's position acts in opposition (or in addition) to the platform behaviour movement - the sprite is effectively moved twice per tick. To make a truly representative analysis you would have to disable the platform behaviour and substitute your own equation in its place (or use a spreadsheet :p ).

  • Also it seems C2 tends to give an higher jump height than desired when the framerate is correct, we should try your version in bad framerates to see if it solves the initial issue: varying/too short jump heights, as the actual jump height is lower, we can expect that the framerate variation can make it even lower.

  • Aphrodite: I’m not sure how to test it effectively with lower framerates, except 60 fps. I set minimum framerate to 60 and tested the program. With “null,” jump height is about 216.7, with “plus,” it’s around 211.4, and with “minus,” it’s around 222.1. I think we can consider my original suggestion (subtracting (1/2)*a*t^2) quite well out of the running, and the additive method gives a more accurate result than the built-in behavior, but to see how well it keeps accuracy with changing framerates, we would probably have to turn back to the spreadsheets.

  • well if the minimum framerate is set to 60, I guess the game will slow down instead of "skipping" so that would be fine, however that does not mean it will be framerate independent enough if people don't have this kind of setting, for now this subject is more about errors in the platform behavior which should be corrected.

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