Citrus-Engine icon indicating copy to clipboard operation
Citrus-Engine copied to clipboard

physics step problem

Open blackgun opened this issue 12 years ago • 18 comments

physics simulation is not same in different system, For example, on some system, a ball (like angry bird) can be shoot to 500 pixel with 60fps, but on other system, it will be shoot to 250 pixel with 30fps. on some system, you can't control the fps, it runs from 20-40 fps, you won't know it will be shoot how long.

I found the code problem:

package citrus.physics.nape Nape.as Line89:

override public function update(timeDelta:Number):void {            
    super.update(timeDelta);

    // 0.05 = 1 / 20
    _space.step(0.05, 8, 8);

It should be:

    _space.step(timeDelta, 8, 8);

After that, it be solved.

blackgun avatar Jan 08 '13 22:01 blackgun

By the way, the 3.1.1 source code doesn't include Signal, Nape source or SWC lib files.

blackgun avatar Jan 08 '13 22:01 blackgun

Have you tried using the frame rate independent motion system - which you would set up in your state like so : _frim = true; _timeStep = time;

still in experimental stage since it was added, where time would be the expected time of a frame so that even if your device drops at 30fps and you expect 60, it should still simulate the world correctly...

though I have tried it myself and sometimes nape and/or box2D (don't remember which one) doesn't like when we mess up with its time step which could sometimes results in artefacts... but most of the time it would actually work correctly and I would see what I expect to see even if the frame rate was variable.

(don't know about the latest swc version but I'm in sync with github almost everyday and all signal nape etc I get from the lib folder, or bin/libraries)

gsynuh avatar Jan 09 '13 01:01 gsynuh

Thanks for your advice, I just tried them.

with _frim = true , different fps will got same result, but it looks artificial , very wired.

with _timeStep , it will cause the physics object's image shaking (very tiny, like phone buzzing).

They are unacceptable , "_space.step(timeDelta, 8, 8)" is the best way.

blackgun avatar Jan 09 '13 02:01 blackgun

I've quickly downloaded some 3.1.1 swcs and tested them in in FlashDevelop : I've access to Signal, Nape, dragonbones... stuff. Could you tell me more on your issue?

I understand the issue around the physics step. The Frame Rate In Motion isn't ready at all. I'm missing some good working examples! In your change, the timeDelta won't be the same : on Nape it may work, but on Box2d it would be a disaster.

I think everything is there http://gafferongames.com/game-physics/fix-your-timestep/ but my implementation has certainly miss something, if you would like to give it a look ;)

alamboley avatar Jan 09 '13 08:01 alamboley

Sorry, I found Signal,Nape.. in another folder, Nape physics is not in same folder of Box2D, so I ignore them.

I took a quick look at that article, one point is very accurate: rendering produce time, physics custom time. Every physics engine step need to know how long time rendering took since last step. In fact, in my case with _space.step(timeDetlta) will fix the wrong simulation (such as my first post about 250px vs 500px). But the simulation is not perfect, I guess the moment when CE implement the step (argument timeDelta) is not perfect. And physics engine itself produce time too, so I have a suggestion like this:

function update():void {
    this.timeNow = new time(); 
    deltaTime = this.timeNow - this.timeLast;
    space.step(deltaTime);
    this.timeLast = this.timeNow + 0; // break the reference
}

I haven't try that way, I will try that after I finished my work.

blackgun avatar Jan 09 '13 22:01 blackgun

You are right that CE should be aware of how long one frame took to render, to compensate on next frame and/or on the physics step so that we try to efficiently "fill the gaps" If you are going to try that actually, you may try seeing if a native EXIT_FRAME event could help to stop the time on frame exit - which might not be the same thing as the next enter frame time.

gsynuh avatar Jan 09 '13 23:01 gsynuh

We know the time it tooks to render a frame thanks to the main loop :

//Update the state
if (_state && _playing)
{
    var nowTime:Number = new Date().time;
    var timeSinceLastFrame:Number = nowTime - _gameTime;
    var timeDelta:Number = timeSinceLastFrame * 0.001;
    _gameTime = nowTime;

    _state.update(timeDelta);
}

If you take a look on my frim implementation :

// Call update on all objects using the frim
if (_frim) {

    n = _objects.length;

    if (timeDelta > 0.25)
        timeDelta = 0.25;

    _accumulator += timeDelta;

    while (_accumulator >= _timeStep) {

        for (i = 0; i < n; ++i) {
            object = _objects[i];
            object.update(0.05);
        }

        _accumulator -= _timeStep;
    }

}

Each objects are using the frim, maybe it should only be the physics engine? Morover it shouldn't be 0.5 , but I'm not sure of the value. Also the timeDelta value is never used inside objects class.

The RevolvingPlatform works without frim activated, whatever the fps is, it's always rotating at the same speed :

override public function update(timeDelta:Number):void {

    var platformVec:b2Vec2;
    var differenceVec:b2Vec2;
    var passengerVec:b2Vec2;

    super.update(timeDelta);

    _accAngle += timeDelta;

    // calculate new position
    platformVec = new b2Vec2();
    platformVec.x = _center.x + Math.sin(_accAngle * speed) * _xOffset;
    platformVec.y = _center.y + Math.cos(_accAngle * speed) * _yOffset;

    // get the difference between the new position and the current position
    differenceVec = platformVec.Copy();
    differenceVec.Subtract(_body.GetPosition());

    // update passenger positions to account for platform's motion
    for each (var b:b2Body in _passengers) {
        passengerVec = b.GetPosition();
        passengerVec.Add(differenceVec);
        b.SetPosition(passengerVec);
    }

    // update platform's position
    _body.SetPosition(platformVec);
}

alamboley avatar Jan 10 '13 08:01 alamboley

@alamboley , all object is controlled by physics, deltaTime is meaningless for them.

I asked Nape author about this question, his advice is: "You should always fix the framerate of the physics simulation, regardless of device otherwise you will not even get consistent physics behaviour on the same computer. You do not need to use the same physics framerate as you do for graphics; let graphics update as fast as it can, but fix the physics framerate (Using interpolation if necessary to smoothen things graphically)."

I totally agree with him, maybe you can make an independent timer for physics step (such as 1/30 or 1/60 whatever) instead of being driven by enterFrameEvent. Then everything become simple, physics step in its independent time world, rendering system step itself. CE just update all objects' position/velocity/angle vel when it needs.

blackgun avatar Jan 10 '13 18:01 blackgun

"all object is controlled by physics, deltaTime is meaningless for them", yes that make sense!

I've went through a forum where they said to always use an enterframe for the main loop instead of a timer so you're sure that everything is synchronized with the application&flash player. Moreover timer aren't more reliable than an enter frame.

In my frim method, I've to know which value I put into the object.update(0.05); and then probably update the physics step with this value.

alamboley avatar Jan 10 '13 18:01 alamboley

however deltaTime can be needed in citrus objects (I thought that was the objects you were talking about @alamboley ) such as in the RevolvingPlatform example.

it should be out of the question to use an idependent Timer. (only sound should run by a different clock due to its nature and the frequency of sample that we couldn't handle even with normal timers) or we could get out of sync with flash very quickly...

gsynuh avatar Jan 10 '13 18:01 gsynuh

@gsynuh , Timer is also driven by CPU clock. you don't need to worry about it. In fact ,we need 2 thread to handle physics step and rendering update. Rendering update thread is driven by enterFrameEvent. Physics step thread is driven by physicsTimerEvent. @alamboley , the main loop is still driven by enterFrameEvent, cause that is for frame rendering. But for physics, you can make another physics timer with event listener. I often used this way before, but not for physics, is for AI. I don't need AI update as fast as rendering. They are two different thread. Now it is same for physics simulation.

blackgun avatar Jan 10 '13 18:01 blackgun

well @blackgun you could have a Worker thread to put the physics engine in it. but emulating threads is all that can be done right now so we're sure it works everywhere.

gsynuh avatar Jan 10 '13 18:01 gsynuh

A very long discussion with lots of good advices is already there : https://plus.google.com/110495278155587072613/posts/BuvUhiR9zzi Let me know what you think!

alamboley avatar Jan 11 '13 06:01 alamboley

In my scenario , simulation with fixed deltaTime is better than the variable deltaTime. Everything looks smooth and accurate in replay mode.

I can't set a fixed rendering FPS for different devices. Most iOS devices can reach 60FPS, but Android is a high degree of market fragmentation, with 20fps-60fps. So I agreed with Nape's author's solution. Let rendering and physics go in different time line. And some people are pretty sure that works in the link you provided. Timer is not a problem, so what do you worry?

blackgun avatar Jan 11 '13 22:01 blackgun

I agree with you concerning the timer for physics step, but not for the main rendering.

Sorry for the delay, I needed time and references to think about that. This last week, I've started to play with Unity (which is very good but expensive) and they have a fix for that!

In Unity there is a Update function which is the FPS, like the enter frame in Flash, and a FixedUpdate which is performed at the same time that the physics engine which is doing a step.

So what I suggest is to replicate this behavior:

  • keep our update function with the time delta as a parameter coming from the main then the state using an enter frame and the Date.time, and create a protected var in APhysicsObject to register the timedelta (so it can be used in FixedUpdate).
  • then create a fixedUpdate function in APhysicsObject which will be called each time the physics engine make a step. The step of the engine comes from a Timer.

To call the fixedUpdate on each physics object, we may dispatch the event from the timer and listen it into the State and then perform the update. Or directly in the fixedUpdate method from the physics engine, call the fixedUpdate on all its object added to the world/space.

What do you think?

alamboley avatar Feb 13 '13 12:02 alamboley

Not sure if I understood correctly, but that means we'd have a "display update" and a "physics update"? if that forces us and everyone to manage our logic a bit better, and subconsciously realising that both SHOULD be seen as two different things, then I agree 100%

gsynuh avatar Feb 13 '13 15:02 gsynuh

Let's take the example of the CitrusSprite. This update function looks like this:

override public function update(timeDelta:Number):void {
    super.update(timeDelta);

    x += (_velocity.x * timeDelta);
    y += (_velocity.y * timeDelta);
}

Here we multiply the velocity by the timeDelta. The CitrusSprite is already FRIM enable. Create a basic project, add a velocity to a CitrusSprite and change the Flash FPS, it will move exactly at the same speed, but it graphics won't be render at the same speed.

Now if we use a physics engine: its update loop should not be powered by the update function, since its frequency may change if there is some lag/delay/ect. So we put a timer, which has the same value than the timeStep of the physics engine, this timer will perform the step and call a fixedUpdate on each objects added to the physic world/space. The objects physics code should be moved into this new function (it will change apps behavior, so we have to take care with this update). And we will keep a track to the time delta thanks to a var (it may be also useful in this fixedUpdate function). Also we will keep the update function, if we want to manage time, or non physics properties ect.

What do you think?

alamboley avatar Feb 13 '13 16:02 alamboley

Timer for physics is good. I am looking forward to see it.

On Wed, Feb 13, 2013 at 8:11 AM, Aymeric Lamboley [email protected]:

Let's take the example of the CitrusSprite. This update function looks like this:

override public function update(timeDelta:Number):void {

super.update(timeDelta);

x += (_velocity.x * timeDelta);
y += (_velocity.y * timeDelta);}

Here we multiply the velocity by the timeDelta. The CitrusSprite is already FRIM enable. Create a basic project, add a velocity to a CitrusSprite and change the Flash FPS, it will move exactly at the same speed, but it graphics won't be render at the same speed.

Now if we use a physics engine: its update loop should not be powered by the object update function, since its frequency may change if there is some lag/delay/ect. So we put a timer, which has the same value than the timeStep of the physics engine, this timer will perform the step and call a fixedUpdate on each objects added to the physic world/space. The objects physics code should be moved into this new function (it will change apps behavior, so we have to take care with this update). And we will keep a track to the time delta thanks to a var (it may be also useful in this fixedUpdate function). Also we will keep the update function, if we want to manage time, are non physics properties ect.

What do you think?

— Reply to this email directly or view it on GitHubhttps://github.com/alamboley/Citrus-Engine/issues/22#issuecomment-13501912.

blackgun avatar Feb 13 '13 17:02 blackgun