La mise en page de votre code doit suivre certaines régles :
Le nesting
quand un code est contenu dans des balises {} il faut ajouter une tabulation à toute ses lignes. Plus le code est “profond” c’est à dire utilisé dans des balises qui sont elles meme dans des balises {{}} plus on ajoute de tab
Si on applique la règle des tab alors on distingue beaucoup mieux les ensembles et sous ensembles du code.
Il est également conseillé de construire la logique de son code de manière à limiter les sous ensembles
Evitez de :
Déclarer des fonctions dans des fonctions dans des fonctions
Imbriquer beaucoup de if(if(if()))
Essayons de diminuer le “nesting” de cette fonction qui filtre des nodes selon 3 conditions :
// on cherche un read avec le mots clef '_B' et dont l'attribu "POSITION.X" est suppérieur à 20constsnode= selection.selectedNode(0)functionis_what_we_want(_node){if(node.type(_node)=="READ"){// de type "READ"if(node.getName(_node).indexOf("_B")!=-1){// a le _B dans son nomif(node.getTextAttr(_node,"POSITION.X",0)>20){// c'est bien ce qu'on cherche ! returntrue } } }// toute les conditons n'ont pas été rempliesreturnfalse}MessageBox.information(is_what_we_want(snode))
Ce code a un peu une sorte de scoliose non ?
Ce genre de code devient de plus en plus dure à lire à force de rajouter des conditions
Deux solution au “nesting”:
1 utiliser une syntaxe booléenne : on concatène nos conditions en une seul syntaxe booléenne , c’est concis mais un peu dur à lire
// on cherche un read avec le mots clef '_B' et dont l'attribu "POSITION.X" est suppérieur à 20constsnode= selection.selectedNode(0)functionis_what_we_want(_node){// on stock les conditions dans des variables varis_read = node.type(_node)=="READ"varas_kw = node.getName(_node).indexOf("_B")!=-1varpos_20 = node.getTextAttr(_node,"POSITION.X",0)>20// on test d'un coup si elles sont toutes vrais avec l'opérateur &&returnis_read && as_kw && pos_20}MessageBox.information(is_what_we_want(snode))
2 inverser les conditions : au lieu de vérifier si une condition est “true” on vérifie si l’inverse de la condition est “true”. De cette manière on return très tôt dans la fonction. Si l’inverse est true pas besoin d’aller plus loin…
// on cherche un read avec le mots clef '_B' et dont l'attribu "POSITION.X" est suppérieur à 20constsnode= selection.selectedNode(0)functionis_what_we_want(_node){if(node.type(_node)!="READ"){// n'est PAS de type read !returnfalse }if(node.getName(_node).indexOf("_B")==-1){// n'a PAS '_B' dans son nomreturnfalse }if(node.getTextAttr(_node,"POSITION.X",0)<=20){// POSITION.X est INFERIEUR ou égale à 20 returnfalse }// tout les obstacles sont franchi ! ce node est bien celui qu'on cherchereturntrue}MessageBox.information(is_what_we_want(snode))
Les for loop sont présents dans quasiment tout les scripts Harmony. Le role du for loop est de faire “boucler” le bloc de code entre {} un certain nombre de fois.
On s’en sert typiquement pour exécuter un bloc de code sur chaque élément d’une array.
Ecrivons un script qui met la position en Z de tout les nodes sélectionnés à 0
On pourrait écrire une première version du code comme ceci :
Le problème avec ce code c’est qu’il ne fonctionnera que si l’utilisateur n’a sélectionné précisément que 7 nodes. Il faudrait un code qui fonctionne avec 0 ou plusieurs nodes sélectionnés .
On pourra améliorer le code comme ceci :
constsnodes = selection.selectedNodes()functionset_z_to_zero(_node){node.setTextAttr(_node,"POSITION.Z",frame.current(),0)}varindex = 0// on test si l'index existes dans l'array snode : if(typeofarrayName[index] !== 'undefined'){// il y a un node à cet index on peut lui appliquer la fonction set_z_to_zero(snodes[index ])}// on incrémente l'index -> 1index+=1if(typeofarrayName[index] !== 'undefined'){// il y a un node à cet index on peut lui appliquer la fonction set_z_to_zero(snodes[index ])}// on incrémente l'index -> 2index+=1if(typeofarrayName[index] !== 'undefined'){// il y a un node à cet index on peut lui appliquer la fonction set_z_to_zero(snodes[index ])}// on incrémente l'index -> 3index+=1if(typeofarrayName[index] !== 'undefined'){// il y a un node à cet index on peut lui appliquer la fonction set_z_to_zero(snodes[index ])}// on incrémente l'index -> 4index+=1if(typeofarrayName[index] !== 'undefined'){// il y a un node à cet index on peut lui appliquer la fonction set_z_to_zero(snodes[index ])}
C’est un peu mieux, l’utilisateur peut maintenant sélectionner entre 0 et 5 nodes et le code ne crash pas car on test à chaque foi si l’index existe.
ça fonctionne, mais Normalement vous devriez ressentir une sorte de profond désarroi esthétique en voyant ce code.
Il faudrait que l’utilisateur puisse selectionner autant de nodes qu’il souhaite
Essayons avec le mots clé while.
Le mots-clé while exécute un bloc de code tant qu’une proposition booléenne est vrais. Comme dans un if, la proposition booléenne ou condition se trouve entre parenthèses après le while.
while ( a < b ) {}
Si dans votre description du code vous utilisez le mots “tant que” il est fort probable que vous utiliserez le mots clef “while”
constsnodes = selection.selectedNodes()functionset_z_to_zero(_node){node.setTextAttr(_node,"POSITION.Z",frame.current(),0)}// on commence à l'index 0 de l'array varindex = 0// tant que l'index n'est pas égal au dernier index ( nombre d'élément dans l'array -1 )while (index < snodes.length-1){// on applique la fonctionset_z_to_zero(snodes[index ])// on augment l'index pour passer à l'élément suivantindex+=1}
On gagne tout de suite un peu d’espace mais ça fait encore pas mal de lignes.
Personnellement je déconseille l’utilisation du mots-clé while car si il y a une erreur dans la condition le code peut facilement boucler à l’infini.
Ce code par exemple comptera jusqu’à l’infini (enfin jusqu’à ce que Harmony crash)
// a ne pas utiliser sauf si vous voulez faire crasher harmony biensur ! varlimit = 100varnumber = 0while(limit < 200){ // cette condtion est toujours true car limit ne change pas !number+=1MessageLog.trace(number)}// la partie du code qui ne sera jamais exécutée car on se sortira jamais de cette boucle whileMessageLog.trace("code finito!")
le mots-clé for
Pour palier aux defaults du while: le for nous permet de boucler mais d’une manière plus contrôlée ! voici sa syntaxe :
for ( var i = 0 ; i < my_int ; i++ ) {}
constsnodes = selection.selectedNodes()functionset_z_to_zero(_node){node.setTextAttr(_node,"POSITION.Z",frame.current(),0)}// on retrouve le code du while mais à l'horizontale : for(varindex = 0 ; index < snodes.length ; index++){ set_z_to_zero(snodes[index])}
On ne peut pas faire plus concis ! 3 lignes
Néanmoins cette syntaxe devrait vous perturber un peu :
des “;” dans des parenthèses ? c’est une sorte de micro code ?
Qu’est ce qui se passe dans ces parenthèses ?
Pour comprendre un for Loop il faut bien comprendre les trois blocs contenus dans ses parenthèses ()
Bloc Initialisation : on déclare une variable d’index ( souvent i ) et on lui attribue une valeur de départ ( souvent 0 ) . C’est une déclaration de variable normale vous pouvez lui donner le nom et la valeur que vous voulez.
Bloc Condition : fonctionne comme un while , si la condition est true on exécute le code dans le bloc d’exécution ainsi que celui contenu entre les {}
Bloc Exécution : une ligne de code à exécuter quand la condition est true. Dans 99% des cas le code exécuté incrémente l’index (i++)
On commence par l’initialisation puis on exécute le 3eme bloc et le code entre {} tant que la condition du deuxième bloc est true !
Quelques exemples de ce qu’on peut trouver dans les parenthèses
Initialisation
Condition
Exécution
(var index = 0 ;
index < 10;
index++)
(var i = 0 ;
i < arr.length;
i++)
(var s = 0;
s < snodes.length;
s++)
(var j = arr.length-1;
j >= 0;
j–)
Comportement de chaque parenthèses
for 1—> exécute le code 10 fois
for 2—> exécute le code pour chaque élément de l’array
for 3—> exécute le code pour chaque node sélectionné
for 4—> exécute le code pour chaque node sélectionnédu dernier au premier
Je vous conseille de tester le code suivant pour bien comprendre le comportement des différents for loop :
constsnodes = selection.selectedNodes()// executer le code X fois constX = 10for(vari = 0; i < X ; i++){MessageLog.trace(i)}// parcourir une array en commençant le premier element [0]for(vari = 0 ; i < snodes.length ; i++){MessageLog.trace(i)MessageLog.trace(snodes[i])}// parcourir une array en commençant par le dernier element [length-1] et en comptant à rebourt i--for(vari = snodes.length-1 ; i >= 0 ; i--){MessageLog.trace(i)MessageLog.trace(snodes[i])}
Court-circuiter la boucle : continue et break
Et si le node selectionné courant snodes[i] n’est pas un PEG ? Si, par exemple il est de type ‘COMPOSITE’
Cela ne nous intéressera pas de changer sa position en Z car il n’en a pas. Donc nous risquons de perdre une boucle pour rien, et donc de gaspiller des ressources en calcul.
Comment pourrait-on “sauter” cette boucle ?
Comment pourrait-on et passer à la boucle suivante si le node n’est pas de type ‘PEG’ ?
constsnodes = selection.selectedNodes()functionset_z_to_zero(_node){node.setTextAttr(_node,"POSITION.Z",frame.current(),0)}for(varindex = 0 ; index < snodes.length ; index++){if(node.type(snodes[i])!="PEG"){// ce n'est pas un peg }// essaye quand meme de changer son Z ? set_z_to_zero(snodes[index])}
Le mots-clé “continue”
Le mots clé continue dis à l’ordinateur ‘pas besoin de lire les lignes suivante , passe à la boucle suivante avec une nouvelle valeur d’index (i+1)’ ‘pioche une nouvelle carte’
constsnodes = selection.selectedNodes()functionset_z_to_zero(_node){node.setTextAttr(_node,"POSITION.Z",frame.current(),0)}for(varindex = 0 ; index < snodes.length ; index++){// est ce que c'est un peg ? if(node.type(snodes[i])!="PEG"){// ce n'est pas un PEG passe à la boucle suivante !continue }// c'est un peg tu peux executer la suite du code de la boucle set_z_to_zero(snodes[index])}
continue est très puissant pour optimiser un scripte. Il vous entraine aussi à bien définir les cas où il est utile d’exécuter une ligne ou non.
Si maintenant j’écris un for loop qui cherche quelque chose. Mettons le premier node dans la sélection qui contient la chaîne de charactères : “HEAD” si je veux trouver le node de la tête dans un rig.
constsnodes = selection.selectedNodes()varfound_node = ""for(varindex = 0 ; index < snodes.length ; index++){constnode_name = node.getName(snodes[i])if(node_name.indexOf("HEAD")!=-1){// on a trouvé un node avec HEAD ! on peut ranger son chemin dans la variable 'found_node'found_node = snodes[i] }// on continue à chercher meme si on a dit qu'on en voulait qu'un seul }
Ce code n’est pas très optimal ! est ce que vous comprenez pourquoi ?
Imaginons un pirate qui parcourt une liste de 4 îles pour retrouver son trésor oublié. Imaginons qu’il trouve son trésor dans la deuxième île mais qu’il continue quand même à chercher dans les îles 3 et 4 pour voir si le trésor qu’il vient de trouver ne s’y trouve pas… (oui c’est un pirate fou).
Eh bien ce code se comporte un peu comme le pirate. Il parcourt tout les nodes, même si il a trouvé celui qu’il cherchait.
Il faudrait trouver un moyen de lui dire d’arrêter de chercher :
c’est le mots-clé break
constsnodes = selection.selectedNodes()varfound_node = ""for(varindex = 0 ; index < snodes.length ; index++){constnode_name = node.getName(snodes[i])if(node_name.indexOf("HEAD")!=-1){found_node = snodes[i]// on a trouvé le node ! tu peux t'arrêter là ! break }}
Le mots-clé break permet de sortir de la boucle
Quand l’ordinateur rencontre le mots-clé break dans une boucle il saute directement après le } de la boucle et lit les lignes suivantes du programme. On gagne ainsi en temps d’exécution ! surtout si quantité parcourue est grande.
La Notation condensée ‘for in’
il est possible d’écrire une boucle qui parcourt une array avec une syntaxe encore plus condensée :
for ( var i in my_array ) {}
Avec ce que nous venons de voir vous devriez comprendre ce que fait le code suivant
Exactement la même chose qu’avec un for loop classique !
Attentions cette notation est trompeuse car beaucoup de choses sont implicites :
Ce code parcourt l’array en commençant par l’index 0 et en incrémentant de 1 jusqu’a la fin de l’array.
Cette notation condensée existe car c’est le type de for loop qu’on utilise dans la grande majorité des cas avec une array. Pas besoin d’écrire .length ou i++ : tout est inclu dans le “in” , mais ses possibilités sont limitées !