Hopefully it is painfully clear though on what I think and why I think it.
This is posted for 2 reasons, People who know about this already to point out my errors and maybe give me some helpful tips/pointers about this and other functions.
so any one who doesn't want to bother with it can end up going to the end of this thread for a copy/paste solution to standardized, worry free pushing that always applies the same force, no mater what.
Seriously I am sick to death of burning hours to find out information that might or might not be correct, since I tend to screw up occasionally lol. When the information I am looking for has no excuse for not being explicitly stated in the function documentation.
Alright, This is my attempt to understand how the push function works.
Trying to identify, and compensate for, all the factors affecting a push call in a script.
Energy - the best compensator for energy is to make sure you have 1.0 before you push
(I will experiment with how low energy attenuates pushes after I have high quality compensators for the other factors)
Distance - Commonly compensated for using the function ToPush *= llPow(Dist,3);
That function works correctly for all tested Dist greater than about 0.4 to 0.5
I have not yet experimented much with different sizes of prims and how that would affect the distance compensator functions lower limits, Just stayed with distances 0.5 and up.
TargetMass Commonly compensated for using the function ToPush *= TargetMass;
That function works properly for all tested masses.
MyMass( Mass of the Object containing the push script) I did not find anything about compensating for my own mass, first guess was that my mass acted like a multiplier, so the first compensator looked like, ToPush /= MyMass; This worked for all masses tested.
A problem later appeared, It turns out that, like the distance function, the MyMass compensator has an inner distance limit, below which it fails. Unlike the distance compensator, the inner distance is not small, or even stable..
That is the InnerDistance limit varies. Shortly it became clear that the InnerDistance was linked to the Mass of the object being compensated for....
After a few tests it turned out that you could express the InnerDistance as a function of the mass of the pusher prim (this is all approximation since its not really possible to take decent measurements in SL of physical objects in motion.)
(MyMass/10)^(1/3) the Cubic root of one tenth the mass of the pusher was the InnerDistance.
At least now I could tell when the MyMass compensator would fail, since I could tell when something was to close.
Observing the effect of pusher prim mass inside the InnerDistance, The effect of mass falls off inside the InnerDistance, the farther inside you are the less effect PusherMass has.
Using a large prim to get a large InnerRadius for testing, it was easy to see that the fall off was far faster than linear, llPow((Dist/InnerRange),3 is my approximation of the falloff.
Dist/InnerRange expresses how far inside the InnerDistance the pushee is, and the falloff is cubic.
Relavant Code:
CODE
StandardPush(key Tgt,vector ToPush, float Mass,vector TargetPos)
{
//Compensate for Target Mass;
if(DEBUG)llOwnerSay("Target Mass: " + (string)Mass);
ToPush *= Mass;
//compensate for Distance
float Dist = llVecDist(llGetPos(),TargetPos);
//Distance Compensator breaks down with distances lower than 0.5
if(DEBUG)llOwnerSay("Target Distance: " + (string) Dist);
ToPush *= llPow(Dist,3);
//Compensate for My Mass - Breaks down when objects get near each other
//to near seems like (MyMass/10)^(1/3)
float MyMass = llGetMass();
if(DEBUG)llOwnerSay("My Mass: " + (string)MyMass);
float InnerRange = llPow(MyMass/10,1.0/3.0);
if(DEBUG)llOwnerSay("Inner Range Estimate: " + (string)InnerRange);
if(Dist >= InnerRange)ToPush /= MyMass;
//Atempting to Compensate for Dist < InnerRange.
//The Effect of the Mass of the pusher, is constant at Dist > InnerRange, When Dist < InnerRange
//The Effect of mass falls off very quickly, the further inside the InnerRange you get.
// Dist/InnerRange gives you how far inside the InnerRage you are as a float from 0-1
//the falloff was way faster than linear cubes semed popula
//that guestimate seems fairly acurate so far
else ToPush /= (MyMass*llPow((Dist/InnerRange),3));
float Magx = llFabs(ToPush.x);
float Magy = llFabs(ToPush.y);
float Magz = llFabs(ToPush.z);
if( Magx > MAX_MAGNITUDE || Magy > MAX_MAGNITUDE || Magz > MAX_MAGNITUDE)
llOwnerSay("Warning: Magnitude Overrun");
//Do Push
if(DEBUG)llOwnerSay("Pushing Vector: "+(string)ToPush + " With Energy Level: "+(string)llGetEnergy());
llPushObject(Target,ToPush,ZERO_VECTOR,FALSE);
}
FullScript:
CODE
//Notes
//A Prim's Energy is proportional to its mass,
//a 1X1X0.1 prim == 1KG. A 10,10,10 == 10,000KG and takes 10,000 times as long to reach full energy.
//It is important to note and remember, that Energy does NOT provide ANY power to llPushObject
//MASS provides the power, Energy is used as a sort of meter for the maximum amount of force
//any one prim is capable of producing over time.
//If you use 2 prims, one 1KG the other 10,000KG to apply the exact same force to a third object
//unless the push is trivial BOTH prims will have their energy reduced to 0.
//it does take a larger push to reduce the 10,00KG prims energy to 0, but the push required to do so is
//not very large.
//anyway... after applying the push, the small prim is ready again almost instantly, because its small
//energy pool refilled very fast, while the large prim takes 10,000 times as long to regen.
//the moral of the story is, use the smallest possible prim to achieve the needed Push,
//Because the smaller the prim you use, the faster it regens to full energy.
//Or, Larger Prims are capable of larger pushes(due to MASS), but can not be fired as often(due to ENERGY)
//Aprox 50 sec for 10X10X10 prim to go from 0 to 1 energy.
//Constants
integer DEBUG = TRUE;
integer MAX_MAGNITUDE = 2147483647; //Maximum Pos number for a Signed Int
//Variables
key Target = NULL_KEY;
//Functions
StandardPush(key Tgt,vector ToPush, float Mass,vector TargetPos)
{
//Compensate for Target Mass;
if(DEBUG)llOwnerSay("Target Mass: " + (string)Mass);
ToPush *= Mass;
//compensate for Distance
float Dist = llVecDist(llGetPos(),TargetPos);
//Distance Compensator breaks down with distances lower than 0.5
if(DEBUG)llOwnerSay("Target Distance: " + (string) Dist);
ToPush *= llPow(Dist,3);
//Compensate for My Mass - Breaks down when objects get near each other
//to near seems like (MyMass/10)^(1/3)
float MyMass = llGetMass();
if(DEBUG)llOwnerSay("My Mass: " + (string)MyMass);
float InnerRange = llPow(MyMass/10,1.0/3.0);
if(DEBUG)llOwnerSay("Inner Range Estimate: " + (string)InnerRange);
if(Dist >= InnerRange)ToPush /= MyMass;
//Atempting to Compensate for Dist < InnerRange.
//The Effect of the Mass of the pusher, is constant at Dist > InnerRange, When Dist < InnerRange
//The Effect of mass falls off very quickly, the further inside the InnerRange you get.
// Dist/InnerRange gives you how far inside the InnerRage you are as a float from 0-1
//the falloff was way faster than linear cubes semed popula
//that guestimate seems fairly acurate so far
else ToPush /= (MyMass*llPow((Dist/InnerRange),3));
float Magx = llFabs(ToPush.x);
float Magy = llFabs(ToPush.y);
float Magz = llFabs(ToPush.z);
if( Magx > MAX_MAGNITUDE || Magy > MAX_MAGNITUDE || Magz > MAX_MAGNITUDE)
llOwnerSay("Warning: Magnitude Overrun");
//Do Push
if(DEBUG)llOwnerSay("Pushing Vector: "+(string)ToPush + " With Energy Level: "+(string)llGetEnergy());
llPushObject(Target,ToPush,ZERO_VECTOR,FALSE);
}
//States
default
{
on_rez(integer param)
{
llResetScript();
}
state_entry()
{
//if(DEBUG)llOwnerSay((string)llGetMass());
}
touch_start(integer num)
{
if(llDetectedKey(0)!= llGetOwner()) return;
if (Target == NULL_KEY)
{
llRezObject("TargetBox",llGetPos() + <10,0,0>,ZERO_VECTOR,ZERO_ROTATION,0);
return;
}
if(DEBUG)llOwnerSay((string)Target);
llSensor("",Target,SCRIPTED,96.0,TWO_PI);
}
object_rez(key id)
{
if(DEBUG)llOwnerSay("Setting Target: " + (string)id);
Target = id;
}
sensor(integer Num)
{
vector ToPush = <0,0,50>;
StandardPush(Target,ToPush,llGetObjectMass(llDetectedKey(0)),llDetectedPos(0));
}
no_sensor()
{
if(DEBUG)llOwnerSay("No Target Found: Clearing Key");
Target = NULL_KEY;
}
}