Aide pour After Effect, techniques, expressions.

mercredi 22 avril 2009

je t'aime, moi non plus.











Aujourd'hui, quelque chose de rapide et simple.
Utilisons les vecteurs autrement. Nous allons faire en sorte que des objets soient attirés ou repoussés pas un autre.
Ne vous attendez pas à une entière simulation du champs électromagnétique, il s'agit juste d'une petite astuce.

Créez un Null "controle", et rajoutez lui 4 paramètres glissière : min, max, force et chaos.
Créez un solide, puis un autre, plus petit, et ouvrez la position de ce dernier.
C'est parti.

min=thisComp.layer("controle").effect("min")("Curseur");
max=thisComp.layer("controle").effect("max")("Curseur");
force=thisComp.layer("controle").effect("force")"Curseur")/100;
chaos=thisComp.layer("controle").effect("chaos")("Curseur");


J'ai divisé force par 100, car ça sera un réglage fin.

seedRandom(5,true);
hasard=random(-chaos,chaos);

Ça, ça sera donc pour la partie hasard si on en veut.

target=thisComp.layer("target").transform.position;
P=position;

vecteur=sub(P,target);
distance=length(vecteur);
F=ease(distance,min,max,force+chaos,0);

P+vecteur*F

Voilà, vous connaissez déjà tous les outils. On crée le vecteur qui va de la cible au calque, et on fait en sorte que plus la cible est proche, plus on "rajoute du vecteur" à la position du calque.
Comme on multiplie le vecteur par F, qui est au final la valeur de la force, vous comprenez pourquoi "force" doit rester petit.

Petite astuce, si vous rajouter à peu près la même expression dans l'échelle, en faisant en sorte, avec un truc du genre:

ease(distance,min,max,150,100);

Les calque grossiront quand ils seront proches de la cible. On aura ainsi l'impression qu'ils passent au dessus du calque cible.

Si vous mettez une valeur de force négative, les calques seront attirés par la cible.
Seul bémol de ce système D, le déplacement se fait par rapport à la position du calque AVANT l'expression. Donc si la cible passe très proche du calque dans sa position originale, le calque passera proche de la cible.
Mais bon, hein....

Il y a plein d'utilisation de cette expression, on peut aussi animer les valeurs max et force.

dimanche 19 avril 2009

marchons carré















L'astuce du jour à plusieurs mérites :

-La technique est relativement simple, vous en connaissez tous les outils (si vous avez tout lu!), la difficulté se trouve plutôt dans la traduction de ce que l'on veut produire dans After Effect.
-Cette animation est infaisable avec des clefs.

Je parle de mérite, car ce sont les deux points principaux que j'essaye de développer sur ce blog.

Aujourd'hui, nous allons faire marcher un carré !


Alors alors....Analysons ce que nous voulons dire à After Effect.
Nous allons devoir créer une expression pour la position. Le mouvement sur X est simple, et celui sur Y sautille. Nous devons également créer une expression pour la rotation, et que tout ce beau monde soit synchrone.

C'est parti.
Créez un solide carré, la taille vous regarde.
Rajoutez lui un paramètre glissière, et nommez ce dernier "vitesse". Mettez 300 dans ce paramètre glissière comme valeur de base.

Commençons par la position. Pour le moment, on ne vas s'occuper que de X.

temps=time-inPoint;
V=effect("vitesse")("Curseur");

x=temps*V;

y=value[1];


[x,y]+value


Rien de bien méchant, on demande à ce que le carré se déplace de 300 pixels/sec vers la droite.
On fera Y après. Comme on l'a déjà vu, temps=time-inPoint permet d'avoir une variable de temps qui comme à 0 au moment où le calque commence.

Passons à la rotation.
Commençons par comprendre ce dont on a besoin.
Plus le carré est grand, moins il doit faire de rotation pour une même distance.
En fait, à chaque fois qu'il parcoure une distance égale à la longueur de son coté, il fait une rotation de 90° :

