Que fait pixelfx ?
Effets pixel art sous forme de code. Chaque effet est un fichier, src/fx/<catégorie>/<nom>.js, qui dessine dans deux tampons :
couleur (pixels : opaques, tramés ou vraiment translucides) et lumière (lueur additive). Pas d'images, donc un effet
peut être joué en direct, recoloré, mis en miroir, réensemencé et rendu en feuille de sprites pour n'importe quel moteur.
La méthode est celle d'Hugo Duprez (Comment faire de bons effets pixel art avec Claude) : le code est de l'argile, faites confiance à vos yeux, nourrissez-vous de références. Les prompts uniques donnent des effets médiocres. Les ébauches que vous rendez, observez et raffinez donnent de bons résultats.
Avant tout
- Travaillez dans un répertoire pixelfx. Si le répertoire de travail ne contient pas
tools/render.mjs, clonez-en un à côté du projet :git clone https://github.com/patch-gg/pixelfx. Pas d'installation : les outils utilisent uniquement Node. - Vérifiez que ça fonctionne :
node tools/render.mjs fire/torchécritrecords/fire/torch/contact.png. Ouvrez ce PNG avec votre outil de visualisation d'images. Cette planche contact est ainsi que vous verrez chaque effet désormais.
1. Cherchez d'abord un effet existant
node tools/list.mjs <mots> recherche dans les identifiants, noms, tags et descriptions (node tools/list.mjs givre,
node tools/list.mjs entaille boucle). docs/CATALOG.md liste tout par catégorie. Rendez les candidats et
regardez-les avant de choisir. La plupart des demandes sont un effet existant avec des options différentes :
palette: la rampe principale, du sombre au clair, sous forme de chaînes hexadécimales. Toute rampePALdanssrc/core/color.js(feu braise lave fumée poussière glace eau électrique volt poison sacré guérison arcane ombre sang terre pierre or feuille bois métal os néon cyber matrice bonbon nuit) ou la vôtre. Un feu bleu est{"palette": PAL.glace}.seedpour une version différente mais équivalente ;flippour faire face à gauche ;anglepour tourner ;light(0..1+) pour la force de la lueur ; plus les propres boutons de chaque effet dans sesmeta.options(taille, nombre, rayon, texte, hauteur...).- Essayez les options sans éditer le code :
node tools/render.mjs fire/torch --opts '{"palette":["#102","#215","#43a","#8cf","#fff"]}'.
2. Mettez-le dans le jeu
Jeux Canvas ou WebGL en JS ou TS (canvas simple, Phaser, PixiJS, Kaplay, three.js) : utilisez le code directement. Il
n'a pas de dépendances ; copiez src/core et les fichiers d'effet dont vous avez besoin, ou dépendez du dépôt.
import { createFrame, clearFrame, createFx, composite } from "pixelfx"; // src/core/index.js
import * as givreNova from "pixelfx/fx/glace/gyivre-nova"; // src/fx/glace/givre-nova.js
const frame = createFrame(320, 180); // la résolution pixel du jeu ; ox, oy la défile avec la caméra
const img = new ImageData(320, 180);
const fx = createFx(givreNova, { x: hero.x, y: hero.y, flip: hero.facing < 0 });
// à chaque tick
fx.update(dt);
clearFrame(frame);
fx.draw(frame, "back"); // ce qui va derrière les personnages
drawCharacters(); // vos propres sprites, ou dessinez-les dans frame.color vous-même
fx.draw(frame, "front");
composite(frame, img.data, background); // couleur par-dessus le fond plus la lumière ; ou null pour transparent
ctx.putImageData(img, 0, 0); // puis mettez à l'échelle le canvas avec image-rendering: pixelated
if (fx.done()) removeIt(); // les one-shots se terminent seuls ; fx.stop() fade une boucle
Plusieurs effets peuvent partager une même frame. Dans un moteur WebGL, téléchargez le RGBA composite vers une texture à chaque frame, ou rendez l'effet en feuille de sprites une fois (ci-dessous) et jouez-le comme n'importe quelle autre animation.
N'importe quel moteur (Godot, Unity, GameMaker, Defold, LÖVE, Bevy, Construct) : exportez des feuilles de sprites.
node tools/render.mjs glace/givre-nova --sheet écrit records/glace/givre-nova/givre-nova.png (frames
transparentes dans une grille) et gyivre-nova.json (frames au style Aseprite, donc le chargeur d'atlas de Phaser, Godot, Unity
et la plupart des outils le lisent ; meta.pixelfx a fps, boucle, taille de frame, colonnes et le pivot). Définissez le
pivot à meta.pixelfx.pivot (l'origine de l'effet, par exemple les pieds), importez avec un filtrage au plus proche voisin et sans
compression, et jouez à meta.pixelfx.fps. La lueur est cuite dans la feuille en tant que couleur avec alpha, qui se lit comme
additive sur des scènes sombres ; pour des scènes lumineuses, rendez une version recolorée ou gardez l'effet sur une couche sombre.
Pour la bibliothèque complète : node tools/render.mjs <catégorie> --sheet fait une catégorie entière.
Un coup a besoin d'un corps derrière lui. Un effet seul ressemble à un autocollant. Les effets avec un personnage portent un act : quand le corps
s'arme, quand se figer, comment la cible réagit. Utilisez-le selon la façon dont le jeu est construit :
- Avec le personnage standard :
createAct(entaille, { x, y })au lieu decreateFx.act.update(dt),act.draw(frame), et jetez l'image paract.shake. Il se tient immobile seul pendant un gel.node tools/act.mjs <id> --sheetcuit le tout en feuille de sprites. - Avec le propre personnage du jeu : lisez le timing,
entaille.actoumeta.pixelfx.actdans le JSON de la feuille (windup,recovery,hitsavectetfreeze). Jouez l'animation d'attaque du personnagewindupsecondes avant l'effet, et à chaque coup arrêtez de tout mettre à jour, effet et personnages, pendantfreezesecondes. Le hit-stop côté hôte fonctionne sur n'importe quel effet, avec ou sans act.
src/fx/moves/ contient des attaques entières enchaînées à partir des effets, de l'armement à la récupération : commencez là quand quelqu'un demande un mouvement, pas un effet.
3. Créez-en un nouveau
Lisez d'abord docs/AUTHORING.md : c'est le contrat (meta, draw(p, t, o) pour les effets purs ou create(o) pour les
simulés, coordonnées locales, boucles sans couture, fin vide), la boîte à outils, l'API du peintre et la barre de qualité.
Ensuite :
- Dites-le en une ligne avec une direction artistique : ce que c'est, comment ça bouge, sa palette, son timing. "Une nova de givre : des cristaux éclatent en anneau des pieds vers l'extérieur, un éclair blanc au centre, des éclats partent en vrille, le sol reste givré et s'estompe."
- Trouvez des références dans la bibliothèque :
node tools/list.mjs <mots>, puis lisez les deux ou trois fichiers d'effet les plus proches et empruntez leurs techniques (ParticlesetBitsdanssrc/core/sim.jspour les étincelles, la fumée et les débris,p.discpour les volumes translucides,cyclepour les particules sans couture,tileFbmdéfile pour les flammes et l'eau). Regardez aussi les effets les plus proches de Hugo Duprez : ils sont la barre de qualité. Si les siens ont plus de détails, de lumière ou de vie à la même taille à l'écran, le vôtre n'est pas fini. - Ébauchez.
node tools/new.mjs <catégorie>/<nom>(ajoutez--looppour une boucle) donne un fichier fonctionnel. Quand la direction est ouverte, écrivez 3 à 5 ébauches vraiment différentes (<nom>-a.js..-e.js), rendez-les toutes (node tools/render.mjs "<catégorie>/<nom>-*") et regardez chaque planche contact avant de choisir. Montrez à l'utilisateur les deux ou trois plus fortes s'il est là (--giffait des animations à partager). - Regardez, jugez, corrigez. Rendez, ouvrez
records/<id>/contact.png, et jugez comme un pixel artiste exigeant. Corrigez la pire chose, rendez à nouveau. Trois rounds minimum ; une pièce maîtresse en prend plus.--allmontre chaque frame,--mp4montre le mouvement à pleine vitesse. - Jouez-le si un personnage est impliqué : ajoutez
export const act(voir "Acts" dansdocs/AUTHORING.md), avec un armement avant t = 0, un changement de pose sur la frame de lâcher, un gel au contact et une récupération.node tools/act.mjs <id>montre 12 fps consécutifs : jugez le mouvement à partir de ceux-ci et de--mp4, pas à partir des échantillons d'une planche contact. - Vérifiez.
node tools/check.mjs <id>détecte le dessin hors du canvas, les one-shots qui ne se terminent pas vides, les coutures de boucle, l'aléatoire non ensemencé et les frames lentes, et dans un act : les touches dans le désordre, les champs de pose inconnus et les mains qui téléportent. Corrigez jusqu'à ce que tout soit propre. Supprimez les ébauches que vous n'avez pas gardées.
Ce qu'il faut chercher, et la correction habituelle :
| vous voyez | correction |
|---|---|
| ça se lit comme de la bouillie à 1x | moins de couleurs, une silhouette claire, un point focal, des formes plus grandes |
| mouchetis : pixels de bruit isolés dans les formes | seuil de bruit moins élevé, quantifiez avec at(), pas de bruit sur les petites formes |
| flottant, sans impact | rapide dedans, lent dehors (ease.outQuart/outExpo), une frame blanche héroïque de 1 à 2 frames, temps total plus court |
| rigide ou mécanique | variez chaque particule (taille, vitesse, délai, vie), ajoutez un mouvement secondaire (étincelles, fumée, débris) |
| délavé | moins de lueur (k 0,2 à 0,5), des pas de bord plus sombres, gardez le blanc pour le cœur le plus chaud seulement |
| plat | lumière d'un côté : plus clair en haut à gauche, plus sombre en bas à droite ; un pas de rampe plus sombre à la base |
| dégradés lisses, aspect vectoriel | pas de rampe durs dans la couleur ; douceur uniquement dans la lumière et la fumée translucide |
| un damier là où la fumée devrait être | particules SMOKE, VAPOUR ou DUST, ou p.disc : vraiment translucides, éclairées par-dessus |
| couture quand une boucle se referme | cycle, loopNoise(phase), défilements tileNoise de cellule entière, sin(phase * TAU * n) |
| un one-shot qui disparaît brusquement | laissez-le rétrécir, se tramer ou tomber avant t = durée |
| un super effet sur un personnage mort | donnez-lui un act : armement, pose de lâcher, gel au contact, une cible qui réagit, récupération |
| l'act semble sans poids | un armement dans la direction opposée au coup, un gel plus long (jusqu'à 0,2 s), un plus grand changement de silhouette, secousse |
| le coup arrive hors synchronisation | déplacez la touche du coup vers la propre constante de l'effet, pas vers un temps que vous avez estimé |
| il tient mal l'arme | gardez une flexion dans le coude, les manches devant la poitrine ; sur un swing, la lame retarde la main ; les bras atteignent environ 15 px et ne s'étirent pas, donc déplacez le corps |
| ses bras se croisent ou la pose semble plate | il fait face aux trois quarts vers nous : pour les bras ouverts, la main proche va à gauche (fx < 0), la main éloignée à droite, avec turn: 1 ; yaw le tourne vraiment |
Les effets directionnels font face à droite, les effets de sol se tiennent en y = 0, les effets d'acteur enveloppent o.actor (masque et pixels d'un personnage standard de 24 x 38), et rien n'utilise Math.random. Les motifs génériques sont acceptables ; ne recréez jamais les sprites, personnages ou logos d'un jeu spécifique.
4. Code natif au lieu de JS
Si le jeu ne peut pas exécuter JS et qu'une feuille de sprites ne suffit pas (un effet qui suit une cible mobile, ou a besoin de
couleurs par instance à l'exécution), portez l'effet. Les effets purs sont une fonction du temps ; les simulés déplacent les particules dans
src/core/sim.js. Gardez l'idée des deux tampons, le seuil Bayer 4x4 pour l'alpha tramé et les recherches de rampe de palette.
Rendez l'original JS et votre portage côte à côte pour vérifier qu'ils correspondent.