Car (wheel) friction & propulsion code (Help needed)

Car (wheel) friction & propulsion code (Help needed)

Postby mbue » 13 Jan 2010, 16:09

I have some physics code in my car racing project that needs some steering/propulsion code that works witht he physics engine (chipmunk).

Short summary of the code I already have.
Each car has a (variable) number of wheels that have various settings ... does it propulse and/or steer? What friction value is applied in the forward (rolling/braking/propulsion) and sideways (steering/drifting) direction?
The code should ...
1) evaluate the current speed of the wheel relative to the ground.
2) Then it calculates & applies the forces caused by friction, propulsion and steering to them via the physics engine.
3) The physics engine then calculates the movement of the car-body depending on the applied forces tot he wheels.

The problem is that I seem to do something very wrong in the function Phys_CalculateForce (see below) since the steering is waaay off :-(
Are there any physics gurus out there that could help me with this?

EDIT: I think the main problem is that the current velocity of the car is not redirected by the steering wheels at all (they act more or less as pure friction).
I'm at a loss how to calculate the correct forces here :(

Many thanks in advance for any help,
Martin

The relevant code (from sv_physics.cpp and common.cpp).
This is development code and may not be in sync with the current content of the git repository of the project.
The original code was trying to simulate the example here: http://www.gamedev.net/reference/progra ... /page4.asp
{l Code}: {l Select All Code}
/**
 * @brief Returns the velocity of a point on a body. Takes into account angular velocity.
 * @param[in] body The chipmunk body where the point is located on/in.
 * @param[in] offset The location of the point relative to the body center. (bodyspace)
 * @param[in] angVel The angular velocity to use.
 * @return The current speed of the point (worldspace)
 * @sa cpvPointVel
 * @note The basic code for this function was adapted from the code in this tutorial by Matt Kincaid: http://www.gamedev.net/reference/programming/features/2dcarphys/page4.asp
 */
cpVect cpvPointVel_PredefAngVel (cpBody *body, cpVect offset, double angVel)
{
   cpVect tangent;

   // Rotate the point to its current location (i.e. take into account body rotation).
   // Values are still relative to body location.
   offset = cpvrotate(offset, body->rot);

   // Create tangent of the CoM->offset vector.
   tangent = cpv(-offset.y, offset.x);

   // Apply angular velocity to tangent (tangent length == angVel radius) and add body velocity.
   return cpvadd(cpvmult(tangent, angVel), body->v);
   //return cpvadd(cpvmult(cpvnormalize(tangent), angVel * cpvlength(offset)), body->v);
}


/**
 * @brief Sets the forward- and side vectors of the given object to be in sync with the wanted steering angle.
 * @note The basic code for this function was adapted from the code in this tutorial by Matt Kincaid: http://www.gamedev.net/reference/programming/features/2dcarphys/page4.asp
 */
static void Phys_SetSteeringAngle (player_t *player, const int wheelIdx, double angle)
{
   // Forward and side vector
   angle = limitAngleRad(angle);

   cpVect fwd = cpv(1, 0);
   cpVect side = cpv(0, 1);

   player->fwd[wheelIdx] = cpvrotate(fwd, cpvforangle(angle));
   player->side[wheelIdx] = cpvrotate(side, cpvforangle(angle));
}

/**
 * @brief Calculates the (opposing) force working on a wheel of a car while driving (friction, steering).
 * @param[in] player The player (or rather his/her car) we want to calculate the force on the wheel for.
 * @param[in] wheelIndex Index of the wheel on the car.
 * @param[in] originalAngVel Currect angular velocity of the car.
 * @note wheelSpeed and the fwd/side vectors need to be updated before this function is called.
 * @note The basic code for this function was adapted from the code in this tutorial by Matt Kincaid: http://www.gamedev.net/reference/programming/features/2dcarphys/page4.asp
 */
static cpVect Phys_CalculateForce (player_t *player, int wheelIndex, double originalAngVel)
{
   wheel_t *wheel;
   double fwdFriction;
   double sideFriction;
   const double groundFriction = 0.55f; /**< @todo Get ground friction from current surface-property. */
   cpVect vel;         // Velocity of the wheel relative to the ground.
   cpVect wheelRotVel;   // Velocity of wheel surface.

   cpVect wheelSidePatchVel;
   cpVect wheelFwdPatchVel;
   cpVect sideFrictionVel;
   cpVect fwdFrictionVel;
   cpVect frictionForce;
   cpVect propVel;

   cpVect responseForce;

   if (!player)
      return cpvzero;

   wheel = &player->car->wheels[wheelIndex];

   // 1) Calculate friction values for forward (rolling friction) and sideway (sideway grip) motion of the wheel.
   sideFriction = wheel->sideFriction * groundFriction;
   if (!player->keyStates[MOVE_BRAKE]) {
      const double brakeFriction = 0.65f;
      fwdFriction =  brakeFriction * groundFriction;
   } else {
      fwdFriction = wheel->rollFriction * groundFriction;
   }

   // 2) Get the velocity of the wheel relative to the ground.
   vel = cpvPointVel_PredefAngVel(player->body, wheel->pos, originalAngVel);
   //vel = cpvPointVel(player->body, wheel->pos);

   // 3) Calculate friction forces (ignores wheel acceleration)
   // Project body velocity on the side axis
   wheelSidePatchVel = cpvproject(vel, player->side[wheelIndex]);
   // Project body velocity on the forward axis
   wheelFwdPatchVel = cpvproject(vel, player->fwd[wheelIndex]);

   sideFrictionVel = cpvneg(cpvmult(wheelSidePatchVel, 1.0-sideFriction));

   //fwdFrictionVel = cpvneg(cpvmult(wheelFwdPatchVel, 1.0-fwdFriction));
   /** @todo: Temporary solution. How can we solve this better? */
   fwdFrictionVel = cpvzero;

   // Calculate final friction force on this wheel
   frictionForce = cpvadd(sideFrictionVel, fwdFrictionVel);

   // 4) Calculate propulsion forces
   if (wheel->propulsion) {
      wheelRotVel = cpvmult(player->fwd[wheelIndex], player->wheelSpeed[wheelIndex]);
      propVel = cpvsub(wheelRotVel, wheelFwdPatchVel);
   } else {
      propVel = cpvzero;
   }

   // 5) Combine forces.
   responseForce = cpvadd(frictionForce, propVel);

   return responseForce;
}

/**
 * @brief Calculate steering values for this player.
 * @param[in] eng Used for debugging only (rendering forces).
 * @todo Implement brakes for front-only and back-only (i.e. wheel-dependent). In that case propelling wheels may not be set to 0 in some cases. Also adapt Phys_CalculateForce.
 */
static void Phys_Steering (engine_t *eng, player_t *player)
{
   car_t *car;
   int i;
   Uint32 timeDiff;
   double dVel = 0;
   double wheelSpeed;
   double angle, angleTemp, angleVelTemp;
   cpVect force;
   cpVect offset;
   bool rolloutFwd = false;
   bool rolloutBack = false;

   timeDiff = SDL_GetTicks() - player->lastUpdate;   // Spent time (milliseconds) since last update.

   car = player->car;

   angleTemp = player->body->a;
   angleVelTemp = player->body->w;

   for (i = 0; i < car->numWheels; i++) {
      if (car->wheels[i].propulsion) {
         rolloutFwd = false;
         rolloutBack = false;

         if (!player->keyStates[MOVE_BRAKE]) {
            player->wheelSpeed[i] = 0;
         } else {
            wheelSpeed = player->wheelSpeed[i];
            if (!player->keyStates[MOVE_FWD]) {
                  dVel = car->maxAcc * (double)timeDiff / 1000.0;
            } else if (!player->keyStates[MOVE_REVERSE]) {
                  dVel = -car->maxAcc * (double)timeDiff / 1000.0;
            } else {
               if (wheelSpeed > 0) {
                  dVel = -car->slowAcc * (double)timeDiff / 1000.0;
                  rolloutFwd = true;
               } else if (wheelSpeed < 0) {
                  dVel = car->slowAcc * (double)timeDiff / 1000.0;
                  rolloutBack = true;
               }
            }
            wheelSpeed += dVel;

            if ((rolloutFwd && wheelSpeed < 0)
             || (rolloutBack && wheelSpeed > 0)) {
               wheelSpeed = 0;
            } else if (wheelSpeed > E_GetMaxSpeed(player)) {
               wheelSpeed = E_GetMaxSpeed(player);
            } else if (wheelSpeed < car->minSpeed) {
               wheelSpeed = car->minSpeed;
            }
            player->wheelSpeed[i] = wheelSpeed;
         }
      }

      angle = angleTemp;
      if (car->wheels[i].steering) {
         // Calculate steering angle for this wheel.
         if (!player->keyStates[MOVE_LEFT]) {
            if (car->wheels[i].steeringBack)
               // Backward steering wheels
               angle -= deg2rad(car->maxSteerAng);
            else
               // Forward steering wheels (default in most cars)
               angle += deg2rad(car->maxSteerAng);
         } else if (!player->keyStates[MOVE_RIGHT]) {
            if (car->wheels[i].steeringBack)
               // Backward steering wheels
               angle += deg2rad(car->maxSteerAng);
            else
               // Forward steering wheels (default in most cars)
               angle -= deg2rad(car->maxSteerAng);
         }
      }

      /* Set the calculated steering angle. */
      Phys_SetSteeringAngle(player, i, angle);

      /* Calculate force that is applied to the body (at the wheel location)
       * Depends on acceleration, friction and steering direction of the wheel. */
      force = Phys_CalculateForce(player, i, angleVelTemp);

      // Calculate the offset of the wheel.
      offset = cpvrotate(car->wheels[i].pos, player->body->rot);

      // Apply the calculated force at the location of the wheel in/on the body.
      cpBodyApplyImpulse(player->body, force, offset);

      // Store force-vectors for later debug display.
      if (eng->drawVectors && player->uid == eng->myPlayer->uid) {
         cpVect pos = cpBodyLocal2World(player->body, car->wheels[i].pos);
         eng->forceDebugLines[i][0] = cpv(
            eng->screenWidth / 2 + player->body->p.x - pos.x,
            eng->screenHeight / 2 + player->body->p.y - pos.y);
         eng->forceDebugLines[i][1] = cpvadd(eng->forceDebugLines[i][0], cpvmult(force, 10));
      }
   }
}
Last edited by mbue on 14 Jan 2010, 09:45, edited 2 times in total.
My Levels for Nikki and the Robots
RACR, a Micro Machines-like car racing game (early WIP!)
User avatar
mbue
 
Posts: 15
Joined: 13 Jan 2010, 12:04
Location: Vienna, Austria

Re: Car (wheel) friction & propulsion code (Help needed)

Postby Julius » 14 Jan 2010, 00:07

http://www.amazon.com/gp/product/0596000065

This has a chapter on car physics... but I have to admit I don't know if it is good.
User avatar
Julius
Community Moderator
 
Posts: 3297
Joined: 06 Dec 2009, 14:02

Re: Car (wheel) friction & propulsion code (Help needed)

Postby mbue » 14 Jan 2010, 09:28

Thanks for the book link, I'll check it out and see if it's worth buying :-)

And for anybody out there searching for car physics links I guess I can put mine them here:

Martin
My Levels for Nikki and the Robots
RACR, a Micro Machines-like car racing game (early WIP!)
User avatar
mbue
 
Posts: 15
Joined: 13 Jan 2010, 12:04
Location: Vienna, Austria

Who is online

Users browsing this forum: No registered users and 1 guest