temps=time-inPoint;
V=effect("vitesse")("Curseur");
cote=width*scale[0]/100;
x=temps*V;

Rien de bien méchant, on retrouve ainsi la valeur de X, qui nous donne la distance parcourue par le carré.
On a également créer "cote". Il vaut la largeur multipliée par l'échelle, divisé par 100. Ainsi, on pourra modifier la taille du carré directement par l'échelle sans passer par les propriétés du calque. Je divise par 100 puisque l'échelle 1 vaut 100.

Donc on a dit que pour chaque
"cote" parcouru, le carré tourne de 90°. on a donc :

(x/cote)*90

Et voilà, on a écrit notre rotation. Il y a plein de façon de trouver la même valeur, c'en était une.

Passons à Y.
Si on regarde notre animation, on comprend qu'à chaque fois que notre carré est à 45°, il doit être au plus haut, en tenant donc sur son coin du bas. Il faut donc trouver de combien.

Comme vous le savez, a²+b²=c² quand abc est un triangle rectangle donc C est l'hypothénuse.
Ici, nous avons un carré, donc a=b. Donc imaginons que a=1, on a donc c=racine carrée de 2.
Cette valeur est très connue, mais un petit rappel de raisonnement ne fait pas de mal.

Le point de rotation est bien évidemment au centre. De combien monte-il ?
Voilà un petit dessin (fait exprès pour vous !) avec lequel c'est tout simple:



















Assis, notre carré mesure X. Debout, il mesure donc
XX=X multiplié par racine carré de 2.
La différence est donc XX-X. Cette différence est valable pour le point le plus haut du carré selon sa position, mais pour le centre du carré, il s'agit donc de la moitie.

On a donc :

V=effect("vitesse")("Curseur");
cote=width*scale[0]/100;
diagonale=cote*Math.pow(2,.5);

montee=(diagonale-cote)/2;

Voilà, on sait de combien doit monter le carré : "montee"

Maintenant, il faut qu'on trouve à quel moment.
Comme on l'a dit, il faut qu'il soit au plus haut quand le carré est à 45°.
C'est le moment de se remettre en tête le cercle trigo.
Nous allons utiliser le sinus. Le sinus vaut sa plus haute valeur, 1, quand l'angle est à 90°. Pour toujours avoir une valeur positive (puisque le carré doit "rebondir" et non avoir un mouvement sinusoïdale), nous allons utiliser Math.abs.
Le sinus vaut 1 tous les 180°. Or, nous avons besoin que le sin valle 1 tous les 90°. Il faut donc que la fréquence de la rotation soit doublée pour avoir un sin=1 tous les 90°.

Ce qui est chouette, c'est qu'on est en train de faire une pierre deux coups:
En effet, on a besoin que sin=1 quand rotation=45. Or, sin=1 quand rotation=90. Il faudrait décaler la valeur de 45 pour corriger ça, sauf que ça n'est pas la peine:
Etant donné qu'on a multiplié l'angle par 2, quand la rotation vaut 45, sa valeur dans l'expression vaudra donc 90 ! Donc tout rentre dans l'ordre.

Ouf, vous pouvez lacher votre apnée, voilà les dernières lignes:

y=Math.abs ( Math.sin ( degreesToRadians ( (rotation))*2))*-montee;

Regardez bien, il y a un - devant montee. Ca permet d'avoir la valeur négative de la variable sans devoir écrire montee*-1. Pourquoi *-1 d'ailleurs ? Car dans After Effect, le 0 de Y est en haut, donc si on veut qu'un calque monte, il faut que Y baisse.

La dernière ligne :

[x,y]+value

Le +value permet biensur de pouvoir mettre votre carré où vous voulez pour le départ.

Et voilà ! Vous avez un carré qui marche !

