[SOLVED]CollisionResponseAnimator problem (frame dependent)?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
krisbsx
Posts: 6
Joined: Mon Apr 13, 2020 10:48 pm
Location: Lyon, France

[SOLVED]CollisionResponseAnimator problem (frame dependent)?

Post by krisbsx »

Hi there,

I am not quite a beginner on Irrlicht because I have been working on a small game for several years on my free time, however, I am confronted for a while on a small problem of beginner.

I noticed that the gravity affecting my player change completely depending on the framerate.

I use very basic stuff to apply gravity to my player: The player is a "ISceneNode" and the gravity is done by a "ISceneNodeAnimatorCollisionResponse".

My problem is that I noticed that when the framerate is lower, the gravity is lower, the sceneNode fell more slowly, as if it was floating in the air, and he jumps much lower when I use the "jump ()" method of the animator. I really don't know what is wrong.

I'm pretty sure gravity in Irrlicht is frame independent and is based on elapsed time between frames, so I don't understand.

I did a lot of research on the forum but did not find an answer allowing me to understand and solve this problem. I also did a lot of testing and the calculation of the time between two frames is done correctly, I have different functions that use this process in my program. Only the gravity calculated by Irrlicht is problematic ...

Have you got an idea of what can be causing this troubles?

Thank you in advance to those who will take the time to read this message and reply to me.

I post below some extracts of my code.

The player and animator:

Code: Select all

/* The player is loaded from a .irr scene containing the sceneNode */
scene::ISceneNode* playerNode = sceneManager->getSceneNodeFromName(playerNodeName);
 
scene::ISceneNodeAnimatorCollisionResponse* playerNodeColResponse = sceneManager->createCollisionResponseAnimator(
    terrainMeta, // world (scene::IMetaTriangleSelector),
    playerNode, // SceneNode
    core::vector3df(18, 1, 18), //ellipsoidRadius
    core::vector3df(0, 0 - 2.0f, 0), //gravityPerSecond
    core::vector3df(0, 0, 0) //ellipsoidTranslation
);
The jump event (in game loop, before drawing any graphics on screen):

Code: Select all

if(!jumping && controller->isAPressed() == true){
 
    //Start jump animation
    jumping = true;
    playerNodeColResponse->jump(1.2f);
 
    // Display this text in a console to be sure the jump happens only one time
    cout << "jump!" << endl;
 
}
 
Last edited by krisbsx on Fri Apr 17, 2020 1:01 pm, edited 1 time in total.
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: CollisionResponseAnimator problem (look frame dependent)

Post by Seven »

in the changes.txt file in the 1.6 section it says

" - TODO: set Gravity to Physically frame independent values..
current response uses an frame depdended acceleration vector.
~9.81 m/s^2 was achieved at around 50 fps with a setting of -0.03
may effect existing application.."
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: CollisionResponseAnimator problem (look frame dependent)

Post by Seven »

Code: Select all

 
void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 timeMs)
{
    CollisionOccurred = false;
 
    if (node != Object)
        setNode(node);
 
    if(!Object || !World)
        return;
 
    // trigger reset
    if ( timeMs == 0 )
    {
        FirstUpdate = true;
        timeMs = LastTime;
    }
 
    if ( FirstUpdate )
    {
        LastPosition = Object->getPosition();
        Falling = false;
        LastTime = timeMs;
        FallingVelocity.set ( 0, 0, 0 );
 
        FirstUpdate = false;
    }
 
    const u32 diff = timeMs - LastTime;
    LastTime = timeMs;
 
    CollisionResultPosition = Object->getPosition();
    core::vector3df vel = CollisionResultPosition - LastPosition;
 
    FallingVelocity += Gravity * (f32)diff * 0.001f;
 
    CollisionTriangle = RefTriangle;
    CollisionPoint = core::vector3df();
    CollisionResultPosition = core::vector3df();
    CollisionNode = 0;
 
    // core::vector3df force = vel + FallingVelocity;
 
    if ( AnimateCameraTarget )
    {
        // TODO: divide SlidingSpeed by frame time
 
        bool f = false;
        CollisionResultPosition
            = SceneManager->getSceneCollisionManager()->getCollisionResultPosition(
                World, LastPosition-Translation,
                Radius, vel, CollisionTriangle, CollisionPoint, f,
                CollisionNode, SlidingSpeed, FallingVelocity);
 
        CollisionOccurred = (CollisionTriangle != RefTriangle);
 
        CollisionResultPosition += Translation;
 
        if (f)//CollisionTriangle == RefTriangle)
        {
            Falling = true;
        }
        else
        {
            Falling = false;
            FallingVelocity.set(0, 0, 0);
        }
 
        bool collisionConsumed = false;
 
        if (CollisionOccurred && CollisionCallback)
            collisionConsumed = CollisionCallback->onCollision(*this);
 
        if(!collisionConsumed)
            Object->setPosition(CollisionResultPosition);
    }
 
    // move camera target
    if (AnimateCameraTarget && IsCamera)
    {
        const core::vector3df pdiff = Object->getPosition() - LastPosition - vel;
        ICameraSceneNode* cam = (ICameraSceneNode*)Object;
        cam->setTarget(cam->getTarget() + pdiff);
    }
 
    LastPosition = Object->getPosition();
}
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: CollisionResponseAnimator problem (look frame dependent)

