Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Simple Guided Missile

Kage Seraph
I Dig Giant Mecha
Join date: 3 Nov 2004
Posts: 513
12-08-2005 10:30
Hey gang, whipped up a simple and open air-to-air missile last night. It is having trouble seeking the targets, which I suspect has something to do with the missile's damping speed and predictive target positioning. I can't get it to track a target unless it is already a pretty close shot. Thoughts? Also, I'm very interested in optimizing this code (the final version will not have a 0.1 second, 96m, PI sensor repeat-- instead, these values will be eased up a bit once it is fully working.

Comments on either optimization and targeting much appreciated.

The logic is to gather info about target avs, make sure they aren't the owner(!), and then walk through the list (closest target first) and point to and move to the target. Once in range, blow up.

Notes: the missile object should be temp on rez; currently the only llDie() is called when near a target. Could certainly build in a lifetime function as well.

No, it doesn't deal damage, push, or teleport people home. =)

CODE

//Beachhead Aerospace Simple Guided Missile v. 0.1
//Use, modify, sell, run amok.
//This script is released without support. Too many versions to debug once (if)
//people start modding it. =) --Kage Seraph

float interval = 0.1; //the interval between scans. faster = more accurate = more serverload
vector target; //where the missile will try to move to
integer minDistance = 15; //inside this range (meters), the missile will airburst.
integer targetingFailure =FALSE;

//from http://secondlife.com/badgeo/wakka.php?wakka=llLookAt
//used to make missile point along positive X instead of positive Z
vector AXIS_FWD = <1,0,0>;
float strength = 1.0; //these need tweaking badly.
float damping = 0.1;

rotation getRotToPointAxisAt(vector axis, vector target)
{
return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}


// Particle Script 0.3
// Created by Ama Omega
// 10-10-2003

// Mask Flags - set to TRUE to enable
integer glow = FALSE; // Make the particles glow
integer bounce = FALSE; // Make particles bounce on Z plan of object
integer interpColor = TRUE; // Go from start to end color
integer interpSize = TRUE; // Go from start to end size
integer wind = FALSE; // Particles effected by wind
integer followSource = FALSE; // Particles follow the source
integer followVel = TRUE; // Particles turn to velocity direction

// Choose a pattern from the following:
// PSYS_SRC_PATTERN_EXPLODE
// PSYS_SRC_PATTERN_DROP
// PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
// PSYS_SRC_PATTERN_ANGLE_CONE
// PSYS_SRC_PATTERN_ANGLE
integer pattern = PSYS_SRC_PATTERN_EXPLODE;

// Select a target for particles to go towards
// "" for no target, "owner" will follow object owner
// and "self" will target this object
// or put the key of an object for particles to go to
key targetpart = "";

// Particle paramaters
float age = 2; // Life of each particle
float maxSpeed = 4; // Max speed each particle is spit out at
float minSpeed = 3; // Min speed each particle is spit out at
string texture ="fire"; // Texture used for particles, default used if blank
float startAlpha = 1; // Start alpha (transparency) value
float endAlpha = 0; // End alpha (transparency) value
vector startColor = <1,1,1>; // Start color of particles <R,G,B>
vector endColor = <0.5,0.5,0.5>; // End color of particles <R,G,B> (if interpColor == TRUE)
vector startSize = <5,5,5>; // Start size of particles
vector endSize = <0,0,0>; // End size of particles (if interpSize == TRUE)
vector push = <0,0,5>; // Force pushed on particles

// System paramaters
float rate = 0.1; // How fast (rate) to emit particles
float radius = 2; // Radius to emit particles for BURST pattern
integer count = 15; // How many particles to emit per BURST
float outerAngle = 1.54; // Outer angle for all ANGLE patterns
float innerAngle = 1.55; // Inner angle for all ANGLE patterns
vector omega = <0,0,10>; // Rotation of ANGLE patterns around the source
float life = 0; // Life in seconds for the system to make particles

// Script variables
integer flags;

updateParticles()
{
flags = 0;
if (targetpart == "owner") targetpart = llGetOwner();
if (targetpart == "self") targetpart = llGetKey();
if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK;
if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK;
if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK;
if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK;
if (wind) flags = flags | PSYS_PART_WIND_MASK;
if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK;
if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK;
if (targetpart != "") flags = flags | PSYS_PART_TARGET_POS_MASK;

llParticleSystem([ PSYS_PART_MAX_AGE,age,
PSYS_PART_FLAGS,flags,
PSYS_PART_START_COLOR, startColor,
PSYS_PART_END_COLOR, endColor,
PSYS_PART_START_SCALE,startSize,
PSYS_PART_END_SCALE,endSize,
PSYS_SRC_PATTERN, pattern,
PSYS_SRC_BURST_RATE,rate,
PSYS_SRC_ACCEL, push,
PSYS_SRC_BURST_PART_COUNT,count,
PSYS_SRC_BURST_RADIUS,radius,
PSYS_SRC_BURST_SPEED_MIN,minSpeed,
PSYS_SRC_BURST_SPEED_MAX,maxSpeed,
PSYS_SRC_TARGET_KEY,targetpart,
PSYS_SRC_INNERANGLE,innerAngle,
PSYS_SRC_OUTERANGLE,outerAngle,
PSYS_SRC_OMEGA, omega,
PSYS_SRC_MAX_AGE, life,
PSYS_SRC_TEXTURE, texture,
PSYS_PART_START_ALPHA, startAlpha,
PSYS_PART_END_ALPHA, endAlpha
]);
}
killparticles()
{
llParticleSystem([]);
}


default
{
on_rez(integer start_param)
{
llSetBuoyancy(1);
llSensorRepeat("","",AGENT,96,PI,interval);//This is hard on the sim. Tweak it down.
}

state_entry()
{
llSetBuoyancy(1);
llSensorRepeat("","",AGENT,96,PI,interval);
}

sensor(integer num_detected)
{
llStopLookAt();
integer i;
list targetKey = [];//clear the lists
list targetVel = [];
list targetPos = [];
list targetRot = [];
integer targetSelected = FALSE;
for( i = 0 ; i <= num_detected ; i++)
{//dump all detected avs and their info into lists
targetKey += [llDetectedKey(i)];
targetVel += [llDetectedVel(i)];
targetPos += [llDetectedPos(i)];
targetRot += [llDetectedRot(i)];
}
integer x = 0;
do
{//now we walk through the list for the first good target.
if(llList2Key(targetKey,x) == llGetOwner() )
{//we've targeted the owner. No good, keep trying
x++;
targetSelected = FALSE;
}
else if(llList2Key(targetKey,x) != NULL_KEY)
{
targetSelected = TRUE;
target = llList2Vector(targetPos,x) + llRot2Fwd(llList2Rot(targetRot,x)) * llVecMag(llList2Vector(targetVel,x));
// This line points the fwd (X) axis at the target:
llRotLookAt(getRotToPointAxisAt(AXIS_FWD, target), strength, damping);
llSetForce(<80,0,0>,TRUE);
//llOwnerSay("target is "+llKey2Name(llList2Key(targetKey,x))+ " at "+(string)llList2Vector(targetPos,x));
}
else
{//we've got no targets. Scan again
if(!targetingFailure)
{
llOwnerSay("No target");
targetingFailure = TRUE;
}
}
}while(!targetSelected);

if( llAbs(llRound(llVecMag(llGetPos() - llList2Vector(targetPos,x)))) < minDistance )
{//detonation routine
llSay(11235814,(string)llGetOwner() + " NULL_KEY 20");//for air combat scripts
llPlaySound("rocket explode",1);
llMessageLinked(LINK_SET,0,"collision","");//to trigger any other scripts as necessary
llSetStatus(STATUS_PHYSICS, FALSE);
updateParticles();
llSleep(2);
killparticles();
llDie();
}

}
}
Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869
12-08-2005 12:45
hmm... just started reading but this jumps out at me:

CODE
rotation getRotToPointAxisAt(vector axis, vector target)
{
return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}


it should be:

CODE
rotation getRotToPointAxisAt(vector axis, vector target)
{
return llRotBetween(axis, llVecNorm(target - llGetPos()));
}
_____________________
Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869
12-08-2005 12:53
other than that your target selection seems to gather information it doesn't need. Maybe I'm reading it wrong but it picks and targets the first valid target, yet you first pack all the sensor return info into lists? If you aren't going to compare every target but just use the first one, I'd step through the keys llDetectedKey(x) until I found one with valid characteristics, then update my sensor to detect only that key.
_____________________
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
12-08-2005 13:08
Also, in a crowd, this will potentially switch targets each time the sensor fires, because a new av could be closest. I think, anyway :)
Kage Seraph
I Dig Giant Mecha
Join date: 3 Nov 2004
Posts: 513
12-08-2005 15:02
From: Ziggy Puff
Also, in a crowd, this will potentially switch targets each time the sensor fires, because a new av could be closest. I think, anyway :)


