Guided or Seeker Projectiles.
by Derk Adams · 11/30/2004 (9:28 am) · 54 comments
Download Code File
Background:
I was surprised when I purchased Torque that there was not at least a rudimentary tracking system for projectiles. When I started looking, the only answer I received was that Beaver Patrol (www.gamebeavers.com) had a "seeker" missile. Upon further inspection, the seeker code was embedded within their game and getting the code was not an easy task. After a couple months, I was able to get a copy of the Beaver code and I used it as a starting point. I have modified the code sufficiently to be confident that it can stand alone as a separate resource. I have removed many of the "features" and modified the method of tracking.
Discussion:
The guided projectile uses a flag called "isGuided" to activate the tracking system. If activated and a valid target object id is provided, the projectile will align itself to the target location. There are two attributes of the tracking; "trackDelay" which allows for a delay from the weapon firing to the time the projectile begins to track the target, and "precision" which is a percentage of vector change each tick. By default, isGuided is set to false, the target is null, and trackDelay and precision are set to 0. This makes sure that it will seamlessly integrate into existing projects without requiring any script modifications.
IsGuided will work with isBallistic, but the tracking will work against gravity if necessary.
In the script file, I have saved the target object's id in "$Character::targetedObject" on the client's system. You will need to use one of the tracking resources to figure out how to get the object id to pass to the projectile system.
Acknowledgements:
The seeker projectile code from Game Beavers at http://www.gamebeavers.com was useful in pointing me in the right direction.
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.
Development Environment:
November 2004 - February 2006
Head 1.2.2 - 1.4
Win32
Single Player - Multiplayer
Implementation:
EngineFile: /game/projectile.h
EngineFile: /game/projectile.cc
ScriptFile: /server/scripts/crossbow.cs
Background:
I was surprised when I purchased Torque that there was not at least a rudimentary tracking system for projectiles. When I started looking, the only answer I received was that Beaver Patrol (www.gamebeavers.com) had a "seeker" missile. Upon further inspection, the seeker code was embedded within their game and getting the code was not an easy task. After a couple months, I was able to get a copy of the Beaver code and I used it as a starting point. I have modified the code sufficiently to be confident that it can stand alone as a separate resource. I have removed many of the "features" and modified the method of tracking.
Discussion:
The guided projectile uses a flag called "isGuided" to activate the tracking system. If activated and a valid target object id is provided, the projectile will align itself to the target location. There are two attributes of the tracking; "trackDelay" which allows for a delay from the weapon firing to the time the projectile begins to track the target, and "precision" which is a percentage of vector change each tick. By default, isGuided is set to false, the target is null, and trackDelay and precision are set to 0. This makes sure that it will seamlessly integrate into existing projects without requiring any script modifications.
IsGuided will work with isBallistic, but the tracking will work against gravity if necessary.
In the script file, I have saved the target object's id in "$Character::targetedObject" on the client's system. You will need to use one of the tracking resources to figure out how to get the object id to pass to the projectile system.
Acknowledgements:
The seeker projectile code from Game Beavers at http://www.gamebeavers.com was useful in pointing me in the right direction.
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.
Development Environment:
November 2004 - February 2006
Head 1.2.2 - 1.4
Win32
Single Player - Multiplayer
Implementation:
EngineFile: /game/projectile.h
...
class ProjectileData : public GameBaseData
{
...
public:
...
/// Should it arc?
bool isBallistic;
+ /// Should it track target?
+ bool isGuided;
+ F32 precision;
+ S32 trackDelay;
/// How HIGH should it bounce (parallel to normal), [0,1]
F32 bounceElasticity;
...
class Projectile : public GameBase
{
typedef GameBase Parent;
ProjectileData* mDataBlock;
+ SimObjectPtr<ShapeBase> mTarget;
+ S32 mTargetId;
public:
// Initial conditions
enum ProjectileConstants {
SourceIdTimeoutTicks = 7, // = 231 ms
DeleteWaitTime = 500, ///< 500 ms delete timeout (for network transmission delays)
ExcessVelDirBits = 7,
MaxLivingTicks = 4095,
};
enum UpdateMasks {
BounceMask = Parent::NextFreeMask,
ExplosionMask = Parent::NextFreeMask << 1,
- NextFreeMask = Parent::NextFreeMask << 2
+ GuideMask = Parent::NextFreeMask << 2,
+ NextFreeMask = Parent::NextFreeMask << 3
};
...EngineFile: /game/projectile.cc
...
ProjectileData::ProjectileData()
{
...
isBallistic = false;
+ isGuided = false;
+ precision = 0;
+ trackDelay = 0;
velInheritFactor = 1.0;
...
void ProjectileData::initPersistFields()
{
...
addNamedField(isBallistic, TypeBool, ProjectileData);
+ addNamedField(isGuided, TypeBool, ProjectileData);
+ addNamedFieldV(precision, TypeF32, ProjectileData, new FRangeValidator(0, 100));
+ addNamedFieldV(trackDelay, TypeS32, ProjectileData, new FRangeValidator(0, 100000));
addNamedFieldV(velInheritFactor, TypeF32, ProjectileData, new FRangeValidator(0, 1));
...
void ProjectileData::packData(BitStream* stream)
{
...
if(stream->writeFlag(isBallistic))
{
stream->write(gravityMod);
stream->write(bounceElasticity);
stream->write(bounceFriction);
}
+ if(stream->writeFlag(isGuided))
+ {
+ stream->write(precision);
+ stream->write(trackDelay);
+ }
stream->writeFlag(doDynamicClientHits);
...
void ProjectileData::unpackData(BitStream* stream)
{
...
if(isBallistic)
{
stream->read(&gravityMod);
stream->read(&bounceElasticity);
stream->read(&bounceFriction);
}
+ isGuided = stream->readFlag();
+ if(isGuided)
+ {
+ stream->read(&precision);
+ stream->read(&trackDelay);
+ }
doDynamicClientHits = stream->readFlag();
...
Projectile::Projectile()
{
...
mMaintainThread = NULL;
+ mTarget = NULL;
+ mTargetId = -1;
mHidden = false;
...
void Projectile::initPersistFields()
{
...
endGroup("Source");
+ addField("target", TypeS32, Offset(mTargetId, Projectile));
}
...
bool Projectile::onAdd()
{
...
if (isServerObject())
{
ShapeBase* ptr;
if (Sim::findObject(mSourceObjectId, ptr))
mSourceObject = ptr;
else
{
if (mSourceObjectId != -1)
Con::errorf(ConsoleLogEntry::General, "Projectile::onAdd: mSourceObjectId is invalid");
mSourceObject = NULL;
}
+ ShapeBase* tptr;
+ mTarget = NULL;
+ if(mTargetId != -1)
+ if(Sim::findObject(mTargetId, tptr))
+ mTarget = tptr;
// If we're on the server, we need to inherit some of our parent's velocity
//
mCurrTick = 0;
}
...
void Projectile::processTick(const Move* move)
{
...
if(mDataBlock->isBallistic)
mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * (F32(TickMs) / 1000.0f);
+ // Tracking updates
+ if(mDataBlock->isGuided) {
+ // Only process if there is a target and the projectile is locked on
+ if((bool)mTarget && mCurrTick > mDataBlock->trackDelay) {
+ // Be sure to update clients on changes
+ setMaskBits(GuideMask);
+ // Set up variables
+ F32 speed;
+ Point3F targetDir;
+ Point3F targetPos;
+ // Get target position
+ targetPos = mTarget->getPosition();
+ // Adjust z to hit middle of target's bounding box
+ targetPos.z += (mTarget->getObjBox().len_z()/2);
+ // Remember current speed
+ speed = mCurrVelocity.len();
+ // Calculate direction change necessary to get to target
+ targetDir = targetPos - mCurrPosition;
+ // Normalize target direction
+ targetDir.normalize();
+ // Normalize current direction
+ mCurrVelocity.normalize();
+ // Adjust target direction based on precision
+ targetDir *= mDataBlock->precision;
+ // Adjust current direction based on precision
+ mCurrVelocity *= (100 - mDataBlock->precision);
+ // Combine directions
+ targetDir += mCurrVelocity;
+ // Normalize current direction
+ targetDir.normalize();
+ // Scale new velocity to remembered speed
+ targetDir *= speed;
+ // Set current velocity to new velocity
+ mCurrVelocity = targetDir;
+ }
+ }
newPosition = oldPosition + mCurrVelocity * (F32(TickMs) / 1000.0f);
...
U32 Projectile::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
...
else if (stream->writeFlag(mask & BounceMask))
{
// Bounce against dynamic object
mathWrite(*stream, mCurrPosition);
mathWrite(*stream, mCurrVelocity);
}
+ else if (stream->writeFlag(mask & GuideMask))
+ {
+ mathWrite(*stream, mCurrPosition);
+ mathWrite(*stream, mCurrVelocity);
+ }
return retMask;
...
void Projectile::unpackUpdate(NetConnection* con, BitStream* stream)
{
...
else if(stream->readFlag())
{
mathRead(*stream, &mCurrPosition);
mathRead(*stream, &mCurrVelocity);
}
+ else if(stream->readFlag())
+ {
+ mathRead(*stream, &mCurrPosition);
+ mathRead(*stream, &mCurrVelocity);
+ }
}
...ScriptFile: /server/scripts/crossbow.cs
...
datablock ProjectileData(CrossbowProjectile)
{
...
isBallistic = true;
gravityMod = 0.80;
+ isGuided = true;
+ // Precision is how acurately the projectile tracks the target.
+ // 0 is no tracking (same as not guided)
+ // 100 is exact tracking
+ precision = 5;
+ // TrackDelay is the number of milliseconds after firing to begin tracking
+ trackDelay = 40;
hasLight = true;
...
function CrossbowImage::onFire(%this, %obj, %slot)
{
...
%p = new (%this.projectileType)() {
dataBlock = %projectile;
initialVelocity = %muzzleVelocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
+ target = $Character::targetedObject;
};About the author
Recent Blogs
• Damage Texture with Transparency• Projectile Spread
• Plan for Derk Adams
• Continuous Laser
• Plan for Derk Adams
#42
what i mean is that lets say the missle is shot, and its one its way....
it moves in a streight line until a target gets within 10 feet of it, then it starts to track the tagret, or if a target is located with in a fov, and if more targets are there, then the closest one is selected.
that way you send it, and dont need to really select a target, sort of like auto-targeting for special missles.
im currently learning torque as well as c++, so i really have no clue how one would do this
08/30/2006 (11:25 am)
a quick question, could it be made tha the missle activly looks for targets?what i mean is that lets say the missle is shot, and its one its way....
it moves in a streight line until a target gets within 10 feet of it, then it starts to track the tagret, or if a target is located with in a fov, and if more targets are there, then the closest one is selected.
that way you send it, and dont need to really select a target, sort of like auto-targeting for special missles.
im currently learning torque as well as c++, so i really have no clue how one would do this
#43
You will find that Torque can do anything... as long as you can tell it to do so.
Good luck. I didn't know C++ two years ago, so it is possible.
Thanks.
08/30/2006 (11:47 am)
Tom,You will find that Torque can do anything... as long as you can tell it to do so.
Good luck. I didn't know C++ two years ago, so it is possible.
Thanks.
#44
01/24/2007 (8:34 am)
Great resource... Im giving it a 4, since the tracking is choppy... Has anyone looked into this, or even better have solution to make it track smoothly?
#45
10/14/2007 (9:43 am)
Has anyone have this working in TGEA v1.02 or v1.03? I'm just curios, since after I took the leap over to v1.02, this has stopped working for me. So i'm not sure if it's me... ;)
#46
10/15/2007 (9:17 am)
Got it working using a clean copy of projectile.cc/h
#47
Excellent resource, and a very useful feature. However, has anyone experienced problems when the mTarget is deleted while the projectile is trying to track it? I don't know whether this is an issue, but I added a Projectile::onDeleteNotify just in case, that zeroes out the mTarget and mTargetId members.
01/21/2008 (7:25 am)
Seems my comment got eaten. Not to worry.Excellent resource, and a very useful feature. However, has anyone experienced problems when the mTarget is deleted while the projectile is trying to track it? I don't know whether this is an issue, but I added a Projectile::onDeleteNotify just in case, that zeroes out the mTarget and mTargetId members.
#48
10/06/2008 (10:38 am)
has anyone tried this resource with TGE 1.5.2?
#50
I also made some tweaks to make this "magnetically" attracted to the target in that the closer the projectile gets the more accurate and faster is gets as well. Here is a replacement I used for the Projectile::processTick code:
If you do this, set a proper trackDelay, and make the player the target, you can make a nice boomerang projectile :)
I would also be interested in finding out how to make the trajectory a lot smoother. I don't know enough yet about how the client and server interact to fix it myself.
06/08/2009 (1:37 am)
In case anyone is interested, I got this working great with TGEA 1.8.1.I also made some tweaks to make this "magnetically" attracted to the target in that the closer the projectile gets the more accurate and faster is gets as well. Here is a replacement I used for the Projectile::processTick code:
// Tracking updates
if(mDataBlock->isGuided) {
// Only process if there is a target and the projectile is locked on
if((bool)mTarget && mCurrTick >= mDataBlock->trackDelay) {
// Be sure to update clients on changes
setMaskBits(GuideMask);
// Set up variables
F32 maxSpeed = 40;
F32 targetDist;
Point3F targetDir;
Point3F targetPos;
// Get target position
targetPos = mTarget->getPosition();
// Adjust z to hit middle of target's bounding box
targetPos.z += (mTarget->getObjBox().len_z()/2);
// Calculate direction change necessary to get to target
targetDir = targetPos - mCurrPosition;
targetDist = targetDir.len();
// Normalize target direction
targetDir.normalize();
// Adjust target direction based on precision and distance
targetDir *= ((mDataBlock->precision) / (targetDist * 2));
// Combine directions
targetDir += mCurrVelocity;
if (targetDir.len() > maxSpeed)
{
targetDir.normalize();
targetDir *= maxSpeed;
}
// Set current velocity to new velocity
mCurrVelocity = targetDir;
}
}If you do this, set a proper trackDelay, and make the player the target, you can make a nice boomerang projectile :)
I would also be interested in finding out how to make the trajectory a lot smoother. I don't know enough yet about how the client and server interact to fix it myself.
#51
06/13/2009 (5:50 am)
@ben, please send me your TGEA 1.8.1 updates and I'll clean it up and post it. thanks!
#52
THANK YOU!!!!
THANK YOU!!!!
THANK YOU!!!!
Got it working with Torque3D, thank you resource. Learned alot and saved my a**. Cheers.
08/23/2012 (4:31 am)
THANK YOU!!!!THANK YOU!!!!
THANK YOU!!!!
THANK YOU!!!!
Got it working with Torque3D, thank you resource. Learned alot and saved my a**. Cheers.
#53
08/25/2012 (6:04 pm)
Would you mind sharing your T3D version Sean? thanks
#54
One thing I don't believe I've seen anyone talk about is tracking types. I'm guessing we are mainly tlaking about Heat tracking, but has anyone thought about radar, optical, laser, sound, or biometric, and of cause this adds all the different counter-measures, flares, chaff, buzzers, smoke, etc...
I know I'd love to figure out all this and have it coded in the source and then have it tagged in the ammo config as "Guided" with "true" or "false" options, followed by what type of tracking system used "IR", "Laser", "optical", etc...
That way the weapon/launcher itself has little to do with guidance, and it'd all weapons to have ammo options of standard or smart ammo.
Well it's something I'm planing to work on, if anyone has already looked into this, please share your information with the rest of us, and hopefully we can enhance it.
03/18/2013 (3:57 pm)
Yes would love to see Sean's version also.One thing I don't believe I've seen anyone talk about is tracking types. I'm guessing we are mainly tlaking about Heat tracking, but has anyone thought about radar, optical, laser, sound, or biometric, and of cause this adds all the different counter-measures, flares, chaff, buzzers, smoke, etc...
I know I'd love to figure out all this and have it coded in the source and then have it tagged in the ammo config as "Guided" with "true" or "false" options, followed by what type of tracking system used "IR", "Laser", "optical", etc...
That way the weapon/launcher itself has little to do with guidance, and it'd all weapons to have ammo options of standard or smart ammo.
Well it's something I'm planing to work on, if anyone has already looked into this, please share your information with the rest of us, and hopefully we can enhance it.

Torque Owner CIMO