Post by Seven »

it seems like this line :

"FallingVelocity += Gravity * (f32)diff * 0.001f;"

is at least trying to keep it framerate independent though.....
krisbsx
Posts: 6
Joined: Mon Apr 13, 2020 10:48 pm
Location: Lyon, France

Re: CollisionResponseAnimator problem (look frame dependent)

Post by krisbsx »

Thank you for your response, Seven.

(I have seen your post before your edit :wink: )

Sorry, i was thinking the gravity was frame independent. I seem to have seen it in different places that it did.

In addition I thought that the speed of gravity was calculated in the method:

CSceneNodeAnimatorCollisionResponse::animateNode

Code: Select all

void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 timeMs)
{
    CollisionOccurred = false;
 
    if (node != Object)
        setNode(node);
 
    if(!Object || !World)
        return;
 
    // trigger reset
    if ( timeMs == 0 )
    {
        FirstUpdate = true;
        timeMs = LastTime;
    }
 
    if ( FirstUpdate )
    {
        LastPosition = Object->getPosition();
        Falling = false;
        LastTime = timeMs;
        FallingVelocity.set ( 0, 0, 0 );
 
        FirstUpdate = false;
    }
 
    const u32 diff = timeMs - LastTime;
    LastTime = timeMs;
 
    CollisionResultPosition = Object->getPosition();
    core::vector3df vel = CollisionResultPosition - LastPosition;
 
    FallingVelocity += Gravity * (f32)diff * 0.001f;
 
    CollisionTriangle = RefTriangle;
    CollisionPoint = core::vector3df();
    CollisionResultPosition = core::vector3df();
    CollisionNode = 0;
 
    // core::vector3df force = vel + FallingVelocity;
 
    if ( AnimateCameraTarget )
    {
        // TODO: divide SlidingSpeed by frame time
 
        bool f = false;
        CollisionResultPosition
            = SceneManager->getSceneCollisionManager()->getCollisionResultPosition(
                World, LastPosition-Translation,
                Radius, vel, CollisionTriangle, CollisionPoint, f,
                CollisionNode, SlidingSpeed, FallingVelocity);
 
        CollisionOccurred = (CollisionTriangle != RefTriangle);
 
        CollisionResultPosition += Translation;
 
        if (f)//CollisionTriangle == RefTriangle)
        {
            Falling = true;
        }
        else
        {
            Falling = false;
            FallingVelocity.set(0, 0, 0);
        }
 
        bool collisionConsumed = false;
 
        if (CollisionOccurred && CollisionCallback)
            collisionConsumed = CollisionCallback->onCollision(*this);
 
        if(!collisionConsumed)
            Object->setPosition(CollisionResultPosition);
    }
 
    // move camera target
    if (AnimateCameraTarget && IsCamera)
    {
        const core::vector3df pdiff = Object->getPosition() - LastPosition - vel;
        ICameraSceneNode* cam = (ICameraSceneNode*)Object;
        cam->setTarget(cam->getTarget() + pdiff);
    }
 
    LastPosition = Object->getPosition();
}
Don't know why i have this problem of gravity :? ...
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: CollisionResponseAnimator problem (look frame dependent)

Post by Seven »

Sorry but I use PhysX for my movement and am not so familiar with the collision animator. that's a little bit of what caught my attention with your question so I thought I would dig around a little bit. If I find anything I will let you know.
krisbsx
Posts: 6
Joined: Mon Apr 13, 2020 10:48 pm
Location: Lyon, France