Vous pouvez biensur mettre une valeur négative dans la vitesse pour aller de droite à gauche.
vous pouvez modifier l'échelle. Si vous faites un carré plus grand, il tournera moins vite, et inversement avec un carré plus petit.
Installez 3 carrés de 3 tailles différentes, avec la meme vitesse. Vous verrez que les 3 iront à la même vitesse, meme si le plus petit a l'air de franchement galérer pour suivre les deux autres.

Ce que vous ne pouvez pas faire :
Changer la taille du carré dans le temps. En effet, sans rentrer dans les détails, dans cette expression, on ne rajoute pas à chaque image une valeur de position et de rotation. Pour chaque image, After Effect calcule l'image comme unique. Si vous changez l'échelle dans le temps, il va replacer le carré à chaque image comme si sa taille avait toujours été celle de l'image courante.

Vous pouvez également enlever les "time", et ainsi faire bouger votre carré en animant la valeur "vitesse"


vendredi 17 avril 2009

un exercice pour ce week end

ça vous rappelle rien ?... Les indices du jour sont :

-particular (Tracpode), j'ai pas testé avec Particle Word ou autre...
-l'expression magique est utilisée en partie à 2 endroits.




Aller hop ! au boulot. Vous avez droit à une question sous forme de commentaire.

jeudi 16 avril 2009

amélioration des lumières

Nous allons aujourd'hui essayer d'améliorer le système d'éclairage d'After Effect.
Comme vous le savez, After Effect propose plusieurs type d'éclairage, point, concentré, parallèle, ambiante.
Nous allons nous interesser aux plus utilisés, point et concentré (le spot).

After Effect permet de gérer en gros les ombres, les ombres douces, le spéculaire, la puissance. Il gère également l'angle camera-objet-lumière.

Il y a un paramètre que After Effect ne gère pas, c'est la déperdition de la puissance lumineuse. En effet, qu'importe la distance objet-lampe, l'objet reste toujours autant éclairé. Hors, bien évidemment, plus la lampe est loin de l'objet, moins la puissance est grande.

On va corriger ça, autant que faire ce peut.

Voilà notre schéma du jour:
















La boule jaune, c'est la lumière, et en bas, c'est le calque.
Alors on pourrait se dire :
"Et bah on assimiler la distance lampe-calque à la "diffuse" du calque !"

