View our Privacy Policy
Copyright © 2000 - 2005
All Rights Reserved
Guided or Seeker Projectiles.
Avg. Rating (of 6) 5.0
Basic Information
Author: Derk Adams (Nov 30, 2004)
Categories: Mods, Patches, Scripting
Code File: guided.txt

Synopsis:
This resource adds a target tracking option to the projectile class. The system is simple but effective.

Keywords:
guided homing missile seeker projectile tracking

Article:
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
Head 1.2.2
Win32
Single Player

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 transmissio
n 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;
};

Submit ResourceSubmit your own resources!

Eric Prem   (Nov 30, 2004 at 17:33 GMT)
Hey this looks pretty shwankin! I'll have to check this out as soon as I get home from school!

Kirby Webber   (Nov 30, 2004 at 19:50 GMT)
Definitely adding this to my project.

You couldn't have posted this at a better time for me. =)

~Cheers

Jase   (Nov 30, 2004 at 20:52 GMT)
Sounds great, I could find some usage for this.

-Jase

Master Treb   (Nov 30, 2004 at 21:39 GMT)
thnx I can definetely use this I was waiting for something like this to come out.

Master Treb   (Nov 30, 2004 at 22:05 GMT)
is there code that makes a projectile skim along the ground? I have no Idea how to do that except make each one a hover vehicle which would innihilate the fps.

Derk Adams   (Nov 30, 2004 at 22:42 GMT)
Treb,

What you are asking for is a ground following missile. I have plans to implement that in the future, but don't have time at the moment.

It involves detecting the distance above the terrain and adjusting the hight of the projectile. It shouldn't be too difficult, just add it to processTick. Or you can wait for me, but it could be up to a year from now.

Thanks.

Master Treb   (Nov 30, 2004 at 23:06 GMT)
thanks a lot dirk I've been trying it for a while with no luck

Ahulo   (Dec 01, 2004 at 00:01 GMT)
hi, Derk Adams:

Does it work in tge v1.3.0 ?
I use tge 1.3.0, followed your step and modified the "projectile.h/cc", and in CrossBow.cs, i changed to:
isGuided = true;
precision = 100;
trackDelay = 5;
but when i run the torqueDemo_DEBUG.exe, the Guided or Seeker Projectiles
effects does not display.
what's the problem?
Could you give me some advices?
Thanks!

Derk Adams   (Dec 01, 2004 at 02:43 GMT)
Ahulo,

Make sure you are sending it a valid target id. Without one, the guidance system is not activated. It is sent in the Crossbow::onFire script.

Thanks.
Edited on Dec 01, 2004 02:43 GMT

Markus Nuebel   (Dec 01, 2004 at 05:32 GMT)
The download does not work for me.

Anyone else able to download it?

Ahulo   (Dec 01, 2004 at 06:22 GMT)
Adams:

Thanks!
I'm a newie to tge. I have no ideas to do so. Can you give some cs examples to show how getting the target id and sending it to the projectile system.

And, the gg link doesn't work.
Edited on Dec 01, 2004 06:27 GMT

Mike Kuklinski   (Dec 01, 2004 at 10:59 GMT)   Resource Rating: 5
When I implement this, I will add the following to it:

TargetID PackUpdate -- So the client will be warned he is being followed.

Decoys -- So the missile can be averted.

Spin -- So the missile spins along its Z axis, for cool effect.

Derk Adams   (Dec 01, 2004 at 14:21 GMT)
Greetings,

I have fixed the upload, but it is just a text file of this resource.

Ahulo: I modified the Player Target Locking to get the ids of the bots in the mission.

Mike: Glad to hear it. I specifically kept the modifications simple because different people will have different uses for their missiles. I have added a spread option to keep the missiles from hitting the same place and plan to add ground following in the future.

Thanks.

Mike Kuklinski   (Dec 01, 2004 at 21:45 GMT)   Resource Rating: 5
I decided to rate this 5 because of it's good usage.

Jonathan Rose   (Dec 07, 2004 at 10:16 GMT)   Resource Rating: 5
that... is soooo awesome

Martin "Muerte" Schultz   (Dec 19, 2004 at 10:09 GMT)   Resource Rating: 5
Extremely useful! Rated it 5! :-)

Eric Lavigne   (Dec 21, 2004 at 17:28 GMT)   Resource Rating: 5
I created the ground following missiles that Master Treb wanted. The resource is here.

Thanks, Derk. I don't want homing missiles in my game, but I learned a lot by studying your work.

Master Treb   (Dec 27, 2004 at 15:38 GMT)
thanks a lot Erik It's just what I wanted.

tyq.fk   (Jan 24, 2005 at 08:09 GMT)   Resource Rating: 5
It's great!how could it works in network?
Edited on Jan 24, 2005 08:16 GMT

Derk Adams   (Jan 24, 2005 at 15:16 GMT)
Tyq.fk,

It should work over the network, just be sure to have the target correctly selected (see comments above).

Thanks.

tyq.fk   (Jan 25, 2005 at 05:15 GMT)   Resource Rating: 5
Derk,Thanks for you reply.which ID I would set to the target when connet to dedicated server?

tyq.fk   (Jan 26, 2005 at 10:33 GMT)   Resource Rating: 5
I had figured out the question,Thanks Derk!

[edit]

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;
target = %obj.client.getSelectedObj(); // my change
};
...

note: I modify the Object selection in Torque

get the ID of the target in the mission.Now it works over the network.
Edited on Jan 27, 2005 02:51 GMT

Ricardo Cabral   (Feb 07, 2005 at 19:27 GMT)
i wonder how can we pass a target id to the missile, i've seen player target locking, but it seems to work with bots only. does anyone have an idea on how to make it work for other players and some objects?

Derk Adams   (Feb 07, 2005 at 22:25 GMT)
Ricardo,

Here is what I do:

/server/scripts/game.cs

function startGame()
{
...
// Setup Tracking for Targeting
$allPlayers = new SimSet(allPlayers);
...
}
...
function GameConnection::createPlayer(%client, %spawnPoint)
{
...
$allPlayers.add(%player);
...
}

/server/scripts/aiPlayer.cs

function AIPlayer::spawn(%file)
{
...
$allPlayers.add(%player);
...
}

Be sure to use the following where ever you eliminate a player or bot so they are not selectable (mine are in non-standard places):

  $allPlayers.remove(%this);

Then use something like this to run through the list to select the targets.

    // Find current target's index
for(%i=0;%i<$allPlayers.getCount();%i++) {
if (%player.targetedObject == $allPlayers.getObject(%i)) {
%start = %i;
}
}


You will then need to pass the info back and forth from the server to client.

HIH.
Edited on Feb 07, 2005 22:26 GMT

Ricardo Cabral   (Feb 13, 2005 at 18:02 GMT)
thanks for the info derk,

i added the following locking code and maded it so it is called by the engine code when you aim at a shape that has a name (defined in the guiCrosshairHud.cc).


function lockOn() {

echo("locked on");
// Find current target's index
for(%i=0;%i<$allPlayers.getCount();%i++) {
if (%player.targetedObject == $allPlayers.getObject(%i)) {
%start = %i;
}
}
$Character::targetedObject = %start;
}


the function gets called, as the console prints the echo functions i've put there but the missile still doesn't track. i assume i have to pass that info to the server as you told me, but how is that done? i haven't seen any documentation on torque's networking so far..

thanks for your help,

-nd

You must be a member and be logged in to either append comments or rate this resource.


Torque Commercial License

Newsletter Sign Up