Re: CollisionResponseAnimator problem (look frame dependent)

Post by krisbsx »

Thank you.

I will continue to try to understand the problem and find a solution. I could change for a physics engine like PhysX but my needs are very basic for now.

Here are the results of a little test:

I added a litte test in CSceneNodeAnimatorCollisionResponse::animateNode() to test the elapsed time and accumulated gravity.Y when my player jump.

When the window of my game is 800X600:
FPS: 272
Elapsed time: 1196 ms
Accumulated Gravity Y: 590


When i resize the window of my game to full screen size (1600X900):
FPS: 34
Elapsed time: 1237 ms
Accumulated Gravity Y: 154


We can see that the elapsed time is approximately the same, but the accumulated gravity changes enormously.

Here is the code I used to test. Note: totalGravityY and totalElapsedTime are reset in CSceneNodeAnimatorCollisionResponse::jump().

CSceneNodeAnimatorCollisionResponse::animateNode

Code: Select all

// After this line
FallingVelocity += Gravity * (f32)diff * 0.001f;
 
/** TEST **/
if(Falling){
 
    // Only display results for the player node
    std::string nodeName = "ninja";
    std::string nodeNameIn = node->getName();
    if(nodeNameIn.compare(nodeName) == 0){
        totalElapsedTime += diff;
        if(Gravity.Y < 0){
        totalGravityY -= Gravity.Y;
        }
        std::cout << "t: " << totalElapsedTime << ", y: " << totalGravityY << std::endl;
    }
}
/** END TEST */
Really strange :?

I think something is not frame independent with the gravity.
CuteAlien
Admin
Posts: 9652
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CollisionResponseAnimator problem (look frame dependent)

Post by CuteAlien »

Yeah formula doesn't look correct, it doesn't work like that with exponential growth. Just scaling by time but we have less calculation steps (each calculation adds some velocity so if you for example double the amount of calculations each second calculation will have the added velocity from the first one). Been a while since I learned about that, will have to read up how to calculate this correctly (something involving e^x if I remember right which I probably do not).
I guess a simple/ugly workaround would be to replace FallingVelocity += Gravity * diffSec; by a loop with tiny time-steps (the smaller you make them the closer you get to the real value ... once you can make them infinite small you will pass your Analysis 1 course in University).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
krisbsx
Posts: 6
Joined: Mon Apr 13, 2020 10:48 pm
Location: Lyon, France

Re: CollisionResponseAnimator problem (look frame dependent)

Post by krisbsx »

Thank you for your answer, CuteAlien.

My test may not be reliable, I did it only to highlight the problem of gravity and maybe find a solution.

Your remark concerning the replacement of the velocity calculation by a loop is relevant.

I'm going to test this to see if it's conclusive for my little project.
CuteAlien
Admin
Posts: 9652
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CollisionResponseAnimator problem (look frame dependent)

Post by CuteAlien »

edit: Hm, sorry, in first tests the stuff below seemed to improve it, but after some more testing ... not so sure anymore.


I got a solution, but not yet checked-in (I still need some more tests how it behaves compared to old variables and probably have to add another variable to allow users to keep old behavior so they can use it for existing games).

But if you want to test yourself, change CSceneNodeAnimatorCollisionResponse::animateNode like this:

Search for second line and add first one before it.

Code: Select all

 
irr::core::vector3df oldVelocity = FallingVelocity;
FallingVelocity += Gravity * diffSec;
 
Seach for CollisionResultPosition position and replace "FallingVelocity" by "0.5f*(FallingVelocity+oldVelocity)*diffSec".

Code: Select all

 
CollisionResultPosition
            = SceneManager->getSceneCollisionManager()->getCollisionResultPosition(
                World, LastPosition-Translation,
                Radius, vel, CollisionTriangle, CollisionPoint, f,
                CollisionNode, SlidingSpeed, 0.5f*(FallingVelocity+oldVelocity)*diffSec);
 
Or just wait a few days,then it's probably in svn.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
CuteAlien
Admin
Posts: 9652
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CollisionResponseAnimator problem (look frame dependent)

Post by CuteAlien »

