How to build Autobahn public and private roads Version 0
You can build two types of Autobahn roads.
1) Simple roads where the road is mostly flat and you don't have complex overpasses. The target position is always above the prim a set distance making it easy to drop a update script into an existing road.
2) Roads where the road controls the rotation of the vehicle.
This makes great roller coasters.
For an overview see
Autobahn Public roads that can be auto navigated by scripts
The prim name holds a key of the next road prim for each lane so that the vehicle can use
llKey2Name to get the next prim's data.
The prim name also holds the road target position and rotation so that the vehicle only has to use a scanner when it first starts.
Each lane is composed of prims named "PublicRoad" Followed by data and choices of next keys
Fields are separated by ";" symbols to make it easier for the script to parse the string using
llParseStringKeepNulls
- Standard version. = 0
- This Lane number = 1 added to make road updates easier
- Maximum recommended speed in m/s (optional)
- Descriptive road name (optional)
- Lane target position <X,Y,Z>. This is 1m above the road surface so that the vehicle does not have to process the rotation if it is built with a 1m high center.
- Lane Rotation <x,y,z,s> or blank for a simple Autobahn Road section. ZERO_ROTATION is defined as upright and pointing east.
- key for next prim of lane 1
- key for next prim of lane 2(optional)
- key for next prim of lane 3(optional)
- more lanes...
You can force vehicle to change lanes by leaving the key for that lane blank.
The vehicle should move to the nearest valid lane.
Making a simple Autobahn Road section
A simple section sets the "lane rotation" setting to blank and allows the vehicle to decide what direction to drive in the lanes. This allows for existing roads to updated to use the autobahn system by dropping a script into them.
The disadvantage is that by not defining what side of the road to drive on, you can have head on collisions.
Example:
Name "PublicRoad;0;1;20;Simple Road;" + (string)(llGetPos() + <0.,0.,1.5>) + ";" + (string)NextKey;
where the key's are the keys of the surrounding prims.
Making an Autobahn Road section
Getting the prim rotation correct is important since the vehicle uses this to orientate itself on to the road.
There is a test road in Charissa (10, 10, 95)
The forward direction is defined by the script.
<0,0,0,1> points east with Z up (ZERO_ROTATION)
<0,0,-1,0> points west with Z up
<0,0, 0.707, 0.707 points north with Z up
<0,0, -0.707, 0.707 points south with Z up
Since the name actually controls the road direction, you can make the prim any direction and modify the road update script
- Start by making a prim, then set the X axis to 10m. This will be the length of the section.
- Set the Y axis to thickness to 1m. This will be the resulting road thickness.
- Set the Z axis to 6m. This will be the lane width minus some shoulder.
- Now rotate it so that what was up is now pointing north. The Rotation should now be X=? Y=90 Z=270
- Set the texture to blank, color to grey and the repeats per side to 1 on all sides to improve frame rate.
- Select the texture on just the top and south side and set it to the library road texture.
Library->?
- Select the top texture and set the texture to the following.
U repeat = 1
V repeat = 0.95
Rotation to 0 degrees.
U offset = 0
V offset = .025
- Select the side texture and set the texture to the following.
X repeat = 1
Y repeat = 0.1
Rotation to 0 degrees.
X offset = 0
Y offset = 0
- Rotate it to point east and get the prim rotation to update the script.
Scripts
This script works as long as the forward direction is correct for the scanner to see the next road segment.
It allows you to remove them using the "/65 remove" command and then allows you to update them again by dropping the script into one prim and giving it the "/65 update" command.
//public Road configuration script
//
//Released into the public domain by grumble Loudon and LaserFur Leonov
//
//V0.4 (not done, but works)
//
//defaults are used if not in notecard
integer m_Channel = 65;
integer m_lane = 1;
string m_RoadName = "TestRoad";
integer m_PinNumber = 123;
//Rotate the road prim east (Z up) and touch to get "Actual Prim rotation" to set here
//rotation m_RotationAdjustment = < -0.7, 0, -0.7, 0>;
//rotation m_RotationAdjustment = <0.0, 0.0, 0.0, 1.0>;
rotation m_RotationAdjustment = <-0.70711, -0.00000, 0.00000, 0.70711>;
vector m_LaneAdjustment = <0.0,0.0,1.5>;// set to 1m above prim surface.
//****************************************************************************************
integer m_DetectedScan = -1;
integer m_UpdateScript = 0;
integer m_MessageTimer = 0;
integer m_reSendMessage = 0;
string m_reSendMessageStr = "";
//current data
key m_lane1Key;
vector m_LanePos;
rotation m_LaneRot;
vector m_testPos;
//****************************************************************************************
UpdateFloatText()
{
string Text;
Text ="";
Text += "Lane " + (string)m_lane + "\n";
if (m_DetectedScan >= 0){
Text += "Road is sensing " + (string)m_DetectedScan + " Road Prims";
};
Text += "\n Actual Prim rotation = " + (string)llGetRot();
Text += "\n Lane pos = " + (string)m_LanePos;
Text += "\n Lane Rot = " + (string)m_LaneRot;
Text += "\n Adj pos = " + (string)m_testPos;
llSetText(Text ,<1,1,1>,1);
}
//*******************************************************************************
UpdateDescr(){
//object name = PublicRoad;0;Lane;speed;name;<pos 1m Up>;<Rotation>;key1;key2 ect...
vector myPos = llGetPos();
rotation myRot = llGetRot();
rotation tRot1 = ZERO_ROTATION /myRot;//
rotation tRot2 = m_RotationAdjustment; //
m_LaneRot = tRot1 * tRot2 ; // adjust for custom direction from prims view
vector LaneAdj = m_LaneAdjustment * m_LaneRot ; //adjust the lane surface
m_testPos = LaneAdj;
m_LanePos = myPos + LaneAdj; //1m above road surface
string name;
name = "PublicRoad;0;"; //prim name and version
name += (string)m_lane + ";"; // Lane number
name += "20;"; //recomended Speed limit in m/sec
name += m_RoadName + ";"; //road name
name += (string)m_LanePos + ";"; // X,y,z Lane position
name += (string)m_LaneRot + ";"; // rotation.
//<0,0,0,1> points east,
//<0,0,-1,0> points west
//<0,0, 0.707, 0.707 points north
//<0,0, -0.707, 0.707 points south
name += (string)m_lane1Key; //Key to next prim
llSetObjectName(name);
UpdateFloatText();
}// update
//********************************************************************************
integer ProcessCommand(string message,integer FromNotecard) // from either chat or notecard
{// processes message
//returns true if message should be re sent on chat
integer resend = -1;
if (message == "Scan"){
llSensorRepeat("",NULL_KEY,PASSIVE|ACTIVE,18,0.5,3.1);
resend = 0;
}else if (message == "stop"){
llSensorRemove();
m_DetectedScan = -1;
if (!FromNotecard){
llSleep(.2);
llWhisper(m_Channel,message);
};
}else if (message == "remove"){
if (!FromNotecard){
llSetText("" ,<1,1,1>,1);
llSleep(.2);
llWhisper(m_Channel,message);
llRemoveInventory(llGetScriptName());
};
resend = 0;
}else if (message == "update"){
if (!FromNotecard){
m_UpdateScript = TRUE; //rez start pram tells next script
};
}else{
UpdateDescr();
};//
return resend ;
}//ProcessCommand
//********************************************************************************
default
{
state_entry()
{
//used to get m_RotationAdjustment
//llOwnerSay( (string) (ZERO_ROTATION / llGetRot()));
llSetRemoteScriptAccessPin(m_PinNumber);
llListen(m_Channel,"",NULL_KEY,"");
UpdateDescr();
m_lane1Key = NULL_KEY;
if (llGetStartParameter() == 1) m_UpdateScript = TRUE; //replicate
llSensorRepeat("",NULL_KEY,PASSIVE|ACTIVE,18,0.5,2.4);
llSetTimerEvent(1.2);
} //Entry
//*****************************************************************************
on_rez(integer Reznumber){
if (Reznumber == 1){
// replicate
m_UpdateScript = TRUE;
}else{
llResetScript();
};
}
//*****************************************************************************
timer(){
if (m_MessageTimer != 0) --m_MessageTimer;
} //timer
//*****************************************************************************
touch(integer num){
llOwnerSay((string)llGetRot());
}
//*****************************************************************************
listen(integer channel, string name, key id, string message){
if (m_MessageTimer == 0) //we only respond to one command every 3 seconds
{
m_MessageTimer = 2;
if ( m_reSendMessageStr != message ) //repeating message?
{
m_reSendMessageStr = message;
m_reSendMessage = ProcessCommand(message,FALSE);
}; //same
}; //timer
}// listen
//*****************************************************************************
sensor(integer Num_Detected)
{
m_DetectedScan = 0;
integer useNum = -1;
vector myPos = llGetPos();
float closest = 100;
integer i;
for (i = 0 ; i < Num_Detected; ++i){
string name = llDetectedName(i);
key dKey = llDetectedKey(i);
if (llGetSubString(name,0,10) == "PublicRoad;"){
if ( llDetectedOwner(i) == llGetOwner()){
//does it need upating
if (m_UpdateScript){
if (llDetectedType(i) == PASSIVE){ //no script running
if ( llDetectedOwner(i) == llGetOwner()){
llRemoteLoadScriptPin(dKey, llGetScriptName(), m_PinNumber, TRUE, 1);
jump ExitFor; //rescan
};//owner
};//passive
}; // update
//is it my lane?
list Prams = llParseStringKeepNulls(name,[";"],[]);
integer listLenght = llGetListLength(Prams);
if (listLenght >= 7 ){
//0 PublicRoad
//1 version
integer DetLane = (integer)llList2String(Prams,2);//2 Lane
//3 speed
//m_DetectedRoadName = llList2String(Prams,4); //4 Name
if (DetLane == m_lane){
//is it ahead of me?
vector NextPos = llDetectedPos(i);
float dist = llVecDist(myPos , NextPos);
// check angle
// To do
if (dist < closest ){
useNum = i;
closest = dist;
};
++m_DetectedScan;
};//lane
};//list
}; // owner
};//name
}; //sensor for
if (useNum != -1){
key nearestKey = llDetectedKey(useNum);
if (m_lane1Key != nearestKey){
m_lane1Key = nearestKey;
UpdateDescr();
};
m_UpdateScript = FALSE;
};
@ExitFor;
UpdateDescr(); //test
UpdateFloatText();
}//sensor
no_sensor()
{
m_DetectedScan = 0;
UpdateDescr(); //test
UpdateFloatText();
}//no sensor
//*****************************************************************************
} //default
//*****************************************************************************
Notes
The vector and keys are at the end since the update script can replace them when the the editor truncates them.
As of 1.13 the editor truncates the name field to fit in the edit box when the prim it edited.
Note: pipe symbols and other symbols don't work in the description field since it's like a filename (V1.12)
As of 1.12 you can't read the description of other objects!