Archivi categoria: Lezioni LSL

Notecard configurazione LSL

Uno dei primi problemi che affronta uno scripter LSL non esperto (ma anche uno esperto!) è quello di creare un file di testo esterno allo script, dove salvare i valori iniziali di una variabile.

Questa operazione è estremamente utile per aumentare la modularità di uno script e per attuare delle modifiche senza dover modificare e ricompilare il codice.

In script aventi una complessità medio-alta, la tecnica di salvare informazioni in un file di testo esterno è estremamente diffusa.

Il presente articolo si prefigge l’obiettivo di semplificare e rendere automatica una operazione di questo tipo. Lo script prevede una fase di impostazione delle variabile ed una fase di validazione delle stesse.

Inizio con il postare il codice completo del template per poi discutere le parti essenziali con un esempio.

// ReadConfiguration
//
// Il seguente template viene usato per leggere un file di configurazione.
// Il file di configurazione può essere formattato nel seguente modo:
// <var>        Singola variabile
// <var>=<var>    Variabile <var> che deve essere inizializzata con il valore in <var>
// # line        Linea di commento
//
// @version 1.0
// @author MarcoDuff Palen (https://www.marcoduff.com/)

// Variabili
string configNotecard;
integer line;
key requestId;

// Legge i singoli comandi di configurazione.
//
// @param var    Nome della variabile letta (sempre totalmente maiuscola).
// @param val    Valore della variabile associata o una stringa vuota in caso di nessuna inizializzazione.
readConfig(string var, string val) {
}

// Valida la configurazione.
//
// @return    Restituisce TRUE se la configurazione è valida, FALSE altrimenti.
integer validateConfig() {
    return TRUE;
}

// Stato di inizializzazione.
default {
    state_entry() {
        configNotecard = llGetScriptName()+".config";
        line = 0;
        requestId = llGetNotecardLine(configNotecard,line);
    }

    state_exit() {
        line = -1;
        requestId = NULL_KEY;
    }

    dataserver(key request_id, string data) {
        if(requestId==request_id) {
            if(data!=EOF) {
                data = llStringTrim(data,STRING_TRIM);
                if(llSubStringIndex(data,"#")!=0&&llStringLength(data)>0) {
                    list dataList = llParseString2List(data,["="],[]);
                    string var = llToUpper(llStringTrim(llList2String(dataList,0),STRING_TRIM));
                    string val = "";
                    if(llGetListLength(dataList)==2) val = llStringTrim(llList2String(dataList,1),STRING_TRIM);
                    readConfig(var,val);
                }
                line++;
                requestId = llGetNotecardLine(configNotecard,line);
            }
            else {
                integer validate = validateConfig();
                if(validate == TRUE) state ConfigOkState;
                else state ConfigErrorState;
            }
        }
    }
}

