## LSL Wiki : LibraryFPPMovement1

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org

#### Overview

The basic movement script is going to have one behaviour. It receives a MOVETO link message it will start the object moving at SPEED and then steer the object to within RANGE of the NEW_TARGET. When it is within range of the new position, it's going to stop the object and then send an ATPOS link message.

##### How it works
We are going to set a constant force on the pet using the llSetForce function. This force is going to constantly push the pet in the direction of its x axis.
At the same time, we are going to set up a rotational force to turn the pet in the direction of the target location. llRotLookAt will do that.
As the pet turns, the forward force is going to turn as well so the pet always moves forward. But because of inertia, it will still be actually moving in its previous direction. To counteract this slide, we will apply an impulse of force, using llApplyImpulse, in the opposite direction to the pet's motion. This will simulate friction, cancelling out all the pet's motion, except the forward motion which is being replenished by the constant force.
Getting all these forces working correctly is a balancing act, the movement script will need to be tuned before it produces the movement you like.

##### Force = Mass x Acceleration
In a lot of the movement code you will see that I always multiply the acceleration I want by the mass of the object. As in
`llSetForce(<0,0,0> * llGetMass(),TRUE);`

This is because the acceleration of an object depends on both the force used and the object's mass, as Acceleration = Force / Mass. If you push an object that has a mass of 1kg with a force of 1N, it will accelerate at 1 meter per second2. If you double the mass and keep the force the same, the acceleration will be only 0.5 m/s2.
If you want to bring a moving physical object to a complete stop, hit it with an impulse in the opposite direction to which it's moving. To get the exact impulse needed, multiply the object's mass by its velocity.
```llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
//Note the FALSE at the end.  llGetVel() returns the global velocity, not the local velocity.
//This means the friction impulse must be in global terms.```

Of course if the object still has a force being applied to it, it will start moving again. And don't forget that the wind can push your physical object around.
One very useful side effect of multiplying all accelerations by the mass is that the action of the code becomes independent of the mass of the object. So if your script is set to give an acceleration of 2 meters per second2 and you put in a new model that has twice the mass, it should still accelerate at the same rate because the force being applied will be double what it was before.

#### States and Events

The basic movement script only needs one state

#### User Defined Functions

##### getrottopointaxist()
This script is going to use llRotLookAt() to turn the pet. But llRotLookAt tries to point the z axis at the target. We want the x axis to be the pointer. Luckly, someone on the Wiki has already solved this problem. The example function here povides a solution.

##### all_stop
There are going to be times when we want to bring a physical object to a complete stop, without turning off physics itself.
```all_stop()
{
//Turn off linear and rotational forces
llSetForceAndTorque(<0,0,0>,<0,0,0>,TRUE);
//Stop any movement by applying an impulse of force in the opposite direction
llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
// Do the same for any rotational velocity
llApplyRotationalImpulse(-llGetTorque() * llGetMass(),FALSE);
}```

#### Events

##### state_entry
Set all the physical parameters requried, including setting the object to physical. The state_entry event also starts the timer event. This should be relatively slow. I find 0.5 a good interval, although I have used slower. If you think you need this to be faster, consider increasing your friction instead, it will have the same effect.
```state_entry()
{
llSetTimerEvent(0.5);
//Every half a second, call the timer event to apply some friction.
llSetBuoyancy(1);
//Setting the buoyancy to 1 cancels out the gravity.  The object will now float.
llSetStatus(STATUS_ROTATE_X,FALSE);
llSetStatus(STATUS_ROTATE_Y,FALSE);
//The two lines above stop the object from rotating in anything but the z axis.
//It can spin but cannot pitch or roll.  This keeps our pet upright
llSetStatus(STATUS_PHANTOM,TRUE);
//Make the object phantom.  Dynamic objects are processing heavy, especially if they are colliding with a lot of other objects.
//After the brain has been improved and some object avoidance added, this line can be removed.
//But if your intending to have several pets flying around at the same time, it might be best to leave them as phantom

llSetStatus(STATUS_PHYSICS,TRUE);
//Finally, set the Physics flag on, so the object is now subject to forces, inertia and all the other dynamic features.
}```

When a link message is received, the link message event needs to check that it is a MOVETO message. If it is:
Remove any existing Targets that have been set up.
Set up a new Target.
Set up the forward force
Set up the rotational force to turn the pet
```link_message(integer sender,integer num, string data,key id)
{
if(num == company_channel)
{
list data = llParseStringKeepNulls( str, ["-=-"], [] );
string Cmd = llList2String(data,0);
if( Cmd == "MOVETO" )
{
//Extract the new data from the link message's string
new_target = (vector)llList2String(data,1);
float speed = (float)llList2String(data,2);
float range = (float)llList2String(data,3);
llTargetRemove(target_id);
//Remove any old targets
target_id = llTarget(new_target,range);
//Set up a new target at location new_target and range range.
llSetForce(<speed,0,0> * llGetMass(),TRUE);
//Set up a force equal to the the object's mass multiplied by the variable speed.
//Set the force so it always pushes in the direction of the x axis
llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
//Set up a rotational force towards new_target.
}
}```

