PROJET AUTOBLOG


Marien Fressinaud

Site original : Marien Fressinaud

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

La low-tech existe-t-elle dans le numérique ?

dimanche 8 mars 2020 à 10:30

Je développe une gêne croissante vis-à-vis de la notion de « low-tech » en informatique : il me semble y déceler un biais inconfortable dans sa définition même. Je précise que je parle en tant que développeur travaillant principalement dans le Web. Cet article est – volontairement – biaisé.

Pour comprendre l’origine de mon problème, il me faut commencer par définir ce que j’entends par low-tech. De mon point de vue, il s’agit avant tout d’une notion qui s’oppose à celle de high-tech. Si vous effacez cette dernière du paysage, la low-tech disparaît par la même occasion car elle perdrait son sens, sa raison d’exister. Je parle ici de la notion et non pas de l’existence « d’objets low-tech » (une roue en bois par exemple) qui ne disparaitront pas par magie. Il nous faudrait toutefois désormais définir ce qui ferait la spécificité d’un numérique high-tech pour en tirer ce qui s’y oppose. Or le numérique est intrinsèquement high-tech car il se base sur des connaissances et des techniques de pointe. Le fonctionnement d’un ordinateur et déjà par lui-même quelque chose de pointu ; que dire alors de sa fabrication ? de celle de nos ordiphones ? et des milliers de kilomètres de câbles parcourant la terre et les océans pour former l’Internet mondial ?

Comment définir un terme – la low-tech numérique – si son existence même est impossible ? 1

Il est toutefois évident que certaines pratiques et outils numériques décrivent une réalité low-tech, ne serait-ce qu’à travers la définition que leur donne leurs concepteurs et conceptrices. On peut notamment citer le site du « low-tech magazine », tournant sur un serveur alimenté en énergie solaire et qui passe hors-ligne lorsque le soleil vient à se cacher trop longtemps. En moins extrême, Gauthier Roussilhe a raconté la transition de son site vers un site « low-tech ». Plus récemment, Geoffrey Dorne a également listé quelques sites (à inspiration) low-tech. J’ai moi-même créé un générateur « low-tech » de sites statiques.

Une chose me frappe alors : le numérique low-tech serait-il le Web « un point zéro » des débuts ? de simples sites statiques à l’occasion d’un grand bond en arrière dans nos usages ? C’est évidemment une manière très caricaturale de voir les choses et il suffit de reprendre l’article de Geoffrey pour cela : il y cite notamment Wikipédia et un thème pour Wordpress. On reste évidemment sur des systèmes très simples, mais on voit que la low-tech n’est pas uniquement réservée aux sites statiques ; une part de « dynamique » y est autorisée.

Reste à déterminer ce qui rentre dans la case et ce qui n’y rentre pas. Une application métier peut-elle être low-tech ? Si je me base sur mon ressenti j’ai envie de répondre par l’affirmative. Qu’en est-il d’un service de streaming de vidéos ? Ici, j’ai envie de répondre par un franc « non »… mais si les vidéos sont optimisées au poil de cul, mises en cache et partagées sur le réseau local au point que leur impact écologique ne constitue plus qu’une ridicule broutille ?

Ce n’est peut-être pas aussi simple…

On voit toutefois sur cet exemple de site de streaming que pour faire du low-tech, j’ai dû user de l’argument d’un certain savoir-faire technique ; d’une technique qui me permette de réduire l’impact écologique du site en question. Je m’étais d’ailleurs fait cette réflexion en lisant l’article de Gauthier qui expliquait avoir réduit de 75 % le poids des vidéos qu’il hébergeait, soit un passage de quelque 14 Go à un poids final d’environ 3 Go. Finalement, les autres optimisations qu’il avait pu faire par ailleurs ne représentaient plus grand-chose comparé au gain fait sur les vidéos. On aurait pu le questionner sur l’impact de la démarche elle-même – le temps de calcul pour optimiser les images par exemple – comparé aux gains. Quant aux vidéos il avait fait le choix de les conserver, au moins en partie. Pourtant, tout cela ne semblait pas avoir été fait en vain ; l’article a même été abondamment relayé autour de moi, comme pour me certifier qu’il s’agissait bien là d’un article de référence dans le paysage de la low-tech.

De tout cela, j’en tirai la conclusion que « faire de la low-tech » n’est pas tant lié au résultat final qu’à une démarche volontaire.

Finalement, c’est Bertrand Keller qui l’exprimait le mieux dans son article « La Low-Tech, concept positif » (la mise en gras est de moi) :

Dans l’article (Les tables de la Low (Tech)), j’ai repris la définition donnée par le Low-Tech Lab, pour la questionner. Je n’ai pas vu dans la définition, de notion de perte d’usage. On parle d’utilité, d’accessibilité, de durabilité ; c’est-à-dire un concept de but mais pas de perte d’usage sous prétexte que ça consomme moins d’énergie.

Pour comprendre la low-tech, il faudrait donc prendre le problème par l’autre bout : pour un usage donné que je considère utile, comment puis-je le rendre le plus accessible et durable possible ?

À ce point-là de mon article, je ne suis toujours pas aligné avec le terme même de low-tech, mais sa finalité positive me parle bien.

Toutefois, quelque chose continue de me chiffonner et je vais l’illustrer avec une petite anecdote. Lors du dernier Snowcamp, une conférence – Développement Zéro Déchet – a fait salle comble (c’est un euphémisme tellement ça débordait de partout). Les intervenant·es n’abordaient pas frontalement la notion de low-tech, mais vous conviendrez que quand on parle de « zéro déchet », on n’en est quand même pas bien loin. Il et elle expliquaient les étapes pour optimiser une application développée précédemment en se basant sur des principes « zéro déchet ». Sauf que – et je ne sais pas si ça a été décidé consciemment avec le souci d’illustrer – l’application avait été tellement mal conçue à l’origine que je trouvais l’exercice totalement aberrant. J’ai plutôt eu l’impression d’assister à un cours d’optimisation assez basique que réellement une réflexion autour d’un hypothétique « développement zéro déchet ».

La démarche n’en était pas moins intéressante, d’autant plus que c’est ce qui m’a mis la puce à l’oreille qu’il pouvait y avoir un problème plus profond dans cette histoire de low-tech numérique. Finalement ne s’agirait-il pas d’une forme de green-washing pour développeur·ice en quête de sens dans son métier ? Sous couvert de vouloir alléger notre empreinte carbone, nous commencerions (enfin) à développer nos sites de manière correcte et optimisée ; de l’optimisation technico-politique en fin de compte.

Cela cache par ailleurs un problème que je n’ai vu nulle part posé : de quel impact concret parle-t-on ? Je n’en ai pas la réponse, mais si des études existent sur le sujet je ne serais pas étonné que le gain du passage d’un site dynamique à du statique soit ridiculement faible face au visionnage d’une vidéo de chaton sur Youtube, lui-même minime face à la fabrication d’un terminal. « Développer des sites statiques », bientôt dans les conseils du gouvernement pour combattre le réchauffement climatique au côté de « faire pipi sous la douche » et « trier ses courriels » !

Mais ce serait considérer le problème uniquement d’un point de vue comptable.

On en revient doucement à l’éternel débat : les actions individuelles de faible impact valent-elles le coût d’être menées ? J’ai tranché pour mon cas personnel depuis un moment en considérant que c’est très justement à chaque individu d’y apporter une réponse. Les encourager ne me semble toutefois pas idiot en cela qu’une action individuelle reste souvent un premier pas facile au sein d’une démarche politique plus large (ici, « réduire notre impact environnemental »). Cela ne doit pas occulter que les vrais changements surviendront à un niveau systémique… mais est-ce qu’un ensemble d’individus ne peut pas former un « système » en capacité de s’opposer à d’autres forces systémiques ?2

