[Tutoriel] Simplifier le rez d'un objet et le transfert de données [màj 19/12/09]

Répondre
Partager Rechercher
Utiliser un système automatique de rez qui s'occupe de tout (ou presque) ? [[Mise à jour 19/12/09]]

Ce mini tuto s'adresse à ceux qui souhaitent avoir un système rez extrêmement simplifié pour ne pas avoir à créer 10 fois la même chose dans des projets de script.

Qu'est-ce qui est simplifié ? (explications juste après)

  • [1] La synchronisation de l'envoi de données entre le rezzer et l'objet rezzé ;
  • [2] Le rez de plusieurs objets à la suite ainsi que la multi-synchronisation de données ( [1] ) ;
  • [3] La vérification si le rez est possible à cette position ;
  • [4] L'utilisation d'un proxy.
  • [5] La distance de rez de plus de 10 mètres est gérée automatiquement



[1] - La synchronisation des données

Cela concerne la nécessité de rez un objet par script ainsi que lui transmettre des données par le biais d'une des fonctions Say (llWhisper, llSay, llShout, llRegionSay) de façon automatique. Comment savoir quand l'objet est rez et opérationnel (même dans des sims ultra laggy) pour ne pas lui transmettre les données trop tôt ? Comment faire fonctionner cette transmission sur n'importe quel canal de façon à être plus "sécurisée" ? On verra par la suite que ces tâches deviennent très simples.


[2] - Le rez de plusieurs objets à la suite + synchronisation de données

Même chose que le [1] mais cette fois avec un rez de plusieurs objets à la suite tout en restant avec une synchronisation parfaite.

[3] - Vérifier si le rez est possible

Rez un objet et lui envoyer des données de façon synchronisée peut déjà s'avérer assez lourd et répétitif pour un débutant, mais pour vérifier ensuite si un objet peut être rez à la position (parcel no build, offworld, au dessus de 4096 mètres) c'est encore pire. Le système va également s'occuper de ça et dans certain cas utiliser un rez à distance ( voir le [4] ).


[4] - Le proxy (ou rez à distance)

Il serait dommage de ne pas y ajouter une fonction utile comme celle du rez à distance ( ou proxy). Si vous êtes au dessus de 4096 mètres ou dans une parcel qui n'autorise pas le build, comment faire pour rez un objet par script ? Nous verrons par la suite que c'est tout à fait possible.

[5] - La distance de rez

La fonction llRezObject possède une limite bien contraignante, on ne peut pas rez plus loin que 10 mètres. Si une position qui est trop loin est donnée en paramètre à la fonction, le rez ne fonctionnera pas. Le système va s'occuper de régler ce problème automatiquement en réduisant la distance si elle dépasse cette limite pour éviter une erreur inutile.



Première étape : Le script principal (Rezzer) :


Code PHP:


// !! NOTE !! Le script a été mis à jour le 19/12/09 et fonctionne un peu différemment de l'ancienne version.


//----------------------------------------------------------------------------------
//                              LIBRARY FUNCTIONS
//----------------------------------------------------------------------------------



// -- Fonction de "correction" de vecteurs de position pour ne pas aller offworld ou au dessus de 4096m --
// @ param [vector] position à corriger
// @ return [vector] retourne le vecteur corrigé

vector CorrectVectorPosvector p ) {
    
    if( 
p.256.0 p.256.0;
    if( 
p.0.0 p.0.0;   
    if( 
p.256.0 p.256.0;
    if( 
p.0.0 p.0.0;
    if( 
p.4096.0 p.4096.0;
    
    return 
p;
    
}




// -- Fonction permettant de vérifier si l'objet est autorisé à rez à cette position --
// @ param [vector] position à vérifier
// @ return [bool]  retourne true si il est autorisé, false dans le cas contraire

integer ParcelCheckvector p ) {
    
    
integer flags llGetParcelFlags);
    
    if( 
flags PARCEL_FLAG_ALLOW_CREATE_OBJECTS || 
       (
flags PARCEL_FLAG_ALLOW_CREATE_GROUP_OBJECTS && 
            
llList2Key(llGetObjectDetails(llGetKey(), [OBJECT_GROUP]), 0) == llList2Key(llGetParcelDetails(p, [PARCEL_DETAILS_GROUP]), 0)) )
    return 
TRUE;
    
    return 
FALSE;    
    
}




// -- Fonction qui génere un canal au hasard (négatif) --
// @ return [integer] retourne le canal

integer GetRandomChannel() {
    return ~(integer)
llFrand( (float)DEBUG_CHANNEL );   
}




//----------------------------------------------------------------------------------
//                               SCRIPT FUNCTIONS
//----------------------------------------------------------------------------------



// -- Fonction llRezObject protégée qui vérifie si le rez est possible et envoie des données à l'objet
// de façon automatique --
// @ param [string] nom de l'objet dans l'inventaire
// @ param [vector] position du rez
// @ param [vector] vélocité de l'objet rezzé
// @ param [rotation] rotation de l'objet
// @ param [integer] canal de l'objet pour transmettre les données
// @ param [integer] type de rez, position locale/rotation locale ou non
// @ param [string] les données à envoyer

SafeRezObjectstring namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
    
    
// Est-ce que l'objet est bien un objet et présent dans l'inventaire ? si non, on envoie une erreur au owner.
    
if( llGetInventoryType(name) != INVENTORY_OBJECT ) {
        
llOwnerSay"Erreur :: " name " n'est pas un objet ou n'est pas présent dans l'inventaire." );
        return;   
    }
    
    
// on stock temporairement la pos/rot de l'objet/avatar
    
vector myPos llGetPos();
    
rotation myRot llGetRot();
    
    
vector rezPos myPos pos;
    
    
// on calcule le type de rez demandé
    
if( rezType POS_TYPE_REGION )
        
rezPos pos;
    else if( 
rezType POS_TYPE_LOCAL_ROTATED )
        
rezPos myPos pos myRot;
    if( 
rezType ROT_TYPE_LOCAL )
        
rot *= myRot;
    
    
    
// si la distance de rez est plus de 10 mètres, on la réduit au max possible
    // pour éviter une erreur de rez silencieuse
    
if( llVecDist(myPosrezPos) > 10.0 )
        
rezPos myPos llVecNorm(rezPos-myPos) * 10.0;    
    
    
    
// Si il y a possibilité de rez à la position   
    
if( CorrectVectorPos(rezPos) == rezPos && ParcelCheck(rezPos) ) {
        
        
// Si le canal n'est pas 0 et que les données à envoyer ne sont pas vides, on crée une connexion
        
if( chan != && data != "" ) {          
            if( !
CreateConnection(chandata) )
                return;    
        }
        
llRezObjectnamerezPosvelrotchan );       
        
    
// Sinon on vérifie la présence d'un proxy dans la région           
    
} else if( IsProxyAvailable() ) {
        
        
// Si le canal n'est pas 0 et que les données à envoyer ne sont pas vides, on crée une connexion
        
if( chan != && data != "" ) {          
            if( !
CreateConnection(chandata) )
                return;    
        }     
        
llGiveInventorygProxyKeyname );
        
llSleep0.2 );
        
