The weird outstanding behavior : my understanding reading the docs is that one needs to set the buoyancy to 1 in order to get gravity-neutral behavior, but I find for this particular vehicle that if I do that, as soon as I "ride" (i.e. turn on the vehicle and physics) the vehicle, it starts to drift upwards. If I set buoyancy to 0.9, it works the way I want:
CODE
llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0);
llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY , 0);
llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1000);
llSetVehicleFloatParam(VEHICLE_BUOYANCY , 0.9);
What's going on here? Why 0.9 to get gravity-neutral behavior?
The other problem I have is with vertical motion. I want the thing to work so that it moves forward and back when I push the forward and back keys, and it moves up and down when I push the up and down keys. (I know, weird choices, but there you go.)
Forward and back seems to mostly be working... although I get a little bit of up motion when I move forward, but not much.
However, whenever I move up or down, the vehicle always picks up forward (never backward) motion, and it's in fact a bit faster than the up or down motion. Now, I've used llSay() to tell me what it's setting the motor to -- and, indeed, it is setting just the z-axis of the motor when I try to move up or down without any forward motion. Yet, the vehicle picks up forward velocity.
The root prim of the vehicle is oriented along the cardinal directions -- z is up, x is forward -- so it's not a frame of reference thing. (I used to use a different root prim, and moved to this one in case my setting of the frame of refrerence was done wrong, but I'm still seeing the problem.) Also, forward really does move almost entirely forward, so it's not a simple axis rotation.
Full code from my script follows. It's GPLed, so feel free to use it under that license. I can supply a copy of the license if anybody needs it. Since I'm now showing this to other people, I'll have to bump the version number in my own copy so that there's no confusion when I later distribute a modified version