Pour résumer tout ce que j’ai dit jusque-là, on a vu que la notion de low-tech en numérique n’a pas tellement de réalité « physique » (ou alors peut-on parler de « low-tech-on-high-tech » ?) Il s’agirait aujourd’hui plutôt d’un mouvement d’individus adoptant une démarche politique volontaire d’optimisation de leurs sites Internet afin de réduire leur impact écologique. Ça peut sembler extrêmement réducteur comme vision et je rappelle qu’il s’agit uniquement de la mienne, exprimée à travers mes propres biais. Connaître les limites d’une telle démarche me semble toutefois important pour prendre conscience qu’il ne s’agit que d’un outil parmi d’autres pour ne pas s’en contenter. Cela peut également aider à répondre à des critiques.

Vous ne sauverez certainement pas la planète avec des sites statiques, mais vous aiguiserez vos engagements tout en rendant vos visiteur·heureuses3.

Pour ma part, je ne pense pas / plus utiliser le terme de low-tech car je le trouve mal adapté à ce qu’il cherche à définir. Pour l’instant je lui préfère la notion de « sobriété » qui a l’avantage de ne pas s’opposer frontalement à la high-tech (ce que le numérique est intrinsèquement). Il s’agit évidemment d’un choix personnel, et Bertrand – cité plus haut – s’est aussi questionné sur le sujet en faisant le choix inverse. Cela n’enlève rien à l’utilité de cet outil qui cherche à questionner l’impact du numérique sur le monde physique qui nous entoure. Il vient donc s’ajouter à ma besace, en compagnie de la notion de logiciel libre qui questionne le concept de propriété. Dans ces deux cas, ce n’est pas tant leur finalité qui m’intéresse que les imaginaires positifs qu’ils tentent de bâtir, ainsi que les cultures qui en émergent ; comme des remèdes à un monde qui ne tourne parfois plus si rond.


  1. Sauf à vivre en Océania en 1984, évidemment. 

  2. J’avais initialement écrit « qu’est-ce qu’un « système » si ce n’est un ensemble d’individus ? » On m’a très justement fait remarquer que cela occultait la présence d’autres « systèmes » comme les États ou Marchés. 

  3. L’écriture inclusive permet également d’inventer de nouveaux type de jeu de mots. On est d’accord que ce n’est pas super lisible par contre ! 

2020

vendredi 3 janvier 2020 à 10:57

Mon année 2019 aura sans aucun doute été très riche sur le plan personnel. J’ai notamment :

Mais ce que j’ai fait ne m’intéresse guère, c’est plutôt le ressenti que tout cela me laisse qui fait que je suis content de mon année. Pour l’année 2020, je vais essayer de me recentrer pour approfondir quelques voies empruntées.

Valider que Flus est un projet viable (ou non). Il y a encore du boulot, et parfois beaucoup trop. J’avance pas (à pas) mais jamais aussi vite que ce que j’aimerais, probablement lié à ma capacité à m’éparpiller. Je travaille dessus. Et il faut que je m’améliore en communication et marketing. En tout cas ça m’amuse et je suis rentable pour l’instant, c’est déjà ça de pris.

Continuer de faire du pain (genre, beaucoup). J’ai commencé à en faire après le passage de Thomas à l’appartement, j’ai trouvé ça chouette. Au début vraiment une fois de temps en temps, j’ai finalement pris un rythme soutenu depuis la fermeture de ma boulangerie préférée à Grenoble (désormais à Chambéry). Je me dis que ça ferait une chouette reconversion plus tard.

Continuer d’écrire (genre, pas mal). Pour ce blog, j’aimerais réussir à faire quelque chose de peut-être plus personnel. La liste des titres de mes articles de 2019, dans leur majorité, me laisse une impression un peu dérangeante, comme si je ne parlais jamais de moi dedans ; bizarre pour un blog lié à un site personnel. Je le ressens aussi dans le « ton » que j’emploie, dans les mots que je choisis. J’aimerais me rapprocher d’un ton plus « parlé ». J’ai commencé à travailler dessus mais ne suis pas encore totalement satisfait.