llRegionSaygProxyChanname PRIM_SEP + (string)vel PRIM_SEP + (string)rot PRIM_SEP + (string)chan );   
    }
     
    
}



// -- Vérifie si un proxy est disponible --
// @ return [bool] true : disponible, false : non disponible

integer IsProxyAvailable() {
    return ( 
PROXY_ACTIVE && llKey2Name(gProxyKey) != "" );
}




// -- Crée une connexion et stock les données dans une mémoire tampon en attendant le rez --
// @ param [integer] canal du transfert
// @ param [string] les données à envoyer
// @ return [bool] true : la connexion est établie, false : echec de la création d'une connexion

integer CreateConnectioninteger chanstring data ) {
    
    if( (
gListenChan != []) > 64 )
        return 
FALSE;
       
    
gListenData += data;
    
gListenChan += chan;
    
gListenID += llListenchan""NULL_KEY"get" );
    return 
TRUE;
    
}





// -- Envoie les données mises en mémoire sur le canal concerné --
// @ param [integer] le canal du transfert

FlushDatainteger chan ) {
    
    
integer fd llListFindListgListenChan, [chan] );
    
    
// si le canal possède bien des données à envoyer
    
    
if( ~fd ) {   
        
llListenRemovellList2Integer(gListenIDfd) );
        
llRegionSaychanllList2String(gListenDatafd) );
        
gListenID llDeleteSubListgListenIDfdfd );
        
gListenChan llDeleteSubListgListenChanfdfd );
        
gListenData llDeleteSubListgListenDatafdfd );         
    }    
    
}



//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------





//--------------------------------------------------------------------------------------
//                                          CONSTANTS
//--------------------------------------------------------------------------------------
//----------------------------------------------
//                INTERNAL COMM
//----------------------------------------------
//----------------
//      ME
//----------------
integer IC_REZ_REQUEST = -1621184001;
integer IC_REZ_CONNECTION = -250865153;
integer IC_REZ_FLUSH = -605179777;
integer IC_REZ_CLEAR = -2105587073;
//----------------
//SCRIPT ADDRESSES
//----------------

//----------------
//    GLOBAL
//----------------
integer IC_GLOBAL_REZ_RETCONNECTION = -1204281601;

//----------------------------------------------
//                  PARAMETERS
//----------------------------------------------
// activer ou désactiver la fonction du proxy.
integer PROXY_ACTIVE FALSE;
// le délais de rafraichissement de timer de vérification
float PROXY_REFRESH 5.0;
// le nom du proxy dans l'inventaire
string PROXY_NAME "MyProxy";
// le temps de vie du proxy
float PROXY_LIFE 600.0// 10 minutes
// rez d'un autre proxy X secondes avant que l'ancien se supprime 
float PROXY_TIME_REZ 20.0;
// offset de rez du proxy
vector PROXY_REZ_OFFSET = <0.00.02.0>;
//----------------------------------------------
//                   REZ TYPE
//----------------------------------------------
// rez par position relative à la région
integer POS_TYPE_REGION 0x01;
// rez par position locale selon la rotation de l'objet/l'avatar
integer POS_TYPE_LOCAL_ROTATED 0x02;
// rez par rotation locale
integer ROT_TYPE_LOCAL 0x04;
//----------------------------------------------
//                     UTIL
//----------------------------------------------
string PRIM_SEP "|";
string SUB_SEP ":";

//--------------------------------------------------------------------------------------
//                           GLOBAL VARIABLES | State : Default
//--------------------------------------------------------------------------------------
//----------------------------------------------
//                  MEMORY
//----------------------------------------------
list gListenData;
list 
gListenChan;
list 
gListenID;
//----------------------------------------------
//                  PROXY
//----------------------------------------------
key gProxyKey;
integer gProxyChan;
//----------------------------------------------
//                  OTHERS
//----------------------------------------------
key gOwnerKey;


default
{
    
    
on_rezinteger r ) {
        
state clearconnections;   
    }
    
    
    
state_entry() {
        
gOwnerKey llGetOwner();  
        if( 
PROXY_ACTIVE )
            
llSetTimerEvent0.1 );
          
    }
    
    
    
changedinteger c ) {
        
        if( 
CHANGED_REGION )
            
state clearconnections;   
    }
    
    
    
link_messageinteger linkinteger addressstring datakey data2 ) {
        
// Si l'adresse correspond à une requête de rez, on reforme le packet pour l'envoyer à la fonction
        
if( address == IC_REZ_REQUEST ) {
            list 
packet llParseString2Listdata, [PRIM_SEP], [] );
            
SafeRezObject(
                            
llList2String(packet0), // name
                            
(vector)llList2String(packet1), // pos
                            
(vector)llList2String(packet2), // vel
                            
(rotation)llList2String(packet3), // rot
                            
(integer)llList2String(packet4), // chan
                            
(integer)llList2String(packet5), // rezType
                            
(string)data2 // data
                          
);   
        } 
        
        
