Terrain Following Missiles
by Eric Lavigne · 12/28/2004 (2:45 pm) · 22 comments
Acknowledgement:
I learned a lot from Derk Adams' guided missile resource. It helped me to create this resource.
Discussion:
To control a projectile's terrain following behavior, you will need to set four variables in the ProjectileData datablock. This is the same place where you would set such attributes as isBallistic, gravityMod, or, if you use Derk's resource, isGuided. The four variables are:
isTerrainFollowing - set to true if you want this behavior
minTerrainHeight - how low is the projectile allowed to go? (meters)
maxTerrainHeight - how high is the projectile allowed to go? (meters)
speedTerrainFollowing - how much verticle speed can TerrainFollowing
use to avoid leaving the specified range? (meters per second)
In general, you should choose a high value for speedTerrainFollowing (greater than the speed of the projectile) and set isBallistic=false. Lower values of speedTerrainFollowing are used if you want the missile to only have a small tendency to stay within the range that you specified.
Ordinarily you would set isBallistic=false. Otherwise TerrainFollowing and Ballistic will fight eachother. Setting both of them to true could be useful though. TerrainFollowing would be able to hold off gravity for a while (smoothly following terrain) then suddenly plummet and explode on the ground. If you want it to follow the ground for time %
Development Environment:
December 2004
TGE 1.3 (installer)
WinXP
Single Player
Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.
Implementation:
EngineFile: /terrain/terrData.h (near the bottom)
I learned a lot from Derk Adams' guided missile resource. It helped me to create this resource.
Discussion:
To control a projectile's terrain following behavior, you will need to set four variables in the ProjectileData datablock. This is the same place where you would set such attributes as isBallistic, gravityMod, or, if you use Derk's resource, isGuided. The four variables are:
isTerrainFollowing - set to true if you want this behavior
minTerrainHeight - how low is the projectile allowed to go? (meters)
maxTerrainHeight - how high is the projectile allowed to go? (meters)
speedTerrainFollowing - how much verticle speed can TerrainFollowing
use to avoid leaving the specified range? (meters per second)
In general, you should choose a high value for speedTerrainFollowing (greater than the speed of the projectile) and set isBallistic=false. Lower values of speedTerrainFollowing are used if you want the missile to only have a small tendency to stay within the range that you specified.
Ordinarily you would set isBallistic=false. Otherwise TerrainFollowing and Ballistic will fight eachother. Setting both of them to true could be useful though. TerrainFollowing would be able to hold off gravity for a while (smoothly following terrain) then suddenly plummet and explode on the ground. If you want it to follow the ground for time %
// follow the ground for 15 seconds, then drop and explode on the ground
datablock ProjectileData(CrossbowProjectile)
{
%timeuntilcrash=15
isBallistic = true;
gravityMod = 0.8;
isTerrainFollowing = true;
minTerrainHeight = 5;
maxTerrainHeight = 5;
speedTerrainFollowing = %timeuntilcrash * gravityMod * 9.8;
...
}Development Environment:
December 2004
TGE 1.3 (installer)
WinXP
Single Player
Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.
Implementation:
EngineFile: /terrain/terrData.h (near the bottom)
extern ResourceInstance *constructTerrainFile(Stream &stream); + F32 getTerrainHeight(Point2F position); #endifEngineFile: /terrain/terrData.cc
ConsoleMethod(TerrainBlock, ...
{
...
}
+ F32 getTerrainHeight(Point2F position)
+ {
+ F32 height = 0.0f;
+ TerrainBlock * terrain = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));
+ if(terrain)
+ if(terrain->isServerObject())
+ {
+ Point3F offset;
+ terrain->getTransform().getColumn(3, &offset);
+ position -= Point2F(offset.x, offset.y);
+ terrain->getHeight(position, &height);
+ }
+ return height;
+ }
ConsoleFunction(getTerrainHeight, F32, 2, 2, "(Point2I pos) - gets the terrain height at the specified position.")
{
Point2F pos;
- F32 height = 0.0f;
dSscanf(argv[1],"%f %f",&pos.x,&pos.y);
- TerrainBlock * terrain = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));
- if(terrain)
- if(terrain->isServerObject())
- {
- Point3F offset;
- terrain->getTransform().getColumn(3, &offset);
- pos -= Point2F(offset.x, offset.y);
- terrain->getHeight(pos, &height);
- }
- return height;
+ return getTerrainHeight(pos);
}
ConsoleMethod(TerrainBlock, ...
...EngineFile: game/projectile.h/// Should it arc? bool isBallistic; + /// Should it follow the terrain? + bool isTerrainFollowing; + /// How close can it be to the terrain? + F32 minTerrainHeight; + /// How far can it be from the terrain? + F32 maxTerrainHeight; + /// If outside these bounds, how quickly does it return? + F32 speedTerrainFollowing; /// How HIGH should it bounce (parallel to normal), [0,1] F32 bounceElasticity; /// How much momentum should be lost when it bounces (perpendicular to normal), [0,1] F32 bounceFriction;EngineFile: game/projectile.cc
#include "sim/decalManager.h"
+ #include "terrain/terrData.h"
IMPLEMENT_CO_DATABLOCK_V1(ProjectileData);
...
ProjectileData::ProjectileData()
{
...
faceViewer = false;
scale.set( 1.0, 1.0, 1.0 );
+ /// Terrain following is off by default.
+ /// If turned on though, its other defaults
+ /// lead to strong and obvious behavior.
+ /// Good for people who are cluelessly
+ /// experimenting.
+ isTerrainFollowing = false;
+ minTerrainHeight = 5;
+ maxTerrainHeight = 5;
+ speedTerrainFollowing = 100;
isBallistic = false;
...
void ProjectileData::initPersistFields()
{
...
addNamedField(hasWaterLight, TypeBool, ProjectileData);
addNamedField(waterLightColor, TypeColorF, ProjectileData);
+ addNamedField(isTerrainFollowing, TypeBool, ProjectileData);
+ addNamedFieldV(minTerrainHeight, TypeF32, ProjectileData, new FRangeValidator(-5000, 5000));
+ addNamedFieldV(maxTerrainHeight, TypeF32, ProjectileData, new FRangeValidator(-5000, 5000));
+ addNamedFieldV(speedTerrainFollowing, TypeF32, ProjectileData, new FRangeValidator(0, 10000));
addNamedField(isBallistic, TypeBool, ProjectileData);
...
void ProjectileData::packData(BitStream* stream)
{
...
if(stream->writeFlag(isBallistic))
{
stream->write(gravityMod);
stream->write(bounceElasticity);
stream->write(bounceFriction);
}
+ if(stream->writeFlag(isTerrainFollowing))
+ {
+ stream->write(minTerrainHeight);
+ stream->write(maxTerrainHeight);
+ stream->write(speedTerrainFollowing);
+ }
// Neither velInheritVelocity nor muzzleVelocity are transmitted to the
// client. This is because in stock Torque, it is not needed for the
// client-side simulation - in general, it is good design to not transmit
// useless information. You could easily add stream->write() calls here,
// and read calls in unpackData, if you did have need of them.
}
void ProjectileData::unpackData(BitStream* stream)
{
...
isBallistic = stream->readFlag();
if(isBallistic)
{
stream->read(&gravityMod);
stream->read(&bounceElasticity);
stream->read(&bounceFriction);
}
+ isTerrainFollowing = stream->readFlag();
+ if(isTerrainFollowing)
+ {
+ stream->read(&minTerrainHeight);
+ stream->read(&maxTerrainHeight);
+ stream->read(&speedTerrainFollowing);
+ }
}
...
void Projectile::processTick(const Move* move)
{
...
oldPosition = mCurrPosition;
if(mDataBlock->isBallistic)
mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * (F32(TickMs) / 1000.0f);
newPosition = oldPosition + mCurrVelocity * (F32(TickMs) / 1000.0f);
+ if(mDataBlock->isTerrainFollowing)
+ {
+ F32 maxcorrection = mDataBlock->speedTerrainFollowing * (F32(TickMs)/1000.0f);
+ Point2F projectileshadow;
+ projectileshadow.x = newPosition.x;
+ projectileshadow.y = newPosition.y;
+ F32 terrainheight = getTerrainHeight(projectileshadow);
+ if(newPosition.z > mDataBlock->maxTerrainHeight + terrainheight)
+ {
+ if(newPosition.z > mDataBlock->maxTerrainHeight + terrainheight + maxcorrection)
+ newPosition.z -= maxcorrection;
+ else
+ newPosition.z = mDataBlock->maxTerrainHeight + terrainheight;
+ }
+ if(newPosition.z < mDataBlock->minTerrainHeight + terrainheight)
+ {
+ if(newPosition.z < mDataBlock->minTerrainHeight + terrainheight - maxcorrection)
+ newPosition.z += maxcorrection;
+ else
+ newPosition.z = mDataBlock->minTerrainHeight + terrainheight;
+ }
+ }
// disable the source objects collision reponse while we determine
// if the projectile is capable of moving from the old position
// to the new position, otherwise we'll hit ourself
if (bool(mSourceObject))
mSourceObject->disableCollision();
#22
08/17/2008 (3:58 pm)
Tom: This resource only helps you to create missiles that follow the terrain. You can, of course, modify it to meet your needs.
Torque Owner Tom