C'est en effet ce que l'on va faire, mais il y a un léger hic. AE éclaire le calque de manière uniforme (sans prendre en compte le spéculaire). Imaginez que la lampe se déplace sur le plan parallele au calque (donc là sur l'axe XY), la distance lampe-calque va augmenter.

Hors, si le calque fait 2000x2000, et que la le calque se déplace de 1000 sur X, la lumière va baisser (car la distance augmente), alors que la lumière est toujours "au dessus" du calque.

Nous allons donc séparer la distance Z, et la distance XY (la ligne jaune au sol)

Aller c'est parti.

Créons un calque Null de control, qui va nous permettre de regler l'atténuation. Ajoutez 2 paramètre glissère, "début" et "fin"

Créez également une lumière "point".

Créez un 1er calque 3d, aller dans le paramètre diffuse, et écrivez :

a=position; b=thisComp.layer("Lumière 1").transform.position;
debut=thisComp.layer("controle lumiere").effect("debut")("Curseur");
fin=thisComp.layer("controle lumiere").effect("fin")("Curseur");

J'explique pas.
La suite:

V=sub(b,a);
Vec=fromWorldVec(V);


La 1ere ligne, vous connaissez, on crée un vecteur de a vers b.
Pour la 2eme, je m'explique.
Voyez sur le dessin la ligne orange, le vecteur. C'est un vecteur qui appartient à l'espace général de AE. Nous avons besoin que ce vecteur soit regardé par le calque, pour pouvoir séparer Z,X et Y. C'est le role de fromWorldVec. On change de référenciel, du "monde" vers le calque.

On se libère ainsi des valeurs de rotation du calque.

distanceZ=Math.abs(Vec[2]);

On récupère ainsi la composante Z du vecteur. Si on avait faire ça avec V et non Vec, la composante Z ([2]) aurait été celle du monde AE, qu'importe l'angle du calque.
Le math.abs permet d'éviter le probleme d'orientation du calque.
Voilà, on a crée une valeur qui donne la distance perpendiculaire du calque vers la lampe.

valz=ease(distanceZ,debut,fin,value,0);

Là on a crée la valeur de la diffuse en fonction de distanceZ.
Vous comprenez ainsi que la valeur "début" doit correspondre au début de l'atténuation, et "fin" doit correspondre à la distance où le calque sera noir.

Passons à l'atténuation sur le plan parallèle au calque, le plan XY.
On va faire en sorte que la déperdition commence à début+"en gros la taille du calque". Ainsi, si début=0, l'atténuation commencera quand la lampe "sortira" du calque.

distanceM=(width+height)/4;
distanceXY=Math.abs ( add(Vec[0],Vec[1]) );
valXY=ease(distanceXY,debut+distanceM,fin+distanceM,value,0);

On crée donc une variable très approximative (mais ça fonctionne pas trop mal).
C'est donc la moyenne entre la largeur et la hauteur du calque (width+height)/2, que je redivise par 2, pour n'avoir que la moitié. En effet, on considèrera que le point d'ancrage est au milieu du calque.
Ensuite, on crée la longueur du trait jaune (sur le dessin), c'est à dire la projection du vecteur sur le calque. "add" fait l'addition de 2 vecteurs. Regardez le dessin, on voit bien que x+y=trait jaune.
Ensuite, on fait la même chose que pour Z, mais avec XY. C'est pour cela qu'on rajoute distanceM à début et fin, pour tout décaler.

La dernière ligne :

Math.pow(valz*valXY,.5);

C'est donc la multiplication des 2 valeurs crées, le tout à la racine carrée.
Ainsi, on reste au maximum à 50, et quand une des 2 valeurs est à 0, le tout est bien à 0.

On peut évidemment remplacer les 0 par une valeur positive, pour ne jamais avoir de calque noir.
Voilà, ça aide un peu, ça a ses limites... Si vous voulez utiliser une valeur spéculaire pour le calque, copier l'expression également dedans.

Aller, l'expression en entier :

a=position; b=thisComp.layer("Lumière 1").transform.position;
debut=thisComp.layer("controle lumiere").effect("debut")("Curseur");
fin=thisComp.layer("controle lumiere").effect("fin")("Curseur");

V=sub(b,a);

Vec=fromWorldVec(V);

distanceZ=Math.abs(Vec[2]);

valz=ease(distanceZ,debut,fin,value,0);


distanceM=(width+height)/4;
distanceXY=Math.abs ( add(Vec[0],Vec[1]) );
valXY=ease(distanceXY,debut+distanceM,fin+distanceM,value,0);

valeur=Math.pow(valz*valXY,.5);


Ca marche avec une seule lampe, c'est compliqué de le faire avec plusieurs lampes pour plusieurs raison :

On utilise un gros système D, qui fait que le calque devient lui meme plus sombre selon la distance de la lampe. Donc s'il y a une lampe rouge et une lampe bleue, et que la lampe bleu est loin, et la lampe rouge, ça ne marche pas : le calque sera bien éclairé car la rouge est proche, mais le calque sera donc violet, à cause de la lumière bleue.



Voilà, j'espère avoir été clair !
Plus début et fin sont petits, plus l effet sera fort.
Rechargez la page pour que les 2 animations soient syncrho.

avec:












sans:













un autre exemple, notez dans le "avec" le coin supérieur gauche qui s'assombri, et évidemment, tout devient noir quand la lumière monte trop haut.

jeudi 26 mars 2009

pouet pouet

Aller, un petit truc truc simple !













L'idée est la suivante :
Qu'importe la méthode pour bouger le carré, il se déforme tout seul quand il touche le sol.
Pour ça, nous allons non pas bouger le carré lui même, mais un Null.
Ensuite, on dira ceci au carré :
Tu suis le Null dans sa position, mais si ta position en Y atteint une certaine valeur, tu ne bouges sur Y.
On va déjà faire ça.
Créez un Null, et un petit carré. Rajoutez à ce petit carré un paramètre glissière, et nommez le "sol". Mettez y 350 (ma compo fait 480x360, je vous rappelle que 350, c'est en bas).

Dans le point d'ancrage du carré, corrigez Y pour qu'il soit en bas. Si votre carré fait 50x50, vous aurez donc [25,50].

Dans la position du carré, on va écrire ça :

N=thisComp.layer("Nul 1").transform.position; //position du Null
sol=effect("sol")("Curseur"); //valeur de "sol"

Y=Math.min(N[1],sol); // On dit que Y vaut la valeur minimum entre le y du Null, et la valeur de sol. Ainsi, quand le y du Null vaut plus que 350 (plus bas donc), Y garde la valeur du sol.

[N[0],Y] // On veut que le carré ait la valeur de x du Null pour x, et Y pour y.

Bougez votre Null, le carré ne descendra pas sous la valeur du sol.
C'était quand même pas sorcier.

Passons à la déformation.
Voilà ce qu'on va dire au carré.
Plus tu es loin du Null, plus ton échelle sur X augmente, et plus ton échelle sur Y diminue.
Tout d'abord, rajoutez un paramètre glissière au carré, et nommez le "facteur", avec 3 comme valeur.

Écrivez dans l'échelle du carré :

p=position;
N=thisComp.layer("Nul 1").transform.position;
w=width;
F=effect("facteur")("Curseur");

Même pas mal.
Ensuite :

dist=p[1]-N[1];

Voilà, comme ça, on a une variable qui donne la distance verticale entre le carré et le Null. J'aurais même pu écrire dist=length(p,N), puisque de toute manière, ils ont toujours le même X.

Ca fonctionne car quand le carré est en l'air, dist vaut forcément 0 puisqu'il suit le Null. Mais quand il est au sol, c'est à ce moment que le Null se sépare du carré, et que dist prend une valeur non nulle.

x=linear(dist,-w/2,0,F*100,100);
y=linear(dist,-w/2,0,100/F,100);


Ca a l'air compliqué comme ça, mais en fait, non.
C'est simple, on veut ceci:
Plus le Null est loin, plus la déformation se fait.
Quand Null s'éloigne du carré, son Y est plus grand que celui du carré. Donc dist devient négatif.
C'est pour cela que j'ai choisi -w/2 comme distance de déformation : la moitié de la hauteur du carré. Ainsi, il suffit que le Null descende "d'un demi carré" pour que la déformation se fasse au maximum. C'est une valeur subjective, mais elle a le mérite d'être fonction de la taille du carré. On aurait pu aussi metre 2, 25 ou 250.
Et donc, quand dist=-w/2, et bien x=300, car nous avons mis 3 dans la valeur facteur.
Donc si on multiplie X par 3, on va diviser Y par 3.
D'où le
100/F, qui divise bien 100 par 3.

Et évidemment, la dernière ligne :

[x,y]

Et voilà, vous pouvez tester, ça fonctionne, plus le "facteur" est grand, plus la déformation sera grande (3 c'est déjà pas mal)

Vous remarquerez une finesse, le carré fait de légère rotation. Nous voulons évidemment que les rotations se fassent uniquement en l'air, et non quand le carré est au sol.

Pour la rotation :

haut=100;
bas=300;
F=ease(position[1],haut,bas,60,0);
wiggle(2,F)

En dessous de 300, la rotation vaut 0, et elle augmente quand y va de 300 jusqu à 100. Au delà de 100, elle se maintient.

Il ne reste plus qu'à bruiter tout ça, avec gout.



Pour que la hauteur du son varie, j'ai activé le remappage temporel pour chaque son, en mettant ça dans l'expression :

seedRandom(index,true);
max=thisComp.layer("Nul 1").effect("son max")("Curseur");
min=thisComp.layer("Nul 1").effect("son min")("Curseur");
R=random(min,max);
(time-inPoint)*R

max vaut 1.5 et min vaut 0.5
Ainsi, chaque son va à sa propre vitesse.
Pourquoi time-inPoint ?
time est fonction de la compo, pas de chaque calque.
Ainsi, si un calque commence à la seconde 5, à sa première image, time vaudra 5.
donc comme on veut partir de 0, on rajoute - inPoint (qui vaut la valeur du temps au début du calque)
C'est un moyen simple pour avoir la valeur de temps en partant de 0 au début du calque.

Petite astuce :
Sur ces deux animations, le mouvement du Null a été fait par dessin de trajectoire, et en m'appliquant à la souris pour faire un truc qui ressemble à quelque chose.
Toutefois, il n'est pas évident sur ce type de mouvement de s'arrêter en bas au bon endroit:
Si vous descendez trop, le carré va rester trop longtemps écrasé. Et si vous ne descendez pas assez, il ne vas pas être écrasé assez longtemps.
Voilà comment j'ai fait :
J'ai fait mon mouvement, ensuite j'ai sélectionné toutes les clefs de position crées, et je les ai déplacées pour que les clefs du bas soit au bon endroit.

dimanche 22 mars 2009

plein et délier

Comme je suis généreux, on va voir encore un petit truc... Franchement vous allez voir, c'est vraiment pas mal.
Certains d'entre vous connaisse peut etre l'effet "tracé dynamique" ("write on" en anglais je crois)
On anime un point dans sa position, et ça dessine un trait. On peut évidemment attacher ce point à un objet pour que ce dernier laisse un tracé, etc, ça sert à plein de choses.

Mais ici, on va faire un truc chouette, on va gérer les pleins et les déliers. Telle une plume, selon la direction du dessin, l'épaisseur sera différente. On évite ainsi le feutre rond qui ne change jamais de taille.













Voilà ce que nous allons faire :
Il faut que le diamètre varie selon la direction du tracé.
Donc tout le problème est là, trouver l'angle qui définit la direction du tracé.

C'est parti,
Créez un solide plein pot dans la compo, et ajouter l'effet "tracé dynamique".
Comme nous allons faire varier le diamètre DANS LE TEMPS, dans "tracé dans le temps", choisissez "épaisseur"

Rajoutez 3 paramètres glissières "angle", "min" et "max"
angle va représenter l'angle de la plume. Rentrez par 40 degré.
Dans min, mettez 3, et 20 pour max.

Nous allons maintenant créer une expression dans "épaisseur" dans l'effet "tracé dynamique".

taillemax=effect("max")("Curseur");
taillemin=effect("min")("Curseur");
angleplume=effect("angle")("Curseur");

J'explique meme pas, ça serait une insulte pour vous.
On passe au coeur du problème, la direction du tracé.
Voilà comment on va faire :
Nous allons récupérer 2 positions :
-Celle à l'image N
-celle à l'image N-1
En créant un vecteur qui va d'un point à l'autre, on a bien la direction !
C'est parti.

pos=effect("Tracé dynamique")("Position");
d=thisComp.frameDuration;
posd=pos.valueAtTime(time-d);

Alors...
pos, c'est la position du point à l'image courante.
On crée d, qui correspond à la valeur temporelle d'une image. Par exemple, si vous ètes à 25i/s, d=1/25
Enuite, posd correspond à la position, mais une image avant (time-d).

vecteur=sub(pos,posd);
Hop, ça c'est le vecteur.

vari=radiansToDegrees(Math.atan2(vecteur[1],vecteur[0]));

Exactement ce qu'on a vu dans le post précédent, on trouve l'angle en décomposant le vecteur.
Ici, vous remarquez une nuance, au lieu d'avoir Math.atan (coté opp/coté adj), j'ai Math.atan2(coté opp, coté adj), on peut faire les deux.
Voilà, on a récupéré la direction
du tracé !

taille=Math.sin(degreesToRadians(vari + angleplume));

Et là, on chope le sinus de l'angle. on part de l'angle de la plume ecrit dans le paramètre glissère (40 là)+la variation dans le temp de la direction. On ainsi taille qui varie de -1 à 1.
On va utiliser sa valeur absolue pour que ça varie de 0 à 1.
Dernière ligne :

Math.abs(taille)*taillemax + taillemin

C'est ici qu'on a la valeur de l'épaisseur. On multiplie donc la valeur absolue de taille (qui varie entre 0 et 1) à taillemax, le tout additionné à taillemin, qui est l'épaisseur minimum.
Ainsi, selon l'angle, taillemax se rajoutera à taillemin, addition de 0 à 20 ici.

Et voilà !!!

vous pouvez également animer les valeurs taillemin et taillemax dans le temps, ça marche.
Vous pouvez rajouter un wiggle à la position ou à l'épaisseur, etc.
Il y a différentes façon de créer une trajectoire.
Avec des clefs, en s'accrochant à un objet sur lequel on a effectué un dessin de trajectoire, en collant un masque dans la position qui se transforme en position, etc.

Merci la trigo.

look me in 2d

Alors alors, comment faire pour qu'un calque en regarde un autre, ou du moins que sa rotation soit fonction de la position d'un autre.
Tout d'abord, reregardons ça:






















Imaginez que notre objet est sur le point 0, et que l'objet cible soit sur le point "tan", ou du moins sur l'axe de la ligne verte, ça revient au meme.
On cherche donc l'angle A.
Et bien on a cette formule :
tan(A)=coté opposé / coté adjacent.

Par coté adjacent, on parle du coté qui appartient à l'angle, est qui n'est pas la ligne verte (la ligne du cosinus donc, de 0 à 1)
Et par coté opposé, et bien c'est l'autre segment, celui qui n'appartient pas à l'angle (et toujours différent de la ligne verte), la ligne qui va donc de 1 à tan.

Passons à la pratique :

a=position;
b=thisComp.layer("Rouge sombre uni 1").transform.position;

Ca c'est simple, ce sont les deux variables de position. Celle du calque, et celle de la cible.

V=sub(b,a);

Là on crée le vecteur qui correspond à la ligne verte, la ligne qui va de a vers b.

radiansToDegrees ( Math.atan2 ( V[1],V[0] ))

Math.atan2 : il ne s'agit pas de la tangente, mais de la tangente dans le sens inverse.
Quand vous écrivez Math.tan(angle), ça vous donne la tangente de l'angle.
Math.atan2(tan), ça vous donne l'angle de la tangente.
Même chose pour avec Math.acos et Math.asin.
Petite finesse avec atan2, les deux "coté" sont séparés par une virgule.

Donc dans Math.atan2(), on doit inscrire la tangente, c'est donc coté opposé/coté adjacent.
C'est très simple, il suffit de prendre les composantes sur x et y du vecteur.
Reregardez le dessin, surtout la ligne verte. Le coté adjacent et le coté opposé corresponde bien aux composantes x et y de la ligne verte.
On note donc V[0] (sur x) et V[1] (sur y).
Et comme d'habitude, radiansToDegrees, car Math.atan2 donne un angle en radians.

Un petit exemple mixant plein de choses qu'on a vu, j'ai jamais dit que c'était joli.













On verra plus tard le coup de la taille qui change, mais vous avez les outils pour le faire.