// Si l'adresse est une demande de création de connexion
        
else if( address == IC_REZ_CONNECTION 
            
llMessageLinkedLINK_SETIC_GLOBAL_REZ_RETCONNECTION, (string)data2, (string)CreateConnection((integer)data, (string)data2) );
        
        
// Si l'adresse correspond à une requête d'envoi de données
        
else if( address == IC_REZ_FLUSH )
            
FlushData( (integer)data );   
        
        
// Demande de clearconnections
        
else if( address == IC_REZ_CLEAR )
            
state clearconnections;
    }
    
    
    
listeninteger chanstring namekey idstring msg ) {
        if( 
llGetOwnerKey(id) == gOwnerKey ) {
            
FlushDatachan );
            if( 
name == PROXY_NAME )
                    
gProxyKey id;
        }
    }
    
    
    
timer() {
        
         
// si aucun proxy détecté ou en fin de vie, on en rez un autre
         
if( llKey2Name(gProxyKey) == "" || (llGetTime() + PROXY_TIME_REZ) >= PROXY_LIFE  ) {
              
gProxyChan GetRandomChannel();
              
SafeRezObjectPROXY_NAMEPROXY_REZ_OFFSETZERO_VECTORZERO_ROTATIONgProxyChan0, (string)PROXY_LIFE );        
              
llResetTime();
         }
            
         
llSetTimerEventPROXY_REFRESH );
    }


}





// State permettant de désactiver tous les listens du script rapidement
// ainsi qu'éffacer la mémoire.

state clearconnections
{
    
       
state_entry() {
            
gListenData = [];
            
gListenChan = [];
            
gListenID = [];        
            
state default;
       }   
    


J'ai pas mal commenté les fonctions afin de comprendre le fonctionnement au minimum. Pour commencer, créez un objet puis un script à l'intérieur que vous appellerez par exemple "Rezzer", copiez/collez le code ci-dessus dans votre script et enregistrez-le.





Deuxième étape : Comprendre avec un exemple simple



On va commencer avec un exemple simple, celui de rez un objet sans envoyer de données en utilisant notre système. Pour ce faire , il y a 2 solutions que le script propose :

- Utiliser sa fonction SafeRezObject en modifiant le script
- Créer un second script et utiliser la fonction SafeRezObject "à distance"

Je choisis la seconde solution pour ce tuto qui me parait être la plus simple à comprendre. On va donc créer un second script vide dans notre objet que vous appellerez "Testeur" et utiliser la fonction SafeRezObject à distance. Pour ce faire, copiez/collez ce code dans votre second script :

Code PHP:


SafeRezObject
string namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
llMessageLinkedLINK_SET, -1621184001
                                               
name "|" 
                                               (string)
pos "|" 
                                               (string)
vel "|" 
                                               (string)
rot "|" 
                                               (string)
chan "|" +
                                               (string)
rezTypedata 
                    
);         
}



default
{
    
    
    
state_entry()
    {
        
    }

    
touch_start(integer total_number)
    {
        
llSay0"Touched !" );
    }

Et enregistrez-le. Maintenant placez un objet dans l'inventaire avec les 2 scripts. Vous pouvez y mettre ce que vous voulez. Pour ma part j'y ai placé un objet nommé "Cube", qui est, comme son nom l'indique, un cube simple et sans script.


secondStepInventorySnap.jpg



On va maintenant passer à l'édition du second script (Testeur) en commençant par utiliser la fonction llRezObject habituelle puis ensuite SafeRezObject pour voir les différences.

Petit rappel :

La fonction llRezObject ( détails ici ) se compose de 5 paramètres :

llRezObject( nom, position, vélocité, rotation, nombre );

  • nom : Le nom de l'objet à rez ( prévisible )
  • position : La position du rez ( où l'objet doit se trouver quand il sera rezzé )
  • vélocité : La vitesse de départ de l'objet ( dans le cas d'un objet physique )
  • rotation : Sa rotation de départ
  • nombre : Un nombre envoyé au script dans l'objet rezzé

Note : La position ne doit pas être à plus de 10 mètres de l'objet qui rez sinon ça ne fonctionnera pas (valable seulement si vous utilisez la fonction officielle).



Nous allons déclencher le rez au touché, c'est à dire dans l'event touch_start. Le nom de l'objet est "Cube", on va le rez à 2 mètres au dessus du cube rezzer ( axe Z ), avec une rotation nulle. Voilà le résultat :



Code PHP:


SafeRezObject
string namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
llMessageLinkedLINK_SET, -1621184001
                                               
name "|" 
                                               (string)
pos "|" 
                                               (string)
vel "|" 
                                               (string)
rot "|" 
                                               (string)
chan "|" +
                                               (string)
rezTypedata 
                    
);         
}



default
{
    
    
    
state_entry()
    {
        
llSay0"Hello, Avatar !" );   
    }

    
touch_start(integer total_number)
    {
        
llRezObject"Cube"llGetPos() + <0.00.02.0>, ZERO_VECTORZERO_ROTATION);
    }


Quand on touche note objet, ça se rez bien 2 mètres au dessus. Je vais pas aller plus loin dans les explications de la fonction llRezObject qui devrait être comprise pour suivre le reste du tuto.


On va pratiquer le même test mais cette fois-ci avec la fonction SafeRezObject du système. Cette fonction se compose non pas de 5 mais 7 paramètres, les 5 premiers étant exactement les même que llRezObject(); :

