Jump to content

Question about pathfinding


Laurent Bechir
 Share

You are about to reply to a thread that has been inactive for 3649 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

  I've been doing things like "pathfinding" with things like fish for YEARS, since long before LL added code to do pathfinding in the server. The scrit below impliments the bare bones of swimming motion in a single prim. It moves around in the water, curves smoothly away from different boundaries. You can add a back and forth motion, add llSensor to avoid or attack avatars, put in more tests, change the distance that it looks for certain borders, change the acceleration for different obsticals, etc. Even changing the order of the tests results in different behavior.

 

    // Swimmer    //      A script that gets pretty smooth motion and    //    moves a critter around sort of like "pathfinding" in in water.    //        This version is an example of how to do that using the    //            llSetKeyframedMotion function.    //    //        Put this script in a prim that is long in the X direction, it will swim in the    //            direction of it's local X axis. Paint it with a texture that allows you to    //            tell which way is the top (the local Y axis).    //    //      This simple critter behaves in the following ways:    //  It moves in a straight line until:    //  moves out of the water but falls back down    //  avoids getting close to the bottom    //  turns away from the SIM edges    //  turns away from parcel boundary edges.    //    turns away from the waters edge (llGround>llWater)
//
// I give this code away, use it in your products for sale. float DEFLECT=0.20; //how hard things deflect me away (m/s/s) float LOOKAHEAD=2.0; //how far to look ahead to avoid stuf integer run=1; //flag indicating if I'm running or not list params=[ //make the smoke PSYS_PART_FLAGS,0 , PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_DROP , PSYS_SRC_BURST_RATE,0.125, PSYS_SRC_BURST_PART_COUNT ,1, PSYS_PART_MAX_AGE,30.0, PSYS_PART_START_SCALE, <.75,.75,0>, //magenta smoke PSYS_PART_START_COLOR, <1,0,1> ]; // This function, OKfly, checks for all the places that you don't want your prim to move. // Across sim boundaries, into the ground, onto dry land. // I use parcels to create boundaries where the critter has to stay, so I test // for parcel boundaries here also. // You could test for other things, like a fish can't leave the water or a bird // can't enter water, but fish jump and birds dive so I do that elsewhere. // Nothing prevents this critter from passing through solid objects, even if it is not // phantom. But if it is not phantom it will stop when colliding with an avatar. integer OKfly(vector pos) //return true if OK to move to pos { //don't go outside the edges of the region if (pos.x<=0.0 || pos.x>=256.0 || pos.y<=0.0 || pos.y>=256.0) return FALSE; vector vel=pos-llGetPos(); float gnd = llGround(vel); if (pos.z<gnd) //don't go below ground return FALSE; if (gnd>llWater(vel)) //don't go where the ground is above the water return FALSE; //don't leave the current parcel key curpar = llList2Key(llGetParcelDetails(llGetPos(),[PARCEL_DETAILS_ID]),0); key nxtpar = llList2Key(llGetParcelDetails(pos, [PARCEL_DETAILS_ID]),0); if (curpar!=nxtpar) return FALSE; //insert other rules here, like fish not allowed out of the water return TRUE; //otherwise, it is OK to fly here! } //Single Frame motion routines // These 3 functions impliment smooth motion using llSetKeyFramedMotion // The way it works is this: // You call SFrame with a new position, rotation and time // I usually call SFrame once a second from the timer event with an updated //position and rotation. //But it works over longer times and distances. //UNLIKE llSetKeyframedMotion, here you specify the region co-ordinates and rotation! //I did it in these tree functions to make it modular, I can replace all 3 of them //with routines that work on grids that don't have llSetKeyframedMotion SFsetup() //any necessary setup { //This setup is necessary in SL, and may cause errors on Open Sim, if so delete it llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]); } //move the calling root prim from its current position and rotation //to the requested position and rotation in the requested time. //UNLIKE llSetKeyframedMotion, you specify the region co-ordinates and rotation! SFrame(vector pos,rotation rot, float seconds) { llSetKeyframedMotion([pos-llGetPos(),rot/llGetRot(),seconds], [KFM_MODE,KFM_FORWARD,KFM_DATA,KFM_TRANSLATION|KFM_ROTATION]); } SFstop() //stop the key framed motion { llSetKeyframedMotion([],[KFM_COMMAND,KFM_CMD_STOP]); } SFnotat() //call this from your not_at_target event { //not necessary with llSetKeyframedMotion, so this function is a NOP } default { state_entry() { llOwnerSay("reset"); llParticleSystem(params); //Start the smoke llSetTimerEvent(1.0); //recalculte movement every second SFsetup(); } on_rez(integer param) { //turn to a random direction on rez llSetRot(llEuler2Rot(<0,llFrand(PI-PI/2.0),llFrand(TWO_PI)>)); } timer() //every second, calculate a new direction to turn and move { vector pos=llGetPos(); //get my current position rotation rot=llGetRot(); //and rotation vector vel=<1,0,0>*rot; //use my direction as velocity //here are the RULES that give this critter behavior: //first (four) rule(s): Avoid the edges of things. vector xvel=llVecNorm(<vel.x,0,0>); //get orthagonal components of velocity vector yvel=llVecNorm(<0,vel.y,0>); if (!OKfly(pos+LOOKAHEAD*xvel)) //so you can pong off the edges of the sim vel -= DEFLECT*xvel; //slow down as you approach X edge. if (!OKfly(pos-LOOKAHEAD*xvel)) //checking both sides makes me vel += DEFLECT*xvel; //accelerate away from walls if (!OKfly(pos+LOOKAHEAD*yvel)) //do the same thing in Y vel -= DEFLECT*yvel; if (!OKfly(pos-LOOKAHEAD*yvel)) vel += DEFLECT*yvel; //I could use LOOKAHEAD to avoid running out of the water (I do that for the //ground below) but I thought it would be fun to allow this swimmer to jump //out of the water and fall back in float wat=llWater(ZERO_VECTOR); if (pos.z>wat) //after I have already popped out of the water, vel -= <0,0,1>*DEFLECT; //accelerate back down //When the critter gets within LOOKAHEAD meters of the ground, I start //accelerating back up. Using the ground normal makes it turn sideways //away from cliffs instead of always turning straight up. vector npos=pos+vel; //next position if ((npos.z-LOOKAHEAD)<llGround(vel)) //if my next position is too close to the ground vel += llGroundNormal(vel)*DEFLECT; //deflect away from the ground normal //I'm limiting this critter to 1 meter per second, you could go faster //but beware, llAxes2Rot requires unit vectors! You would have to //calculate a separate vector that is the normalized velocity and use that below. vel = llVecNorm(vel); //limit my velocity to 1m/sec //here I convert the velocity vector into a rotation. These steps result in the //prim always rotating to keep the head "up". Actually the local Y axis is always //parallell to the XY plane, the local Z axis is allowed to rotate away from //straight up to turn the nose to rise or fall. vector lft=llVecNorm(<0,0,1>%vel); rot = llAxes2Rot(vel,lft,vel%lft); //calculate new rotation in direction of vel //SFrame will refuse to go somplace that OKfly does not like, but I do //another test here and try to do something random to avoid getting stuck. if (!OKfly(pos+vel)) //final test: If I'm still going out of bounds, { if (llVecMag(lft)<0.5) //detect Gymbol lock! lft=<0,1,0>; //and make a hard turn in this unusual case. vel = llVecNorm(vel+lft*(llFrand(2.0)-1.0)); //randomly turn left or right lft=llVecNorm(<0,0,1>%vel); //to try to get out of edge lock rot = llAxes2Rot(vel,lft,vel%lft); //re-calc the rotation vel=ZERO_VECTOR; //stop and wait for rotation to turn me } SFrame(pos+vel,rot,1.0); //start moving and turning } //once SFrame is running, even the build dialog cannot change the position //or rotation without it being changed back. So I added this touch to stop the //critter so I could move it or rotate it, then touch start it moving again. touch_start(integer num) { if ((run = run^1) ==0) //toggle the run flag { llSetTimerEvent(0); SFstop(); //demonstrate how to use SFstop! llParticleSystem([]); //turn off the smoke while stopped. llOwnerSay("stopped"); } else { llSetTimerEvent(1.0); //the next call to SFrame in timer will start it up again llParticleSystem(params); llOwnerSay("running"); } } }

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 3649 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...