Philip P. Ide

Author, programmer, science enthusiast, half-wit.
Life is sweet. Have you tasted it lately?

User Tools

Site Tools


blog:opensim:scripts:moveonaline

Movement Along a Line

I had an issue where I wanted an NPC to move toward an object, but not actually trample all over or bump into it. This is particularly important if the NPC is moving toward an agent (an avatar with a hooman behind it). I came up with several solutions which are detailed below.

For almost all of these solutions, you need to know:

  1. The starting vector
  2. The target vector (of the object you want to move toward)
  3. The distance between the two
  4. The distance you want to stop short of the target
vector start = llGetPos();
vector target = llList2Vector( llGetObjectDetails( uuid_of_target_object, [OBJECT_POS] ), 0 );
vector direction = target - start;
float distance = llVecDist( start, target );
float short = 1.0; // in meters
float target_distance = distance - short;

Sphere and Ray Intersection

For this to work, you need to know where the beginning of the line is (the vector where the NPC/Object you want to move is), where the endpoint is (the vector the target object is) and how far short of the target you want to move.

The idea is you imagine a sphere about the start vector, with a radius equal to target_distance. Then you cast a ray from the start vector toward the target vector, then determine where the intersection of the ray and the sphere is.

The SecondLife wiki has some geometric functions in the Geometric Library, and for this we'll use the function gSRxX().

vector gSRxX( start, target_distance, start, direction );

Notes: This code is complex and the other methods are simpler.

Polling Position

This works by sending the object/NPC toward the target and then polling the current position.

// assumes this script is in an NPC attachment, hence use of llGetOwner() to identify NPC
 
vector target;
float short;
 
default {
    state_entry {
        list data = llGetObjectDetails( uuid_of_target_object, [OBJECT_POS] );
        target = llList2Vector( data, 0 );
        short = 1.0; // in meters
        osNpcMoveToTarget( llGetOwner(), target, OS_NPC_NO_FLY );
        llSetTimerEvent(0.1);
    }
    timer() {
        vector p = llGetPos();
        if( llVecDist( p, target ) <= short ){
            llSetTimerEvent(0.0);
            osNpcStopMoveToTarget( llGetOwner() );
            llSetTimerEvent(0.0);
        }
    }
}

Notes: This is fairly simple code, but inefficient because of the polling. Also, the NPC could stop anywhere between the short distance and the target, and may even overshoot, depending on simulator FPS and script-thread time-dilation.

Starting Position Plus Direction

This is my favourite solution.

integer targetHandle;
 
// llVecNorm( vector direction ) is mathematically equivalent to:
// vector norm = vec / llSqrt( vec.x * vec.x + vec.y * vec.y + vec.z * vec.z );
//
vector endVector( vector start, vector dir, float distance ){
    float z = start.z;
    vector udir = llVecNorm( dir ); // turn direction into a unit vector
    vector displace = udir * distance; // scale unit vector
    vector end_vec = start + displace;
    end_vec.z = z; // restore elevation, we only want to move in x/y plane
    return end_vec;
}
 
default {
    state_entry() {
        vector start = llGetPos();
        vector target = llList2Vector( llGetObjectDetails( uuid_of_target_object, [OBJECT_POS] ), 0 );
        float short = 1.0; // in meters
        vector direction = target - start;
        float distance = llVecDist( start, target ) - short;
 
        vector newTarget = endVector( start, direction, distance );
        targetHandle = llTarget( newTarget, 0.2 );
        osNpcMoveToTarget( llGetOwner(), newTarget, OS_NPC_NO_FLY );
    }
    at_target( integer tnum, vector targetpos, vector curpos){
        if( tnum == targetHandle ){
            llTargetRemove( targetHandle );
            osNpcStopMoveToTarget( llGetOwner() );
        }
    }
}

Notes: This is very efficient. It calculates the location once, then waits for the at_target() event to occur. Note there isn't a not_at_target() event. If there were, this would reduce efficiency by continuously triggering not_at_target() events until the target was reached.

Note too, the endVector() only returns a difference in the x/y planes by restoring the start.z value to the end_vec result. If you want your NPC to change elevation, you'll want to change that (and probably change OS_NPC_NO_FLY to OS_NPC_FLY|OS_NPC_LAND_AT_TARGET.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
blog/opensim/scripts/moveonaline.txt · Last modified: by Phil Ide

Except where otherwise noted, content on this wiki is licensed under the following license: Copyright © Phil Ide
Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki