FlatInk

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
}

Repères : ton espace (local) vs le monde

Deux espaces, comme dans tout logiciel d’animation :

À 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)
}

(À 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.