That's not a bug, it's a feature! =D I'll try those suggestions, Rickard & post the result. Thanks!
Senuka Harbinger
A-Life, one bit at a time
Join date: 24 Oct 2005
Posts: 491
12-08-2005 17:42
From: Kage Seraph
That's not a bug, it's a feature! =D I'll try those suggestions, Rickard & post the result. Thanks!


I have a "simpler" homing missile script for my combat system, where it's simpler is that it selects a key (given by the radar selected target, but easily modified to be the closest avatar), and then uses llApplyImpulse and llLookAt to push and orientate the missile towards the target.
_____________________
My SLExchange shop

Typos are forgiven; desecrating the english language with reckless abandon and necrophilic acts is not.


The function is working perfectly fine. It's just not working the way you wanted it to work.
Kage Seraph
I Dig Giant Mecha
Join date: 3 Nov 2004
Posts: 513
12-09-2005 16:05
Mark II, incorporating some improvements and suggestions. Rickard, I couldn't get the redefinition of the "rotlookatusingpositive-x" function to work, so I stuck with the old one. This seems to work pretty well for my purposes. It doesn't damp immediately to the target, which is most effective, but somehow not viscerally satisfying enough. Rather, it tries to track a bit like a real missile. Sort of. =) Perfect for my needs-- simple friendly dogfighting.

CODE
float interval = 0.5;//the interval between scans. faster = more accurate = more serverload
vector target;
integer minDistance = 10;
integer noTargetYet = TRUE;
integer notified = FALSE;
integer powerplant = 30;
key targetKey = NULL_KEY;

//from http://secondlife.com/badgeo/wakka.php?wakka=llLookAt
vector AXIS_FWD = <1,0,0>;
float strength = 10;
float damping = 0.04;

rotation getRotToPointAxisAt(vector axis, vector target)
{
return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}


// Particle Script 0.3
// Created by Ama Omega
// 10-10-2003

// Mask Flags - set to TRUE to enable
integer glow = FALSE; // Make the particles glow
integer bounce = FALSE; // Make particles bounce on Z plan of object
integer interpColor = TRUE; // Go from start to end color
integer interpSize = TRUE; // Go from start to end size
integer wind = FALSE; // Particles effected by wind
integer followSource = FALSE; // Particles follow the source
integer followVel = TRUE; // Particles turn to velocity direction

// Choose a pattern from the following:
// PSYS_SRC_PATTERN_EXPLODE
// PSYS_SRC_PATTERN_DROP
// PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
// PSYS_SRC_PATTERN_ANGLE_CONE
// PSYS_SRC_PATTERN_ANGLE
integer pattern = PSYS_SRC_PATTERN_EXPLODE;

// Select a target for particles to go towards
// "" for no target, "owner" will follow object owner
// and "self" will target this object
// or put the key of an object for particles to go to
key targetpart = "";

// Particle paramaters
float age = 2; // Life of each particle
float maxSpeed = 4; // Max speed each particle is spit out at
float minSpeed = 3; // Min speed each particle is spit out at
string texture ="fire"; // Texture used for particles, default used if blank
float startAlpha = 1; // Start alpha (transparency) value
float endAlpha = 0; // End alpha (transparency) value
vector startColor = <1,1,1>; // Start color of particles <R,G,B>
vector endColor = <0.5,0.5,0.5>; // End color of particles <R,G,B> (if interpColor == TRUE)
vector startSize = <5,5,5>; // Start size of particles
vector endSize = <0,0,0>; // End size of particles (if interpSize == TRUE)
vector push = <0,0,2.5>; // Force pushed on particles

// System paramaters
float rate = 0.1; // How fast (rate) to emit particles
float radius = 2; // Radius to emit particles for BURST pattern
integer count = 15; // How many particles to emit per BURST
float outerAngle = 1.54; // Outer angle for all ANGLE patterns
float innerAngle = 1.55; // Inner angle for all ANGLE patterns
vector omega = <0,0,10>; // Rotation of ANGLE patterns around the source
float life = 0; // Life in seconds for the system to make particles

// Script variables
integer flags;