One minor note - your totalGravityY should change. As it's not framerate independent the way you sum it up. It's just a constant which you increase the amount of times the function is called. What you should be interested it in is how much the y position of your node changes over time.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
CuteAlien
Admin
Posts: 9652
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CollisionResponseAnimator problem (look frame dependent)

Post by CuteAlien »

Sorry, I can't reproduce the problem (my first test where I thought I had it was simpy not good enough). It might be a tiny bit fps-dependent, but hardly noticable (and a bit tricky to test without writing more code). Not sure if my solution above would really improved it, so not adding that for now.

My test:

Code: Select all

 
#include <irrlicht.h>
#include <iostream>
 
using namespace irr;
using namespace core;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
int main()
{
    IrrlichtDevice *  device = createDevice(video::EDT_OPENGL, dimension2d<u32>(800,600));
    if (!device) return 1;
    scene::ISceneManager* smgr = device->getSceneManager();
    video::IVideoDriver * videoDriver = device->getVideoDriver ();
   
    irr::scene::ICameraSceneNode * camera = smgr->addCameraSceneNode(0, core::vector3df(0, -100, -200), core::vector3df(0,-100,0));
 
    scene::IMeshSceneNode * n1 = smgr->addCubeSceneNode();
    n1->getMaterial(0).Lighting = false;
 
    scene::ITriangleSelector * dummyWorld =  smgr->createMetaTriangleSelector();
 
    scene::ISceneNodeAnimatorCollisionResponse* colResponse = smgr->createCollisionResponseAnimator(
        dummyWorld, n1, 
        core::vector3df(18, 1, 18), //ellipsoidRadius
        core::vector3df(0, -2.0f, 0) //gravityPerSecond
    );
    n1->addAnimator(colResponse);
    colResponse->drop();
 
    irr::u32 tOld = device->getTimer()->getRealTime();
 
    while ( device->run() && device->getTimer()->getRealTime()-tOld < 15000  )
    {
        videoDriver->beginScene(true, true);
        smgr->drawAll();
        videoDriver->endScene();
 
        // modify to play with different fps
        device->sleep( 100 );   
    }
 
    std::cout << "drop: " << n1->getPosition().Y << " FPS: " << videoDriver->getFPS();
    std::cin.get();
 
    device->closeDevice();
    device->drop();
 
    return 0;
}
 
Note that tiny differences in the results for my tests can happen simply because it might have one frame more/less due to hitting the time-check here exactly or being some milliseconds off (to write a better test I'd have to control the time-ticks myself, but I'm not going there).


So... that doesn't solve your problem I suppose. But it might be a hint that the problem is something else. Maybe it's the "jump". I took a look at that - and it just adds a value to velocity. Which should be fine in most cases, but maybe not always what you want. For example you might want to have a jump which always resets velocity (thought that should be mostly the case already as you generally jump from floor where velocity is reset due to collision).

Anyway - I need a test-case which reproduces your problem, otherwise not much I can help with.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
krisbsx
Posts: 6
Joined: Mon Apr 13, 2020 10:48 pm
Location: Lyon, France

Re: CollisionResponseAnimator problem (look frame dependent)

Post by krisbsx »

Thanks for your answers, cuteAlien.

You know, I don't think the problem with gravity is only mine.
I think this is a problem inside Irrlicht and it may seem strange to many people who will try to make a basic game with Irrlicht only (although there may not be so many since there are many many full-featured game engines on the market).

My development knowledge may be too limited and I should perhaps consider finishing my little basic game on a real game engine which may be more accessible for me :oops: .

However, to thank you for the time you spent trying to help me and better demonstrate the problem I observed, I made a small code that allows you to easily verify that the gravity and the jump depend on the framerate .

I took your code and modified it a bit for the demonstration.

With this code, you will be able to verify that by enlarging the size of the window (which decreases the framerate), the jump of the cube is much lower, and the speed of fall is lower.

The functioning is simple: every 5 seconds, the cube jumps, and the maximum Y position is written to the console when the cube falls to the ground.

On my computer, when the window is 800X600, the Cube reaches 349. When the window is maximized (1600X900), the Cube reaches 93.

In my little game, the top of the jump and the speed of gravity change depending on the number of items displayed on the screen ...

Here is the code to test that:

Code: Select all

#include <irrlicht.h>
#include <iostream>
 
using namespace irr;
using namespace core;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
int main()
{
    IrrlichtDevice *  device = createDevice(video::EDT_OPENGL, dimension2d<u32>(800,600));
    if (!device) return 1;
 
    // Enable window resize because enlarging the window drops the framerate
    device->setResizable(true);//Resizable window
 
    scene::ISceneManager* smgr = device->getSceneManager();
    video::IVideoDriver * videoDriver = device->getVideoDriver ();
 
    irr::scene::ICameraSceneNode * camera = smgr->addCameraSceneNode(0, core::vector3df(0, 100.0f, -200), core::vector3df(0,50.0f,0));
 
    scene::IMeshSceneNode * n1 = smgr->addCubeSceneNode(10.0f, 0, -1, core::vector3df(0, 5, 0));
    n1->getMaterial(0).Lighting = false;
    smgr->getMeshManipulator()->setVertexColors(n1->getMesh(), SColor(255,255,0,0));
 
    const irr::scene::IGeometryCreator *geomentryCreator = smgr->getGeometryCreator();
    irr::scene::IMesh* plane = geomentryCreator->createPlaneMesh(irr::core::dimension2d<irr::f32>(5.0f, 5.0f), irr::core::dimension2d<irr::u32>(5.0f, 5.0f));
 
    scene::IMeshSceneNode * planeSceneNode = smgr->addMeshSceneNode(plane, 0, -1, core::vector3df(0.0f, 0.0f, 0.0f));
    planeSceneNode->getMaterial(0).Lighting = false;
    smgr->getMeshManipulator()->setVertexColors(planeSceneNode->getMesh(), SColor(255,0,255,0));
 
    scene::ITriangleSelector * world = smgr->createTriangleSelector((planeSceneNode)->getMesh(), planeSceneNode);
 
    scene::ISceneNodeAnimatorCollisionResponse* colResponse = smgr->createCollisionResponseAnimator(
        world, n1,
        core::vector3df(18, 5.0f, 18), //ellipsoidRadius
        core::vector3df(0, -2.0f, 0) //gravityPerSecond
    );
    n1->addAnimator(colResponse);
    colResponse->drop();
 
 
    // Variables to call "jump()" every 5 seconds
    irr::u32 lastJump = device->getTimer()->getRealTime();
    irr::u32 delayBetweenJumps = 5000; // Jump every 5 seconds
 
    // Record higher position when jumping
    irr::f32 jumpMaxPosition = 0.0f;
 
    bool isJumping = false;
 
    while ( device->run())
    {
 
        irr::u32 time = device->getTimer()->getRealTime();
 
        // Display max value when jump is finished
        if(isJumping && colResponse->collisionOccurred()){
            isJumping = false;
            std::cout << "Jump max value: " << jumpMaxPosition << std::endl;
        }
 
        // Get max position Y
        if(n1->getPosition().Y > jumpMaxPosition){
            jumpMaxPosition = n1->getPosition().Y;
        }
 
 
        if(time - lastJump >= delayBetweenJumps){
 
            // Reset higher position
            jumpMaxPosition = 0.0f;
 
            // Jump now
            colResponse->jump(2.0f);
            lastJump = time;
 
            isJumping = true;
 
        }
 
        videoDriver->beginScene(true, true, video::SColor(255, 255, 255, 255));
        smgr->drawAll();
        videoDriver->endScene();
 
    }
 
    device->closeDevice();
    device->drop();
 
    return 0;
}
 
Perhaps one solution would be to force the framerate to stay at a constant rate. I don't know if I have the skills to do something very reliable at this level. Another solution would be to use a physics engine or to completely change for a full game engine.

I specify that I have a fairly weak computer without a powerful graphics card, and my framerate easily falls to 30.

And, please excuse me for my bad english, I try to do my best with what I know (and a little google translation) :lol: .
CuteAlien
Admin
Posts: 9652
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CollisionResponseAnimator problem (look frame dependent)

Post by CuteAlien »

Argh - sorry - so long since last release that I forget people still work with that. Irrlicht svn trunk (the current version) has that (and a few more things about it) fixed. But can't change it for 1.8 as that would break existing games using it.

And no worries - your english is good as far as I can tell (I'm also not a native speaker).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
AReichl
Posts: 269
Joined: Wed Jul 13, 2011 2:34 pm

Re: CollisionResponseAnimator problem (look frame dependent)

Post by AReichl »

Happy Corona-Timeout!
i just wanted to reply, that i cannot reproduce the problem - didn't know it was fixed.
Post Reply