Game Development Community

dev|Pro Game Development Curriculum

Projectile ballistic coefficients (drag factors)

by Mark Owen · 01/27/2004 (12:19 pm) · 3 comments

Add a field to the projectile datablock for the ballistic coefficient...

In engine\game\projectile.h immediately behind the declaration
bool isBallistic;
add
F32 ballisticCoefficient;

Provide a suitable default value in the constructor...

In engine\game\projectile.cc in the ProjectileData::ProjectileData()
constructor just behind the statement
isBallistic = false;
add
ballisticCoefficient = 1.0; // completely frictionless

Make the field console accessible...

In engine\game\projectile.cc at the end of the ProjectileData::initPersistFields()
function add
addNamedFieldV(ballisticCoefficient, TypeF32, ProjectileData, new FRangeValidator(0, 1));

Add client/server data exchange support for it...

In engine\game\projectile.cc in the ProjectileData::packData(BitStream* stream)
function just behind the statement
stream->write(gravityMod);
add
stream->write(ballisticCoefficient);

In engine\game\projectile.cc in the ProjectileData::unpackData(BitStream* stream)
function just behind the statement
stream->read(&gravityMod);
add
stream->read(&ballisticCoefficient);

Now for the physics simulation changes...

In engine\game\projectile.cc in the Projectile::processTick(const Move* move)
function change the block of code
// Otherwise, we have to do some simulation work.
   oldPosition = mCurrPosition;

   if( mDataBlock->isBallistic ) 
      mCurrVelocity.z -= 9.81f * mDataBlock->gravityMod * (F32(TickMs) / 1000.0f);
   
   newPosition = oldPosition + mCurrVelocity * (F32(TickMs) / 1000.0f);

   if (bool(mSourceObject))
      mSourceObject->disableCollision();
to read as follows
// Otherwise, we have to do some simulation work.
   oldPosition = mCurrPosition;

   F32 dT = F32(TickMs) * 0.001f; // 1/1000.0 division avoidance
   if( mDataBlock->isBallistic ) {
   // modified to account for drag coefficient or form factor
      F32		dV = (1-mDataBlock->ballisticCoefficient) * dT;
      Point3F	d(mCurrVelocity.x * dV,mCurrVelocity.y * dV, 9.81f * mDataBlock->gravityMod * dT);
      mCurrVelocity -= d;
	}
	newPosition = oldPosition + mCurrVelocity * dT;

   if (bool(mSourceObject))
      mSourceObject->disableCollision();

Do a Build of the engine.

Now for some example script changes to put the ballistic coefficient into service...

In example\fps\server\scripts\rifle.cs in datablock ProjectileData(RifleProjectile)
add
ballisticCoefficient = 0.95; // 0 - horrid drag, 1 - no drag

And for comparison purposes...

In example\starter.fps\server\scripts\crossbow.cs in datablock ProjectileData(CrossbowProjectile)
add
ballisticCoefficient = 0.85; // 0 - horrid drag, 1 - no drag


Once this change is incorporated, if you set a projectiles ballisticCoefficient to any value less than 1.0 its velocity drops off in a non linear manner during flight. If set to 1.0 ballisticCoefficient has no effect. If set to 0.0, the projectile will drop to your feet (infinite drag, essentially). Good values may be derived empirically. Note that the field name is a bit of a misnomer since this is not a real ballistic coefficient (BC) (derived by measurements, based on a standard atmosphere, gravity, etc. and relative to a an established standard projectile), but the name conveys the effect acheived through its use. Real rifle bullets BCs typically range from about 0.15 to 0.55 so if you want to tie your BCs to real numbers, adjust by adding a constant of 0.40 to the real BC to get a value that
will work well in the simulation.

#1
01/28/2004 (7:28 pm)
That sounds pretty sharp. I'll give that one try. Thanks

Matt
#2
01/31/2004 (6:54 pm)
This is perfect for trying to get an 'arc' on your ballistic projectiles...
#3
09/16/2005 (8:25 am)
Experimenting with this, I discovered a discrepancy that is very noticeable with high coefficients. My projetiles were swooping up in a concave arc before reaching apogee. After a bunch of experimenting and coffee, I realized this is because air resistance is calculated for x and y, but not for z. Our projectile gets a free ride in the z axis and this causes an error in the ballistics equation.

The fix is to simply incorporate the BC into the z axis as well like this:

Point3F d(mCurrVelocity.x * dV,mCurrVelocity.y * dV, 9.81f * mDataBlock->gravityMod * dT + mcurrVelocity.z * dV);

This will now ensure our projectile is affected by medium resistance along all axes and give us a proper ballistics arc from get to go. It also gives us a terminal velocity, which is kind of neat.

I also discovered the engine comes with a TickSec function which divides the current tick by 1000 for us automatically so we don't have to write the math to convert milliseconds to seconds.

So, instead of this:

F32 dT = F32(TickMs) * 0.001f; // 1/1000.0 division avoidance

We can simply use this, if we want to: TickSec.

Purists will notice the TickSec function divides. If you prefer multiplication, you'll want to roll your own.