SafeRezObject( nom, position, vélocité, rotation, nombre, rezType, données );


  • nom : Le nom de l'objet à rez ( prévisible )
  • position : La position du rez ( où l'objet doit se trouver quand il sera rezzé )
  • vélocité : La vitesse de départ de l'objet ( dans le cas d'un objet physique )
  • rotation : Sa rotation de départ
  • nombre : Un nombre envoyé au script dans l'objet rezzé
  • rezType : le 6ème paramètre de la fonction du système sert à indiquer quel type de rez on veut utiliser (position relative à la region, locale, rotation locale etc) ne vous en préoccupez pas on verra ça plutard, notez juste qu'on met 0 quand on ne l'utilise pas.
  • données : le 7ème paramètre de cette fonction est justement celui qui servira à envoyer des données à notre objet de façon synchronisée.


Vu que notre Cube n'a pas besoin de données, le paramètre données restera vide pour ce premier exemple. Concernant la position de rez, notez qu'il n'y a pas besoin de s'embêter à utiliser llGetPos() le système le fait pour vous, ce paramètre est donc utilisé de façon locale .



Les différents types de positions (locales et non locales) sont expliqués plus bas dans le tuto mais je montre tout de même une différence :

Code PHP:


// une position non locale se fait par rapport à la région

vector positionPasLocale llGetPos() +  <0.00.02.0>;

// une position locale se fait par rapport à la position de l'objet ou de l'avatar ( ici 2 mètres au dessus de notre objet/avatar)

vector positionLOCALE = <0.00.02.0>; 



Code PHP:


SafeRezObject
string namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
llMessageLinkedLINK_SET, -1621184001
                                               
name "|" 
                                               (string)
pos "|" 
                                               (string)
vel "|" 
                                               (string)
rot "|" 
                                               (string)
chan "|" +
                                               (string)
rezTypedata 
                    
);         
}



default
{
    
    
    
state_entry()
    {
        
llSay0"Hello, Avatar !" );   
    }

    
touch_start(integer total_number)
    {
        
SafeRezObject"Cube", <0.00.02.0>, ZERO_VECTORZERO_ROTATION00"" );
    }


Le changement est très déroutant, n'est-ce pas ? . Vous remarquerez que le rez demande un peu plus de temps qu'avec la fonction llRezObject, c'est tout à fait normal cela est dû au script Rezzer qui va utiliser plusieurs processus avant de rez votre objet.





Troisième étape : Envoyer des données à l'objet


Le premier exemple n'était qu'une introduction. Nous entrons dans le vif du sujet maintenant . Le fait d'avoir testé la fonction SafeRezObject pour rez un simple cube n'a vraiment que trop peu d'intérêts et utiliserait des ressources inutilement alors que llRezObject est amplement suffisant pour ça.

Là où ça devient intéressant, c'est de faire des tests sur sa principale utilisation : Le transfert de données.


Nous allons garder notre objet tel qu'il était au premier exemple :

secondStepInventorySnap.jpg




Je vais commencer par expliquer le déroulement du transfert entre le rezzer et l'objet rezzé. Nous allons utiliser AUCUN canal fixe pour transmettre nos données, la raison est que je trouve ça totalement pas adapté et beaucoup moins sécurisé. Un canal fixe devrait être seulement utilisé pour le transfert Avatar -> Script. Il y a 3 types de système de canaux sur SL :


  • [1] Les canaux fixes ( par exemple mes 2 objets s'échangent des données sur le canal 100 )
  • [2] Les canaux dynamiques ( qui sont uniques et ne changent que selon des critères. Exemple : un canal par rapport à une key )
  • [3] Les canaux non fixes ( qui sont simplement des canaux au hasard )



Nous allons prendre le 3 ème cas, soit des canaux au hasard qui changeront à chaque rez. Voici le déroulement :

  • Le script Testeur génère un canal au hasard
  • Le script Testeur utilise la fonction SafeRezObject avec la donnée "Hello"
  • Le script Rezzer rez le Cube
  • Le Cube demande les données sur le canal généré
  • Le Rezzer envoie la donnée "Hello" au Cube




Citation :
Comment générer un canal au hasard ?
La fonction llFrand permet de générer un nombre décimal aléatoire c'est d'ailleurs ce que j'utilise. Je propose donc ma fonction qui renvoie un canal au hasard négatif :

Code PHP:

integer GetRandomChannel() {
    return ~(integer)
llFrand( (float)DEBUG_CHANNEL );   



Elle s'utilise tout bêtement de cette façon :


Code PHP:


integer monCanalAuHasard 
GetRandomChannel();

llOwnerSay"Mon canal au hasard : " + (string)monCanalAuHasard ); 

Citation :
Comment le Cube pourra connaitre ce canal au hasard ?

Comme vu plus haut, la fonction llRezObject contient 5 paramètres :

llRezObject( nom, position, vélocité, rotation, nombre );

Le paramètre nombre permet d'envoyer un nombre au script de l'objet rezzé dans l'event on_rez. C'est avec cette technique que nous transmettrons le canal au Cube.

Note : C'est le même paramètre pour la fonction SafeRezObject. Vous pouvez voir plus haut qu'elle dispose également d'un paramètre nombre.



On va donc commencer par rezzer nous-même l'objet ( nommé Cube dans mon exemple ) pour y ajouter un script. Créez un script vide et nommez-le par exemple test cube puis copiez-collez ce code :



Code PHP:

integer gCanal;

integer gListenID;

default
{
    
    
    
// Se déclenche quand l'objet est rezzé
    
on_rezinteger nombre ) {
        
        
// on reçoit le paramètre nombre
        
        
if( nombre != // on vérifie si le canal n'est pas 0
        
{
            
             
gCanal nombre// on stock le numéro du canal généré
             
             
gListenID llListengCanal""NULL_KEY"" ); // on écoute le canal
             
             
llRegionSaygCanal"get" ); // on demande les données au Rezzer  
            
        
}
        
    }
    
     
      
    
// Se déclenche quand un message est reçu sur le canal écouté   
    
listeninteger canalstring nomkey idstring msg ) {
        
         if( 
llGetOwnerKey(id) == llGetOwner() ) { // si le owner du message = mon owner
             
                
                // les données sont stockées dans msg
                
                
llOwnerSay"J'ai reçu : " msg " sur le canal : " + (string)canal );
                
                
                
// on désactive le listen une fois qu'on a reçu les données
                
llListenRemovegListenID );    
            
         }    
          
    } 
    


