Tutoriels
Des récits à embranchements sans plat de spaghettis

Huit principes pour qu’un roman graphique à embranchements reste maintenable dans PanelWave : séparer le déroulement, l’état et le contenu, construire chaque choix comme une triade, faire converger les chemins tôt, confier les conséquences aux variables et tester chaque chemin avant que les lecteurs ne trouvent celui que vous avez oublié.
Toute histoire interactive commence de la même façon : un embranchement net en page trois. Au chapitre quatre, l’organigramme ressemble à une assiette de pâtes, la moitié des cases sont des copies d’autres cases avec une réplique changée, et plus personne n’ose toucher au graphe. Voici comment éviter d’en arriver là. Nous partons du principe que vous connaissez les bases de l’éditeur (voir Premiers pas avec PanelWave CMS) et allons droit aux choix de conception qui décident si une œuvre à embranchements reste modifiable.
En bref : PanelWave vous donne trois outils distincts, et les spaghettis apparaissent quand vous n’en utilisez qu’un. Le déroulement vit dans le graphe du chapitre. L’état vit dans les variables. Le contenu réagit à l’état grâce aux variantes de case et aux conditions de visibilité. Gardez le graphe petit, confiez les conséquences aux variables, et laissez les cases les refléter.
Principe 1 : choisir le bon outil pour chaque différence
Avant de tracer un embranchement, demandez-vous ce qui diffère réellement entre les deux chemins. Trois réponses sont possibles, chacune liée à un mécanisme différent du format :
La documentation résume le compromis dans un tableau sous Variantes et autres mécanismes conditionnels. La plupart des graphes spaghettis sont en fait des variantes et des conditions de visibilité dessinées comme des embranchements.
- Les lecteurs voient des cases différentes. C’est une différence de déroulement. Utilisez des arêtes conditionnelles dans le Graph Editor ; la référence du graphe détaille chaque propriété.
- Les lecteurs voient la même case avec un contenu différent. Un autre arrière-plan, une surimpression en plus, une réplique modifiée. C’est une variante de case : le même nœud dans le graphe, un contenu alternatif choisi par une condition. Le déroulement de l’histoire reste fixe.
- Un seul élément apparaît ou non. Une zone interactive qui n’apparaît que lorsque le lecteur a la clé, une bulle qui ne s’affiche qu’après un choix. C’est une condition de visibilité sur cet élément, définie avec le Condition Builder, sans toucher au reste de la case.
Principe 2 : déclarer l’état avant d’embrancher
Les variables sont la mémoire de l’histoire ; elles sont typées et déclarées à l’avance. Donnez-leur des identifiants préfixés, séparés par des points, qui indiquent à quoi elles se rattachent : story.choice, stats.trust, inventory.hasKey, prefs.hints. Utilisez une énumération pour tout choix au nombre de résultats fixe : une faute de frappe dans une condition provoque alors une erreur visible, au lieu de ne jamais rien déclencher sans prévenir.
La portée, c’est la décision qu’on oublie. Elle détermine quand une valeur est réinitialisée : page, chapitre, global, session ou persistant. La règle pratique du guide des variables : les décisions dont toute l’œuvre doit se souvenir vont en global ou persistant, l’état éphémère d’une énigme en chapitre ou page, et les indicateurs valables pour cette visite seulement, comme une aide fermée, en session. Les variables persistantes survivent d’une visite à l’autre, ce qui rend possibles les contenus à débloquer et les marqueurs « terminé » (sauvegarder la progression dans le lecteur).
Dans le CMS, cela se passe dans le Variables designer : ouvrez le Backstage d’une œuvre, choisissez Variables, et déclarez chaque variable une fois avec son identifiant, son type, sa portée et sa valeur par défaut. La boîte de dialogue valide les identifiants, les listes d’énumération et les valeurs par défaut avant tout enregistrement, et cette même liste alimente le manifeste, les surcharges de la page Preview et le PanelWave Player. Vous écrivez votre manifeste à la main ? Export JSON fournit les définitions sous forme de fragment à coller, et l’import d’un manifeste conserve sa section variables. Le guide des variables présente le designer ; le schéma des variables donne le format exact.
{
"variables": {
"definitions": [
{
"id": "story.choice",
"type": "enum",
"enum": ["left", "right", "none"],
"default": "none",
"scope": "chapter"
},
{ "id": "stats.trust", "type": "integer", "default": 50, "scope": "global" }
]
}
}Principe 3 : construire chaque choix comme une triade
Dans PanelWave, un choix n’est jamais un objet unique. Il se compose de trois éléments liés à une seule variable : une zone interactive qui la définit, une arête par issue qui la teste, et une arête sans condition comme repli. Construisez les trois à chaque fois, et les lecteurs ne pourront jamais rester bloqués.
Sur la case du choix, dessinez une zone interactive par option et donnez à chacune une action Set Variables : story.choice à left, story.choice à right. Dans le Graph Editor, sélectionnez la case source, appuyez sur C, choisissez la cible et appuyez sur Entrée pour tracer les arêtes, puis sélectionnez chaque arête et tapez sa condition. Les conditions sont en JSON Logic : un opérateur, une référence de variable, une valeur. La section Conditions d’arête propose des exemples à copier et adapter pour l’égalité, le « supérieur à » et les règles combinées.
{ "from": "p3", "to": "p4-left", "condition": { "==": [{ "var": "story.choice" }, "left"] }, "priority": 1 }
{ "from": "p3", "to": "p4-right", "condition": { "==": [{ "var": "story.choice" }, "right"] }, "priority": 1 }
{ "from": "p3", "to": "p4-left", "priority": 0 }La troisième arête est le filet de sécurité : si le lecteur avance sans cliquer sur aucune porte, le PanelWave Player suit l’arête sans condition. Le champ priority départage les arêtes concurrentes quand plusieurs correspondent, comme décrit sous Embranchements. Le Player évalue tout cela à chaque étape ; l’algorithme est détaillé dans Comment le lecteur parcourt le graphe.
Un piège de nommage fréquent : sur une arête, les modifications de variables vont dans la liste action ; sur une zone interactive, dans mutations. La référence du graphe le signale explicitement. Dans le CMS, vous touchez rarement l’un ou l’autre directement, mais cela compte quand vous lisez un manifeste exporté.
Principe 4 : embrancher, puis resserrer
La forme classique d’une histoire à embranchements maintenable est le losange : on bifurque, on déroule deux ou trois cases par chemin, puis on converge sur une case commune. Chaque embranchement qui ne converge pas double la quantité d’illustrations à dessiner et le nombre de chemins à tester. Convergez tôt et souvent.
Mais converger, c’est risquer l’amnésie si l’histoire oublie ce qui s’est passé. C’est là que l’état entre en jeu. Le choix vit déjà dans story.choice ; les cases communes après le goulet peuvent le refléter avec une variante de case dont la condition when teste la variable et dont les surcharges remplacent les calques ou le dialogue. Un seul nœud de case, deux versions de la scène, aucune case dupliquée dans le graphe. La documentation appelle cela le reflet du choix et en montre un exemple complet dans Le modèle du choix.
Deux règles sur les variantes vous éviteront du débogage. Les variantes sont évaluées dans l’ordre du tableau et la première correspondance l’emporte : classez-les donc de la plus spécifique à la moins spécifique quand les conditions peuvent se recouper (Ordre de sélection). Et un tableau layers en surcharge remplace toute la pile : répétez les calques de base que vous voulez garder. Dans l’éditeur, les cases avec variantes portent un badge V dans l’arborescence, et la barre de filtres de l’arborescence peut n’afficher que ces cases (Pages et cases).
Principe 5 : hubs, boucles et compteurs
Les cycles sont autorisés dans un graphe PanelWave, et c’est ainsi que l’on construit des hubs : une pièce où le lecteur revient entre deux explorations, un écran de carte, un menu de conversation. Le panneau de validation du graphe signale les cycles par un avertissement, mais simplement pour information ; une boucle voulue ne pose aucun problème (Validation).
Ce qui empêche un hub de devenir une boucle infinie, c’est un compteur. Donnez à chaque quête secondaire une zone interactive qui incrémente stats.visits ou bascule un booléen comme story.metCourier, et soumettez l’arête de sortie du hub à une condition, par exemple stats.visits supérieur ou égal à trois. Les opérations de mutation sont set, increment, toggle, append et remove (Mutation). Les lecteurs explorent dans l’ordre qu’ils veulent, la sortie se débloque quand ils ont fini, et le graphe reste une étoile bien rangée au lieu d’une permutation de tous les ordres possibles.
Principe 6 : conditionner les éléments, pas dupliquer les cases
Quand un seul élément d’une case dépend de l’état, conditionnez cet élément. Les zones interactives et les bulles ont une section Visibility Condition dans l’inspecteur ; cliquez sur Build Condition et le Condition Builder écrit l’expression pour vous : variable, type, opérateur, valeur, combinés avec AND ou OR. Cas d’école : la porte verrouillée. La zone interactive A met inventory.hasKey à true ailleurs dans l’histoire, et la zone interactive B sur la porte ne devient cliquable que lorsque inventory.hasKey vaut true (Conditions sur les zones interactives).
Les mutations des zones interactives vont un cran plus loin : un déclencheur When Variable Changes peut animer l’opacité, l’échelle, la position ou la couleur au moment où l’état change, si bien que l’illustration réagit à un choix sans aucun changement de case. Dans l’œuvre publiée, les lecteurs voient les zones interactives comme des régions au contour doucement pulsant, entièrement accessibles au clavier (le point de vue du lecteur) ; votre logique de conditions leur reste invisible.
Principe 7 : garder la carte lisible
Un graphe que vous ne savez plus lire, vous finirez par le casser. Quelques habitudes le gardent lisible à mesure que l’œuvre grandit :
- Un chapitre, un graphe. Chaque chapitre a son propre graphe et sa propre case d’entrée. Traitez les chapitres comme l’unité d’embranchement : un chapitre doit contenir une bifurcation complète, convergence comprise, pas une moitié.
- Nommez vos cases. Donnez aux cases un Friendly ID comme courier-door-left dans les Basic Properties de l’inspecteur (Pages et cases) ; le Graph Editor étiquette les nœuds avec les titres des cases, et le manifeste peut porter des étiquettes de nœud destinées aux lecteurs pour les cartes de chapitre.
- Laissez la machine faire la mise en page. Auto-Layout (L) dispose le graphe de gauche à droite selon la profondeur dans l’histoire et minimise les croisements ; les positions sont mémorisées par chapitre. La bascule List affiche le même graphe sous forme de plan imbriqué, adapté aux lecteurs d’écran, avec des badges d’entrée et de fin (Se déplacer).
- Cachez ce qui doit rester caché. Si le chapitre utilise le canevas infini, réglez les embranchements secrets sur Hidden until visited dans les modes de révélation pour que le zoom d’ensemble ne les dévoile pas ; le même réglage est recommandé pour les cases sous paywall.
Principe 8 : tester chaque chemin, puis mesurer les vrais
Les bugs d’embranchement se cachent dans le chemin sur lequel vous n’avez pas cliqué. PanelWave vous donne trois niveaux de vérification, qui répondent à des questions différentes.
Une fois l’œuvre en ligne, les statistiques vous disent quels embranchements les lecteurs prennent vraiment. Le parcours de lecture montre les sessions par case dans l’ordre de lecture : une chute brutale juste après un embranchement signifie généralement que le choix n’était pas clair. La carte de chaleur des clics montre où les lecteurs cliquent, zone interactive ou non ; un amas de clics en dehors de toute zone interactive signifie qu’ils attendaient un choix que vous n’avez pas proposé. Dans les deux cas, c’est moins cher que de deviner.
- Structure : le mode Simulate du Graph Editor parcourt le graphe depuis l’entrée, liste chaque arête sortante sous Where to next avec sa condition en ligne, et signale les impasses. Il ne parcourt que la structure et n’évalue pas les conditions (Simuler le déroulement). Appuyez sur V pour relancer la validation, qui signale les nœuds inaccessibles, les nœuds isolés et les cases d’entrée manquantes, avec des corrections en un clic pour les cas courants.
- Logique : Preview liste chaque variable avec un champ typé. Forcez story.choice sur chaque valeur et relisez le chapitre ; enregistrez la combinaison comme scénario pour la prochaine fois (Surcharges de variables). Le bug d’embranchement le plus courant, selon la documentation, est une condition qui ne peut jamais être vraie parce que rien ne définit jamais la variable. Testez les deux côtés de chaque embranchement.
- Publication : Preflight lance des vérifications sur toute l’œuvre avant la publication, y compris les problèmes de déroulement comme des cases inaccessibles depuis l’entrée du chapitre ou des références d’arête cassées (Validation et Preflight). Les erreurs bloquent la publication.
Un exemple complet : le coursier
Voici un chapitre complet conçu avec ces principes. Sept cases, un embranchement, un hub, aucune illustration dupliquée.
Trois arêtes sortant de la case 3, repli compris, trois boucles par le hub, une sortie. Le graphe tient sur un écran, Simulate le parcourt en moins d’une minute, et chaque conséquence des choix du lecteur vit dans trois variables que vous pouvez inspecter dans Preview.
- Variables : story.choice (énumération : left, right, none ; portée chapitre), stats.visits (entier, portée chapitre), inventory.hasKey (booléen, portée globale).
- Cases 1 à 3 : linéaires. La case 3 est le choix : deux zones interactives sur les portes, chacune avec une action Set Variables sur story.choice et une action Navigate vers la case 4.
- Case 4 : une case, deux variantes. La variante gauche ajoute une surimpression et la réplique « Tu as pris l’escalier de gauche » ; la variante droite remplace l’arrière-plan. La case de base sert de repli pour les lecteurs qui ont avancé sans choisir.
- Case 5 : le hub, une cour avec trois zones interactives. Chacune mène à une case annexe (6a, 6b, 6c) dont l’arête de retour incrémente stats.visits ; celle qui contient la clé met aussi inventory.hasKey à true. La zone interactive de sortie du hub a la condition de visibilité stats.visits supérieur ou égal à deux.
- Case 7 : la fin. Son arête depuis le hub est sans condition, et une variante teste inventory.hasKey pour afficher la version déverrouillée.
Pour aller plus loin
- Navigation par graphe : pourquoi un graphe plutôt qu’un ordre de pages, et comment le lecteur le parcourt.
- Variables : le modèle d’état derrière chaque condition.
- État et conditions dans le lecteur : comment le moteur d’exécution évalue le JSON Logic et stocke l’état.
- Les zones interactives dans le format : les actions goTo, setVariables, openExtras, openModal et pluginEvent.
- Monétisation : où placer une limite de paywall dans un chapitre à embranchements, et comment prévisualiser en lecteur gratuit ou premium.

