Why jitter/jerk when following sprite with layer? (video included)

Just as a proof of concept I have a bare project with:

player->runAction(MoveTo::create(10.0f, Vec2(player->getPositionX(), BOTTOM_OFFSET)));

Director::getInstance()->getRunningScene()->getChildByTag(layerTag)->runAction(Follow::create(player, Rect(0, 0, map->getContentSize().width, map->getContentSize().height) ));

This is cocos2d-x v3.2 with physics integration disabled.

Here is a video of the result (of course when the recording software lags my pc a bit you can see this jerk/jitter better… its not quite as visible when your just running game alone but enough to give disappointing results)

Link:

http://bouncer.htbindustries.org/~pranq/pub/jitter.avi

Thanks in advance!

2 Likes

Not sure I can help much, but my first advice would be to try this on a device rather than the emulator to see if the results are the same - I spend ages trying to get my frame rate up and stop jitters until I finally hacked my iPad to be able to run my game on it - and the framerate was a steady 60fps with nary a judder!

I now inly use the emulator when I don’t have the iPad handy - or I want to test how things look on different sized screens.

Hey thanks the framerate is stable and this is a win32 app. No emulator

UPDATE: I dont think this has anything to do with the framerate because as you see the sprite position is jumping up and down … the framerate would just cause it to be choppy movement

@pr4nq interesting question. This is definitely not because of frame rate. Seems you have done everything correct. Did you try this code on iOS or Android device? I have noticed that Widows support is weak. Sometime there are unsupported functions with blank bodies that work excellent for iOS and Android. Try to change the system.

No have not tried it on a device, but did send a fellow cocos’er the project to let him test. It jittered very little and only happened once per fall for him and I also tracked last/current x,y for both moving nodes and they were always going in the same direction which was as expected.

I almost take back my words on saying that its not framerate related and that its creating an illusion that its bouncing up and down but its really just the scene trying to keep up with the moving player under a microscope so to speak so my cocos’er friend advised me to not worry about it, but in the meantime if anyone is able to figure out why this happens or give any insight to a not so experienced cocos’er :stuck_out_tongue: then please do educate!

@pr4nq I don’t believe it is because of FPS. In your video the FPS does not go below 40. Also if it is because of the FPS then you should not only see choppy movement of the player but also the cubes around it. So I think your first thought was right. Sill I insist to try it on other platform except Windows. As I consider Visual Studio the best tool for C++ development (of course with latest Visual Assist) then the most part of my project I develop on Windows. Hence, I have encountered lots of cases where Widows part of cocos2d-x sucks but iOS and Android work. That is all I can advice for now.

Trying to target windows along with mobile devices so unfortunately the answer doesn’t sing well to my ears :smiley: You may very well be right, but my thinking is that other games can play smoothly then why can’t mine? Especially a barebones project like this again I’ve lost confidence in proceeding with this particular game.

cocos boils down to OpenGL routines? so why can other OpenGL games run just fine, but this has jitter :stuck_out_tongue: There has to be some solution hehe…

Thanks for lending your opinion I’m not disagreeing just debating so no offense :smiley: Maybe someone will chime in with a fix ha ha

UPDATE:

as far as the framerate… when monitoring it even during a release build it has this jitter… but where I was going with this was that when I monitor the jitter as long as the fps is sitting pretty at 60fps there is no jitter but as soon as its drops even by 1-5 fps the jerk starts to occur… It seems that I could possibly fix by basing the movement off the delta time, but isnt cocos Actions doin that already? hmmm… my inexperience with cocos makes it even more challenging to determine the cause and I’ve done logged every property I can think of and did all the tricks I was aware of to try to limit it down to one thing, but I’m still stuck it appears to be something along the lines of moving the scene thats causing the hiccup.

The problem is just fps related. You are correct, that the fps is not going below 40fps in the video, but it is fluctuating. And this is the problem.

Testing it for Debug and also Release, I can assure you, it’s jitter-free. Why is that? Cause on my system the fps don’t fluctuate, they are stable at 60fps all the time.

It’s the same effect you experience with games from the C64/Amiga area when doing screen scrolling. As more sprites and graphics are presented on the screen, it starts to flicker, as the fps are not constant over time. The effect on those machines were just minor, cause the video chips had support of hardware sprites and no pipelining, fixed execution times and special hardware registers, so you were able to adopt/time/optimize this behavior.