Enregistrez-le, prenez une copie et remplacez par l'ancien dans l'inventaire de l'objet.

Notre objet est désormais fin prêt pour recevoir les données que nous lui aurons envoyé. Il nous reste plus qu'à changer le script Testeur. Notre script est :


Code PHP:

SafeRezObjectstring namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
llMessageLinkedLINK_SET, -1621184001
                                               
name "|" 
                                               (string)
pos "|" 
                                               (string)
vel "|" 
                                               (string)
rot "|" 
                                               (string)
chan "|" +
                                               (string)
rezTypedata 
                    
);         
}



default 

     
     
    
state_entry() 
    { 
        
llSay0"Hello, Avatar !" );    
    } 

    
touch_start(integer total_number
    { 
        
SafeRezObject"Cube", <0.00.02.0>, ZERO_VECTORZERO_ROTATION00"" ); 
    } 


Ce qu'il faut ajouter :

  • Un canal généré au hasard
  • Mettre la donnée "Hello" à envoyer
  • Mettre le canal généré dans la fonction SafeRezObject


Résultat :

Code PHP:

SafeRezObjectstring namevector posvector velrotation rotinteger chaninteger rezTypestring data ) {
    
llMessageLinkedLINK_SET, -1621184001
                                               
name "|" 
                                               (string)
pos "|" 
                                               (string)
vel "|" 
                                               (string)
rot "|" 
                                               (string)
chan "|" +
                                               (string)
rezTypedata 
                    
);         
}


integer GetRandomChannel() {
    return ~(integer)
llFrand( (float)DEBUG_CHANNEL );   
}


default 

     
     
    
state_entry() 
    { 
        
llSay0"Hello, Avatar !" );    
    } 

    
touch_start(integer total_number
    { 
        
        
// on prend un canal au hasard
        
integer monCanalAuHasard GetRandomChannel();
        
// la donnée à envoyer
        
string data "Hello";
        
        
SafeRezObject"Cube", <0.00.02.0>, ZERO_VECTORZERO_ROTATIONmonCanalAuHasard0data ); 
    } 


Maintenant cliquez sur votre objet et le résultat devrait être un message comme :

Citation :
[11:47] Cube: J'ai reçu : Hello sur le canal : -1649279489
Essayez de cliquer 3 fois de suite, vous verrez qu'il y a bien une synchronisation de tous les objets :

Citation :
[11:48] Cube: J'ai reçu : Hello sur le canal : -1444719745
[11:48] Cube: J'ai reçu : Hello sur le canal : -1925779201
[11:48] Cube: J'ai reçu : Hello sur le canal : -662922049
Vous remarquerez également que le canal change pour chaque objet .




Les positions Locales / Non locales


Introduction

Une région ou sim est un espace en 3 dimensions et donc 3 axes que l'on note X, Y et Z . Pour mettre nos objets dans une région nous avons besoin de connaitre exactement où les placer. Pour ce faire, nous utilisons 3 axes qui représentent un point dans l'espace de cette région.

X et Y sont tous les deux des axes correspondants à la surface de la région. Voici une sim vu en hauteur avec les 2 axes X et Y :

mapExe.jpg

Le 3 ème, Z est la hauteur dans une région.

Sur SecondLife, les 3 axes sont en mètres et une région fait 256 mètres en longueur comme en largeur. Ce qui fait qu'une sim en surface va de :

X : 0, Y : 0
à
X : 256, Y : 256

Comme vous avez déjà pu le voir en LSL on affiche les 3 axes dans un vecteur qui se présente sous cette forme :

<X, Y, Z>

quelques exemples :

<125, 147, 236>
<12, 0, 147>
<256, 256, 23>

pour être plus précis on peut également mettre des nombres décimaux mais attention : Un nombre décimal ne s'écrit pas avec une virgule mais un point en LSL

Exemple d'un nombre décimal :
12.7 ( qui est 12,7 )

Exemple d'un vecteur avec nombres décimaux :

<12.3, 147.85, 212.1>
<X, Y, Z>



Les positions Non locales :


Les positions non locales correspondent à un point dans la région, comme je l'ai expliqué dans l'introduction. Par exemple je veux envoyer mon objet au milieux de la sim, on a vu qu'une sim faisait 256 mètres en longueur/largeur il faudra simplement utiliser le vecteur :

<128, 128, Z>

Note : Je n'ai pas précisé le Z tout simplement car ça n'a pas d'importance puisqu'il s'agit de la hauteur, toute valeur différente de Z ne changera rien, l'objet sera toujours au milieux de la sim.


Les positions locales :


Après les positions relatives à la région (non locales), nous voilà rendu aux locales. C'est tout aussi simple à comprendre, je vais essayer d'expliquer par plusieurs exemples simples .


Imaginons que nous avons notre objet Rezzer à cette position :

<112, 105, 28>

Et que nous voulons rez notre objet Cube à 2 mètres de distance (à côté de l'objet Rezzer) sur l'axe X .

Le vecteur du Cube sera soit :

<114, 105, 28> car 112 + 2 = 114

Ou :

<110, 105, 28> car 112 - 2 = 110


Il faut donc utiliser l'addition ou la soustraction (cela dépendra le sens de l'axe X que vous utiliserez ). Nous avons donc 2 vecteurs à additionner ou soustraire qui sont :

La position de notre objet Rezzer :
<112, 105, 28>

Le vecteur avec les 2 mètres de distance sur l'axe X :
<2, 0, 0>


ce qui donne en position NON locale :

<112, 105, 28> + <2, 0, 0> = <114, 105, 28>

ou :

<112, 105, 28> - <2, 0, 0> = <110, 105, 28>

J'ai bien précisé NON-Locale car on se réfère à un point par rapport aux axes de la région .

Maintenant, imaginons que notre position doit se référer aux axes de notre objet Rezzer. Son centre sera donc le vecteur :

<0, 0, 0>