Concevoir une V2 pour Lessy (et la terminer). J’ai laissé trainer ce projet en 2019, mais je continue de l’utiliser quotidiennement. J’ai eu une fulgurance le 1er janvier, et toutes les idées que j’avais pour l’améliorer se sont agencées correctement. Je ne me vois pas « améliorer » la version actuelle, donc ce sera un redéveloppement sur des bases plus simples, dans l’idéal tendant sur du low-tech pour une maintenance minimale une fois que j’aurai terminé. En tout cas la vision que j’ai des fonctionnalités est très claire.

Bon, et puis j’ai décidé d’arrêter de procrastiner pour avoir le temps de faire plus de choses. Ça fait quatre jours que je me suis fait une note mentale qui fait qu’à chaque fois que « j’ai la flemme », je me réponds « bouge-toi » et que ça marche. Vous inquiétez pas, ça va pas durer.

Et bonne année !

Une histoire de pizzas : les étapes

mardi 31 décembre 2019 à 10:39

Après une petite pause, voici le sixième épisode de ma série d’articles retraçant la conception d’un mini-jeu vidéo. Comme d’habitude, vous êtes invité·es à lire les articles précédents pour vous y retrouver.

Aujourd’hui, on va faire simple : on va afficher la liste des étapes que le joueur devra suivre pour avancer dans le jeu. Le gros du travail se fera dans l’article suivant.

Pour ce faire, on va commencer par déclarer les étapes en JavaScript :

// On déclare les étapes dans un objet...
const steps = [
    { id: 'STEP_ROLL_OUT_DOUGH', label: 'Étaler la pâte avec les doigts' },
    { id: 'STEP_GRAB_TOMATO', label: 'Aller chercher la sauce tomate sur l’étagère' },
    { id: 'STEP_SPREAD_TOMATO', label: 'Verser la sauce tomate sur la pizza' },
    { id: 'STEP_GRAB_MOZZA', label: 'Aller chercher la mozzarella dans le frigo' },
    { id: 'STEP_SPREAD_MOZZA', label: 'Disposer la mozzarella sur la sauce tomate' },
    { id: 'STEP_GRAB_PIZZA_1', label: 'Prendre la pizza' },
    { id: 'STEP_PUT_PIZZA_IN_OVEN', label: 'Mettre la pizza au four' },
    { id: 'STEP_BAKE_PIZZA', label: 'Cuire 5 secondes' },
    { id: 'STEP_GRAB_PIZZA_2', label: 'Prendre la pizza' },
    { id: 'STEP_DELIVER_PIZZA', label: 'Emporter la pizza jusqu’au passe-plat' },
    { id: 'STEP_FINISH' },
];

const app = new Vue({
    el: '#app',
    data: {
        size: BOARD_SIZE,
        store: gameStore,
        // ... que l'on passe ensuite à data pour pouvoir y accéder depuis la vue
        steps,
    },

    // ...
});

C’est tout bête, rien de bien méchant ici. Il nous faut désormais afficher cette liste dans le HTML.

<div id="app">
    <div class="steps">
        <div class="steps-title">Recette</div>

        <transition v-for="(step, index) in steps" :key="step.id">
            <div
                v-if="step.label"
                :class="['steps-item', { current: step.id === currentStep }]"
            >
                <div class="steps-item-index">{{ index + 1 }}</div>
                <div class="steps-item-label">{{ step.label }}</div>
            </div>
        </transition>
    </div>

    <!-- ... -->
</div>

Vous remarquerez peut-être que je déclare une class .current pour indiquer quelle est l’étape en cours. Néanmoins, je fais appel à une variable currentStep que nous n’avons pas encore déclarée. Il s’agit d’une valeur qui va évoluer au fil du temps à travers des actions liées aux clics du joueur, c’est-à-dire dans notre système onClickSystem. Cette valeur doit donc être déclarée dans le store du jeu. Retour dans le JavaScript :