You also see that effect in modern AAA games, if you experience a fps drop(e.g. too much particles in an explosion), or your screen refresh rate and the GPU blitting get out of sync. The result will be screen tearing, jittering and stuttering. All because you have a fluctuation in fps instead of a smooth and stable framerate.

Don’t forget that OpenGL is just an API for rendering 2D and 3D graphics. It’s the driver, which implements it, not the engine. Also each engine may have a different approach how to structure and render things. Some are using a scenegraph, others don’t or are using different structures and algorithms for optimizing stuff.
There are engines that don’t even use OpenGL at all for various things or utilize the GPU in a different way depending on the used hardware.

You see you cannot judge, why the same game might runs better on a different engine, by a small scale.

My problem was because I did not stop my layer Follow action and restart it… when I stopped and restarted the player moveTo action when repositioning the player at the top of the screen.

I was in the mentality that once you follow you were locked on, but I guess when you stop/start a action on what your following it doesnt go down well :smiley:

@pr4nq from your explanation I have a feeling that this is because of floating point calculation error accumulation, right? See this article for more details http://www.koonsolo.com/news/dewitters-gameloop/ Search for “rounding error” in this article and read that part. Also as you can see from the explanation how Follow works http://www.learn-cocos2d.com/2012/12/ways-scrolling-cocos2d-explained/ that player should never move on the screen, as camera and player are static in case of using Follow (except the last part when player lands on the ground). So here the only moving thing is the layer with background cubes.

@iQD I could not understand what you mean. Is there any article that explains how FPS fluctuation can cause such problems . If the animations and all motion is velocity based, i.e. they move by delta which is equal to deltaTimeOfCurrentFrame * myVelocity then they should be drawn without shift between objects like it is in the porblem of @pr4nq. I ask you to explain more what is the basis of this problem. Thanks!

@naghekyan I updated my post probably after you wrote this… read my response above lol was my own inexperience thinking i didn’t have to stop the follow action when i reset my player.

You would have to look at a bunch of articles, which explains how the human eye perceives motion, sub-pixel rendering and display synchronization.

The problem is, that the delta time of a frame can differ a lot, if the framerate drops. The frame time/delta time is increased and so is the delta of the movement of the sprite.
E.g. 60fps are moving the sprite down the screen by 10 pixels per sec for a distance of 100 pixels. A frame-rate drop to 45fps lowers the movement speed to 75% percent and also changes the sub-pixel position of the sprite. Therefore the engine/GPU has to interpolate the position and you are getting aliasing. The fps are going back to 60fps again and the sprite is speeding up. This non-constant movement speed and the different acceleration produces the effect of flickering and stuttering/jerking.
The position of the sprite is discrete, not analogue. It could move 10 pixels at frame one but 25 pixels at frame two and another 5 pixels at frame three, as the frame time changes rapidly over time, cause of a frame-drop.
At the difference between 60fps and 45fps you are losing 15 frames, so the sprite has to move faster to reach the same position, as the gaps between each frame position increase:
60fps: 100 pixels / 10 pixels per second needs 10 seconds but 0.0167 secs per frame.
45fps: 100 pixels / 10 pixels per second needs 10 seconds but 0.0222 secs per frame.

The frames at 60fps are presented much faster than the ones with 45fps in the same time span. You are perceiving a motion. At 45fps your eyes are given much lesser information. Now imagine it drops down to 12fps.
The sprite will just alter it’s position every 0.08 secs. There is no information at all to fill in the gaps.

Another thing is that your display may not being in sync with your renderer. It that is the case, you get the impression, that your sprite will jump to the next position, as the display was not ready to draw the correct position on time. Sometimes more, sometimes less, depending on the change in frame-time.

The eye is not perceiving a constant movement speed like it is with movies. Even though movies have a constant fps. afterimages are generated to simulate a flicker free experience and fill in the information gap between updated frames.

The problem @pr4nq had, was caused by some not so ideal sub pixel offset, cause he was not stopping the follow animation. This seemed to be failing some reset of the scheduler/offset calculation and it tried to correct the position after it was set by some other action: Setting the position to 10, correcting it to 9 the following frame. This correction was different each frame cause of the frame fluctuation. Some frame times were more stable and the correction was more or less at about 0 pixels(just sub-pixel alignment), so the flicker was much less notable.
Have to dig more into the engine code, to see what happens, if not stopping the follow action but still relying on it, when you re-position the target to follow…