Pour indiquer que nous voulons que notre Cube se rez à 2 mètres de distance de notre Rezzer, il faut additioner ou soustraire le centre de notre objet Rezzer comme ceci :

<0, 0, 0> + <2, 0, 0> = <2, 0, 0>

ou :

<0, 0, 0> - <2, 0, 0> = <-2, 0, 0>

Ce qui donne <2,0,0> ou <-2,0,0> qui sont des positions locales par rapport au Rezzer.
2 ème partie
Quatrième étape : Utilisation du Proxy

Nous en sommes toujours à notre objet et nos 2 scripts Testeur/Rezzer. Nous allons continuer avec ces exemples pour éviter de tout refaire . Je montre une nouvelle fois la composition de l'inventaire de l'objet :


secondStepInventorySnap.jpg

Rapide explication :

Quand vous êtes dans une parcel no build ou au dessus de 4096 mètres un script ne pourra pas utiliser la fonction llRezObject. Ce qu'il peut faire en revanche c'est utiliser un objet scripté qui va rez l'objet pour lui et ce à distance. Ce que j'appelle un proxy.


Pour commencer nous allons créer notre proxy. Créez une sphère avec comme taille :

X : 0.1
Y : 0.1
Z : 0.1


et mettez-la rouge et phantom. Bien sûr ces paramètres n'ont aucun effets sur le script, vous pouvez mettre ce que vous voulez je donne juste un exemple .


Créez un script vide dedans, nommez-le comme vous le souhaitez. Pour ma part je l'ai appelé "ProxyScript". Copiez-collez ce code :


Code PHP:

// PROXY


float gProxyLife;

key gRezzerKey;

integer gListenID;
integer gChan;

string PRIM_SEP "|";

default
{
    
    
on_rezinteger chan ) {
        
        if( 
chan != ) {     
            
gChan chan;
            
gListenID llListenchan""NULL_KEY"" );
            
llRegionSaychan"get" );
        }   
        
    }
    
    
    
    
listeninteger chanstring namekey idstring msg ) {
        
        if( 
llGetOwnerKey(id) == llGetOwner() ) {
            
            
// on reçoit les données PROXY_LIFE
            
gProxyLife = (float)msg;
            
gRezzerKey id;
            
llSetPrimitiveParams( [PRIM_TEMP_ON_REZFALSE] );
            
            
state Active;
              
        }   
        
    }
    
}



state Active
{
    
    
    
state_entry() {
        
        
llOwnerSay"Je suis activé ! life=" + (string)gProxyLife " secondes." );
        
gListenID llListengChan""gRezzerKey"" );    
        
llResetTime();
        
llSetTimerEvent5.0 );
    }
    
       
    
    
listeninteger chanstring namekey idstring msg ) {
        
        
// on reçoit une requête de rez à distance
        
list packet llParseString2Listmsg, [PRIM_SEP], [] );  
        
        if( 
llGetInventoryType(llList2String(packet0)) == INVENTORY_OBJECT ) {
            
            
llRezObject
                            
llList2String(packet0), // name
                            
llGetPos(), 
                            (
vector)llList2String(packet1), // vel
                            
(rotation)llList2String(packet2), // rot
                            
(integer)llList2String(packet3// chan
                        
);     
        } 
        
    }
    
          
             
    
timer() {
        
// si son temps de vie est dépassé ou le owner n'est plus dans la sim
        
if( llGetTime() >= gProxyLife || llGetAgentSize(llGetOwner()) == ZERO_VECTOR )
            
llDie();    
    }   
    

Et enregistrez-le. Ensuite, nommez votre sphere "MyProxy", mettez-la temporaire et prenez une copie.

Récapitulatif en images :


ballSize.jpg proxyColor.jpg proxyScript.jpg proxyName.jpg proxyTemp.jpg


Et placez le proxy dans votre objet. Au final l'inventaire doit se composer comme ceci :

objectInv4Step.jpg

Il faut maintenant activer la fonction de proxy dans le script Rezzer. Ouvrez-le, au milieux du script vous devriez voir ces lignes :

Code PHP:

//----------------------------------------------
//                  PARAMETERS
//----------------------------------------------
// activer ou désactiver la fonction du proxy.
integer PROXY_ACTIVE FALSE;
// le délais de rafraichissement de timer de vérification
float PROXY_REFRESH 5.0;
// le nom du proxy dans l'inventaire
string PROXY_NAME "MyProxy";
// le temps de vie du proxy
float PROXY_LIFE 600.0// 10 minutes
// rez d'un autre proxy X secondes avant que l'ancien se supprime 
float PROXY_TIME_REZ 20.0;
// offset de rez du proxy
vector PROXY_REZ_OFFSET = <0.00.02.0>; 
Elles sont toutes expliquées. Comme vous devez vous en douter, il faut changer la ligne :

Code PHP:

integer PROXY_ACTIVE FALSE
en :

Code PHP:

integer PROXY_ACTIVE TRUE
Et sauvegardez. Si tout se passe bien, votre sphere devrait apparaitre 2 mètres au dessus de votre objet et vous envoyer un message :

Citation :
[14:11] MyProxy: Je suis activé ! life=600.000000 secondes.
Vous avez activé la fonction de proxy .

Un petit test ?

Prenez une copie de votre objet et supprimez le proxy déjà rez. Attachez votre objet, attendez qu'un autre proxy se rez et allez au dessus de 4096 mètres ou dans une parcel no build. Puis cliquez dessus. Si tout se passe bien les 2 messages seront :

Citation :
[14:23] MyProxy: Je suis activé ! life=600.000000 secondes.
[...]
[14:24] Cube: J'ai reçu : Hello sur le canal : -2118967937

Les paramètres du proxy dans le script Rezzer :

Code PHP:

//----------------------------------------------
//                  PARAMETERS
//----------------------------------------------
// activer ou désactiver la fonction du proxy.
integer PROXY_ACTIVE FALSE;
// le délais de rafraichissement de timer de vérification
float PROXY_REFRESH 5.0;
// le nom du proxy dans l'inventaire
string PROXY_NAME "MyProxy";
// le temps de vie du proxy
float PROXY_LIFE 600.0// 10 minutes
// rez d'un autre proxy X secondes avant que l'ancien se supprime 
float PROXY_TIME_REZ 20.0;
// offset de rez du proxy
vector PROXY_REZ_OFFSET = <0.00.02.0>; 
PROXY_ACTIVE : Mettez TRUE ou FALSE pour Activez/Désactiver la fonction de proxy
PROXY_REFRESH : Le script Rezzer vérifie toutes les X secondes si un proxy est dans la région, dans le cas contraire il va en rez un autre.
PROXY_NAME : Le nom du proxy dans l'inventaire ( par defaut : MyProxy )
PROXY_LIFE : Le temps de vie d'un proxy. Par défault il est réglé à 600 secondes ( 10 minutes ). Note : si vous changez de région les proxy s'auto-détruiront eux-même.
PROXY_TIME_REZ : Définit la fin de vie d'un proxy, quand ce temps est atteint, un autre proxy sera rez avant que l'autre se supprime.
PROXY_REZ_OFFSET : La position locale de rez du proxy par rapport à l'objet, voir au dessus pour une explication sur les positions locales.







Pour aller (un peu) plus loin

Depuis la mise à jour du script, il est possible d'utiliser plusieurs types de position/rotation avec le paramètre rezType .


Pour les positions :


Il y a 3 types de position utilisable avec la fonction SafeRezObject :

  • [1] Local : Qui correspond comme son nom l'indique à une position locale ( type par défaut)
  • [2] Region : Si vous avez besoin d'utiliser des positions relatives à la région, c'est possible
  • [3] Local Rotated : Position locale par rapport à la rotation de l'objet/avatar.

[3] Local Rotated : Ce type de position permet de définir une position locale qui changera selon la rotation de l'objet ou de l'avatar. Exemple : si je veux rez un objet toujours à droite de mon avatar.



Pour les rotations :

Il y a 2 types de rotation :


  • [1] Region : Rotation par rapport à la région (par défaut)
  • [2] Local : Rotation locale par rapport à l'objet/avatar



Voici les flags pour changer les types :

Code PHP:

//----------------------------------------------
//                   REZ TYPE
//----------------------------------------------
// rez par position relative à la région
integer POS_TYPE_REGION 0x01;
// rez par position locale selon la rotation de l'objet/l'avatar
integer POS_TYPE_LOCAL_ROTATED 0x02;
// rez par rotation locale
integer ROT_TYPE_LOCAL 0x04


Petit exemple pour position relative :

Code PHP:


SafeRezObject
"Cube"llGetPos() + <2.00.00.0>, ZERO_VECTORZERO_ROTATION0POS_TYPE_REGION"" ); 
Et si je veux utiliser en plus les rotations locales :


Code PHP:


SafeRezObject
"Cube"llGetPos() + <2.00.00.0>, ZERO_VECTORZERO_ROTATION0POS_TYPE_REGION|ROT_TYPE_LOCAL"" ); 










Conclusion :

Ce type de système permet de pas mal simplifier vos scripts en vous débarrassant des tâches de synchronisations et de vérifications. N'hésitez pas à changer les paramètres et recommencer avec d'autres objets, tout ça reste très basique et faisait office d'exemple. Je publierais un ou plusieurs exemples concrets par la suite.

Si vous avez des questions ou des suggestions sur les scripts (optimisations etc), n'hésitez pas
Tout d’abord bravo pour la clarté de tes explications et de ton code. Tu vas me donner des complexes avec le bordel que je mets dans mes scripts ! Puisque tu demandes si on a des suggestions je me permets de t’en faire quelques unes que je te livre en vrac.


Puisque tu abordes la fonction llRezObject je vais en profiter pour donner une précision pour ceux qui lisent ton tuto au sujet de la position et de la rotation passées en paramètres. Il s’agit de coordonnées régionales, ce qui signifie que la position et la rotation de l’objet rezzeur n’ont aucune importance mais... cela pourrait se comprendre s’il n’y avait pas cette limitation de 10 mètres. Avec cette limitation ça devient ridicule. Cette fonction serait beaucoup plus pratique avec des coordonnées locales. D’ailleurs j’ai rarement vu un code où on ne soit pas obligé de faire cette transposition au niveau des paramètres. J’en viens donc à ta proposition : elle serait plus pertinente si elle autorisait le passage des paramètres de position et rotation en valeurs locales.


Au passage un test de la limitation des 10 mètres serait le bienvenu pour éviter un échec silencieux du rez.


Au niveau de ton code une chose me gêne : dans le cas du proxy il y a une double conversion des paramètres un peu lourde, il serait peut-être plus judicieux d’organiser le code pour l’éviter.


Ta fonction CorrectVectorPos renvoie une position corrigée ramenée aux limites possibles mais tu n’utilises pas le vecteur corrigé :