let gameStore = [
    /*
        On ajoute une nouvelle entité au store pour mémoriser l'étape
        courante. C'est cette valeur qui va permettre d’avancer dans le jeu
        plus tard.
    */
    { id: 'currentStep', value: 'STEP_ROLL_OUT_DOUGH' },

    // ...
];

const app = new Vue({
    // ...

    computed: {
        // On déclare une donnée "computed" : il s'agit d'une donnée qui
        // est calculée depuis une autre donnée déjà stockée dans data
        // (ici, le store). Il s'agit juste de simplifier le code du
        // template HTML.
        currentStep() {
            return getEntity(this.store, "currentStep").value;
        },
    },
});

Avec ça, on a quasiment terminé pour cette fois-ci, on va juste ajouter un peu de CSS pour rendre le tout un poil plus joli.

/* Du CSS pour afficher le bloc des instructions à gauche de la zone de jeu */
#app {
    display: flex;
    justify-content: center;
}

/*
   Et du CSS pour styler les instructions elles-mêmes. Ironiquement, il y a
   besoin de plus de CSS pour cette partie que pour la zone de jeu.
*/
.steps {
    width: 30%;
    margin-right: 1rem;
    padding: .5rem 1rem;

    font-family: sans-serif;

    background-color: #eee;
    border-radius: .5rem;
}

.steps .steps-title {
    margin-bottom: 1rem;
    padding-bottom: .25rem;

    text-align: center;

    border-bottom: 1px solid #ccc;
}

.steps .steps-item {
    display: flex;
    padding: .25rem .5rem;

    align-items: baseline;

    color: #666;
    font-size: .8rem;

    border: .25rem solid transparent;
}

.steps .steps-item.current {
    color: #000;
    font-weight: bold;

    border-color: #00dbcb;
    border-radius: .5rem;
}

.steps .steps-item .steps-item-index {
    display: flex;
    width: 1.25rem;
    height: 1.25rem;
    margin-right: .25rem;

    flex-shrink: 0;
    align-items: center;
    justify-content: center;

    color: #fff;

    background-color: #666;
    border-radius: 50%;
}

.steps .steps-item.current .steps-item-index {
    color: #000;

    background-color: #00dbcb;
}

Avant de finir cet article, je veux juste pointer une petite différence vis-à-vis du jeu initial. J’ai fait le choix ici d’afficher toutes les instructions pour simplifier légèrement le code et ne pas rentrer dans des détails inutiles d’implémentation. Dans le jeu des Ergogames, seulement deux instructions sont affichées à la fois : l’étape courante et l’étape suivante. Si le détail technique n’est pas important, il l’est cependant d’un point de vue utilisabilité. Cette partie a beaucoup évolué avec les retours des utilisateurs et utilisatrices, notamment pour satisfaire la contrainte de l’écran réduit sur mobile. Ce n’est d’ailleurs même pas moi qui aie fait les derniers changements.

Il ne vous reste plus qu’à tester le résultat du code de cet article ici. Il n’y a rien de plus à faire qu’à regarder puisque nous n’avons pas encore implémenté les actions qui vont permettre d’avancer dans le jeu. Ce sera l’objet du prochain article.

Sortie de Boop! 0.4 à l’arrache

samedi 16 novembre 2019 à 20:15

Je me rends compte ce soir que cela fait un an que j’ai commencé mon générateur de sites statiques, Boop!. Comme je n’ai pas sorti de version depuis un petit moment, je fais donc ça maintenant, au pied levé.

Pour les personnes qui débarquent, il s’agit d’un projet totalement personnel, sans ambition aucune : je fais ça pour m’amuser.

Pour les personnes qui se posent la question : oui je continue de l’utiliser, et même de plus en plus. C’est notamment Boop! qui est utilisé pour mon blog « carnet de flus » ainsi que pour le site indiquant le statut de Flus. À chaque fois, cela m’a permis d’améliorer un peu plus le programme. Petit tour d’horizon des nouveautés.

Le truc qui aurait dû être dans la dernière version – je sais pas pourquoi je n’ai pas sorti une autre version d’ailleurs à l’époque – ce sont plus de variables accessibles dans les templates. J’avais notamment besoin d’afficher les séries sur la page du blog.

Tant qu’on est dans les templates, j’ai ajouté un nouveau mot clé (set) permettant d’assigner des variables. 6 lignes de codes en plus, c’est pas cher payé pour la prise de tête que je me suis ainsi évité.

J’ai également ajouté le support des articles privés (qui n’apparaissent ni dans les flux Atom, ni sur la page principale du blog). C’était fait en 5 lignes, je vois pas pourquoi je me serais privé (*ba-dum tss* 🥁).

Ensuite, la possibilité de modifier le « slug » de la page principale du blog permet notamment d’en faire une page d’accueil, comme sur « carnet de flus ». Cette fois-ci j’ai étonnamment enlevé plus de lignes de code que j’en ai ajoutées, ça fait plaisir.

J’ai activé l’extension Markdown qui permet d’ajouter la coloration syntaxique) dans les articles. J’en avais particulièrement besoin pour ma série « Ergogames, une histoire de pizzas ». Ça aurait été illisible sans ça.

Un truc qui m’aura pris un peu plus de temps (2h pour 50 lignes de code) mais me permet de rester serein, c’est l’ajout d’un système de cache pour les articles. Le site est désormais généré en 0,3 seconde au lieu de 2 secondes avant. 2s ça peut paraître peu, mais c’est énorme lorsque je relis un article et que je le régénère plusieurs fois par minute.

J’ajoute à cela des micro-trucs, comme :

Je parle de « micro-trucs » mais ce n’est vraiment pas des détails pour moi : c’est ce qui fait que je trouve mon expérience utilisateur bien au-dessus de celles que j’ai pu avoir avec d’autres générateurs de sites statiques. Par contre je parle bien de « mon » expérience : c’est totalement pensé par et pour moi. Déjà il va falloir que j’améliore grandement la documentation pour en faire un truc plus accessible à d’autres.

Tout ça pour dire que Boop! 0.4 est sortie, youpi ! 🎉

Une histoire de pizzas : coup de pinceau

lundi 11 novembre 2019 à 18:00

Vous aimez les pizzas ? Vous aimez les ratons laveurs ? Alors voici déjà le cinquième épisode de ma série d’articles retraçant la conception d’un mini-jeu vidéo !

Dans le deuxième article, on ne s’était pas trop embêté pour les graphismes : on avait fait de gros blocs de couleurs moches. Il est toutefois temps de corriger ça en passant un coup de peinture sur le jeu. Notre raton va bientôt ressembler à un raton !

Le premier ingrédient qu’il nous faut, c’est un graphiste ; et surprise, je ne le suis pas. Mes anciennes collègues de uxShadow ont fait appel à Goulven Barron pour réaliser les graphismes du jeu (merci à lui !), je n’avais ainsi plus qu’à les intégrer.

Pour cet article, je me suis donc contenté de récupérer des images du jeu d’origine et de les déposer dans un répertoire assets (qu’on peut traduire par « ressources » en bon françois).

Ensuite, il ne nous reste plus qu’à attacher chaque entité à une image et à l’afficher. Pour cela, on va tout simplement créer un nouveau composant au sein des entités (asset) qui contiendra l’URL vers l’image et ajouter cette dernière au HTML à l’aide d’une balise <img />.

<div id="app">
    <div class="board">
        <!-- ... -->

        <div
          v-for="entity in store"
          :key="entity.id"
          :class="['board-cell-entity', entity.id]"
          :style="entityPosition(entity)"
        >
            <!-- On affiche une image si l'entité possède un composant "asset" -->
            <img
                v-if="entity.asset"
                :src="entity.asset"
                alt=""
                class="entity-asset"
            />

            <!-- Sinon, on continue d'afficher le label -->
            <div v-else-if="entity.label" class="entity-label">
                {{ entity.label }}
            </div>
        </div>

        <!-- ... -->
    </div>
</div>

<script>
    // ...

    let gameStore = [
        // On se contente d'ajouter un composant "asset" à chacune de nos
        // entités. Il s’agit juste d'une URL vers une image.
        {
            id: 'meiko',
            asset: 'assets/meiko-face.svg',
            // ...
        },

        {
            id: 'mozza',
            asset: 'assets/pot-mozzarella.svg',
            // ...
        },
        {
            id: 'tomato',
            asset: 'assets/sauce-tomate.svg',
            // ...
        },
        {
            id: 'dough',
            asset: 'assets/pate-pizza-boule.png',
            // ...
        },

        {
            id: 'oven',
            asset: 'assets/four.svg',
            // ...
        },
        {
            id: 'hatch',
            asset: 'assets/passeplat.svg',
            // ...
        },
        {
            id: 'fridge',
            asset: 'assets/refrigirateur.svg',
            // ...
        },
        {
            id: 'workplan',
            asset: 'assets/plan-travail.svg',
            // ...
        },
        {
            id: 'shelf',
            asset: 'assets/etagere-horizontale.svg',
            // ...
        },
    ];

    // ...
</script>

<style type="text/css">
    /* ... */

    /*
        On vire quasiment tout le CSS concernant les entités (sauf les z-index)
        et on le remplace par... seulement ce max-width pour éviter que les
        images débordent des cases. Vous vous attendiez à plus de CSS ? :)
    */
    .board-cell-entity img {
        max-width: 100%;
    }

    /* Bon OK, on en ajoute un pour que la mozza ne déborde pas trop du frigo */
    .board-cell-entity.mozza img {
        max-width: 85%;
    }

    /* ... */
</style>

Et voilà, en seulement quelques lignes, on a largement transformé notre jeu. On est désormais visuellement très proches du jeu d’origine. Un petit détail toutefois reste à régler : on aimerait bien que Meiko se tourne dans la direction vers laquelle on vient de cliquer. Pour cela on va avoir besoin de 4 images représentant Meiko : une pour chaque direction. La question qui se pose est : « comment changer d’image ? »

Je vais aborder ici deux solutions possibles pour faire cela. Le choix que j’ai fait n’est pas forcément le meilleur, mais il sera justifié. Libre à vous de penser que c’est vraiment n’importe quoi et de préférer l’autre solution.

La première façon de faire est de regrouper toutes les images au sein d’un seul fichier : un « sprite » (ou lutin, c’est Wikipédia qui le dit, j’ai toujours entendu parler que de sprite). Ensuite, il faut afficher seulement une partie de l’image, en fonction d’une classe CSS par exemple. Je ne rentre pas dans le détail de l’implémentation, les méthodes sont de plus sensiblement différentes si vous avez un PNG ou un SVG. Cette technique me posait plusieurs problèmes :

Bref, je suis parti sur une seconde solution plus naïve, moins performante et moins élégante, mais qui me permettait au moins d’avancer sans me poser trop de questions.

La technique est simple. Chaque image se trouve dans un fichier à part, et on les rattache via un composant assets (notez le « s » final, c’est un autre composant !) qui va regrouper ces images, indexées par des directions. Lors de l’affichage, on va calculer dynamiquement l’image à afficher en fonction de la direction vers laquelle Meiko est dirigé. Ça ressemble à ça :

<div id="app">
    <div class="board">
        <!-- ... -->

        <div
          v-for="entity in store"
          :key="entity.id"
          :class="['board-cell-entity', entity.id]"
          :style="entityPosition(entity)"
        >
            <!-- On affiche une image si l'entité possède un composant "asset" -->
            <img
                v-if="entity.asset"
                :src="entity.asset"
                alt=""
                class="entity-asset"
            />

            <!--
                Ou bien un composant "assets", il faut alors calculer
                dynamiquement l'image à afficher.
            -->
            <img
                v-else-if="entity.assets"
                :src="entityAssetImage(entity)"
                alt=""
                class="entity-asset"
            />

            <!-- Sinon, on continue d'afficher le label -->
            <div v-else-if="entity.label" class="entity-label">
                {{ entity.label }}
            </div>
        </div>

        <!-- ... -->
    </div>
</div>

<script>
    // ...

    let gameStore = [
        {
            id: 'meiko',

            // Pour Meiko, on remplace le composant "asset" par "assets". Cela
            // nous permettra d'associer plusieurs images, la bonne sera
            // choisie dynamiquement lors de l'affichage.
            assets: {
                bottom: 'assets/meiko-face.svg',
                right: 'assets/meiko-droite.svg',
                left: 'assets/meiko-gauche.svg',
                top: 'assets/meiko-dos.svg',
            },

            // On ajoute également un composant "direction" à Meiko
            direction: 'bottom',

            // ...
        },

        // ...
    ];

    const app = new Vue({
        // ...

        methods: {
            // ...

            // Cette méthode JS calcule dynamiquement l'image à afficher pour
            // les entités possédant un composant "assets"
            entityAssetImage(entity) {
                if (entity.assets == null || entity.direction == null) {
                    return '';
                }

                // Comme le composant "assets" est indexé par les directions,
                // récupérer la bonne image se fait très facilement. Ce code
                // n’est pas très robuste mais comme le jeu est très simple
                // avec peu de contributeurices, on peut s’en contenter pour
                // l'instant.
                return entity.assets[entity.direction];
            },
        },
    });

    // ...
</script>

La dernière étape consiste à changer effectivement la direction de Meiko en fonction d’où le clic a été effectué. Vous l’aurez compris : on va modifier l’état du store, cela se passe donc au sein de notre système onClickSystem.

function onClickSystem(e, store) {
    // ...

    // Notre code doit s'exécuter avoir vérifié que Meiko peut accéder à la
    // case ciblée, sinon il ne doit pas bouger du tout.
    const meiko = getEntity(store, 'meiko');
    if (!positionIsAccessibleFor(meiko, position)) {
        return store;
    }

    // On veut désormais changer la direction de Meiko : il suffit de
    // comparer sa position à la position de la case ciblée pour savoir
    // vers où Meiko doit se tourner.
    let direction = meiko.direction;
    if (position.x > meiko.position.x) { direction = 'right'; }
    else if (position.x < meiko.position.x) { direction = 'left'; }
    else if (position.y < meiko.position.y) { direction = 'top'; }
    else if (position.y > meiko.position.y) { direction = 'bottom'; }

    let updatedStore = setEntityComponents(store, meiko.id, { direction });

    // On fait bien attention à utiliser le updatedStore à partir d’ici au
    // lieu du store sinon Meiko ne se tournera pas.
    const entitiesAtPosition = searchEntitiesAt(updatedStore, position);
    if (entitiesAtPosition.some((entity) => entity.obstruct)) {
        return updatedStore;
    }

    updatedStore = setEntityComponents(updatedStore, meiko.id, { position });

    return updatedStore;
}

Avec ça on a fini notre coup de peinture sur le jeu. D’ailleurs, on a même terminé de toucher au HTML concernant la zone de jeu elle-même : même pas 50 lignes de code. Vous remarquerez sans doute en testant le jeu (le résultat est ici) que Meiko ne change pas immédiatement de direction : il s’agit du temps de charger l’image la première fois. On corrigera ça plus tard dans un article bonus. Pour l’instant, on a quelque chose de bien plus important à faire : ajouter les étapes ainsi que les actions pour avancer dans le jeu. Ce sera l’objet des deux prochains articles (au moins !)