updateParticles()
{
flags = 0;
if (targetpart == "owner") targetpart = llGetOwner();
if (targetpart == "self") targetpart = llGetKey();
if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK;
if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK;
if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK;
if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK;
if (wind) flags = flags | PSYS_PART_WIND_MASK;
if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK;
if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK;
if (targetpart != "") flags = flags | PSYS_PART_TARGET_POS_MASK;

llParticleSystem([ PSYS_PART_MAX_AGE,age,
PSYS_PART_FLAGS,flags,
PSYS_PART_START_COLOR, startColor,
PSYS_PART_END_COLOR, endColor,
PSYS_PART_START_SCALE,startSize,
PSYS_PART_END_SCALE,endSize,
PSYS_SRC_PATTERN, pattern,
PSYS_SRC_BURST_RATE,rate,
PSYS_SRC_ACCEL, push,
PSYS_SRC_BURST_PART_COUNT,count,
PSYS_SRC_BURST_RADIUS,radius,
PSYS_SRC_BURST_SPEED_MIN,minSpeed,
PSYS_SRC_BURST_SPEED_MAX,maxSpeed,
PSYS_SRC_TARGET_KEY,targetpart,
PSYS_SRC_INNERANGLE,innerAngle,
PSYS_SRC_OUTERANGLE,outerAngle,
PSYS_SRC_OMEGA, omega,
PSYS_SRC_MAX_AGE, life,
PSYS_SRC_TEXTURE, texture,
PSYS_PART_START_ALPHA, startAlpha,
PSYS_PART_END_ALPHA, endAlpha
]);
}
killparticles()
{
llParticleSystem([]);
}


default
{
on_rez(integer start_param)
{
llSetBuoyancy(1);
llSensorRepeat("","",AGENT,96,PI/2,interval);

}

state_entry()
{
llSetBuoyancy(1);
llSensorRepeat("","",AGENT,96,PI/2,interval);
}

sensor(integer num_detected)
{
llStopLookAt();
llApplyImpulse(-1 * llGetVel(),FALSE);
integer i;
for ( i = 0; noTargetYet ; i++ )
{
if( llDetectedKey(i) == llGetOwner() )
{

}
else
{
noTargetYet = FALSE;
target = llDetectedPos(i) + llRot2Fwd(llDetectedRot(i)) * llVecMag(llDetectedVel(i) * interval);
llRotLookAt(getRotToPointAxisAt(AXIS_FWD, target), strength, damping);
llSetForce(<powerplant,0,0>,TRUE);
llOwnerSay("targeting "+llKey2Name(llDetectedKey(i)));
targetKey = llDetectedKey(i);
state targetAcquired;
}
}
}
}

state targetAcquired
{
state_entry()
{
llSensorRepeat("",targetKey,AGENT,96,PI,interval);
llOwnerSay("targeting "+llKey2Name(targetKey));
}

on_rez(integer start_param)
{
llResetScript();
}

sensor(integer num_detected)
{
integer i;
llStopLookAt();
llApplyImpulse(-1 * llGetVel(),FALSE);
//reacquire the target:
target = llDetectedPos(i) + llRot2Fwd(llDetectedRot(i)) * llVecMag(llDetectedVel(i) * interval);
//aim at it
llRotLookAt(getRotToPointAxisAt(AXIS_FWD, target), strength, damping);
//move toward it
llSetForce(<powerplant,0,0>,TRUE);

if( llAbs(llRound(llVecMag(llGetPos() - llDetectedPos(i)))) < minDistance )
{
llSay(11235814,(string)llGetOwner() + " NULL_KEY 20");
llPlaySound("rocket explode",1);
llMessageLinked(LINK_SET,0,"collision","");
llMessageLinked(LINK_SET,1,llKey2Name(llDetectedKey(i)),"");
llSetStatus(STATUS_PHYSICS, FALSE);
llOwnerSay("Boom");
updateParticles();
llSleep(2);
killparticles();
llDie();
}
}
no_sensor()
{
llOwnerSay("Lost target");
}
}
scubadiver Albert
Registered User
Join date: 8 Oct 2006
Posts: 24
10-17-2006 09:36
i put the script into a missile and it only tracked it but didn't move how come or do i need to put another script in it to make it work?
Kage Seraph
I Dig Giant Mecha
Join date: 3 Nov 2004
Posts: 513
10-18-2006 15:22
Howdy! Was the missile object physical, or no?
_____________________