Ce programme réalise une animation spectaculaire en trois actes :
- Deux voitures s’approchent l’une de l’autre,
- Elles entrent en collision,
- Elles restent immobiles, écrasées, après l’impact.
L’ensemble repose sur une combinaison de dessin vectoriel, de physique simplifiée, et d’une gestion précise de l’animation image par image.
Voyons comment ce programme construit cette scène.
Préparation de l’environnement graphique
L’animation repose sur un tableau graph, qui stocke toutes les images successives.
La toile fait :
- 600 px de large
- 320 px de haut
La route est placée à routeY = 220, ce qui laisse un ciel bleu en haut et une zone asphaltée en bas.
Un point central, centreImpact, représente l’endroit où les deux voitures se percuteront.
La procédure voiture : dessiner un véhicule complet
Le cœur du programme est une procédure capable de dessiner une voiture dans n’importe quelle position, avec :
- une couleur de carrosserie
- une couleur de toit
- une direction (1 = vers la droite, -1 = vers la gauche)
- un niveau d’écrasement de l’avant
Cette procédure assemble plusieurs éléments graphiques :
Composants du véhicule
- Ombre au sol : ellipse sombre pour l’effet de volume
- Châssis : rectangle arrondi, dont l’avant peut être raccourci pour -simuler l’écrasement
- Pare-chocs : petit rectangle arrondi qui se déforme
- Cabine : bloc supérieur légèrement décalé vers l’arrière
- Vitres : deux rectangles arrondis avec un reflet diagonal
- Phare avant : cercle jaune/blanc
- Feu arrière : cercle rouge
- Roues : cercles sombres avec jantes claires
Le calcul de l’avant et de l’arrière du châssis permet de simuler la déformation lors du choc : plus ecrase augmente, plus l’avant se rapproche du centre du véhicule.
Phase 1 : l’approche
Pendant 25 images, les deux voitures roulent l’une vers l’autre :
- La voiture rouge part de la gauche et avance vers la droite.
- La voiture bleue part de la droite et avance vers la gauche.
La route est dessinée avec :
- un rectangle gris foncé,
- une bordure plus claire,
- des pointillés jaunes espacés régulièrement.
Les positions des voitures évoluent linéairement :
gaucheXpos = 60 + approche * 6 droiteXpos = largeurToile - 60 - approche * 6
Cette phase crée une tension visuelle : les véhicules se rapprochent inexorablement du point d’impact.
Phase 2 : l’impact
C’est la partie la plus riche du programme.
Elle combine :
- déformation progressive des voitures,
- flash lumineux,
- projection de débris,
- étoile d’impact façon bande dessinée,
- rebond léger après le choc.
Flash blanc
Pendant les premières images, un cercle blanc apparaît au centre, diminuant progressivement.
Cela simule l’éclair du choc.
Déformation
L’avant des voitures se comprime :
ecrasement = minimum(impact * 2, 22)
Puis un léger recul se produit après quelques images :
recul = minimum(impact - 6, 8)
Débris projetés
45 débris sont générés avec :
- une direction aléatoire (angle),
- une vitesse aléatoire,
- une couleur choisie parmi quatre teintes (jaune, orange, rouge, gris).
Chaque débris suit une trajectoire parabolique :
- déplacement horizontal et vertical selon sa vitesse,
- ajout d’une petite gravité : 0,3 * impact².
Les débris diminuent de taille au fil du temps.
Étoile “BOUM”
Pendant les premières images, une étoile explosive est dessinée :
- 8 branches,
- couleur rose vif,
- rayon qui augmente à chaque frame.
Cela donne un effet cartoon très dynamique.
Phase 3 : après l’accident
Les voitures sont désormais immobiles :
- avant complètement écrasé (ecrase = 22),
- légèrement reculées,
- débris retombés.
Cette phase dure 20 images, créant un temps de repos après l’action.
Pause finale et animation
Quelques images identiques sont ajoutées pour figer la scène, puis :
animation(graph, 60, 1)
Lance l’animation à 60 ms par image, en boucle.
Conclusion
Ce programme est un excellent exemple de programmation graphique narrative.
Il combine :
- dessin vectoriel détaillé,
- animation image par image,
- physique simplifiée,
- effets visuels dynamiques,
- gestion de déformation,
- éléments stylisés (flash, étoile, débris).
Le résultat est une scène vivante, expressive, presque cinématographique.
Programme :
graph est un tableau
largeurToile est un nombre vaut 600
hauteurToile est un nombre vaut 320
routeY est un nombre vaut 220
// Point de collision (au centre)
centreImpact est un nombre vaut largeurToile / 2
// --- Procedure : dessine une voiture ---
// direction = 1 (regarde/roule vers la droite) ou -1 (vers la gauche)
// ecrase = niveau de deformation de l'AVANT (0 = intacte)
procedure voiture(img, vx, vy, carrosserie, toit, direction, ecrase)
// vx = centre du chassis. L'avant est du cote "direction".
// --- Ombre au sol ---
ellipse(img, vx, vy + 28, 44, 7, #333333)
// --- Bornes avant / arriere du chassis ---
// L'arriere reste fixe, l'avant se rapproche du centre (ecrasement)
arriereX est un nombre vaut vx - direction * 40
avantX est un nombre vaut vx + direction * (40 - ecrase)
// Largeur reelle du chassis apres ecrasement
gaucheX est un nombre vaut minimum(arriereX, avantX)
largChassis est un nombre vaut absolue(avantX - arriereX)
// --- Chassis bas (deux tons pour le volume) ---
rectangle_arrondi(img, gaucheX, vy, largChassis, 26, 6, carrosserie)
// Bas de caisse plus sombre
rectangle(img, gaucheX + 2, vy + 18, largChassis - 4, 6, toit)
// --- Pare-chocs a l'avant (se plie quand ca cogne) ---
pcX est un nombre vaut avantX - direction * 3
rectangle_arrondi(img, pcX - 3, vy + 12, 6, 12, 2, #adb5bd)
// --- Cabine (toit) centree, legerement vers l'arriere ---
cabX est un nombre vaut vx - direction * 4
rectangle_arrondi(img, cabX - 21, vy - 20, 42, 24, 7, toit)
// --- Vitres avec reflet ---
rectangle_arrondi(img, cabX - 16, vy - 16, 15, 15, 3, #a8dadc)
rectangle_arrondi(img, cabX + 1, vy - 16, 15, 15, 3, #a8dadc)
// Reflet diagonal
ligne(img, cabX - 14, vy - 3, cabX - 6, vy - 14, #e8f4f8, 2)
ligne(img, cabX + 3, vy - 3, cabX + 11, vy - 14, #e8f4f8, 2)
// --- Phare a l'avant (sur le nez, meme deforme) ---
phareX est un nombre vaut avantX - direction * 3
cercle(img, phareX, vy + 7, 4, #fff3b0)
cercle(img, phareX, vy + 7, 2, #ffffff)
// --- Feu arriere rouge ---
feuX est un nombre vaut arriereX + direction * 2
cercle(img, feuX, vy + 7, 3, #d00000)
// --- Roues avec jante ---
cercle(img, vx - 22, vy + 26, 10, #1b263b)
cercle(img, vx + 22, vy + 26, 10, #1b263b)
cercle(img, vx - 22, vy + 26, 4, #ced4da)
cercle(img, vx + 22, vy + 26, 4, #ced4da)
fin procedure
// ============================================================
// PHASE 1 : APPROCHE
// ============================================================
approche est un nombre
pour approche de 0 a 24
img est une toile
dimension(img, largeurToile, hauteurToile)
degrade(img, [#caf0f8, #e8f5e9])
// Route
rectangle(img, 0, routeY + 30, largeurToile, hauteurToile - routeY - 30, #495057)
rectangle(img, 0, routeY + 30, largeurToile, 4, #6c757d)
pointille est un nombre
pour pointille de 20 a largeurToile - 1 pas 60
rectangle(img, pointille, routeY + 52, 30, 5, #ffd60a)
fin pour
gaucheXpos est un nombre vaut 60 + approche * 6
droiteXpos est un nombre vaut largeurToile - 60 - approche * 6
// Voiture rouge roule vers la DROITE (direction 1)
appelle voiture(img, gaucheXpos, routeY, #e63946, #a4161a, 1, 0)
// Voiture bleue roule vers la GAUCHE (direction -1)
appelle voiture(img, droiteXpos, routeY, #457b9d, #1d3557, -1, 0)
graph ajoute img
fin pour
// ============================================================
// PHASE 2 : IMPACT
// ============================================================
nbDebris est un nombre vaut 45
debVX, debVY, debTeinte est un tableau
d est un nombre
pour d de 0 a nbDebris - 1
ang est un nombre vaut hasard(0, 628) / 100
vit est un nombre vaut hasard(2, 10)
debVX ajoute cosinus(ang) * vit
debVY ajoute sinus(ang) * vit - 4
choixC est un nombre vaut hasard(1, 4)
si choixC = 1 alors
debTeinte ajoute #ffd60a
sinon si choixC = 2 alors
debTeinte ajoute #ff6d00
sinon si choixC = 3 alors
debTeinte ajoute #e63946
sinon
debTeinte ajoute #6c757d
fin si
fin pour
// Positions au contact : les AVANTS se touchent au centre
gaucheFin est un nombre vaut centreImpact - 40
droiteFin est un nombre vaut centreImpact + 40
impact est un nombre
pour impact de 0 a 30
img est une toile
dimension(img, largeurToile, hauteurToile)
degrade(img, [#caf0f8, #e8f5e9])
rectangle(img, 0, routeY + 30, largeurToile, hauteurToile - routeY - 30, #495057)
rectangle(img, 0, routeY + 30, largeurToile, 4, #6c757d)
pointille2 est un nombre
pour pointille2 de 20 a largeurToile - 1 pas 60
rectangle(img, pointille2, routeY + 52, 30, 5, #ffd60a)
fin pour
// Flash blanc a l'impact
si impact < 4 alors
cercle(img, centreImpact, routeY + 5, 60 - impact * 8, #ffffff)
fin si
// Deformation de l'AVANT qui augmente puis se stabilise
ecrasement est un nombre vaut minimum(impact * 2, 22)
// Petit rebond apres le choc
recul est un nombre vaut 0
si impact > 6 alors
recul vaut minimum(impact - 6, 8)
fin si
appelle voiture(img, gaucheFin - recul, routeY, #e63946, #a4161a, 1, ecrasement)
appelle voiture(img, droiteFin + recul, routeY, #457b9d, #1d3557, -1, ecrasement)
// Debris depuis le point d'impact
db est un nombre
pour db de 0 a nbDebris - 1
px, py est un nombre
px vaut centreImpact + debVX[db] * impact
py vaut routeY + 5 + debVY[db] * impact + 0,3 * impact * impact
si py < routeY + 55 alors
tailleDb est un nombre vaut 4 - impact * 0,08
si tailleDb < 1 alors
tailleDb vaut 1
fin si
cercle(img, px, py, tailleDb, debTeinte[db])
fin si
fin pour
// Etoile d'impact "BOUM"
si impact < 8 alors
rayonEtoile est un nombre vaut 30 + impact * 4
branche est un nombre
pour branche de 0 a 7
angleB est un nombre vaut branche * 0,785
exB est un nombre vaut centreImpact + cosinus(angleB) * rayonEtoile
eyB est un nombre vaut routeY + 5 + sinus(angleB) * rayonEtoile
ligne(img, centreImpact, routeY + 5, exB, eyB, #ff477e, 3)
fin pour
fin si
graph ajoute img
fin pour
// ============================================================
// PHASE 3 : APRES (epaves ecrasees, immobiles)
// ============================================================
apres est un nombre
pour apres de 0 a 20
img est une toile
dimension(img, largeurToile, hauteurToile)
degrade(img, [#caf0f8, #e8f5e9])
rectangle(img, 0, routeY + 30, largeurToile, hauteurToile - routeY - 30, #495057)
rectangle(img, 0, routeY + 30, largeurToile, 4, #6c757d)
pointille3 est un nombre
pour pointille3 de 20 a largeurToile - 1 pas 60
rectangle(img, pointille3, routeY + 52, 30, 5, #ffd60a)
fin pour
// Epaves : avant ecrase, legerement reculees
appelle voiture(img, gaucheFin - 8, routeY, #e63946, #a4161a, 1, 22)
appelle voiture(img, droiteFin + 8, routeY, #457b9d, #1d3557, -1, 22)
graph ajoute img
fin pour
// Pause finale
pauseFin est un nombre
pour pauseFin de 0 a 8
graph ajoute graph[longueur(graph) - 1]
fin pour
animation(graph, 60, 1)





