Jetski Script für Opensim und Bullet Physik
Hier ein weiteres Beispiel meiner Scripte als Schulungskurs zu dem bereits geposteten Grundlagenkurs für Fahrzeuge.
Dieses baut auf dem im Hanggleiter Script bereits erklärten Grundlagen auf. Links siehe am Artikelende.
Ich werde daher diese Grundlagen hier weglassen.
Diesmal gehts es um Wasserfahrzeuge mit Motorantrieb.
Hier die Grundlagen für Fahrzeugsteuerung:
- Das Fahrzeug bewegt sich auf Wasser Level.
- Es ist träge was Beschleunigung und Abbremsung angeht
- Beschleunigung und Bremsen quittiert es mit Neigung von Bug und Heck.
- Lenken quittiert es mit Eintauchen der Innenecke. (Kurvenkränkung)
- Es hat 1 Rückwärtsgang, einen Leerlaufgang, und 4 Vorwärts Gänge
- Das Lenkverhalten ist abhängig vom eingelegten Gang, ebenso die Motorkraft
Eine Anlehnung der Motorkraft an die Motordrehzahl wäre mit lieber gewesen, aber hätte die Berechnungsschleife stärker belastet.
Letzendlich wäre dann das Reaktionsverhalten schlechter geworden. Also habe ich mich zu diesem Kompromiss durchgerungen.
Steuerung:
Die Steuerung erfolgt wie immer mit den 4 Cursortasten sowie Bild AUF/AB
Bild Auf/Ab dient der Gangschaltung
Pfeil Auf gibt Gas und schaltet aus dem Leerlauf in den 1. Gang
Pfeil Ab Bremst
Das Fahrzeug ist für Simwechsel unter Opensim >= 0.9x geeignet.
Man darf aber beim Simwechsel keine anderen Scripte Tragen die einen Reset durchführen und sich die Steuerungstasten danach holen.
Dieses ist bei einigen AOs der Fall.
Die Verwendung einer anderen Physikengine in Opensim ist machbar, lediglich die Motorparameter müssen dann neu ermittelt werden.
In SL funktioniert dieses Script ohne Änderungen nicht.
Das Fahrzeug erwartet 2 Animation - je eine für Vorwärtsfahrt und Rückwärtsfahrt.
Ab einer gewissen Mindestgeschwindigkeit werden Heckwellen Parikel (Gischt) erzeugt.
Die vorgesehene Textur UUID ist im Script zu ändern.
Wird keine (gültige) eingetragen wird weisser Schaum als Default verwendet.
Als Ergänzung zu dem Fahrzeug eignet sich das Beifahrer Sitz Script das ich bereits hier veröffentlicht habe.
Als Basis für die Fahrzeug Erstellung wird wieder ein Prim erwartet das als rootprim dieses Script , und die beiden Animationen trägt.
Das verlinkte Meshfahrzeug wird wieder per Script Phantom gemacht.
Das Rootprim darf jedoch nicht per Hand auf Phantom gesetzt werden, sonst funktioniert die physische Bewegung nicht mehr.
Weitere Infos zu Fahrzeugen findet ihr im Grundlagen Artikel sowie im Hanggleiter.
https://service-silberwelten.de/showthread.php?tid=68
https://service-silberwelten.de/showthread.php?tid=69
Viel Spaß
Tron
Hier ein weiteres Beispiel meiner Scripte als Schulungskurs zu dem bereits geposteten Grundlagenkurs für Fahrzeuge.
Dieses baut auf dem im Hanggleiter Script bereits erklärten Grundlagen auf. Links siehe am Artikelende.
Ich werde daher diese Grundlagen hier weglassen.
Diesmal gehts es um Wasserfahrzeuge mit Motorantrieb.
Hier die Grundlagen für Fahrzeugsteuerung:
- Das Fahrzeug bewegt sich auf Wasser Level.
- Es ist träge was Beschleunigung und Abbremsung angeht
- Beschleunigung und Bremsen quittiert es mit Neigung von Bug und Heck.
- Lenken quittiert es mit Eintauchen der Innenecke. (Kurvenkränkung)
- Es hat 1 Rückwärtsgang, einen Leerlaufgang, und 4 Vorwärts Gänge
- Das Lenkverhalten ist abhängig vom eingelegten Gang, ebenso die Motorkraft
Eine Anlehnung der Motorkraft an die Motordrehzahl wäre mit lieber gewesen, aber hätte die Berechnungsschleife stärker belastet.
Letzendlich wäre dann das Reaktionsverhalten schlechter geworden. Also habe ich mich zu diesem Kompromiss durchgerungen.
Steuerung:
Die Steuerung erfolgt wie immer mit den 4 Cursortasten sowie Bild AUF/AB
Bild Auf/Ab dient der Gangschaltung
Pfeil Auf gibt Gas und schaltet aus dem Leerlauf in den 1. Gang
Pfeil Ab Bremst
Das Fahrzeug ist für Simwechsel unter Opensim >= 0.9x geeignet.
Man darf aber beim Simwechsel keine anderen Scripte Tragen die einen Reset durchführen und sich die Steuerungstasten danach holen.
Dieses ist bei einigen AOs der Fall.
Die Verwendung einer anderen Physikengine in Opensim ist machbar, lediglich die Motorparameter müssen dann neu ermittelt werden.
In SL funktioniert dieses Script ohne Änderungen nicht.
Das Fahrzeug erwartet 2 Animation - je eine für Vorwärtsfahrt und Rückwärtsfahrt.
Ab einer gewissen Mindestgeschwindigkeit werden Heckwellen Parikel (Gischt) erzeugt.
Die vorgesehene Textur UUID ist im Script zu ändern.
Wird keine (gültige) eingetragen wird weisser Schaum als Default verwendet.
Als Ergänzung zu dem Fahrzeug eignet sich das Beifahrer Sitz Script das ich bereits hier veröffentlicht habe.
Als Basis für die Fahrzeug Erstellung wird wieder ein Prim erwartet das als rootprim dieses Script , und die beiden Animationen trägt.
Das verlinkte Meshfahrzeug wird wieder per Script Phantom gemacht.
Das Rootprim darf jedoch nicht per Hand auf Phantom gesetzt werden, sonst funktioniert die physische Bewegung nicht mehr.
Weitere Infos zu Fahrzeugen findet ihr im Grundlagen Artikel sowie im Hanggleiter.
https://service-silberwelten.de/showthread.php?tid=68
https://service-silberwelten.de/showthread.php?tid=69
Viel Spaß
Tron
Code:
// P304S V11 Jetski Script für Simcrossing in Opensim 09 by Tron CC0
// Aktualisiert 02.2021
// Heckwelle , neue Gangschaltung mit Leerlauf, neue Kinematik
// 2.2021 Partikel Textur vorladen auf Fahrzeugprim, damit diese sofort angezeigt wird
//==== Globale User Variablen die ohne Multiposer benötigt werden ====
integer racemode = FALSE; // Zeigt Text an
string vorwaertsAnim = "Jetskifahrer"; // FahrerAnimation, entfällt bei Multiposer
string rueckwaertsAnim = "Jetskirueck"; // FahrerAnimation, entfällt bei Multiposer
vector sitzposition = <0.220, -0.0, 0.75>; // Sitzposition, entfällt bei Multiposer
// === BOOTSTYP KONSTANTEN =====
list getriebeliste = [ -5 , 0, 10, 30, 50, 70 ]; // Vortriebskraft des Linear Motors je Gang
list lenkgetriebeListe = [-1.0, 0 , 1.0, 1.3, 1.1, 1.0 ]; // Lenkkraft des Angular Motors je Gang ,höhere Werte sind wendiger
list gangListe = ["Rückwärts","Leerlauf","1. Gang","2. Gang","3. Gang","4. Gang"]; // Namensgebung für Gänge
float kurvenkraenkung = 0.8; // Seitliches Eintauchen in Kurven : Jetski 0.8, Rennboot 0.6, Yacht 03
//==== SONSTIGE GLOBAL VARIABLEN ====
key kapitan;
integer bootfaehrt;
integer gang; // aktuell eingelegter Gang
float antriebskraft;
float lenkansprechverhalten;
integer ganganzahl;
string animation; // aktuell gespielte Animation
integer heckwelle = FALSE; // aktueller Zusand der Partikel Heckwelle
vector vel;
float fahrtgeschwindigkeit; // Geschwindigkeit
//==== E N D G L O B A L V A R I A B L E D E C L A R A T I O N ====
init_engine()
{
integer lauf;
bootfaehrt = FALSE;
ganganzahl = llGetListLength(getriebeliste);
llSetSitText("Fahrer");
vector gSitTarget_Rot = llRot2Euler( llGetRootRotation() ); // SIT TARGET in Anhängigkeit von Boot Rotation
llSitTarget(sitzposition, llEuler2Rot(DEG_TO_RAD * gSitTarget_Rot));
lauf =2;
do
{
llSetLinkPrimitiveParamsFast(lauf, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
lauf++;
} while (lauf <10);
//llSetLinkPrimitiveParamsFast(LINK_ALL_CHILDREN, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
}
gangschaltung(string schalte)
{
string gangname;
if (schalte == "Leerlauf") { gang = 1;} // in Leerlauf schalten
else if (schalte == "Rückwärts") { gang = 0; llSleep(0.5);} // Rückwärts Gang einlegen
else if (schalte == "Vorwärts") { gang = 2; llSleep(0.5);} // in 1. Gang schalten
else if (schalte == "+") { gang += 1;} // in höheren Gang schalten
else { gang -= 1; } // schalte in niedrigeren Gang
antriebskraft = llList2Float(getriebeliste, gang);
lenkansprechverhalten = llList2Float(lenkgetriebeListe, gang);
gangname = llList2String(gangListe , gang);
llWhisper(0, gangname );
if (gang == 0) // Rückwärtsfahrt
{
llStopAnimation(animation);
animation = rueckwaertsAnim; // Merker für aktuelle Animation setzten
llStartAnimation(animation);
}
else // Stehen oder vorwärts
{
llStopAnimation(animation);
animation = vorwaertsAnim; // Merker für aktuelle Animation setzten
llStartAnimation(animation);
}
if (gang < 2){heckwellenpartikel(FALSE); llSetTimerEvent(0.0); }
else llSetTimerEvent(1.0); // Partikelüberwachung
}
init_followCam()
{
llSetCameraParams([
CAMERA_ACTIVE, 1, // 0=INACTIVE 1=ACTIVE
CAMERA_BEHINDNESS_ANGLE, 2.5, // (0 to 180) DEGREES
CAMERA_BEHINDNESS_LAG, 0.3, // (0 to 3) SECONDS
CAMERA_DISTANCE, 3.0, // ( 0.5 to 10) METERS
CAMERA_PITCH, 12.0, // (-45 to 80) DEGREES
CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
CAMERA_POSITION_LAG, 0.5, // (0 to 3) SECONDS
CAMERA_POSITION_THRESHOLD, 0.0, // (0 to 4) METERS
CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
CAMERA_FOCUS_LAG, 0.0, // (0 to 3) SECONDS
CAMERA_FOCUS_THRESHOLD, 0.0, // (0 to 4) METERS
CAMERA_FOCUS_OFFSET, <-3, 0, 1> // METERS
]);
llForceMouselook(FALSE);
}
set_engine()
{
llSetVehicleType(VEHICLE_TYPE_BOAT);
// default rotation of prim
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, <0.00000, 0.00000, 0.00000, 0.00000>);
// linear motor
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 2.0); // Anlaufzeit bis volle Geschwindigkeit
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 2.0); // Zeit des Ausgleitens in vorwärts Richtung bis Stillstand
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1.0,1.0,10.0> );
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1.10); // Einfluss auf Abrutschen an Hindernissen 1000 gut für Surfer
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1.00);
// angular motor
llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, <1.0,1000.0,1000.0> );
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.20);
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.10);
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.10);
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 10.00);
// somewhat bounscy vertical attractor
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 2.00);
// hover
llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0.15 ); // Eintauchtiefe des Bootes im Stand
llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0.5 );
llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1.0 );
llSetVehicleFloatParam(VEHICLE_BUOYANCY, 1.0 );
// weak negative damped banking
llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 1.0 );
llSetVehicleFloatParam( VEHICLE_BANKING_MIX, 1.0 );
llSetVehicleFloatParam( VEHICLE_BANKING_TIMESCALE, 0.5 );
// remove these flags
llRemoveVehicleFlags( VEHICLE_FLAG_HOVER_TERRAIN_ONLY
| VEHICLE_FLAG_LIMIT_ROLL_ONLY
| VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);
// set these flags
llSetVehicleFlags( VEHICLE_FLAG_NO_DEFLECTION_UP
// | VEHICLE_FLAG_HOVER_WATER_ONLY
// | VEHICLE_FLAG_HOVER_UP_ONLY // verhindert wieder Eintachen im Wasser nach Sprung
| VEHICLE_FLAG_LIMIT_MOTOR_UP );
}
heckwellenpartikel(integer sollsichtbar)
{
if (sollsichtbar)
{
llParticleSystem( // Heckwelle einschalten
[
PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE_CONE,
PSYS_SRC_BURST_RADIUS,1,
PSYS_SRC_ANGLE_BEGIN,PI/2,
PSYS_SRC_ANGLE_END,PI/2 + 0.01,
PSYS_SRC_TARGET_KEY,llGetKey(),
PSYS_PART_START_COLOR,<1.000000,1.000000,1.000000>,
PSYS_PART_END_COLOR,<1.000000,1.000000,1.000000>,
PSYS_PART_START_ALPHA,.7,
PSYS_PART_END_ALPHA,0,
PSYS_PART_START_GLOW,0,
PSYS_PART_END_GLOW,0,
PSYS_PART_BLEND_FUNC_SOURCE,PSYS_PART_BF_SOURCE_ALPHA,
PSYS_PART_BLEND_FUNC_DEST,PSYS_PART_BF_ONE_MINUS_SOURCE_ALPHA,
PSYS_PART_START_SCALE,<2.50000,2.500000,0.5000000>, // viel Gischt**
PSYS_PART_END_SCALE,<2,2, 0.000000>,
PSYS_SRC_TEXTURE,"3a7ea058-e486-4d21-b2e6-8b47462bb45b",
PSYS_SRC_MAX_AGE,0,
PSYS_PART_MAX_AGE,6,
PSYS_SRC_BURST_RATE,0.01,
PSYS_SRC_BURST_PART_COUNT,1,
PSYS_SRC_ACCEL,<0.000000,0.000000,-1.00000>,
PSYS_SRC_OMEGA,<0.000000,0.000000,0.000000>,
PSYS_SRC_BURST_SPEED_MIN,0.6,
PSYS_SRC_BURST_SPEED_MAX,0.6,
PSYS_PART_FLAGS,0
| PSYS_PART_INTERP_COLOR_MASK
| PSYS_PART_INTERP_SCALE_MASK
| PSYS_PART_BOUNCE_MASK
| PSYS_PART_EMISSIVE_MASK
]);
heckwelle = TRUE;
}
else
{
llParticleSystem([]); // Heckwelle ausschalten
heckwelle = FALSE;
}
}
default
{
state_entry()
{
llSetLinkTexture(LINK_THIS,"3a7ea058-e486-4d21-b2e6-8b47462bb45b", ALL_SIDES); // Partikeltextur auf roortprim vorladen damit es sofort angezeigt wird
vector bootposition = llGetPos();
bootposition.z = llGround( ZERO_VECTOR );
float wasserstand = llWater( ZERO_VECTOR );
if( bootposition.z < wasserstand ) { bootposition.z = wasserstand;}
else {llSay(0,"Rezz mich im Wasser!");}
llSetRegionPos(bootposition + <0,0,0.1>);
init_engine();
state Ground;
}
}
state Ground
{
state_entry() { llSetText("",<0,0,0>,1.0);}
on_rez(integer param) { llResetScript();}
changed(integer change)
{
if ((change & CHANGED_LINK) == CHANGED_LINK)
{
kapitan = llAvatarOnSitTarget();
if (kapitan != NULL_KEY)
{ // wir haben einen Kapitän
llSetStatus(STATUS_PHYSICS, TRUE);
llSetStatus(STATUS_ROTATE_Y,TRUE);
llSetStatus(STATUS_ROTATE_Z,TRUE);
set_engine();
llRequestPermissions(kapitan, PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA | PERMISSION_TRACK_CAMERA);
bootfaehrt = TRUE; // Boot fährt
}
else
{ // Kaptiän hat Boot verlassen
llSetStatus(STATUS_PHYSICS, FALSE);
bootfaehrt = FALSE; // Boot steht
init_engine();
llStopAnimation( animation );
heckwellenpartikel(FALSE);
llPushObject(kapitan, <3,3,21>, ZERO_VECTOR, FALSE);
llReleaseControls();
llClearCameraParams();
llSetCameraParams([CAMERA_ACTIVE, 0]);
if(racemode) { llSetText("",<0,0,0>,1.0); }
llResetScript();
}
}
if ((change & CHANGED_REGION) == CHANGED_REGION)
{
llWhisper(0,"Simgrenze überquert");
}
}
run_time_permissions(integer perm)
{
if (perm)
{
gangschaltung("Leerlauf"); // im Leerlauf starten
llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_DOWN | CONTROL_UP | CONTROL_RIGHT | CONTROL_LEFT | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT, TRUE, FALSE);
init_followCam();
llStopAnimation("sit");
animation = vorwaertsAnim; // aktuelle Animation merken
llStartAnimation(animation);
llSleep(1.5);
}
}
control(key id, integer held, integer change)
{
if(bootfaehrt == FALSE){return;}
vel = llGetVel();
fahrtgeschwindigkeit = llVecMag(vel); // Geschwindigkeit
vector AngularMotor;
if(racemode) llSetText( (string)fahrtgeschwindigkeit + " kmh",<1,1,1>,1.0);
// in höheren Gang schalten
if(held & change & CONTROL_UP) { if (gang < (ganganzahl -1)) gangschaltung("+"); }
// in niedrigerne Gang schalten
if(held & change & CONTROL_DOWN) { if (gang > 0) gangschaltung("-"); }
// weiterhin Vorwärts Gas geben
if (held & CONTROL_FWD)
{
if (gang < 2) gangschaltung("Vorwärts");
else llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <antriebskraft,0,0>);
}
// Rückwärts schalten
if (held & change & CONTROL_BACK)
{
if ((fahrtgeschwindigkeit < 0.5 ) && (gang > 0)) { gangschaltung("Rückwärts");}
}
// weiterhin Rückwärts fahren oder Bremsen
if (held & CONTROL_BACK)
{
if (gang > 1) {llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0>);} // Bremsen
else if (gang == 0) // Rückwärts fahren
{
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <antriebskraft, 0, 0>);
llSetCameraParams([CAMERA_BEHINDNESS_ANGLE,-45.0]);
llSetCameraParams([CAMERA_DISTANCE,8.0]);
}
}
// RECHTS lenken
if (held & CONTROL_ROT_RIGHT)
{
if(( gang > 1) && (fahrtgeschwindigkeit > 0.1 )) // Einknicken in Kurvenmittelpunkt
{
AngularMotor.x += ( lenkansprechverhalten * kurvenkraenkung );
AngularMotor.y -= ( lenkansprechverhalten * kurvenkraenkung );
}
else
{
AngularMotor.x = 0; // nicht einknicken bei Rückwärtsfahrt
AngularMotor.y = 0;
}
AngularMotor.z -= ( lenkansprechverhalten);
if(fahrtgeschwindigkeit < 1.0) llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <antriebskraft/2, 0, 0>);
}
// LINKS Lenken
if (held & CONTROL_ROT_LEFT)
{
if(( gang > 1) && (fahrtgeschwindigkeit > 0.1 )) // Einknicken in Kurvenmittelpunkt
{
AngularMotor.x -= ( lenkansprechverhalten * kurvenkraenkung );
AngularMotor.y -= ( lenkansprechverhalten * kurvenkraenkung );
}
else
{
AngularMotor.x = 0;
AngularMotor.y = 0;
}
AngularMotor.z += ( lenkansprechverhalten);
if(fahrtgeschwindigkeit < 1.0) llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <antriebskraft/2, 0, 0>);
}
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, AngularMotor); // Lenkkraft an Angular Motor übergeben
}
timer()
{
vel = llGetVel();
fahrtgeschwindigkeit = llVecMag(vel); // Geschwindigkeit
if(fahrtgeschwindigkeit > 5) { if(!heckwelle) heckwellenpartikel(TRUE);} // Heckwelle anschalten
else {if (heckwelle) heckwellenpartikel(FALSE);}
}
}