// Stato se la configurazione è andata a buon fine.
state ConfigOkState {
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

// Stato se la configurazione è errata.
state ConfigErrorState {
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

Lo script non fa altro che cercare il file di testo nel contents dell’oggetto chiamato come lo script e con estenzione .config, leggere tutte le righe del file ignorando le linee vuote e le linee che iniziano con il carattere #, validare le variabili lette e portarsi nello stato ConfigOkState se tutto è andato a buon fine o nello stato ConfigErrorState in caso di errori.

Questo significa che lo stato di default della nostra applicazione diventa ConfigOkState. Le uniche righe di codice da modificare nel template sono, infatti, l’inserimeto delle variabili utili al problema, il contenuto delle due funzioni readConfig e validateConfig e gli stati ConfigOkState e ConfigErrorState.

Facciamo un esempio che spiega il funzionamento del template.

Supponiamo di voler creare un oggetto che visualizzi un testo sopra di esso. Il testo ed il colore dell’oggetto devono essere specificati in un file di testo esterno allo script. Il testo deve sempre specificato, mentre il colore, se non specificato, deve essere nero.

Il modo più semplice per lavorare con questo template è creare prima il file di testo di configurazione. Suppunendo di voler visualizzare il testo “Prova riuscita!!!” di colore blu, il file di testo risulta essere il seguente:

# Questa è una prova di configurazione dello script ReadConfiguration

# Text indica il testo da visualizzare
text = Prova riuscita!!!

# Color è opzionale, fai i tentativi commentando o impostando la variabile!
color = <0.0,0.0,1.0>

Notate il massiccio uso di commenti (righe che iniziano con il carattere #) che verranno ignorati, insieme alle righe vuote, dallo script.

Come possiamo notare dal file di testo, per soddisfare questo problema abbiamo sicuramente bisogno di due variabili: la prima di tipo string per salvare il testo da visualizzare, la seconda di tipo vector per il colore.

Questo significa che al template bisogna aggiungere in testa il seguente codice:

string text;
vector color;

l’impostazione di queste due variabili viene fatto all’interno della funzione readConfig. Questa funzione, infatti, riceve in input due variabili chiamate var e val che contengono rispettivamente la parte sinistra e quella destra rispetto al simbolo = di ogni riga letta nel file di configurazione (se non è presente il simbolo = allora viene valorizzata solo la variabile var). La variabile var ha la particolarità di essere sempre in maiuscolo, mentre la variabile val mantiene i caratteri originali.

Per fare un esempio, quando lo script leggerà la riga “text = Prova riuscita!!!” richiamerà la funzione readConfig passando per var il valore “TEXT” mentre per val il valore “Prova riuscita!!!”.

Detto questo, modifichiamo l’interno della funzione readConfig per impostare le nostre variabili text e color in questo modo:

readConfig(string var, string val) {
    if(var=="TEXT") text = val;
    else if(var=="COLOR") color = (vector)val;
    else llOwnerSay("[WARNING] Variabile "+var+" alla linea "+(string)(line+1)+" non prevista.");
}

in questo modo quando verranno letti i due valori dal file di testo, questi verranno opportunamente impostati nelle variabili. Notate che in questa particolare implementazione, se viene letto un comando non previsto viene dato un warning all’owner dello script che ha un utile feedback (pensate a quante volte avete perso tempo inutile per un semplice errore di battitura come scrivere “calor” invece di “color” o simili… mettere feedback di questo tipo è sempre molto utile e assolutamente consigliato). Notate anche come ho utilizzato la variabile line che indica la linea corrente di lettura del file (p.s.: ho dovuto incrementarla di 1 visto che la prima riga di un file di testo parte da 0!).

Se vogliamo inserire una validazione o vogliamo mettere dei valori di default per una variabile, dobbiamo modificare il contenuto della funzione validateConfig (se non vogliamo farlo, ci basta lasciare il return TRUE;). Nel nostro esempio dobbiamo controllare che il testo sia stato inserito, visto che lo consideriamo obbligatorio, mentre dobbiamo controllare che se il colore non è stato inserito dobbiamo impostarlo a Nero. La modifica da attuare è la seguente:

integer validateConfig() {
    if(color == ZERO_VECTOR) color = <0.0,0.0,0.0>;

    if(llStringLength(text)==0) llOwnerSay("[ERROR] Non hai impostato la variabile TEXT");
    else return TRUE;

    return FALSE;
}

se noto che la variabile color non è stata impostato, lo imposta io a nero. Se noto che la variabile text non è stata impostata stampo un messaggio di errore. Notare che la funzione restituisce TRUE esclusivamente nel caso in cui la variabile text viene impostata, in tutti gli altri casi restituisce FALSE. Il risultato della funzione TRUE o FALSE impone il passaggio di stato rispettivamente a ConfigOkState a ConfigErrorState.

A questo punto non ci resta che riempire lo stato ConfigOkState con il codice che avremmo scritto nello stato default di un normale script ma supponendo che le variabili a noi utili siano già state impostate, mentre lo stato ConfigErrorState con eventuali messaggi di errore. Ad esempio:

state ConfigOkState {
    state_entry() {
        llSetText(text,color,1.0);
    }

    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

state ConfigErrorState {
    state_entry() {
        llOwnerSay("Errore di configurazione nel file "+configNotecard);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

Notate che tutti e due gli stati gestiscono l’evento changed in modo da captare eventuali modifiche del file di configurazione e resettarsi in automatico (ovviamente se non si vuole questa estenzione basta cancellare l’evento!).

Per chiarezza posto il codice completo dell’esempio:

// ReadConfiguration
//
// Il seguente template viene usato per leggere un file di configurazione.
// Il file di configurazione può essere formattato nel seguente modo:
// <var>        Singola variabile
// <var>=<val>    Variabile <var> che deve essere inizializzata con il valore in <val>
// # line        Linea di commento
//
// @version 1.0
// @author MarcoDuff Palen (https://www.marcoduff.com/)

// QUI INSERISCO LE VARIABILI DA RIEMPIRE TRAMITE IL FILE DI CONFIGURAZIONE
string text;
vector color;

// Variabili
string configNotecard;
integer line;
key requestId;

// Legge i singoli comandi di configurazione.
//
// @param var        Nome della variabile letta (sempre totalmente maiuscola).
// @param val        Valore della variabile associata o una stringa vuota in caso di nessuna inizializzazione.
readConfig(string var, string val) {
    // QUI INSERISCO I SETTAGGI DELLE VARIABILI
    llOwnerSay("[INFO] Reading "+var+" = "+val+" on line "+(string)(line+1));
    if(var=="TEXT") text = val;
    else if(var=="COLOR") color = (vector)val;
    else llOwnerSay("[WARNING] Variabile "+var+" alla linea "+(string)(line+1)+" non prevista.");
}

// Valida la configurazione.
//
// @return            Restituisce TRUE se la configurazione è valida, FALSE altrimenti.
integer validateConfig() {
    // QUI INSERISCO L'EVENTUALE VALIDAZIONE DELLE VARIABILI

    // Il colore non è una variabile richiesta, se non viene impostata la imposto a nero
    if(color == ZERO_VECTOR) color = <0.0,0.0,0.0>;

    // Il testo è una variabile fondamentale, mando un messaggio di errore
    if(llStringLength(text)==0) llOwnerSay("Non hai impostato la variabile TEXT");
    else return TRUE;

    return FALSE;
}

// Funzione per la visualizzazione dell'output.
//
// @param message    Messaggio da visualizzare.
// @param color        Colore di visualizzazione.
showOutput(string message, vector color) {
    llSetText(message,color,1.0);
}

// Stato di inizializzazione.
default {
    state_entry() {
        configNotecard = llGetScriptName()+".config";
        showOutput("Reading "+configNotecard,<0.0,1.0,0.0>);
        line = 0;
        requestId = llGetNotecardLine(configNotecard,line);
    }

    state_exit() {
        line = -1;
        requestId = NULL_KEY;
        showOutput("",ZERO_VECTOR);
    }

    dataserver(key request_id, string data) {
        if(requestId==request_id) {
            if(data!=EOF) {
                data = llStringTrim(data,STRING_TRIM);
                if(llSubStringIndex(data,"#")!=0&&llStringLength(data)>0) {
                    list dataList = llParseString2List(data,["="],[]);
                    string var = llToUpper(llStringTrim(llList2String(dataList,0),STRING_TRIM));
                    string val = "";
                    if(llGetListLength(dataList)==2) val = llStringTrim(llList2String(dataList,1),STRING_TRIM);
                    readConfig(var,val);
                }
                line++;
                requestId = llGetNotecardLine(configNotecard,line);
            }
            else {
                integer validate = validateConfig();
                if(validate == TRUE) state ConfigOkState;
                else state ConfigErrorState;
            }
        }
    }
}

// Stato se la configurazione è andata a buon fine.
state ConfigOkState {
    state_entry() {
        // SE TUTTO E' ANDATO BENE VISUALIZZO IL TESTO SULL'OGGETTO CON I PARAMETRI DEL FILE DI CONFIGURAZIONE
        llSetText(text,color,1.0);
    }

    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

// Stato se la configurazione è errata.
state ConfigErrorState {
    state_entry() {
        // SE TUTTO E' ANDATO MALE VISUALIZZO UN ERRORE
        llOwnerSay("Errore di configurazione nel file "+configNotecard);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

Buono Scripting LSL a tutti!

Quarta Lezione di LSL

Quarta lezione di LSL sempre alla Land del Forum!

Argomento della lezione, questa volta, la comunicazione interna in un oggetto. Come già detto in passato, un oggetto in Second Life è formato da uno o più prim linkati tra di loro. L’ordine dell’operazione di link, effettuata selezionando tutti i prim e premento CTRL + L, imposta il numero del link. In particolare, l’ultimo link selezionato sarà il prim root, ovvero il prim con il numero di link uguale ad 1, tutti gli altri saranno i prim child, con il numero di link decrescente rispetto alla selezione (il penultimo linkato ha valore 2, il precedente 3, e cosi via).

Per scoprire il numero di link abbiamo creato un oggetto formato da tre prim:

  • Un cubo come prim root;
  • Due piramidi con le basi adiacenti a due facce opposte del cubo come child.

Per fare delle prove abbiamo applicato a questi tre prim il seguente script:

default {
	touch_start(integer total_number) {
		string agentName = llDetectedName(0);
		integer linkNumber = llGetLinkNumber();
		llSay(PUBLIC_CHANNEL, agentName+" ha toccato il prim con il link number uguale a "+(string)linkNumber);
	}
}

La funzione llGetLinkNumber() ci restituisce infatti il numero del link in cui sta girando lo script.

Effettuando la separazione dei link, ovvero premento CTRL + SHIFT + L, abbiamo ripetuto il test ed abbiamo notato che il numero di link è pari a 0.

Quindi siamo arrivati alla seguente conclusione: se un oggetto è formato da un unico prim, questo ha numero prim uguale a 0, mentre, se l’oggetto è formato da due o più prim, l’ultimo oggetto selezionato ha valore 1 ed è il prim root, tutti gli altri hanno valore maggiore di 1, in base alla selezione, e sono i prim child.

Detto questo abbiamo posto la nostra attenzione verso un’altra funzione di sistema: llMessageLinked, la cui firma è llMessageLinked(integer linknum, integer num, string str, key id).

Questa funzione viene utilizzata per fare comunicare i prim che formano un oggetto. Il primo parametro, infatti, indica il numero del link del prim destinatario del messaggio. Il primo parametro, oltre al numero secco del prim, accetta anche le seguenti costani:

  • LINK_ROOT: per comunicare con il prim root (ovviamente, come abbiamo visto, questa costante assume valore 1);
  • LINK_SET: per comunicare con tutti i prim che costituiscono l’oggetto;
  • LINK_ALL_OTHERS: per comunicare con tutti i prim diversi da quello in cui sta girando lo script;
  • LINK_ALL_CHILDRENS: per comunicare con tutti i prim child;
  • LINK_THIS: per comunicare con lo stesso link in cui sta girando lo script.

I rimanenti tre paramentri della funzione llMessageLinked servono appunto per passare il messaggio. Possiamo passare contemporaneamente un intero, una stringa e una key rispettivamente nelle variabili num, str e id.

Il nostro obiettivo è creare tre script da inserire nei tre prim che costituiscono il nostro oggetto. Due script molto simili da applicare nelle piramidi per simulare delle frecce, ed uno script nel cubo (il prim root) che riceve i messaggi.

Il particolare, gli script delle piramidi devono essere sensibili al tocco di un agent e devono inviare al prim root il numero 1 o -1 in caso sia la freccia Indietro o Avanti, il nome dell’agent che ha toccato il prim e la key dello stesso.

Lo script per la freccia indietro risulta quindi essere il seguente:

default {
	state_entry() {
		llSetText("Indietro",<1.0,1.0,1.0>,1.0);
	}

	touch_start(integer total_number) {
		key agentKey = llDetectedKey(0);
		string agentName = llDetectedName(0);
		llMessageLinked(LINK_ROOT,-1,agentName,agentKey);
	}
}

mentre lo script per la freccia avanti è il seguente:

default {
	state_entry() {
		llSetText("Avanti",<1.0,1.0,1.0>,1.0);
	}

	touch_start(integer total_number) {
		key agentKey = llDetectedKey(0);
		string agentName = llDetectedName(0);
		llMessageLinked(LINK_ROOT,1,agentName,agentKey);
	}
}

Notate che la prima manda come intero il messaggio -1, mentre la seconda 1.

Lo script posizionato nel cubo, invece, non fa altro che leggere l’input del messaggio e visualizzarlo come testo.

Ogni volta che viene ricevuto un messaggio inviato tramite llMessageLinked, viene sollevato l’evento link_message(integer sender_num, integer num, string str, key id).

Se notate bene, i parametri di ingresso sono del tutto analoghi a llMessageLinked, fatta eccezione per il primo. Questa volta, infatti, il primo paramentro identifica il numero del link del prim mittente del messaggio. I restanti parametri contengono rispettivamente il numero, la stringa e la key inviata dal prim mittente.

Lo script risultante, da inserire nel prim cubo, risulta quindi essere:

default {
	state_entry() {
		llSetText("In attesa che qualcuno tocchi le freccie!",<1.0,1.0,1.0>,1.0);
	}

	link_message(integer sender_number, integer number, string message, key id) {
		llSetText("Il link numero "+(string)sender_number+" mi ha mandato il messaggio ("+(string)number+","+message+","+(string)id+")",<1.0,1.0,1.0>,1.0);
	}
}

Questo oggetto sarà molto utile la prossima lezione, dove lo utilizzeremo per spiegare le liste!

Alla prossima lezione!

Terza Lezione di LSL

Terza Lezione di LSL

Terza lezione di LSL alla Land del Forum!

Abbiamo continuato il discorso iniziato alla seconda lezione di LSL: la Comunicazione.

Ovvero come fa un oggetto ad ascoltare su di un canale ed a rispondere. Abbiamo creato due oggetti: il primo che ascolta su un canale, il secondo che appena toccato parla sullo stesso canale.

In questo modo abbiamo simulato la comunicazione tra i due oggetti. Il progetto consiste in due oggetti distinti, ognuno con il proprio script, che comunicano tra di loro.

Il primo script inizializza un listener tramite la chiamata a funzione llListen sul canale 1234567890. Al listen non vengono applicati filtri, quindi qualunque agent può interagire con esso.
Dentro l’evento listen è stata inserita una chiamata a funzione llSay su canale PUBLIC_CHANNEL (ovveo il canale 0), per avere un feedback del messaggio ascoltato.

integer CHANNEL = 1234567890;

default {
    state_entry() {
        key owner = llGetOwner();
        llSetText("Oggetto che riceve di "+llKey2Name(owner),<0.0,1.0,0.0>,1.0);
        llListen(CHANNEL,"",NULL_KEY,"");
    }

    listen(integer channel, string name, key id, string message) {
        llSay(PUBLIC_CHANNEL,name+" mi ha detto \""+message+"\" sul canale "+(string)channel);
    }

    changed(integer change) {
        if(change & CHANGED_OWNER) llResetScript();
    }
}

Il secondo script si occupa di attendere un click dal proprietario dell’oggetto per inviare nel canale prestabilito la stringa “NOME_AGENT mi ha toccato!”. In questo modo, questa stringa verrà intercettata dallo script del primo oggetto!

integer CHANNEL = 1234567890;

default {
	state_entry() {
		key owner = llGetOwner();
		llSetText("Oggetto che invia di "+llKey2Name(owner),<1.0,0.0,0.0>,1.0);
	}

	touch_start(integer total_number) {
		key agent = llDetectedKey(0);
		if(agent==llGetOwner()) {
			llOwnerSay("Invio messaggio '"+llKey2Name(agent)+" mi ha toccato!' sul canale "+(string)CHANNEL);
			llSay(CHANNEL, llKey2Name(agent)+" mi ha toccato!");
		}
		else {
			llInstantMessage(agent,"Non puoi toccare il mio oggetto!");
		}
	}

	changed(integer change) {
		if(change & CHANGED_OWNER) llResetScript();
	}
}

In tutti e due gli script è stata fatta un’aggiunta: l’evento changed. Questa è stata fatta per gestire un eventuale cambio di owner. In questo caso, infatti, l’intero script viene resettato per permettere la ridefinizione dei listener.

Possiamo provare il funzionamento del progetto in due modi:

  • Cliccando sul secondo oggetto;
  • Parlando sul canale (nel nostro caso ci basta scrivere nella chat “/1234567890 Prova!”).

Alla prossima lezione!

Seconda lezione di LSL

Giorno 26 Maggio si è tenuta la seconda lezione di LSL sempre alla Land del Forum. Oggetto della lezione, questa volta, la Comunicazione degli oggetti.

Abbiamo tre tipi fondamentali di comunicazione nel linguaggio LSL:

  • Comunicazione all’interno dell’oggetto, ovvero tra i prim che compongono l’oggetto;
  • Comunicazione tra un oggetto ed un altro oggetto o un Agent;
  • Comunicazione tra un oggetto e l’esterno.

Nella lezione ci siamo soffermati sulla comunicazione tra un oggetto e l’agent, ma abbiamo già predisposto l’oggetto per effettuare una comunicazione interna tra i prim che lo compongono. Abbiamo, infatti, creato un oggetto formato da due prim: un cubo come root e una piramide come child. Una primissima osservazione che abbiamo fatto è stata quella di notare la seguente differenza:

Uno script inserito nel prim root di un oggetto ha effetto su tutto l’oggetto, mentre uno script inserito in un prim child ha effetto esclusivamente su quel child.

Abbiamo notato questa differenza semplicemente inserendo lo script di default (quello che viene creato in automatico quando si crea un nuovo script) prima sul prim child e poi su quello root.

Ebbene, se inserito solo nel child ottenevamo la scritta “Touched.” esclusivamente se toccavamo il prim piramide dell’oggetto non nel cubo, mentre se inserito nel root ottenevamo la scritta “Touched.” sia se toccavamo la piramide sia se toccavamo il cubo.

Notata questa differenza, ci siamo inoltrati nello studio della Comunicazione tra oggetto ed Agent. Ci siamo quindi dedicati allo studio della funzione

integer llListen(integer canale, string nome, key id, string messaggio)

e del suo evento associato

listen(integer canale, string nome, key id, string messaggio)

I parametri di ingresso, come potete notare da soli, sono del tutto simili ed infatti assumono lo stesso significato:

Canale – Indica il canale di comunicazione. Vi ricordo che il canale pubblico ha valore 0 ed è identificato dalla costante PUBLIC_CHANNEL. Tutti gli altri canali, in un range compreso tra -2.147.483.648 a 2.147.483.647, sono i canali privati. Comunemente bisognerebbe utilizzare i canali negativi per interazioni tra oggetti e quelli positivi per interazioni con agent;
Nome – Indica il filtro di ascolto su un determinato nome;
Id – Indica il filtro di ascolto su una determinata key;
Messaggio – Indica il filtro su un determinato messaggio.

Il nostro intento è stato quello di creare uno script in ascolto su un canale privato della parola “Ciao” detta dall’owner, al quale l’oggetto doveva rispondere con messaggio “Ciao a te!” diretto esclusivamente
all’owner.

Abbiamo dovuto applicare i seguenti filtri alla funzione llListen:
Canale = 1
Nome = “”
Key = llGetOwner()
Messaggio = “Ciao”

Ovvero, abbiamo usato il canale 1, non abbiamo specificato il nome ma la key dell’owner dell’oggetto (recuperata in automatico grazie alla funzione llGetOwner()), ed abbiamo aggiunto il filtro “Ciao” al messaggio.

Tradotto in LSL, il comando da eseguire per impostare un ascoltatore è il seguente:

integer handler = llListen(1, "", llGetOwner(), "Ciao")

questo comando restituisce un handler dell’ascoltatore. Questo può essere usato per disattivare l’ascoltatore in un altro punto del codice. In questo nostro semplice script, abbiamo ignorato l’handler.

Una volta che abbiamo impostato il nostro ascoltatore, non ci resta che rispondere “Ciao a te!” quando viene sollevato l’evento ed il gioco è fatto!

Questo è lo script risultante:

default {
	state_entry() {
		llListen(1, "", llGetOwner(), "Ciao");
	}

	listen(integer canale, string nome, key id, string messaggio) {
		llOwnerSay("Ciao a te!");
	}
}

Per poterlo provare ci basta scrivere:

/1 Ciao

(vi ricordo che il “/1” iniziale significa “ridirigi questa stringa sul canale 1”. Infatti, questa comunicazione non viene letto in modo diretto da nessun agent, non essendo fatta nel canale pubblico).

Ricordatevi che c’è differenza tra “Ciao”, “CIAO” e “ciao”… il nostro script funziona solo con la prima stringa visto che il linguaggio LSL è case-sensitive!

Se tutto va bene il nostro oggetto ci risponderà con “Ciao a te!” e noi abbiamo completato con successo la nostra comunicazione tra Agent e Oggetto!

Alla prossima lezione!

Prima lezione di LSL

Lezione di LSL alla Land del Forum

Giorno 19 Maggio alle 21:30 si è tenuta la prima Lezione di Scripting LSL alla Land del Forum. Era la prima lezione ufficiale, in passato ne avevo tenuta un’altra per fare delle prove generali. Devo dire che nel complesso tutto è andato bene! Alcuni miei problemi legati al client: ho avuto tre crash e addirittura una schermata blu… ma forse quello è più un problema di Vista!

Il log completo della serata e tutte le foto della serata, compresa questa nel mio blog, sono disponibili nel Blog di Eleonora Porta.

Il tema della serata è stato quello di spiegare, banalmente, come è strutturato un listato LSL: che cosa sono gli stati, che cosa sono gli eventi, che cosa sono le funzioni. Fatto questo ci siamo dedicati allo studio di una funzione in particolare: llSetText, utile per visualizzare un testo sopra un prim. La firma della funzione è la seguente:

llSetText(string testo, vector colore, float alpha)

Testo – Indica il testo da visualizzare sopra il prim;
Colore – Indica il colore del testo nelle componenti RGB;
Alpha – Indica il livello di trasparenza in percentuale (1.0 totalmente opaco, 0.0 trasparente).

Molti dubbi sono nati con l’utilizzo del colore. Infatti, non si riesce a capire infatti come rappresentare un colore attraverso tre numeri. La questione è più semplice di quello che sembra. La totalità dei colori rappresentabili non sono altro che una combinazione dei tre colori del modello RGB (Red, Green, Blue).

Per fare qualche esempio:
la totale assenza dei tre colori indica il Nero;
la presenza contemporanea massima dei tre colori indica il Bianco;
la presenza massima delle sole componenti Red è Green indica il Giallo.

Quindi ci basta mischiare nella variabile vector colore la presenza in percentuale dei vari colori, per ottenere il colore voluto.

Alla fine della lezione abbiamo ottenuto il seguente codice:

default {
	state_entry() {
		llSetText("Esempio prima lezione LSL!",<1.0,0.0,0.0>,1.0);
	}
}

che visualizza il testo “Esempio prima lezione LSL!” di colore rosso sopra il prim.

Ringrazio tutti gli intervenuti alla Lezione e rinnovo l’appuntamento per la seconda lezione!