(NOTE : the code below has spaces inside lines that are *not* in what I pasted, and are not in what's in the edit box here. I don't understand; frikkin' web forums drive me nuts. Why can't we all just use our favorite nntp readers and have forums served up by nntp? Why must every forum provide a client, forcing us to take the client instead of just the protocol? Sign.)
CODE
// Space Capsule Vehicle Script v1.0 by Prospero Frobozz
// spacecapsule.lsl is Copyright (C) 2007 Prospero Frobozz
//
// This script is free software, available under the GNU GPL version 2.
// You are free to use it, modify it, or distribute it. If you distribute
// this script or any modified versions, they must also be under the GNU GPL.
// This means that the script itself must be mod/copy/transfer. Objects that
// use the script, or other components of such objects, do not themselves need
// to be mod/copy/transfer, but the script, or any other scripts derived from
// it, must. In addition, the full text of the GNU GPL (in Notecard "GNU GPL
// v2") must be included whenever this script or any script derived from this
// script is distributed.
// Configure the orientation of the vehicle and the sit animation
// vector axiseuler=<-4.71238898038469 , -0.610865238198015 , 0>;
vector axiseuler=<0,0,0>;
rotation axisrot;
string sitanim="pilot";
vector sitpos=<-0.216, 0, -0.390>;
rotation sitrot=<0,0,0,0>;
string vehiclename="Space Capsule";
// Configure the motor performance
float motormaxfwd=20;
float dfwd=2;
float motormaxbkwd=0;
float dbkwd=2;
float motormaxvert=20;
float dvert=2;
// rotmax is for easy mode, drot is for hard mode
float rotmax=0.785398163397448;
float drot=1.5707963267949;
float horizmotor;
float vertmotor;
// Internal variables
integer debug=0;
key pilot;
default
{
state_entry()
{
llSetText("",<0,0,0>,0);
llSitTarget(sitpos,sitrot);
llSetSitText("Ride");
llSetVehicleType(VEHICLE_TYPE_NONE);
llSetStatus(STATUS_PHYSICS , FALSE);
llReleaseControls();
axisrot=llEuler2Rot(axiseuler);
if (sitanim=="") {
sitanim=llGetInventoryName(INVENTORY_ANIMATION,0);
if (sitanim=="") {
sitanim="sit";
}
}
}
changed(integer change) {
if ((change & CHANGED_LINK) && (llAvatarOnSitTarget()!=NULL_KEY)) {
// somebody just sat on us!
if (llAvatarOnSitTarget()!=llGetOwner()) {
llSay(0, "Only the owner may pilot me.");
} else {
llRequestPermissions(llAvatarOnSitTarget(),PERMISSION_TRIGGER_ANIMATION);
}
}
}
run_time_permissions(integer perm) {
if (perm==PERMISSION_TRIGGER_ANIMATION) {
llStopAnimation("sit");
llStartAnimation(sitanim);
state easycapsule;
}
}
touch_start(integer num) {
key toucher=llDetectedKey(0);
llGiveInventory(toucher,"Space Capsule Instructions");
}
}
// *************************************************************
// *************************************************************
// *************************************************************
state easycapsule {
state_entry() {
pilot=llAvatarOnSitTarget();
// Flags -- start with BALLOON, although that shoudln't matter.
// No other flags needed (but decouple the camera, just for yuks.)
llSetVehicleType(VEHICLE_TYPE_BALLOON);
llSetVehicleFlags(VEHICLE_FLAG_CAMERA_DECOUPLED );
llRemoveVehicleFlags(VEHICLE_FLAG_NO_DEFLECTION_UP | VEHICLE_FLAG_LIMIT_ROLL_ONLY | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT | VEHICLE_FLAG_HOVER_TERRAIN_ONLY | VEHICLE_FLAG_HOVER_WATER_ONLY | VEHICLE_FLAG_MOUSELOOK_STEER | VEHICLE_FLAG_MOUSELOOK_BANK );
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME , axisrot);
// Deflection -- fast vertical attraction. No angular deflection,
// high linear deflection, so it moves in the way we're facing.
// Banking is irrelevant.
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 2.0);
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.0);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.2);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1.0);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY , 1);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE , 1);
llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, 0);
llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 0);
llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 0.1);
// Linear Motor; start at rest. Motor should be extremely responsive,
// so set a fast timescale, and slow decay and friction timescales.
horizmotor=0;
vertmotor=0;
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION , <0,0,0>);
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE , 0.2);
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 1000);
llSetVehicleFloatParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, 1000);
// Angular Motor : want it to work more like a steering wheel.
// Make the motor start fast and decay fast, and have relatively
// short friction timescales.
llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE , <2,2,2>);
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE , 0.1);
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE , 0.5);
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION , <0,0,0>);
// For hovering, I *think* I just want to disable it, and let
// the linear motor control the vertical speed. Set buoyancy
// to 1 in an attempt to disable gravity.
// Huh -- for reasons I don't undestand, I have to set buoyancy
// to 0.9 to prevent the vehicle from drifting upward
// unpleasantly when it's supposed to be at rest.
llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0);
llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY , 0);
llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1000);
llSetVehicleFloatParam(VEHICLE_BUOYANCY , 0.9);
// We want a relatively short min event delay so that the thing
// will be responsive; I may have to play with this
llMinEventDelay(0.2);
// Turn on the vehicle. Set up a listener to change modes.
llReleaseControls();
llRequestPermissions(pilot, PERMISSION_TAKE_CONTROLS);
llSetStatus(STATUS_PHYSICS , TRUE);
llListen(5,"",pilot,"");
llSay(0,vehiclename+" in Easy Mode; say '/5 hard' for Hard Mode, '/5 stop' to stop.");
llSetText("",<0,0,0>,0);
if (debug) {
llSetTimerEvent(1.0);
} else {
llSetTimerEvent(0);
}
}
run_time_permissions(integer perm) {
if (perm) {
llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN , TRUE , FALSE);
}
}
control(key id, integer held, integer change) {
// rotation rot;
// vector vel;
// rot=llGetRot();
// vel=llGetVel();
// vel/=rot;
// vel/=axisrot;
// horizmotor=vel.x;
// vertmotor=vel.z;
if ((held & CONTROL_DOWN) || (held & CONTROL_UP) || (held & CONTROL_FWD) || (held & CONTROL_BACK)) {
if (held & CONTROL_DOWN) {
if (vertmotor>0) {
vertmotor=0;
} else {
vertmotor=vertmotor-dvert;
}
}
if (held & CONTROL_UP) {
if (vertmotor<0) {
vertmotor=0;
} else {
vertmotor=vertmotor+dvert;
}
}
if (held & CONTROL_FWD) {
horizmotor=horizmotor+dfwd;
}
if (held & CONTROL_BACK) {
horizmotor=horizmotor-dbkwd;
}
if (horizmotor > motormaxfwd) { horizmotor=motormaxfwd; }
if (horizmotor < motormaxbkwd) { horizmotor=motormaxbkwd; }
if (vertmotor > motormaxvert) { vertmotor=motormaxvert;}
if (vertmotor < -motormaxvert) { vertmotor= -motormaxvert;}
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION , <horizmotor,0,vertmotor>);
// llSay(0,"Setting linear motor to "+(string)<horizmotor,0,vertmotor>);
}
float rotspeed=0;
if ((held & CONTROL_LEFT) || (held & CONTROL_ROT_LEFT)) {
rotspeed=rotspeed+rotmax;
}
if ((held & CONTROL_RIGHT) || (held & CONTROL_ROT_RIGHT)) {
rotspeed=rotspeed-rotmax;
}
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION , <0,0,rotspeed>);
}
changed(integer change) {
if (change & CHANGED_LINK) {
if (llAvatarOnSitTarget() == NULL_KEY) {
// Somebody stood up... stop being a vehicle
llReleaseControls();
state default;
}
}
}
listen(integer chan,string name,key id,string msg) {
if (msg=="hard") {
state hardcapsule;
}
else if (msg=="stop") {
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<0,0,0>);
}
else if (msg=="displayon") {
debug=1;
llSetTimerEvent(1.0);
}
else if (msg=="displayoff") {
debug=0;
llSetTimerEvent(0.0);
llSetText("",<0,0,0>,0);
}
}
// ****
// Timer for debug
timer() {
string text;
vector vel=llGetVel();
text=(string)vel;
vel=vel/llGetRot();
vel=vel/axisrot;
text=text+"\n"+(string)vel;
llSetText(text,<1,1,1>,1);
}
// ****
}
// *******************************************************************
// *******************************************************************
// *******************************************************************
state hardcapsule {
state_entry() {
pilot=llAvatarOnSitTarget();
llSetVehicleType(VEHICLE_TYPE_NONE);
llSetBuoyancy(1.);
llSetStatus(STATUS_PHYSICS,TRUE);
llMinEventDelay(0.2);
llReleaseControls();
llRequestPermissions(pilot, PERMISSION_TAKE_CONTROLS);
llListen(5,"",pilot,"");
llSetText("",<0,0,0>,0);
if (debug) {
llSetTimerEvent(1.0);
} else {
llSetTimerEvent(0);
}
llSay(0,vehiclename+" in hard mode. Say '/5 easy' for easy mode, '/5 stop' to stop.");
}
run_time_permissions(integer perm) {
if (perm) {
llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN , TRUE , FALSE);
}
}
control(key id, integer held, integer change) {
float mass;
vector lookat;
mass=llGetMass();
lookat=<1,0,0>;
lookat*=axisrot;
lookat*=llGetRot();
if ((held & CONTROL_BACK) && !(held & CONTROL_FWD)) {
llApplyImpulse(-mass*lookat,0);
}
else if ((held & CONTROL_FWD) && !(held & CONTROL_BACK)) {
llApplyImpulse(mass*lookat,0);
}
if ((held & CONTROL_ROT_LEFT) && !(held & CONTROL_ROT_RIGHT)) {
llApplyRotationalImpulse(<0,0,drot>*axisrot,TRUE);
}
else if ((held & CONTROL_ROT_RIGHT) && !(held & CONTROL_ROT_LEFT)) {
llApplyRotationalImpulse(<0,0,-drot>*axisrot,TRUE);
}
if ((held & CONTROL_LEFT) && !(held & CONTROL_RIGHT)) {
llApplyRotationalImpulse(<-drot,0,0>*axisrot,TRUE);
}
if ((held & CONTROL_RIGHT) && !(held & CONTROL_LEFT)) {
llApplyRotationalImpulse(<drot,0,0>*axisrot,TRUE);
}
if ((held & CONTROL_UP) && !(held & CONTROL_DOWN)) {
llApplyRotationalImpulse(<0,drot,0>*axisrot,TRUE);
}
else if ((held & CONTROL_DOWN) && !(held & CONTROL_UP)) {
llApplyRotationalImpulse(<0,-drot,0>*axisrot,TRUE);
}
}
changed(integer change) {
if (change & CHANGED_LINK) {
if (llAvatarOnSitTarget() == NULL_KEY) {
// Somebody stood up... stop being a vehicle
llReleaseControls();
state default;
}
}
}
listen(integer chan,string name,key id,string msg) {
if (msg=="easy") {
state easycapsule;
}
else if (msg=="stop") {
llApplyImpulse(-llGetMass()*llGetVel(),FALSE);
llApplyRotationalImpulse(-llGetOmega(),FALSE);
llSay(0,"All Stop...");
}
else if (msg=="displayon") {
debug=1;
llSetTimerEvent(1.0);
}
else if (msg=="displayoff") {
debug=0;
llSetTimerEvent(0.0);
llSetText("",<0,0,0>,0);
}
}
timer() {
string text;
vector vel=llGetVel();
vector rot=llGetOmega();
vel=vel/llGetRot();
vel=vel/axisrot;
text=text+"Vel: "+(string)vel;
rot=rot/llGetRot();
rot=rot/axisrot;
text=text+"\nRot: "+(string)rot;
text=text+"\nEnergy: "+(string)llGetEnergy();
llSetText(text,<1,1,1>,1);
}
}
</pre>