llRotLookAt is a pain to use. First you have to use it with getrottopointaxisat to make it useful. Second, the two parameters rot_str and rot_dmp are very tricky to use. ADD WORKING VALUES HERE

##### not_at_target
Turn and adjust z position . z force set up as gobal variable
```not_at_target()
{
llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
vector currentpos = llGetPos();
if(currentpos.z > new_target.z)
{
//little push downwards
llApplyImpulse(<0,0,-z_force> * llGetMass(),FALSE);
}
else
{
//little upwards
llApplyImpulse(<0,0,z_force> * llGetMass(),FALSE);
}
}```

#### at_target

When the pet has reached the target location, the movement script needs to stop the pet, and then send a link message telling the Brain script that it has arrived.
```at_target()
{
all_stop();
}```

##### timer
Add Friction to counteract inertia. The timer goes off relatively slowly. The float friction is the fraction of the objects velocity that is going to be removed. So make sure it's less than 1. This is a type of negative feedback loop, we are feeding back a negative fraction of the old velocity to create a new velocity.
```timer()
{
float friction = 0.6;
//friction will be a global variable in the complete script.
//Apply some friction
llApplyImpulse(-llGetVel() * llGetMass() * friction,FALSE);
}```

##### on_rez
Handle the rez situation
```on_rez(integer param)
{
llTargetRemove(target_id);
all_stop():
}```

#### Putting it all together

```//Global Constants.  Change these values to tune your movement script.
//These values are set up for a 5kg object.
float heartbeat = 0.5;
//How often the friction impulse should be applied

float rot_str = 2;

float rot_dmp = 0.5;

float friction = 0.6;
//  What fraction of the object's current velocity should be removed each heartbeat

float z_force = 0.2; //check this one
//The amount of force  used to push the pet up or down.
integer company_channel;

//Global Variables:
float speed;
float range;
vector new_target;
integer target_id;

//User defined fucntions
all_stop()
{
//Turn off linear and rotational forces
llSetForceAndTorque(<0,0,0>,<0,0,0>,TRUE);
//Stop any movement by applying an impulse of force in the opposite direction
llApplyImpulse(-llGetVel() * llGetMass(),FALSE);
// Do the same for any rotational velocity
llApplyRotationalImpulse(-llGetTorque() * llGetMass(),FALSE);
}
//Code from Wiki
// AXIS_* constants, represent the unit vector 1 unit on the specified axis.
vector AXIS_UP = <0,0,1>;
vector AXIS_LEFT = <0,1,0>;
vector AXIS_FWD = <1,0,0>;

// getRotToPointAxisAt()
// Gets the rotation to point the specified axis at the specified position.
// @param axis The axis to point. Easiest to just use an AXIS_* constant.
// @param target The target, in region-local coordinates, to point the axis at.
// @return The rotation necessary to point axis at target.
// Created by Ope Rand, modifyed by Christopher Omega
rotation getRotToPointAxisAt(vector axis, vector target) {
return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos());
}
//end of code from Wiki

default
{
state_entry()
{
llSetTimerEvent(heartbeat);
//Every half a second, call the timer event to apply some friction.
llSetBuoyancy(1);
//Setting the buoyancy to 1 cancels out the gravity.  The object will now float.
llSetStatus(STATUS_ROTATE_X,FALSE);
llSetStatus(STATUS_ROTATE_Y,FALSE);
//The two lines above stop the object from rotating in anything but the z axis.
llSetStatus(STATUS_PHANTOM,TRUE);
//Make the object phantom.
llSetStatus(STATUS_PHYSICS,TRUE);
//Finally, set the Physics flag on, so the object is now subject to forces, inertia and all the other dynamic features.
}
link_message(integer sender,integer num, string str,key id)
{
if(num == company_channel)
{
list data = llParseStringKeepNulls( str, ["-=-"], [] );
string Cmd = llList2String(data,0);
if( Cmd == "MOVETO" )
{
//Extract the new data from the link message's string
new_target = (vector)llList2String(data,1);
speed = (float)llList2String(data,2);
range = (float)llList2String(data,3);
llTargetRemove(target_id);
//Remove any old targets
target_id = llTarget(new_target,range);
//Set up a new target at location new_target and range range.
llSetForce(<speed,0,0> * llGetMass(),TRUE);
//Set up a force equal to the the object's mass multiplied by the variable speed.
//Set the force so it always pushes in the direction of the x axis
llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
//Set up a rotational force towards new_target.
}
}
}
not_at_target()
{
llRotLookAt(getRotToPointAxisAt(AXIS_FWD,new_target), rot_str, rot_dmp);
vector currentpos = llGetPos();
if(currentpos.z > new_target.z)
{
//little push downwards
llApplyImpulse(<0,0,-z_force> * llGetMass(),FALSE);
}
else
{
//little upwards
llApplyImpulse(<0,0,z_force> * llGetMass(),FALSE);
}
}
at_target(integer tnum, vector target_pos, vector our_location)
{
all_stop();
}
timer()
{
//Apply some friction
llApplyImpulse(-llGetVel() * llGetMass() * friction,FALSE);
}
on_rez(integer param)
{
llTargetRemove(target_id);
all_stop();
}
}```