Wheeled Vehicle Down Force
by Martin "Founder" Hoover · 06/11/2004 (10:47 pm) · 27 comments
First let me say that I'm really not a programmer, so if there are any obvious blunders, please feel free to tell me.
This is not a proper implementation of downforce. For that you would need to pick a couple points on the front and/or rear of the vehicle model and apply a "downward" force to them. This is much easier as it simply increases the vehicles' gravity as its' speed increases. The first obvious problem this will create is that as the vehicle reaches very high speeds, the downforce will fully compress its' springs, making for a rather bumpy ride. (Though it will still stay stuck to the ground.) To compensate for this I stiffen the springs so the vehicle will still maintain enough of a spring extension to absorb bumps.
First add a new datablock variable so we can control the amount of downforce applied.
In WheeledVehicle.h near the bottom of the definition for WheeledVehicleData add following the sequence variable:
Now move into wheeledVehicle.cc.
Initialize the new variable at the bottom of WheeledVehicleData::WheeledVehicleData()
Add a persistent field for it after the brakeTorque in: void WheeledVehicleData::initPersistFields()
Now we add it to the datablock pack and unPack methods. If you have not messed with adding variables to the pack/unPack methods, it is important to note that the order in which you pack the variables is the order you must use to unPack them. In this case I chose to add it after brakeTorque.
void WheeledVehicleData::packData(BitStream* stream)
Then unPack it.
void WheeledVehicleData::unpackData(BitStream* stream)
Since I decided to stiffen the springs to compensate for the vehicles extra weight, the calculations must be done early in the updateForces function so it can be used for every wheel/spring combination that has ground contact. Then near the bottom it will be applied to the vehicles gravity.
void WheeledVehicle::updateForces(F32 dt)
Find where the following loop begins...
Above that insert the following:
Next, inside the wheel loop find the following:
Change to the following:
Next, to actually apply the extra downforce to the vehicles gravity find:
Change to:
DataBlocks
Now you have a new datablock variable for playing around with the downforce amount. Just slap downforce = x.xx; into a wheeledVehicle datablock and you're set.
A value of 0.01 effectively kills the downforce effect, though if you are very astute you might still notice some downforce at obscenely high speeds.
The most effective range for this is between 0.1 and 1.0, going higher really doesn't gain you much, and if you need it that much higher, you might consider upping the mass of your vehicle.
And now we're done!
My calculations could probably stand some improvements, as they are not based on anything other then achieving numbers that work well for the downforce effect. If you can improve on it, please share.
This is not a proper implementation of downforce. For that you would need to pick a couple points on the front and/or rear of the vehicle model and apply a "downward" force to them. This is much easier as it simply increases the vehicles' gravity as its' speed increases. The first obvious problem this will create is that as the vehicle reaches very high speeds, the downforce will fully compress its' springs, making for a rather bumpy ride. (Though it will still stay stuck to the ground.) To compensate for this I stiffen the springs so the vehicle will still maintain enough of a spring extension to absorb bumps.
First add a new datablock variable so we can control the amount of downforce applied.
In WheeledVehicle.h near the bottom of the definition for WheeledVehicleData add following the sequence variable:
S32 steeringSequence; // Steering animation F32 downForce;
Now move into wheeledVehicle.cc.
Initialize the new variable at the bottom of WheeledVehicleData::WheeledVehicleData()
brakeLightSequence = -1; downForce = 0.1;
Add a persistent field for it after the brakeTorque in: void WheeledVehicleData::initPersistFields()
addField("downForce", TypeF32, Offset(downForce, WheeledVehicleData));Now we add it to the datablock pack and unPack methods. If you have not messed with adding variables to the pack/unPack methods, it is important to note that the order in which you pack the variables is the order you must use to unPack them. In this case I chose to add it after brakeTorque.
void WheeledVehicleData::packData(BitStream* stream)
stream->write(brakeTorque); stream->write(downForce);
Then unPack it.
void WheeledVehicleData::unpackData(BitStream* stream)
stream->read(&brakeTorque); stream->read(&downForce);
Since I decided to stiffen the springs to compensate for the vehicles extra weight, the calculations must be done early in the updateForces function so it can be used for every wheel/spring combination that has ground contact. Then near the bottom it will be applied to the vehicles gravity.
void WheeledVehicle::updateForces(F32 dt)
Find where the following loop begins...
// Sum up spring and wheel torque forces
for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
if (!wheel->tire || !wheel->spring)
continue;Above that insert the following:
//calculate here so we can stiffen the springs a bit based on
//the final amount of downforce
//get the speed
F32 downForce = mRigid.linVelocity.len() <= 1 ? 1 : mRigid.linVelocity.len();
//grab the datablock var
downForce *= mDataBlock->downForce;
//make it a smaller number so we can multiply gravity by it
downForce = mSqrt(downForce);
downForce /= 3;
//ensure that it is not smaller then one, cause mulltiplying gravity by fractions is baaaad
downForce = downForce < 1 ? 1 : downForce;
// Sum up spring and wheel torque forces
for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
if (!wheel->tire || !wheel->spring)
continue;Next, inside the wheel loop find the following:
// Spring force & damping
F32 spring = wheel->spring->force * (1 - wheel->extension);Change to the following:
// Spring force & damping
F32 spring = wheel->spring->force * (1 - wheel->extension);
//Add some stiffness to the springs in accordance with the downforce
//so we don't bottom out at higher speeds.
//you can adjust the fraction in the following to stiffen or weaken the springs. Since I am not making a
//car simulator I have no need to make this adjustable, but it could easily be made a datablock variable
spring += (spring * downForce) * 0.3f;Next, to actually apply the extra downforce to the vehicles gravity find:
// Gravity mRigid.force += Point3F(0, 0, sWheeledVehicleGravity * mRigid.mass);
Change to:
// Container drag & buoyancy //Founder- implement downforce with the gravity to make a nice uniform pressure mRigid.force += Point3F(0, 0, (sWheeledVehicleGravity * mRigid.mass) * downForce);
DataBlocks
Now you have a new datablock variable for playing around with the downforce amount. Just slap downforce = x.xx; into a wheeledVehicle datablock and you're set.
A value of 0.01 effectively kills the downforce effect, though if you are very astute you might still notice some downforce at obscenely high speeds.
The most effective range for this is between 0.1 and 1.0, going higher really doesn't gain you much, and if you need it that much higher, you might consider upping the mass of your vehicle.
And now we're done!
My calculations could probably stand some improvements, as they are not based on anything other then achieving numbers that work well for the downforce effect. If you can improve on it, please share.
#22
02/14/2005 (12:33 am)
#23
and that's all you have to do to activate the effect.
02/14/2005 (2:25 pm)
Well, once the changes to the source have been made the only thing you need to do is add the downForce variable to the WheeledVehicleData dataBlock, i.e.:downforce = 0.5;
and that's all you have to do to activate the effect.
#24
the current vehicle physics. Thank you very much for this.
06/06/2005 (11:48 pm)
This is an exceptionally useful piece of code. This addresses the very problem I was having withthe current vehicle physics. Thank you very much for this.
#25
04/28/2007 (7:29 am)
How would you modify this to allow each vehicle created to have a different downforce ?
#26
Mainly you need to tie the downforce variable into the object instance instead of the datablock. But you must keep in mind that in multiplayer, every client machine simulates the game world, so they must all the same value for each vehicle's downforce, else strange things will occur.
wheeledVehicle.h:
IN:
class WheeledVehicle: public Vehicle
put this near the middle, but above public:
in wheeledVehicle.cc
in bool WheeledVehicle::onAdd()
By default this will populate the mDownforce object value with the value from the datablock to ensure that it has a value.
add this line near the bottom:
Now here is the important part. Every time you modify the downforce for a specific instance of a vehicle, you must tell all of the clients. Fortunately TGE has a system for doing this, unfortunately unless things have changed since I have worked with the source, there are not enough extra maskbits to make a new one just for downforce, so I used the wheelMask bit to signal a change. What this means is, that every time you change the downforce, it will retransmit all the object instance wheel data. The system is effecient, so this really isn't much overhead unless you are wanting to get crazy and change it several times a second.
so find:
U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
immediately after:
if (stream->writeFlag(mask & WheelMask))
{
add
When you add something to packUpdate, you must keep it in the exact same order in unpackUpdate so it will be read into the proper variable:
In:
void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
right after:
if (stream->readFlag())
{
add:
All that is left is to add a couple of functions to enable the console to change the values. Below, the setMaskBits(WheelMask) is what signals the engine to broadcast to all the clients the new value.
To use it from script or console:
vehicleObjectID.setDownforce(xx);
04/29/2007 (7:19 am)
This really isn't very difficult to do, as there are a number of examples in the TGE source. I happened to have already done this so I will provide a little tut on it.Mainly you need to tie the downforce variable into the object instance instead of the datablock. But you must keep in mind that in multiplayer, every client machine simulates the game world, so they must all the same value for each vehicle's downforce, else strange things will occur.
wheeledVehicle.h:
IN:
class WheeledVehicle: public Vehicle
put this near the middle, but above public:
F32 mDownForce;
in wheeledVehicle.cc
in bool WheeledVehicle::onAdd()
By default this will populate the mDownforce object value with the value from the datablock to ensure that it has a value.
add this line near the bottom:
mDownForce = mDataBlock->downForce;
Now here is the important part. Every time you modify the downforce for a specific instance of a vehicle, you must tell all of the clients. Fortunately TGE has a system for doing this, unfortunately unless things have changed since I have worked with the source, there are not enough extra maskbits to make a new one just for downforce, so I used the wheelMask bit to signal a change. What this means is, that every time you change the downforce, it will retransmit all the object instance wheel data. The system is effecient, so this really isn't much overhead unless you are wanting to get crazy and change it several times a second.
so find:
U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
immediately after:
if (stream->writeFlag(mask & WheelMask))
{
add
stream->write(mDownForce);
When you add something to packUpdate, you must keep it in the exact same order in unpackUpdate so it will be read into the proper variable:
In:
void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
right after:
if (stream->readFlag())
{
add:
stream->read(&mDownForce);
All that is left is to add a couple of functions to enable the console to change the values. Below, the setMaskBits(WheelMask) is what signals the engine to broadcast to all the clients the new value.
void WheeledVehicle::setDownForce(F32 downForce)
{
mDownForce = downForce;
setMaskBits(WheelMask);
}
ConsoleMethod( WheeledVehicle, setDownForce, void, 3, 3, "(float amt)")
{
F32 rate = dAtof(argv[2]);
if(rate < 0)
rate = 0;
object->setDownForce(rate);
}To use it from script or console:
vehicleObjectID.setDownforce(xx);
#27
Only problem I have now is when the clients look at each others vehicle the motion is not smothe, they seem to jerk around like packets being sent every 1/4 second or so for updates any settings to solve this?
05/03/2007 (9:54 am)
Works fine now, thanks.Only problem I have now is when the clients look at each others vehicle the motion is not smothe, they seem to jerk around like packets being sent every 1/4 second or so for updates any settings to solve this?

Torque 3D Owner Martin "Founder" Hoover