1
\$\begingroup\$

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: enter image description here

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.

\$\endgroup\$
3
  • \$\begingroup\$ I will add a couple of comments, Im sure there are more well versed people more detail. Firstly, in any physics simulation you need to consider the resistance of the surface to acceleration, there are 2 pieces to this. One, energy required to overcome the resistance to being rolling, and resistance of the surface (and angle of surface obviously) when rolling. these traits usually manifest themselves as co-efficients. If you want to test your engine, a good way would be a skate park U shape, with no resistance, it should continue forever, with resistance, it should eventually stop. GL! \$\endgroup\$
    – ErnieDingo
    Commented Jul 30, 2018 at 22:40
  • \$\begingroup\$ one other note, for resistance, consider that there would be surface and air resistance. Air would be your base resistance value and the surface itself will have some resistance as well. \$\endgroup\$
    – ErnieDingo
    Commented Jul 30, 2018 at 22:41
  • \$\begingroup\$ Also, in order to correctly conserve both linear and angular momentum, it is necessary that the contact forces be properly applied at the point of contact. Only then can the correct torques as well as the correct forces be evaluated. \$\endgroup\$ Commented Aug 1, 2018 at 4:29

1 Answer 1

1
\$\begingroup\$

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);
}
//---------------------------------------------------------------------------
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .