Lektion 13 – Sensoren und Detektoren
In dieser Lektion widmen wir uns einem weiterem und sehr nützlichem Aspekt von LSL, nämlich Sensoren und Detektoren. Ohne Sensoren und Detektoren wäre vieles in Second Life nicht denkbar. Avatar- oder Simscanner zB basieren ganz wesentlich darauf, aber auch nervige Follower und dergl.
Wir wollen uns jedoch lieber den nützlichen Aufgaben widmen, die man mit Sensoren erledigen kann und daher am Ende dieser Lektion auch einen kleinen Avatarscanner bauen.
Los gehts!
Was sind Sensoren
Ein Sensor ist eine LSL Funktion, mit der es möglich ist, nach Objekten und/oder Avataren in einer bestimmten Reichweite und einer bestimmten Richtung zu suchen. Ein Sensor wird ähnlich einem Listener zunächst initialisiert, sprich, ihm wird gesagt, wonach er suchen soll und in welchem Umkreis.
Findet ein Sensor etwas, das auf die Initialisierungsfilter zutrifft, löst er den Event sensor(integer num) aus, andernfalls den Event no_sensor().
Die Integervariable, die der sensor-Event übernimmt, kann mittels Detektoren ausgewertet werden.
Es gibt zwei Arten von Sensoren, nämlich solche, die nur ein einziges mal scannen, nachdem sie initialisiert wurden und solche, die in bestimmten Zeitintervallen regelmäßig scannen, solange, bis sie abgeschaltet werden bzw. das Skript oder das Objekt deaktiviert bzw. gelöscht wird.
Sensoren sind sehr beliebt in SL, denn ohne sie wäre vieles nicht möglich. Andererseits sind Sensoren auch ein wenig verrufen, da sie nicht immer korrekt laufen und zudem Lag erzeugen.
Man sollte daher in der Tat darauf achten, Sensoren so wenig und so schonend wie möglich zu benutzen und wo immer es geht, über Alternativen nachzudenken. Eine dieser Alternativen werde ich im Verlaufe dieser Lektion noch vorstellen.
Was sind Detektoren
Detektoren sind ein Satz von Funktionen, die es ermöglichen in bestimmten Events, Daten des ermittelten Objektes oder Avatars auszugeben. So lässt sich damit zB ermitteln, wie das Objekt/Avatar heißt, wo es sich zum ermittelnden Zeitpunkt befindet, ob und wie schnell es sich bewegt, dessen Owner und/oder Key usw…
Detektoren machen nur in Touch-, Sensor- und Collisionevents Sinn und funktionieren auch nur dort, da nur diese Events Objekt- bzw. Avatardaten aufnehmen.
Eine Liste der wichtigsten Detektorfunktionen findet man wie immer hier im Wiki. Allerdings bietet es sich in diesem Falle an, das Original-Wiki von Lindenlab anzuschauen, da es bisher als einziges alle Funktionen beinhaltet; es kamen nämlich in letzter Zeit einige neue spezielle Funktionen für die 3 Touchevents hinzu.
Wie man Detektoren einsetzt, sehen wir noch im weiteren Verlauf dieser Lektion.
Einen Sensor initialisieren
Ähnlich wie bei Listenern muss ein Sensor zunächst initialisiert werden, sprich man muss ihm sagen, wonach und in welchem Umkreis er zu suchen hat und, falls es sich um einen wiederholenden Sensor handelt, wie oft.
Betrachten wir zuerst einmal die Funktion für einen einmaligen Sensor, sprich ein Sensor, der nur ein einziges mal ausgeführt wird:
Diese lautet: llSensor(string name, key id, integer type, float range, float arc);
Wie wir sehen, liefert diese Funktion also keinen Rückgabewert, erwartet aber einen ganzen Haufen Argumente verschiedenen Formats, mit welchen man die Filter festlegt, sprich wonach und wo gesucht wird. Der Sensor löst danach sofort aus und triggert, falls er einen oder mehrere Treffer hat, die mit den Filtern übereinstimmen, den Event sensor(integer num) bzw. falls er keinen Treffer landet den Event no_sensor().
Die Funktion für einen sich regelmäßig wiederholenden Sensor sieht beinahe genauso aus:
llSensorRepeat(string name, key id, integer type, float range, float arc, float rate);
Sie heißt nur ein wenig anders und hat ein Argument mehr am Schluss, nämlich das für die Wiederholungsrate.
Betrachten wir also nun…..
Die Argumente im Einzelnen
Die ersten beiden Argumente sind noch relativ einfach zu verstehen bzw. zu erklären.
In ihnen gibt man, genau wie bei Listenern auch, an, ob man nach einem Objekt/Avatar mit einem speziellen Namen und/oder spezieller UUID suchen will.
Gibt man für string name zB "Philip Linden" ein, wird der Scanner nach einem Objekt bzw. Avatar (je nach type, dazu gleich) suchen, namens Philip Linden. Ein Objekt mit diesem Namen zu bauen und dieses danach zu finden, dürfte wohl leicht sein. Beim Avatar mit diesem Namen gibts jedoch Probleme, da das scannende Objekt erstens auf der gleichen Sim wie Philip Linden sein muss und zweitens, Lindenlabmitarbeiter, die sich im Admin-Mode befinden, nicht von Sensoren registriert werden können.
Philip Linden also mit einem Sensor zu erfassen, kommt der Wahrscheinlichkeit eines Lottogewinns daher sehr nahe
Will man nicht explizit nach einem speziellen Namen scannen, setzt man einfach einen Leerstring, also "" .
Ähnlich verhält es sich mit dem zweiten Argument, key id. Hier kann man eine spezielle UUID eines Objektes bzw. Avatars eingeben, nach der gesucht werden soll. Auch hier gilt, sucht man nicht nach einer speziellen UUID, gibt man hier einfach NULL_KEY ein.
Das wohl komplexeste Argument ist das dritte, integer type.
Dieses gibt an, nach welchen Typ gesucht werden soll, also nach einem Avatar, einem Objekt und hier wiederum ob nach einen geskripteten, physischen etc. Da dieses Argument recht komplex ist, widme ich mich diesem gleich in einem eigenem Block und behandele zuerst die verbleibenden Argumente.
Das Argument float range gibt den Radius um den Mittelpunkt des Sensorprims an, in welchem gescannt wird, und zwar in Metern. 50 würde also zB 50m um den Prim suchen, in einem Feld, das mit dem gleich beschriebenem Argument float arc, definiert wird. Maximal kann bis 96m weit gescannt werden. Größere Werte führen zwar zu keinem Skriptfehler, erhöhen aber trotzdem nicht die Reichweite.
Das folgende Argument, float arc, gibt im Bogenmaß den Kugelausschnitt des zu scannenden Umfelds an. Das klingt auf Anhieb kompliziert, wird aber relativ leicht verständlich, wenn man dies einmal in SL mit einem Kugelobjekt und der Objekteigenschaft Dimple visualisiert, was ich in den gleich folgenden Bildern getan habe. Das Bogenmaß kennen wir bereits. Wir wissen, dass es hierfür unter anderem Konstanten gibt und die Konstante PI zB einen Winkel von 180 Grad beschreibt oder PI_BY_TWO zB 90 Grad.
Bei Sensoren wird oft vergessen, dass diese im dreidimensionalen Raum scannen und zwar vom Zentrum des Prims (nicht Objekt oder Rootprim) in welchem sich der Sensor befindet, ungeachtet, wie groß dieser Prim ist. Darüber hinaus wird immer um die positive Richtung der X-Achse (die rote Achse) gescannt. Ausgenommen hiervon sind Attachments bzw. HUDs. Diese scannen immer in Blickrichtung des Avatars, also vom Avatar aus gesehen nach vorne, ungeachtet, wie das Objekt am HUD bzw. Avatar ausgerichtet ist. Erwähnt werden muss auch noch, dass ein Objekt sich nicht selber detektieren kann und, wenn es ein Attachment bzw. HUD ist, auch nicht den Avatar, der es trägt.
Wichtig ist nun zu wissen, dass arc einen Winkel um die X-Achse, bzw. Blickrichtung beschreibt.
Das bedeutet, dass zB PI_BY_TWO (PI/2) einen Kugelausschnitt von 90 Grad um die vordere X-Achse bzw. Blickrichtung bildet, was also in einer Halbkugel resultiert. PI (180 Grad) beschreibt somit eine komplette Kugel. Auch dieser Effekt wird häufig übersehen.
Schauen wir uns dazu vier Bilder an. Auf diesen sehen wir einen Standardholzwürfel, um welchen ich exemplarisch den zu scannenden arc abgebildet habe, nämlich in Form einer Kugel mit Dimple.
Auf dem ersten Bild habe ich einen Winkel von 45 Grad simuliert, also PI/4. Man beachte, dass der Raum beim Primmittelpunkt beginnt und einen Kugelausschnitt darstellt in Richtung der positiven X-Achse. Dies entspricht einer Kugel mit Dimple-End 0.25.
Auf dem zweiten Bild sieht man einen Winkel von 90 Grad, also PI_BY_TWO, was in einer Halbkugel resultiert.
Das dritte Bild zeigt einen Winkel von 135 Grad, also 3/4*PI. Der schwarze Bereich wird hierbei nicht gescannt.
Das letzte Bild schließlich zeigt die Konstante PI, 180 Grad und somit eine komplette Kugel um den Prim.
In der Praxis wird man meist in einer vollen Kugel um den Prim suchen wollen, also die Konstante PI verwenden. Wichtig ist noch zu wissen, dass Werte größer als PI zwar keinen Skriptfehler erzeugen, aber in unzuverlässigen Scanergebnissen resultieren können, vor allem bei Sensorrepeats, so zB doppelte Ergebnisse, oder Ergebnisse außerhalb der Reichweite, manchmal sogar weit entfernt auf Nachbarsims. Diese Effekte können eintreten, müssen aber nicht. Um zuverlässige Ergebnisse zu erhalten, sollte man daher den Wert PI nicht übersteigen. Das Argument float arc ergibt also zusammen mit dem Argument float range den Raum, in welchem gescannt wird.
Bei Sensorrepeats gibt das zusätzliche letzte Argument float rate noch an, wie oft gescannt werden soll. 5.5 würde also zB alle fünfeinhalb Sekunden scannen.
Hierbei gilt es jedoch zu beachten, dass dieser Repeat im Gegensatz zu "echten" Timern, von der Simperformance und zwar speziell von der Time Dilation abhängt. Je langsamer und laggender eine Sim läuft, desto ungenauer wird dieser Repeat eingehalten. Hinzu kommt, dass geringe Werte zu ungenauen Ergebnissen und Ergebnisverlusten führen.
Sensorrepeats können, im Gegensatz zu Einmalsensoren über Simgrenzen hinaus scannen innerhalb ihres angegebenen Radius, sie schließen also auch Ergebnisse auf Nachbarsims mit ein. Bei Repeatwerten unter 5 Sekunden funktioniert dies allerdings nur noch sporadisch. Bei Werten unter 1 Sekunde erhält man nur noch alle 4 bis 6 mal überhaupt noch Treffer. Bei Werten unter 0.25 Sekunden sogar nur noch ca alle 20 mal. Zudem erzeugen kleine Werte recht hohen Lag. Benötigt man also keine Ergebnisse auf Nachbarsims und hat man den timer-Event noch nicht anders verplant, sollte man sich überlegen, einen Einmalsensor in einen Timer einzusetzen. Dies führt zu wesentlich zuverlässigeren Ergebnissen.
Das Argument Type
Wie schon angekündigt, ist das komplexeste Argument das dritte Argument, integer type. Dieses gibt an, nach welchem Typ von Objekt bzw. Avatar gesucht werden soll. Dafür setzt man folgende Konstanten ein:
AGENT weist den Sensor an, nach Avataren zu suchen.
ACTIVE weist den Sensor an, nach aktiven Objekten zu suchen. Doch was sind aktive Objekte? Zum einen sind das physische Objekte, die sich bewegen. Aktive Objekte sind aber auch alle Arten von Objekten, in denen ein aktives Skript läuft. Was ist nun wieder ein aktives Skript? Zum einen muss das Skript eingeschaltet sein, natürlich, damit muss es aber noch nicht zwangsweise aktiv sein. Ein Skript gilt nur dann als aktiv, wenn es zu dem Zeitpunkt, zu dem es gescannt wird, gerade etwas tut, also zB einen Event abarbeitet und/oder ein Kommando abwartet, was zB bei Listenern der Fall wäre. (Eingeschaltete) Skripte mit laufenden Listenern gelten also immer als aktiv. Aktive Objekte sind also, sich bewegende Physicals und/oder Objekte mit aktiven Skripten wie zB Listener.
Aktive Objekte verbrauchen höhere Simressourcen.
(Sog. Spy-Detektoren suchen zB nach aktiven Objekten in Chatreichweite, also 20m um den Avatar. Da ein aktives Objekt aber noch nicht unbedingt ein Chatspy sein muss, sondern dies nur ein Indiz dafür ist, sollte man diesen Spy-Detektoren keine hohe Beachtung beimessen, sie dienen lediglich als Anhalt, dass es sich bei dem detektiertem Objekt um einen Chatspy handeln könnte. Gewissheit, dass es sich um einen Chatspy handelt, kann nur eine manuelle Untersuchung des Objektes bringen).
PASSIVE scannt nach allen physischen Objekten, die sich NICHT bewegen und nach nicht physischen Objekten. Zudem muss das Objekt entweder ungeskriptet sein, oder das Skript muss inaktiv sein, d.h. entweder abgeschaltet, oder zum Zeitpunkt des Scannens untätig sowie ohne aktive Listener.
SCRIPTED scannt nach allen Objekten, die min. ein laufendes Skript enthalten, egal ob dieses gerade passiv oder aktiv ist. Wichtig ist nur, dass es eingeschaltet, also Running ist.
Man kann diese Konstanten miteinander kombinieren mit dem bitweisen Verknüpfungsoperator OR | allerdings gibt es dabei eine große Stolperfalle zu beachten:
Die ersten drei Konstanten arbeiten inklusive, d.h. ihre Eigenschaften addieren sich. SCRIPTED jedoch arbeitet exklusiv, d.h. es zieht Eigenschaften ab.
Betrachten wir zuerst die Inklusivität:
ACTIVE|PASSIVE würde zB nach allen Objekten suchen, egal ob physisch oder nicht, bewegend oder nicht, aktiv oder passiv. Jedes Objekt wird in Betracht gezogen.
AGENT|ACTIVE würde zB nach allen aktiven Objekten und Avataren suchen.
AGENT|ACTIVE|PASSIVE sucht zB nach Objekten aller Art und Avataren.
Kompliziert wirds jedoch, wenn die Konstante SCRIPTED mit anderen kombiniert wird, da diese exklusiv arbeitet. Ein Avatar zB ist niemals geskriptet, daher schließt die Kombination von AGENT und SCRIPTED Avatare aus. Betrachten wir 3 mögliche Kombinationen dieser Konstanten:
AGENT|SCRIPTED sucht nach aktiven Skripten oder sich bewegenden physischen Objekten mit einem Skript, egal ob dieses aktiv oder passiv ist; jedoch wie schon besprochen NICHT nach Avataren, diese werden ausgeschlossen.
ACTIVE|SCRIPTED sucht ebenfalls nach aktiven Skripten oder sich bewegenden pysischen Objekten mit einem Skript, egal ob aktiv oder passiv; allerdings werden hier nun sich bewegende physische Objekte ausgeschlossen, die nicht geskriptet sind.
PASSIVE|SCRIPTED sucht nach aktiven Skripten, nicht-geskripteten nichtphysischen Objekten und physischen Objekten, die ein Skript enthalten, egal ob aktiv oder passiv; ausgeschlossen werden jedoch sich nicht bewegende physische Objekte, die kein Skript enthalten.
Wie schon gesagt, das Argument integer type hat es in sich. Die zwei häufigsten Szenarios sind jedoch die Suche nach Avataren, was mit AGENT möglich wäre, oder die Suche nach Objekten aller Art, meist mit einem bestimmten Namen, was mit ACTIVE|PASSIVE möglich wäre in Verbindung mit einem Namensfilter mit dem ersten Argument.
2 Beispiele hierfür:
llSensorRepeat("", NULL_KEY, AGENT, 20, PI, 5.0);
Dies sucht alle 5 Sekunden nach Avataren in Chatreichweite, also 20m Umgebung. Dies würde, da es sich um Sensorrepeat handelt, auch Avatare auf der Nachbarsim mit einschließen, wenn diese sich innerhalb von 20m befinden.
llSensor("Object", NULL_KEY, ACTIVE|PASSIVE, PI_BY_TWO, 96);
Dies würde einmalig nach allen Objekten, egal welchen Typs, suchen, die "Object" heißen und in X-Richtung bzw. bei Attachments in Avatarblickrichtung nach vorne liegen (Halbkreis "nach vorne" um das scannende Objekt).
Die Sensor-Events
Zu einem Sensor gehören 2 Events. Der Event no_sensor() wird ausgelöst, wenn der Sensor keine Treffer gefunden hat, die in seinem zu suchendem Raum liegen und den Filterregeln entsprechen.
Der Event übernimmt keine Daten, wie man an dem leeren Klammerpaar sehen kann. Es wird also lediglich festgestellt, dass der Sensor keinen Treffer gelandet hat.
Der Event sensor(integer num) wird dann ausgelöst, wenn es mindestens einen Treffer gab, also min. ein Objekt/Avatar im zu scannendem Raum auftrat, auf welches die Filterregeln zutrafen. Der empfangene Integerwert hat dabei eine Doppelfunktion, auf die wir gleich zu sprechen kommen.
Doch davor muss noch auf einige Stolperfallen hingewiesen werden:
- Ein Sensor kann nur maximal 16 Treffer gleichzeitig auffangen, wobei dies in der Regel die 16 ihn am nächsten befindlichen Objekte/Avatare sind.
- Zu scannende Objekte liegen nur im Sensorraum, wenn deren Mittelpunkt sich darin befindet, da Objekte immer nach ihrem Mittelpunkt gescannt werden. Objekte werden daher nicht erkannt, wenn sich nur ihr Rand, nicht jedoch ihr Mittelpunkt im Sensorraum befindet.
- Ein Objekt kann weder sich noch seine Childprims erkennen, und falls es attached ist, auch nicht den Avatar, der es trägt
Bei der Funktion llSensorRepeat gibt es noch zwei zusätzliche Dinge zu beachten:
- Der no_sensor-Event wird nicht getriggert, wenn es keinen sensor-Event dazu gibt. Dieser kann jedoch leer sein. Man sollte daher grundsätzlich immer beide Events im Skript eintragen, auch wenn man evtl. einen von beiden nicht braucht. Diesen lässt man dann einfach leer.
- Die Ergebnisse können zufällig und unerwartet sein, wenn man arc größer PI verwendet oder der Repeatwert zu klein wird. Zudem hängt der Repeatwert von der Simperformance ab
Ergebnisbehandlung mittels Detektoren
Wie eben angesprochen übernimmt der Sensor-Event eine Intergervariable, die zudem eine Doppelfunktion hat. Zum einem gibt sie an, wie viele Treffer es gab.
Triggert zB die Funktion llSensor("", NULL_KEY, AGENT, PI, 20); den Sensor-Event und dessen Integervariable hat den Wert 5, so wissen wir, dass sich zum Scannzeitpunkt 5 Avatare in Chatreichweite befanden. (Wie schon erwähnt können höchsten 16 Treffer, also in dem Fall maximal 16 Avatare gescannt werden, allerdings sind 16 Avatare in Chatreichweite schon eine kleine Versammlung:-) )
Die zweite Funktion, die diese Variable hat ist, dass man mit ihr sozusagen als Index mit den Detectionfunktionen Daten abfragen kann. llDetectedName(0), gibt zB den Namen des ersten gefundenen Avatars zurück. Es handelt sich hier wieder um Indizes, daher wird bei 0 zu zählen begonnen, 0 wäre somit der erste, 1 der zweite …. 15 der sechzehnte. Mehr als 16 Ergebnisse sind ja bekanntlich nicht möglich.
llDetectedPos(3) würde zB die Position des 4. gefundenen Objekts/Avatars als vector zurückgeben.
Welche Daten man mit Detektoren abfangen kann, habe ich ja bereits oben mit den Verweisen auf die entsprechenden Wikilinks geschrieben. Am besten man schaut sich hierbei mal jede Detectionfunktion im einzelnen an.
Diese Doppeleigenschaft der Variablen macht man sich sehr häufig zunutze, indem man mit der Anzahl der ermittelnden Ergebnisse eine for-Schleife erstellt und in dieser dann jedes einzelne Ergebnis abarbeitet, wie wir es auch gleich in unserem Beispielskript unten tun werden.
Übrigens haben wir in vorherigen Lektionen schon Detectionfunktionen benutzt, nämlich bei touch-Events. Auch hier wird das gleiche Prinzip genutzt, nämlich eine Integervariable in Doppelfunktion. Sie gibt die Anzahl der Auslöser an und kann gleichzeitig genutzt werden, um diese für die Detection zu indizieren. Bei touch_start bzw. touch_end und collision_start bzw. collision_end gibt es allerdings eigentlich immer nur maximal einen Treffer und man detektiert dann Index 0, da es quasi unmöglich ist, dass 2 Avatare exakt gleichzeitig ein Objekt berühren oder mit diesem kollidieren.
Das war ein Haufen harter Stoff. Setzen wir diesen nun um, indem wir mittels eines Sensors einen handelsüblichen Avatarscanner bauen.
Sensoren in der Praxis – Ein Avatarscanner
Wir basteln uns einen typischen Avatarscanner in Form eines kleinen HUDs.
Dabei soll dieser alle 2 1/2 Sekunden das gesamte Umfeld mit maximaler Reichweite, also 96m, nach Avataren absuchen und diese mit ihrem Abstand zu uns aufzählen.
Wir benutzen anstatt llSensorRepeat einen Timer mit llSensor, da dies zuverlässiger und schonender ist und wir zudem den Timer sonst nicht benötigen. Allerdings schließt dieser dann auch keine Avatare auf Nachbarsims mit ein. Wollte man dieses oder sollte der Timer anderweitig genutzt werden, sollte man natürlich llSensorRepeat verwenden.
Wie immer zuerst das Skript, dann die Erklärung:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 default
{
state_entry()
{
llSetTimerEvent(2.5); //Timer für den Sensor initialisieren
}
timer()
{
llSensor("", NULL_KEY, AGENT, 96, PI); //Sensor nach Avataren, maximale Suchreichweite
}
no_sensor() //nichts gefunden
{
llSetLinkColor(LINK_SET, <0.5,0.5,0.5>, ALL_SIDES); //Objekt grau färben
llSetText("Keine AVs\nin Reichweite", <0,0,0>, 1.0); //Text setzen (mit Zeilenumbruch)
}
sensor(integer num) //Treffer
{
llSetLinkColor(LINK_SET, <0,1,0>, ALL_SIDES); //Objekt grün färben
string text; //lokale Textvariable erstellen
integer x; //Zählvariable für Schleife
for(x=0; x<num; x++) // So oft, wie Avatare gefunden wurden
{
text += llDetectedName(x); //den jeweiligen Namen...
text += " - "; //... und Trennzeichen hinzufügen
float range = llVecDist(llDetectedPos(x), llGetPos()); //Abstand ermitteln
text += (string)llRound(range); //Abstand runden und in Text einsetzen
text += "m\n"; //m und Zeilenumbruch anfügen
}
llSetText(text, <1,1,1>, 1.0); //Text setzen
}
}
In Zeile fünf initialisieren wir unseren 2 1/2 Sekunden Timer, welcher in Zeile 10 dann einen Sensor aufruft. Dieser sucht nach Avataren rund um das Objekt in maximaler Reichweite (96m).
Findet er keine Avatare, wird der no_sensor-Event ab Zeile 13 ausgelöst. In diesem wird das Objekt grau gefärbt und mit schwarzem Text angezeigt, dass es keine Treffer gab.
Gab es min. einen Treffer, wird der sensor-Event ab Zeile 19 ausgelöst. Dieser färbt nun zunächst das Objekt grün und initialisiert dann eine lokale String-Variable, die wir mit den Daten füllen und am Ende in Zeile 32 als Hovertext setzen.
Das Herzstück ist die for-Schleife von Zeile 24 bis 31, die eben diese lokale Textvariable mit Daten füllt.
Diese wird so oft ausgeführt, wie es Treffer gab, nämlich solange x kleiner num ist, wobei num uns ja die Anzahl der Treffer angibt. Danach werden die Treffer der Reihe nach von 0, 1, 2 usw durchgegangen, indem wir uns die Zählervariable x zunutze machen, welche ja nach jedem Durchgang um 1 erhöht wird und bei 0 startet.
Somit holen wir uns in Zeile 26 den Namen am momentanen Index und addieren diesen zu unserem Textstring. Danach fügen wir ein Trennzeichen hinzu.
In Zeile 27 erstellen wir eine lokale Float-Variable namens range und speichern in ihr den Abstand des momentan bearbeiteten Avatars zum HUD. Dazu nutzen wir die Funktion float llVecDist(vector vec1, vector vec2), welche den Abstand zweier Vektoren berechnet. In diese setzen wir die detektierte Position und unsere eigene Position ein.
In Zeile 29 runden wir diesen Wert, casten ihn in einen string und fügen diesen unserer Textvariablen an.
Zeile 30 fügt lediglich noch ein m und einen Zeilenumbruch ein.
Eine typische und praktische Anwendung eines Sensors.
Nachteile von Sensoren
Um es kurz zu machen, Sensoren erzeugen Lag, haben einige Bugs und sind nicht immer zuverlässig. Man sollte sie daher, wo immer möglich vermeiden, auf ein Minimum reduzieren oder ggf. über Alternativen nachdenken.
Eine dieser Alternativen ist die Funktion llVolumeDetect(TRUE); (hier gibts den Wikilink)
Setzt man diese, wird zum einem das Objekt automatisch Phantom zum anderen aber werden die Events collision_start bzw. collision_end ausgelöst, wenn ein Objekt/Avatar mit dem Objekt zusammentrifft, bzw. es wieder verlässt. Die meisten Greeter funktionieren auf diese Art und Weise, so zB auch der Greeter in unserer Sandbox am TP-Punkt. Es wäre nahezu ein Verbrechen, hierfür einen Sensor zu verwenden.
Wie wir wissen, können in diesen Events die Detektoren auf die gleiche Art und Weise benutzt werden, wie bei Sensoren oder Touch-Events.
Zusammenfassung
In dieser Lektion hast du folgendes gesehen:
- Was Sensoren sind und welche zwei Arten es gibt
- Was Detektoren sind und in welchen Events man sie nutzen kann
- Wie man einen Sensor initialisiert bzw. auslöst
- Die Argumente im einzelnen und die Komplexizität des Arguments Type
- Die beiden wohl häufigsten Einsatzgebiete, Avatar- und Objektscanner
- Die beiden Sensor-Events und was es bei beiden zu beachten gilt
- Wie man mittels Detektoren Ergebnisdaten erhält
- Wie ein Avatarscanner in der Praxis funktioniert
- Welche Unterschiede beide Sensorvarianten haben
- Welche Nachteile Sensoren haben
- Eine Alternative mittels der Funktion llVolumeDetect
Sensoren, so hilfreich und häufig wie sie sind, so einfach und doch komplex können sie auch sein. Dennoch, trotz ihrer Nachteile wäre sehr vieles in SL ohne sie nicht denkbar.
In der nächsten Lektion werden wir noch ein weiteres eher theoretisches aber sehr wichtiges Thema abhandeln, bevor wir wieder voll in die Praxis einsteigen, nämlich das Arbeiten mit Listen mittels des Variablentyps list.
Lektion 14 folgt in ein paar Tagen.
