Le langage FlatInk Script
FlatInk Script reste volontairement petit. Pas de boucle infinie, pas d’évaluation cachée, pas de surprise. On décrit ce qui se passe, et l’éditeur garde tout sous les yeux.
Événements
Un événement déclenche un bloc d’actions. On en distingue trois familles.
when loaded { ... } // une fois, au démarrage
every frame { ... } // à chaque image
when clicked { ... } // sur un objet, au clic
when hovered { ... } // entrée du survol
when unhovered { ... } // sortie du survol
when pressed { ... } // pointeur enfoncé sur l'objet
when dragged { ... } // déplacé tant qu'il est tenu
when released { ... } // relâché
when held { ... } // maintenu sans bouger (appui long)
Actions
play
pause
go to frame 10 and play
go to "intro" and pause
score = score + 1 // affecter une variable
grille[i] = 0 // écrire dans un tableau
if vies > 0 { ... } else { ... }
repeat 8 times { ... }
repeat i from 0 to 5 { ... }
lancer() // appeler une procédure
send "gagne", score // émettre un événement vers la page hôte
sound "ding" // jouer un son
Expressions
Les valeurs sont numériques (zéro vaut faux). Les expressions servent à piloter un canal d’animation, ou à calculer dans une action.
rotation = sin(time) * 0.5
x = mouse.x
opacity = vies > 0 ? 1 : 0.3
Variables disponibles : time (en secondes), frame, value (la valeur de base du canal), mouse.x, mouse.y, mouse.dx, mouse.dy, et keys.Espace, keys.ArrowLeft, etc.
Constantes : PI, TAU, E.
Fonctions : sin cos tan, sqrt pow exp log, floor ceil round, abs min max sign hypot, clamp(x, min, max), lerp(a, b, t), mod(a, b), random().
Les canaux que l’on peut piloter sont x, y, scaleX, scaleY, rotation et opacity. Une expression sur un canal prime sur l’interpolation des images-clés.
Référencer un objet par son nom
Tout objet de la scène est lisible par son nom : Nom.x, Nom.y, Nom.rotation, Nom.scaleX, Nom.scaleY, Nom.opacity donnent sa position (et son état) à l’écran, à l’image courante. Et self désigne l’objet courant (ses propres canaux), dans ses liaisons — plus besoin de tenir une variable-miroir pour se situer ou suivre un autre objet.
object "Balle" {
rotation = atan2(Cible.y - self.y, Cible.x - self.x) // pointe vers « Cible », où qu'elle soit
opacity = hypot(Cible.x - self.x, Cible.y - self.y) < 30 ? 0 : 1
}
Ces valeurs sont en lecture seule (pour déplacer un objet, pilotez ses propres canaux ou une variable). self n’existe que dans une liaison de canal d’objet. Si deux objets portent le même nom, c’est le premier qui répond — pour agir sur toutes les instances d’un symbole, utilisez each (ci-dessous). Le nom doit être un identifiant (sans espace) pour la forme Nom.x.
Glisser un objet (drag)
drag rend un objet déplaçable à la souris et écrit sa position dans tes variables (que tu lies ensuite à x/y). Plus besoin d’écrire when pressed/when dragged à la main.
object "Pièce" {
drag px, py // suit le pointeur → écrit px, py
when dropped on Cible { // relâché au-dessus de la zone nommée « Cible »
px = Cible.x
py = Cible.y
}
x = px
y = py
}
drag x, y(deux axes) ·dragX x(horizontal seul) ·dragY y(vertical seul).- Slots (optionnels) :
drag x, y { confine to Zone snap 10 }—confine to <Objet>borne le déplacement à la boîte d’un objet nommé (déplace-le dans la scène, la limite suit) ;snap <n>aimante à une grille. when dropped on <Zone> { … }se déclenche au relâchement quand le centre de l’objet tombe dans la zone nommée. Plusieurs zones = plusieurs blocs.- La position vit dans tes variables → tu peux la lire (
self.x), la contraindre, ou la forcer (snap au drop). Pense à lierx/yà tes variables.
Repères : ton espace (local) vs le monde
Deux espaces, comme dans tout logiciel d’animation :
selfet les canaux (x,y,rotation…) sont dans ton repère local — relatif à ton parent. C’est ce quex = …règle.- Les autres objets et la souris (
Hero.x,mouse.x) sont en coordonnées monde (la scène).
À la racine de la scène, local = monde → tu n’as rien à penser, tout coïncide (c’est le cas courant).
Si ton objet est imbriqué dans un groupe et que tu veux le relier à une position monde, convertis avec toLocalX/Y (en liaison ou dans un when …) :
object "Curseur" { // imbriqué dans un panneau
x = toLocalX(mouse.x, mouse.y) // suit la souris, ramenée dans ton espace local
y = toLocalY(mouse.x, mouse.y)
}
toLocalX(x, y)/toLocalY(x, y): un point MONDE → ton espace (celui de ton parent).toGlobalX(x, y)/toGlobalY(x, y): ton espace → monde.
(À la racine, c’est l’identité — tu n’en as pas besoin. Et le drag fait la conversion pour toi, même imbriqué.)
Variables et tableaux
let score = 0
let briques = fill(32, 1) // un tableau de 32 cases à 1
let couleurs = [0, 1, 2]
Une variable de tableau se lit avec briques[i] et s’écrit avec briques[i] = 0.
Fonctions
fn distance(ax, ay, bx, by) = hypot(ax - bx, ay - by) // renvoie une valeur
fn lancer() { // une procédure
vies = 3
go to frame 1 and play
}
Paquets
use "nom" rend disponibles des fonctions toutes prêtes.
use "collision" // boxHit, dist, near
use "gesture" // snap, snapTo, railX, railY, angle, inZone
use "easing" // easeIn, easeOut, easeInOut, smooth
Voir le mini-jeu à glisser-déposer pour un usage concret de collision et gesture.
Lier toutes les instances d’un symbole
Quand une scène contient beaucoup d’instances d’un même symbole, each les relie d’un coup, avec un index par instance.
each "Brique" as i {
opacity = briques[i]
}
Les instances sont retrouvées par le nom du symbole, pas par sa place dans la bibliothèque. Les dossiers ne servent qu’à ranger la liste — le nom d’un symbole reste son identifiant unique et global, donc le classer dans un dossier ne change jamais la façon dont each ou Nom.x l’atteignent.