Livre dont vous êtes le héros
Objectif
Section titled “Objectif”Créer une histoire interactive où les choix du lecteur influencent la narration. Vous apprendrez à gérer des états narratifs, naviguer entre eux, et structurer une expérience non-linéaire.
Ce que vous allez construire : Une histoire courte (3 chemins, 5+ états) avec choix multiples et fins différentes.
Concepts couverts
Section titled “Concepts couverts”- Navigation entre états narratifs
- Stores Svelte pour gérer l’état global
- Architecture modulaire (séparer logique et contenu)
- Choix qui impactent la narration
Penser l’architecture
Section titled “Penser l’architecture”Avant de coder, pensez structure narrative.
Questions à se poser :
- Combien d’états (scènes) ?
- Quels choix à chaque état ?
- Vers où mènent ces choix ?
- Y a-t-il des états communs (convergence) ?
- Combien de fins possibles ?
Exemple de structure :
DÉBUT ↓État 1: Vous êtes devant une porte → Choix A: Entrer → État 2 → Choix B: Partir → État 3
État 2: Intérieur sombre → Choix A: Allumer → État 4 (fin heureuse) → Choix B: Avancer → État 5 (fin tragique)
État 3: Forêt → Choix A: Continuer → État 5 (convergence) → Choix B: Retour → État 1Structure du projet
Section titled “Structure du projet”src/├── lib/│ ├── stores/│ │ └── storyStore.js // État global de l'histoire│ ├── data/│ │ └── storyData.js // Contenu narratif│ └── components/│ ├── Scene.svelte // Affichage d'une scène│ └── Choice.svelte // Bouton de choix└── routes/ └── +page.svelte // Page principalePrincipe : Séparer données (contenu) et logique (code).
1. Définir les données narratives
Section titled “1. Définir les données narratives”Créez src/lib/data/storyData.js :
// Chaque scène a :// - id unique// - texte narratif// - choix possibles (texte + destination)
export const scenes = { start: { id: 'start', text: "Vous êtes devant une vieille maison abandonnée. La porte grince dans le vent.", choices: [ { text: "Entrer dans la maison", next: 'inside' }, { text: "Partir vers la forêt", next: 'forest' } ] },
inside: { id: 'inside', text: "L'intérieur est plongé dans l'obscurité. Vous sentez une présence.", choices: [ { text: "Allumer votre lampe", next: 'light' }, { text: "Avancer dans le noir", next: 'darkness' } ] },
forest: { id: 'forest', text: "La forêt est dense. Un chemin sinueux s'enfonce dans les arbres.", choices: [ { text: "Suivre le chemin", next: 'darkness' }, { text: "Retourner à la maison", next: 'start' } ] },
light: { id: 'light', text: "La lumière révèle une pièce magnifique remplie de livres anciens. Vous avez trouvé un trésor.", choices: [ { text: "Recommencer", next: 'start' } ], isEnding: true },
darkness: { id: 'darkness', text: "Dans l'obscurité, vous trébuchez et tombez dans un puits sans fond.", choices: [ { text: "Recommencer", next: 'start' } ], isEnding: true }};Structure claire :
- Chaque scène est un objet avec
id,text,choices - Les fins ont un flag
isEnding - Les choix pointent vers d’autres scènes via
next
2. Créer le store d’état
Section titled “2. Créer le store d’état”Créez src/lib/stores/storyStore.js :
import { writable } from 'svelte/store';import { scenes } from '../data/storyData';
// État initial : on commence à la scène 'start'const initialState = { currentSceneId: 'start', history: ['start'] // Historique des scènes visitées};
function createStoryStore() { const { subscribe, set, update } = writable(initialState);
return { subscribe,
// Naviguer vers une nouvelle scène goToScene: (sceneId) => { update(state => ({ currentSceneId: sceneId, history: [...state.history, sceneId] })); },
// Réinitialiser l'histoire reset: () => { set(initialState); },
// Obtenir la scène actuelle getCurrentScene: (state) => { return scenes[state.currentSceneId]; } };}
export const storyStore = createStoryStore();Ce que fait le store :
- Garde l’état actuel de l’histoire
- Permet de naviguer entre scènes
- Garde un historique (optionnel, pour debug ou fonctionnalité “retour”)
- Fournit une méthode pour réinitialiser
3. Composant Choice (bouton de choix)
Section titled “3. Composant Choice (bouton de choix)”Créez src/lib/components/Choice.svelte :
<script> import { storyStore } from '../stores/storyStore';
let { choice } = $props(); // { text: "...", next: "..." }
function handleClick() { storyStore.goToScene(choice.next); }</script>
<button on:click={handleClick}> {choice.text}</button>
<style> button { display: block; width: 100%; padding: 1rem; margin: 0.5rem 0; background: #2a2a2a; color: white; border: 2px solid #444; border-radius: 8px; font-size: 1rem; cursor: pointer; transition: all 0.2s ease; }
button:hover { background: #3a3a3a; border-color: #666; transform: translateX(4px); }</style>4. Composant Scene (affichage d’une scène)
Section titled “4. Composant Scene (affichage d’une scène)”Créez src/lib/components/Scene.svelte :
<script> import Choice from './Choice.svelte';
let { scene } = $props(); // Objet scène complet</script>
<article class="scene" class:ending={scene.isEnding}> <p class="text">{scene.text}</p>
<div class="choices"> {#each scene.choices as choice} <Choice {choice} /> {/each} </div></article>
<style> .scene { max-width: 600px; margin: 2rem auto; padding: 2rem; background: #1a1a1a; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
.scene.ending { border: 2px solid gold; }
.text { font-size: 1.2rem; line-height: 1.6; margin-bottom: 2rem; color: #ddd; }
.choices { display: flex; flex-direction: column; gap: 0.5rem; }</style>5. Page principale
Section titled “5. Page principale”Créez src/routes/+page.svelte :
<script> import { storyStore } from '$lib/stores/storyStore'; import Scene from '$lib/components/Scene.svelte'; import { scenes } from '$lib/data/storyData';
// Réactivité Svelte : se met à jour automatiquement let currentScene = $derived(scenes[$storyStore.currentSceneId]);</script>
<main> <h1>L'histoire dont vous êtes le héros</h1>
{#if currentScene} <Scene scene={currentScene} /> {/if}
{#if $storyStore.history.length > 1} <p class="history"> Scènes visitées : {$storyStore.history.length} </p> {/if}</main>
<style> main { min-height: 100vh; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 2rem; }
h1 { text-align: center; color: white; font-size: 2rem; margin-bottom: 2rem; }
.history { text-align: center; color: #888; margin-top: 2rem; font-size: 0.9rem; }</style>6. Tester
Section titled “6. Tester”npm run devOuvrez http://localhost:5173 et testez votre histoire.
Vérifications :
- ✅ Les choix mènent aux bonnes scènes
- ✅ Les fins affichent un style différent
- ✅ Le compteur d’historique fonctionne
- ✅ Le bouton “Recommencer” réinitialise
Améliorations possibles
Section titled “Améliorations possibles”Transitions entre scènes :
<script> import { fade } from 'svelte/transition';</script>
{#key currentScene.id} <div in:fade={{ duration: 300 }}> <Scene scene={currentScene} /> </div>{/key}Sauvegarder la progression (LocalStorage) :
// Dans storyStore.jsimport { writable } from 'svelte/store';import { browser } from '$app/environment';
const stored = browser ? localStorage.getItem('storyProgress') : null;const initial = stored ? JSON.parse(stored) : initialState;
const store = writable(initial);
// Sauvegarder à chaque changementstore.subscribe(value => { if (browser) { localStorage.setItem('storyProgress', JSON.stringify(value)); }});Variables narratives (inventaire, stats) :
// Ajouter au storeconst initialState = { currentSceneId: 'start', history: [], inventory: [], // Objets trouvés stats: { courage: 0, intelligence: 0 }};Conditions sur les choix :
// Dans storyData.jschoices: [ { text: "Utiliser la clé", next: 'door_opened', condition: (state) => state.inventory.includes('key') }, { text: "Forcer la porte", next: 'door_forced' }]
// Dans Scene.svelte{#each scene.choices as choice} {#if !choice.condition || choice.condition($storyStore)} <Choice {choice} /> {/if}{/each}Ce que vous avez appris
Section titled “Ce que vous avez appris”- ✅ Architecturer une narration interactive
- ✅ Utiliser un store Svelte pour l’état global
- ✅ Séparer données (contenu) et logique (code)
- ✅ Naviguer entre états avec réactivité automatique
- ✅ Créer des composants réutilisables (Scene, Choice)
Exercice
Section titled “Exercice”Créez votre propre histoire :
- Minimum 5 scènes
- Au moins 3 fins différentes
- Un embranchement qui converge (deux chemins mènent au même état)
- Un style visuel cohérent avec votre narration
Astuce : Dessinez d’abord un schéma sur papier avant de coder.