// Si il y a possibilité de rez à la position
if( CorrectVectorPos(pos) == pos && ParcelCheck(pos) ) {


Si la position de rez est en dehors des limites de la région tu actives le proxy, je ne pense pas qu’il puisse grand-chose dans ce cas. Il faudrait limiter le test à la pertinence effective du proxy :
  • hauteur (de l’objet rezzeur ?) à plus de 4096 mètres (ce qui n’arrive pas tous les jours),
  • ou position de rez à plus de 10 mètres de l’objet rezzeur (ce qui est beaucoup plus fréquent),
  • ou parcelle où se trouve l’objet rezzeur no build. (ce qui peut souvent arriver).
Et pour la position de rez en dehors de la région décider si tu corriges d’office la position ou si tu lances un échec.


Pour en revenir au proxy, si tu lui transmets une position de rez qui tombe à plus de 10 mètres de sa position il sera incapable de rezzer l’objet, ce qui serait intéressant serait qu’il puisse se déplacer à ce moment là pour permettre le rez effectif. D’ailleurs j’ai du mal à imaginer un cas pratique d’utilisation du proxy. Si l’objet rezzeur se trouve dans une parcelle no build ou au-delà de 4096 mètres le proxy ne pourra pas se régénérer lorsqu’il arrive à sa limite de vie et le fait de sa génération à proximité lui retire une grande partie de son intérêt.


Si j’imagine un scénario : je porte l’objet rezzeur sur moi et je veux rezzer un objet à un emplacement bien particulier en coordonnées régionales. Soit je suis à proximité et je rezze l’objet directement, soit je suis allé me promener dans la région en ayant pris soin au préalable de laisser traîner dans le coin un proxy et mon objet rezzeur va envoyer un ordre au proxy de rezzer l’objet. Si je dépasse le délai de vie du proxy je perds ma faculté de rezzer là où je voulais le faire.


En résumé ton proxy me laisse un peu perplexe par contre j’aime bien l’aspect sécurisation et systématisation du rez mais je le vois mieux en coordonnées locales, ce qui me paraît beaucoup plus logique. D’ailleurs il serait peut-être intéressant de créer une pseudo surcharge de la fonction. Certains paramètres sont rarement utilisés (par exemple la vélocité) ou alors sont systématiquement à leur valeur par défaut (par exemple la rotation). Le test du nombre de paramètres et leur type pourraient alors suffire :


Paramètres obligatoires :
  1. nom
  2. position
Paramètres optionnels :
  1. canal
  2. data
  3. rotation
  4. vélocité
Voilà pour mes suggestions à la première lecture de ta proposition en espérant ne pas avoir compris de travers certaines choses, mais si c’est le cas je sais que tu me le diras !
Citation :
Publié par bestmomo
D’ailleurs j’ai rarement vu un code où on ne soit pas obligé de faire cette transposition au niveau des paramètres. J’en viens donc à ta proposition : elle serait plus pertinente si elle autorisait le passage des paramètres de position et rotation en valeurs locales.
C'est vrai que la position locale par rapport à l'objet est beaucoup plus pratique dans ce cas mais j'ai fait ce choix dans le but de garder une ressemblance/cohérence avec la fonction llRezObject. Cependant une seconde fonction avec positions/rotations locales est une très bonne idée !


Citation :
Publié par bestmomo
Au passage un test de la limitation des 10 mètres serait le bienvenu pour éviter un échec silencieux du rez.
Effectivement je corrigerais le code ce weekend pour y ajouter cette vérification .


Citation :
Publié par bestmomo
Au niveau de ton code une chose me gêne : dans le cas du proxy il y a une double conversion des paramètres un peu lourde, il serait peut-être plus judicieux d’organiser le code pour l’éviter.

Je pourrais changer la fonction pour y accepter un seul paramètre (une list ou une string comprenant les paramètres non parsés), le seul problème étant si on veut scripter à partir de ce code, le modifier (au lieu d'utiliser un second script), ça peut devenir beaucoup moins pratique. Je n'ai pas fait de tests là dessus mais je doute que les cast ralentissent énormément. Ca reste à vérifier.



Citation :
Publié par bestmomo
Ta fonction CorrectVectorPos renvoie une position corrigée ramenée aux limites possibles mais tu n’utilises pas le vecteur corrigé

Le principe que j'utilise est de vérifier si la position est en dehors des limites possibles de build (si la position donnée == la position corrigée alors il n'y pas de dépassement), dans le cas contraire le proxy se charge de rez l'objet (si il est disponible sur la région). D'ailleurs ton idée sur la distance de 10 mètres est très intéressante dans le cas d'une utilisation du proxy, j'en prend note .


Citation :
Publié par bestmomo
Pour en revenir au proxy, si tu lui transmets une position de rez qui tombe à plus de 10 mètres de sa position il sera incapable de rezzer l’objet
Le proxy ne prend pas en compte la position donnée dans la fonction SafeRezObject, il va utiliser un simple llGetPos pour rez. Je trouve qu'il y a vraiment très peu d'intérêts à rez l'objet avec l'offset donné ou la position car si il y a rez à distance, la position n'aura vraiment que trop peu d'importances. La chose qui compte le plus étant de pouvoir rez son objet dans ce genre de situation.



Citation :
Publié par bestmomo
Si l’objet rezzeur se trouve dans une parcelle no build ou au-delà de 4096 mètres le proxy ne pourra pas se régénérer lorsqu’il arrive à sa limite de vie et le fait de sa génération à proximité lui retire une grande partie de son intérêt.

Si j’imagine un scénario : [...]

Le proxy est en fait une sorte de "plugin" très simple (comme tu as pu le voir le principe est très basique, on envoie l'objet au proxy via giveinventory pour qu'il le rez lui-même) c'est un peu une technique alternative rapide pour pas se retrouver bloqué avec ces limitations. J'ai justement mis une constante de vie dans le script Rezzer on peut très bien la mettre à 10 minutes comme 3 jours pour empêcher une régénération trop fréquente, ce qui pourrait poser problème c'est les parcels ayant un autoreturn assez rapide . Mais il est vrai que j'aurais pu utiliser le proxy en fin de vie pour en rez un autre dans le cas de la non disponibilité du rez.


Sinon pour les paramètres je pourrais changer un peu la structure et proposer plusieurs sortes. En tout cas merci de ton intervention et de tes idées ça va m'aider à améliorer le script .
Tuto mis à jour, merci encore à bestmomo pour son aide.

Les changements :

  • On peut choisir son type de position/rotation (locale/non-locale) [expliqué à la fin du tuto]
  • Les rez à plus de 10 mètres de distance seront réduits automatiquement pour éviter une erreur silencieuse
  • Le script utilise un proxy en fin de vie pour en rez un autre
  • Ajout d'une explication sur les positions locales/non-locales (fin de la 1ère partie)

Si par mégarde je ne me suis pas aperçu d'un bug dans la màj du script, merci de m'en informer que je puisse le corriger .
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés