🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Physics - How to calculate the next position of a ball rolling against a slope?

Started by
3 comments, last by _WeirdCat_ 5 years, 11 months ago

For a small C/C++ game project, I want to add some physics on a ball, to allow it to roll against the slopes of a 3d world, in the same way as the example visible in the image below:Ball_With_Motion.thumb.png.9e06c75af192703cfae659c2d9f82d1e.png

However I have very few skills in physics, and I have some trouble to understand which physical laws should be applied, and how concepts like energy, force and acceleration should be used here. As a beginner, I imagined the situation as follow:

enter image description here

Where:

  • N (in green) is the normal of the current polygon above the ball
  • Fg (in red) is the gravitational force, which should be constant
  • Ff (also in red) is the frictional force, which should be constant
  • P (in blue) is the current ball position
  • P' (also in blue) is the next point to calculate

I also wrote a dedicated function to calculate the physics on my ball, which is called every time a frame is about to be rendered. When this function is called, I know:

  • The elapsed time
  • The current ball position
  • Which polygon is currently below the ball (from which I can extract the N normal shown above, and thus the slope)
  • The constant forces (Fg and Ff shown above)

From these values I tried to calculate the next ball position (P' shown above) based on the physics law F = m * a

This gave the following code:

#define M_CSR_Gravitation 9.81f

typedef struct
{
    CSR_Vector3 m_AccumulatedForce;
    float       m_Mass;
} CSR_Body;

typedef struct
{
    CSR_Vector3 m_Position;
    CSR_Sphere  m_Geometry;
    CSR_Body    m_Body;
} CSR_Ball;

CSR_Ball g_Ball;

void csrPhysicsGravity(float mass, CSR_Vector3* pF)
{
    // the formula for the gravitation is F = m * g
    pF->m_X = 0.0f;
    pF->m_Y = mass * M_CSR_Gravitation;
    pF->m_Z = 0.0f;
}

void ApplyPhysics(float elapsedTime, CSR_Plane groundPlane, const CSR_Vector3& frictionForce)
{
    float       dirAngleX;
    float       dirAngleZ;
    float       velocityAngle;
    CSR_Vector3 planeNormal;
    CSR_Vector3 gravity;
    CSR_Vector3 xDir;
    CSR_Vector3 zDir;
    CSR_Vector3 up;
    CSR_Vector3 dropForce;
    CSR_Vector3 motionForce;
    CSR_Vector3 acceleration;
    CSR_Vector3 prevCenter;

    planeNormal.m_X = groundPlane.m_A;
    planeNormal.m_Y = groundPlane.m_B;
    planeNormal.m_Z = groundPlane.m_C;

    // calculate the gravity force applied on the ball
    csrPhysicsGravity(g_Ball.m_Body.m_Mass, &gravity);

    xDir.m_X = 1.0f;
    xDir.m_Y = 0.0f;
    xDir.m_Z = 0.0f;

    // calculate the angle to drift against the x axis
    csrVec3Dot(&xDir, &planeNormal, &dirAngleX);

    zDir.m_X = 0.0f;
    zDir.m_Y = 0.0f;
    zDir.m_Z = 1.0f;

    // calculate the angle to drift against the z axis
    csrVec3Dot(&zDir, &planeNormal, &dirAngleZ);

    up.m_X = 0.0f;
    up.m_Y = 1.0f;
    up.m_Z = 0.0f;

    // calculate the drift velocity (the steeper is the slope, the higher is the fall speed)
    csrVec3Dot(&up, &planeNormal, &velocityAngle);

    // calculate the drift offsets to apply on the next frame
    dropForce.m_X = dirAngleX * velocityAngle * -gravity.m_Y;
    dropForce.m_Y = 0.0f;
    dropForce.m_Z = dirAngleZ * velocityAngle * -gravity.m_Y;

    motionForce.m_X = g_Ball.m_Body.m_AccumulatedForce.m_X + dropForce.m_X - frictionForce.m_X;
    motionForce.m_Y = g_Ball.m_Body.m_AccumulatedForce.m_Y + dropForce.m_Y - frictionForce.m_Y;
    motionForce.m_Z = g_Ball.m_Body.m_AccumulatedForce.m_Z + dropForce.m_Z - frictionForce.m_Z;

    g_Ball.m_Body.m_AccumulatedForce = motionForce;

    acceleration.m_X = (motionForce.m_X / g_Ball.m_Body.m_Mass) * elapsedTime;
    acceleration.m_Y = (motionForce.m_Y / g_Ball.m_Body.m_Mass) * elapsedTime;
    acceleration.m_Z = (motionForce.m_Z / g_Ball.m_Body.m_Mass) * elapsedTime;

    prevCenter = g_Ball.m_Geometry.m_Center;

    g_Ball.m_Geometry.m_Center.m_X += -acceleration.m_X * elapsedTime;
    g_Ball.m_Geometry.m_Center.m_Y += -acceleration.m_Y * elapsedTime;
    g_Ball.m_Geometry.m_Center.m_Z += -acceleration.m_Z * elapsedTime;
}

It works very roughly. The ball actually rolls down the slope, but never stops. And if I try to throw the ball from the bottom of the slope, it accelerates instead of curbing.

Obviously something is pretty wrong, but what? As I say above, I'm not an expert in physics and I think I tend to confuse notions such acceleration, force and energy. However I tried to resolve this system by myself, without success.

So I would be thankful if somebody can explain to me:

  • which concepts of physics are involved to resolve the above mentioned system
  • How should I correct my code to reach the above described objective (of course as long as the code does not have to be rewritten entirely)
  • And/Or an example of a such algorithm, even if written in pseudo-code or in another programming language

IMPORTANT NOTE I know that there are many physics engines, commercial or open-source, like e.g. Bullet, Box2d or Havok, which I may use to resolve the above issue. THIS IS NOT MY OBJECTIVE, I wan't to use them, at least for now. I WANT TO LEARN AND UNDERSTAND how to resolve this issue before using such engines. And I think that the proposed problem is simple enough to serve for this purpose.

Advertisement

What value are you submitting to ApplyPhysics() for the frictionForce argument?

8 hours ago, Zakwayda said:

What value are you submitting to ApplyPhysics() for the frictionForce argument? 

I think this is one of the key to the solution. The friction force is constant for now, but this seems to not working well. Until now I passed an arbitrary value of more or less 0.1 for each axis. But changing that value has never had a real impact, so I disabled it until reaching an acceptable motion algorithm first.

Finally I found an acceptable solution, based on the following article: The Physics of Golf (search the "Rolling on inclined planes " part)

I'm aware that it is anything but perfect, but it works sufficiently well in my small simulation. Thanks to all which taken a time to post an answer, this really helped me.

Here is my solution:


//---------------------------------------------------------------------------
void csrPhysicsGravity(float mass, float* pF)
{
    // the formula for the gravitation is F = m * g
    *pF = mass * M_CSR_Gravitation;
}
//---------------------------------------------------------------------------
void csrPhysicsRoll(const CSR_Vector3* pSlopeDir,
                          float        mass,
                          float        friction,
                          float        elapsedTime,
                          CSR_Vector3* pVelocity)
{
    float       gravity;
    float       thetaX;
    float       thetaZ;
    float       ffx;
    float       ffz;
    CSR_Vector3 xDir;
    CSR_Vector3 zDir;
    CSR_Vector3 acceleration;

    // calculate the gravity force to apply to the body
    csrPhysicsGravity(mass, &gravity);

    xDir.m_X = 1.0f;
    xDir.m_Y = 0.0f;
    xDir.m_Z = 0.0f;

    // calculate the slope angle on the x axis
    csrVec3Dot(&xDir, pSlopeDir, &thetaX);

    zDir.m_X = 0.0f;
    zDir.m_Y = 0.0f;
    zDir.m_Z = 1.0f;

    // calculate the slope angle on the z axis
    csrVec3Dot(&zDir, pSlopeDir, &thetaZ);

    // the angles should always be positive
    thetaX = fabs(thetaX);
    thetaZ = fabs(thetaZ);

    // calculate the friction force to apply to the body (using the formula a = dv / dt)
    if (elapsedTime)
    {
        ffx = (pVelocity->m_X / (elapsedTime * 1.0f)) * friction;
        ffz = (pVelocity->m_Z / (elapsedTime * 1.0f)) * friction;
    }
    else
    {
        ffx = 0.0f;
        ffz = 0.0f;
    }

    // calculate the body acceleration (using the formula a = ((m * g * sin(theta)) - Ff) / m)
    acceleration.m_X = ((gravity * thetaX) - ffx) / mass;
    acceleration.m_Z = ((gravity * thetaZ) - ffz) / mass;

    // calculate the final body velocity (using the formula v = v + (a * dt))
    pVelocity->m_X += (acceleration.m_X * elapsedTime);
    pVelocity->m_Z += (acceleration.m_Z * elapsedTime);
}
//---------------------------------------------------------------------------

 

Dont treat my answer as solution cause i may be wrong, first of all you apply forcrs yo the ball, gravity, friction (important), air drag, and tge force yyou are kickin bal with if exists (not important),

Then you move ball from last position to new position and apply continous collision detection to see how much the ball  penetrated the surface it collided with, not to mention yoi correct the ball position and apply the bouncr based on the length of penetration vector (you apply the force  based on reflection formula i guess, anyway not a simple topic

This topic is closed to new replies.

Advertisement