Thanks to both :smiley:

There seems to be a bug in the built in physics in cocos2d-x 3.x. Or if not a bug, there is a performance issue that exists when using the built in physics engine which does not exist when using Chipmunk physics directly.

The position of the physics bodies and their sprites seem to have a jitter / instability to them.

Here is a video of the jitter: https://youtu.be/MkgolnJDjlo

My guess is that it has something to do with how the Chipmunk update is executed but I have not been able to isolate the issue yet.

I just ported this code from cocos2d 2.x where the physics was very smooth when using Chipmunk physics directly.
I also have numerous other similar games in cocos2d 2.x and cocos2d-x 3.x that use Chipmunk directly, and there is no jitter issue.

The first 18 seconds of this video show the physics jitter when the world camera setPosition and lookAt are updated each update cycle to center on the vehicle.

The last 10 seconds show the word camera setPosition and lookAt being set by following the race track ground and moving forward 120 points in each update cycle.

Note that the camera is smooth moving and there is no jitter on the centered motorbike racer. The rider that passes is the physics rider, and it has jitter at 22 seconds and 27 seconds into the video, indicating that the jitter can likely be isolated to the physics body position updating.

I have also tried moving the layer instead of the camera. This is often recommended in 2d games so that the world model is updated each update rather than the camera lookAt and position. But since the jitter seems to be coming from the physics engine, the effect is the same.

On the device the frames run at a consistent 60 fps and there does not seem to be a correlation between the jitter and a drop in frame rate. There is no significant drop in frame rate.

EDIT: Even a very minor drop in frame rate, e.g. from 60 to 59, can cause a jitter in a physics simulation.

1 Like

Ahhhhh, I finally figured out the cause and remembered how I fixed it literally over 5 years ago in cocos2d-iphone when using Chipmunk directly. If it was smooth on an iPhone 3 in 2009, why is it so jitter on an iPhone 5? Well…

My port from cocos2d-iphone made it to 2.x and 3.x when I used Chipmunk directly, but the built in physics in 3.x manages the update loop for us, so I did not port my fix for this issue.

It’s not a bug so much as it is a tuning of the physics engine.
The slightest fluctuation in frame rate wreaks havoc on the smoothness of the simulation.
Feed cpSpaceStep smooth time updates and you will get a nice smooth physics simulation. Shake and rattle it with sporadic update delta’s and it will puke your sprites on the screen.

You can trade a little accuracy of the simulation for smoothness by applying a low pass filter to the delta time in the update method of the Physics world to smooth out the simulation.

Here is the core of the low pass filter that I added to PhysicsWorld::update(float delta, bool userCall/* = false*/):

 const float dt = _updateTime * _speed / _substeps;
 // Added _filteredUpdateDelta to the protected interface and initialized it to 0 in the constructor.
 // _filteredUpdateDelta is the output of a low pass filter to smooth out the dt.
 // The existing code wants dt to be zero when the game is paused because _speed is set to zero when the game is paused.
 _filteredUpdateDelta = dt > __FLT_EPSILON__ ? 0.15 * dt + 0.85 * _filteredUpdateDelta : 0.0;

 for (int i = 0; i < _substeps; ++i)
 {
     // Replaced dt in the cpSpaceStep with _filteredUpdateDelta.
     cpSpaceStep(_cpSpace, _filteredUpdateDelta);
     for (auto& body : _bodies)
     {
         body->update(dt);
     }
 }

It is debatable if we should add a low pass filter to the core code. I think we should.

1 Like

@Heyalda thanks for your post. This was a life saver! I was spending time with my code seeing what was wrong, but it turns out that it was the physics engine. Your change fixed my issue too!

@djvoracious glad to hear you found my comments useful :smile:
Hopefully in one of the next releases the built in physics wrapper that we are using will get some attention by the core developers.
Maybe a low pass filter to smooth things out. Or maybe some form of extrapolation/interpolation to smoothly sync the graphics up with the physics. Or maybe a parameter we can pass to select the physics world step style that will benefit our game most. We should find out in the coming months hopefully. https://github.com/cocos2d/cocos2d-x/pull/11860

1 Like

@Heyalda I totally agree. Thanks for your great work.

@Heyalda thanks a lot for your work , fixed mine too. :smile: