singe/docs/fr/lessons/25-particles.adoc

570 lines
22 KiB
Text
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[#_lesson_25_particles]
== Leçon 25 : Les particules
image::learn/25-particles.png[La leçon terminée, 480]
À la fin de cette leçon, un vaisseau traversera le bas de l'écran en traînant
du feu derrière son réacteur, et un bouton le fera exploser en cent quarante
étincelles tournoyantes qui rebondissent sur le sol. Vous n'écrirez pas une
seule ligne qui dessine une étincelle.
C'est toute l'idée de cette leçon. Vous décrivez le feu ; le moteur le
fabrique.
[#_why_you_do_not_draw_the_sparks]
=== Pourquoi vous ne dessinez pas les étincelles
Vous pourriez faire autrement. Vous en savez assez : une table d'étincelles,
comme à la leçon six, chacune avec un x, un y et une vitesse ; une boucle dans
`onOverlayUpdate` qui les déplace toutes, les estompe, retire celles qui
meurent et les dessine. Ça marche. Ce sont aussi deux cents lignes de
comptabilité que chaque jeu réécrit, et ça tourne en Lua, une étincelle à la
fois, soixante fois par seconde.
Singe le fait pour vous à la place. Vous lui donnez une *recette* -- combien
d'étincelles par seconde, combien de temps vit chacune, à quelle vitesse et
dans quelle direction elles partent, ce qui les attire, à quoi elles
ressemblent et vers quelle couleur elles s'estompent -- et il s'occupe lui-même
des étincelles. Ce qui contient la recette et l'exécute s'appelle un
*émetteur*.
Un émetteur n'est pas une image, et ce n'est pas un objet de votre jeu. C'est
la description d'une sorte de feu, rangée quelque part où vous pouvez la
désigner, et il peut produire ce feu partout où vous le demandez.
[#_start_with_something_that_moves]
=== Commencez par quelque chose qui bouge
Les particules sont plus convaincantes quand elles sortent de quelque chose,
alors commencez par un vaisseau. Créez un dossier, copiez-y le dossier `art` du
livre, et mettez ceci dans `particles.singe` :
[source,lua]
----
dofile("Singe/Framework.singe")
local SHIP_WIDTH = 32
local SHIP_HEIGHT = 24
local SHIP_SPEED = 120
local shipSprite = spriteLoad(DIR .. "art/spaceship.png")
local shipX = (overlayGetWidth() - SHIP_WIDTH) / 2
local shipY = overlayGetHeight() - 56
local movingLeft = false
local movingRight = false
local lastTicks = singeGetTicks()
function onInputPressed(what)
if what == SWITCH_LEFT then
movingLeft = true
elseif what == SWITCH_RIGHT then
movingRight = true
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
movingLeft = false
elseif what == SWITCH_RIGHT then
movingRight = false
end
end
function onOverlayUpdate()
local now = singeGetTicks()
local seconds = (now - lastTicks) / 1000
lastTicks = now
if movingLeft then
shipX = shipX - SHIP_SPEED * seconds
end
if movingRight then
shipX = shipX + SHIP_SPEED * seconds
end
overlayClear()
spriteDraw(shipSprite, shipX, shipY)
return OVERLAY_UPDATED
end
----
Lancez-le avec `Singe -R particles` et pilotez avec les touches fléchées gauche
et droite. Rien de nouveau ici : un sprite de la leçon neuf, les contacts de la
leçon trois, et `singeGetTicks` pour que le vaisseau avance à la même vitesse
quelle que soit la fréquence d'images.
La seule ligne qui mérite d'être signalée est `dofile("Singe/Framework.singe")`.
C'est elle qui définit `DIR`, le dossier où se trouve votre script, si bien que
`DIR .. "art/spaceship.png"` trouve l'image quel que soit le dossier depuis
lequel vous avez lancé Singe.
[#_give_it_an_exhaust]
=== Donnez-lui un échappement
Maintenant, le feu. Ajoutez ceci au-dessus des callbacks, après les variables
du vaisseau :
[source,lua]
----
local exhaust = emitterNew()
emitterSetBlend(exhaust, PARTICLE_ADD)
emitterSetRate(exhaust, 90)
emitterSetLife(exhaust, 0.15, 0.40)
emitterSetDirection(exhaust, 0, 1)
emitterSetSpread(exhaust, 12)
emitterSetSpeed(exhaust, 40, 90)
emitterSetSize(exhaust, 7, 1)
emitterSetColor(exhaust, 255, 240, 180, 255, 255, 60, 0, 0)
emitterSetMax(exhaust, 120)
emitterStart(exhaust)
----
Et deux lignes dans `onOverlayUpdate`. La première va juste avant
`overlayClear`, la seconde juste après `spriteDraw` :
[source,lua]
----
emitterSetPosition(exhaust, shipX + SHIP_WIDTH / 2, shipY + SHIP_HEIGHT - 2)
----
[source,lua]
----
emitterDraw(exhaust)
----
Enregistrez. Le vaisseau repose maintenant sur une courte langue de flamme
jaune, et la flamme le suit quand vous le pilotez.
[#_blow_it_up]
=== Faites-le exploser
Un échappement tourne indéfiniment. Une explosion n'arrive qu'une fois, ce que
le moteur traite comme une autre sorte de demande faite au même émetteur.
D'abord, deux fichiers de plus et un nombre de plus. Ceux-ci vont à côté du
sprite du vaisseau, en haut :
[source,lua]
----
local sparkSprite = spriteLoad(DIR .. "art/star.png")
local boomSound = soundLoad(DIR .. "art/boom.wav")
----
et celui-ci va à côté de `shipX` et `shipY`, vingt unités au-dessus du bas de
l'écran :
[source,lua]
----
local ground = overlayGetHeight() - 20
----
Maintenant, la deuxième recette, sous la première :
[source,lua]
----
local boom = emitterNew()
emitterSetTexture(boom, sparkSprite)
emitterSetBlend(boom, PARTICLE_ADD)
emitterSetLife(boom, 0.4, 1.1)
emitterSetSpeed(boom, 40, 220)
emitterSetSpread(boom, 180)
emitterSetGravity(boom, 0, 160)
emitterSetDrag(boom, 0.9)
emitterSetSize(boom, 10, 2, 0.4)
emitterSetSpin(boom, -240, 240)
emitterSetColor(boom, 255, 255, 220, 255, 255, 70, 20, 0)
emitterSetCollide(boom, COLLIDE_FLOOR, 0.35, 0.3, ground)
emitterSetTrail(boom, 4, 2)
emitterSetMax(boom, 400)
----
Remarquez qu'il manque `emitterStart`. Celui-ci ne va jamais émettre en
continu.
Ajoutez une fonction pour le déclencher, au-dessus de `onInputPressed` :
[source,lua]
----
function explode()
if not flying then
return
end
flying = false
emitterStop(exhaust)
emitterSetPosition(boom, shipX + SHIP_WIDTH / 2, shipY + SHIP_HEIGHT / 2)
emitterBurst(boom, 140)
soundPlay(boomSound)
timerAfter(RESPAWN_MS, function()
flying = true
emitterStart(exhaust)
end)
end
----
Il faut encore trois choses : `local RESPAWN_MS = 1400` avec les autres
constantes, `local flying = true` avec les autres variables, et une branche
dans `onInputPressed` :
[source,lua]
----
elseif what == SWITCH_BUTTON1 then
explode()
----
Enfin, le vaisseau ne doit pas être là pendant qu'il est en morceaux. Entourez
le pilotage et le dessin de `onOverlayUpdate` d'un `if flying then ... end`, et
ajoutez `emitterDraw(boom)` à côté de l'autre appel de dessin. Le script
terminé est `learn/25-particles.singe`.
Appuyez sur la *barre d'espace* -- le bouton un, par défaut -- et le vaisseau
se disloque en un nuage d'étoiles qui culbutent, retombent en arc, heurtent le
sol et rebondissent.
[#_what_just_happened_21]
=== Ce qui vient de se passer
Il y a énormément de nouveaux appels dans cette leçon, et ils ont tous la même
forme : l'émetteur d'abord, puis ce que vous réglez. Prenez-les par groupes.
[source,lua]
----
local exhaust = emitterNew()
----
Crée un émetteur et renvoie un *handle* : un nombre dont le moteur se sert pour
le retrouver, exactement comme le nombre que vous donne `spriteLoad`. Sans
argument, vous obtenez un émetteur en deux dimensions, qui vit dans les
coordonnées de l'overlay et que l'on dessine depuis `onOverlayUpdate`. Donnez
plutôt un nœud à `emitterNew` et vous en obtenez un en trois dimensions, qui
vit dans la scène et se dessine tout seul ; tout le reste de cette leçon est
pareil pour les deux.
Un nouvel émetteur a déjà une recette complète -- cinquante particules par
seconde, blanches, une ou deux secondes de vie, qui dérivent vers le haut de
l'écran -- alors chaque appel qui suit, c'est vous qui n'êtes pas d'accord avec
une valeur par défaut. L'entrée de la Référence pour `emitterNew` les donne
toutes.
[source,lua]
----
emitterSetRate(exhaust, 90)
emitterSetLife(exhaust, 0.15, 0.40)
----
Quatre-vingt-dix particules par seconde, chacune vivant entre quinze centièmes
et quatre dixièmes de seconde. Ces deux nombres décident ensemble de la
quantité de feu : quatre-vingt-dix par seconde qui vivent un tiers de seconde,
cela fait environ trente à l'écran à tout moment.
Partout où un appel d'émetteur prend un minimum et un maximum, chaque particule
reçoit sa propre valeur, tirée au hasard entre les deux. C'est de là que vient
l'irrégularité. Un feu où chaque flamme vivrait exactement aussi longtemps que
toutes les autres aurait l'air d'une machine.
[source,lua]
----
emitterSetDirection(exhaust, 0, 1)
emitterSetSpread(exhaust, 12)
emitterSetSpeed(exhaust, 40, 90)
----
Dans quelle direction elles partent, quelle est la largeur de l'éventail, et à
quelle vitesse. Le Y de l'overlay croît *vers le bas*, comme depuis la leçon
un, donc `(0, 1)` va vers le bas de l'écran -- par l'arrière d'un vaisseau qui
pointe vers le haut. `emitterSetSpread` est la moitié de l'angle de
l'éventail : douze degrés font un jet serré, quatre-vingt-dix l'ouvrent sur la
moitié du cercle, et cent quatre-vingts, c'est toutes les directions à la fois.
[source,lua]
----
emitterSetSize(exhaust, 7, 1)
emitterSetColor(exhaust, 255, 240, 180, 255, 255, 60, 0, 0)
----
C'est grâce à ces deux-là que la flamme ressemble à une flamme. Une particule
n'a pas une taille et une couleur ; elle a une taille et une couleur *à sa
naissance* et d'autres *à sa mort*, et elle glisse régulièrement des unes aux
autres pendant la durée de vie qu'elle a tirée. Ainsi, chaque étincelle
commence large de sept unités de l'overlay et jaune pâle, et finit large d'une
unité et rouge orangé, avec un alpha de zéro, c'est-à-dire invisible. Elle
rétrécit et s'efface parce que vous avez dit où elle finit, pas parce que
quelque chose l'a estompée.
`emitterSetColor` prend huit nombres, ce qui fait beaucoup à lire : rouge,
vert, bleu et alpha au départ, puis rouge, vert, bleu et alpha à la fin.
[source,lua]
----
emitterSetBlend(exhaust, PARTICLE_ADD)
----
La façon dont une particule se combine avec ce qui est derrière elle.
`PARTICLE_ALPHA` est la peinture normale, et c'est la valeur par défaut :
servez-vous-en pour la fumée, la poussière, la pluie et les gravats.
`PARTICLE_ADD` ajoute plutôt sa lumière à l'image, si bien que deux étincelles
l'une sur l'autre brillent plus qu'une seule et que rien ne devient jamais plus
sombre. Le feu, les étincelles, les lasers et la magie sont additifs. La fumée
ne l'est pas, et une fumée dessinée en mode additif ressemble à de la vapeur
éclairée de l'intérieur.
[source,lua]
----
emitterSetMax(exhaust, 120)
emitterStart(exhaust)
----
`emitterSetMax` est la taille de la réserve : combien de particules de cet
émetteur peuvent exister à la fois. Quand la réserve est pleine, rien de
nouveau ne naît tant que quelque chose n'est pas mort. Quatre-vingt-dix par
seconde qui vivent jusqu'à quatre dixièmes de seconde demandent environ
trente-six places, donc cent vingt, c'est confortable. Réglez-la avant de
démarrer, parce que la changer ensuite jette toutes les particules vivantes.
`emitterStart` ouvre le robinet. À partir de là, l'émetteur fabrique des
particules à chaque image, que vous le dessiniez ou non, jusqu'à ce que
`emitterStop` le referme. Notez ce que `emitterStop` ne fait *pas* : il arrête
les nouvelles particules, et celles qui sont déjà en vie finissent leur vie et
s'effacent normalement. C'est pour cela que l'échappement s'étiole au lieu de
disparaître quand le vaisseau explose. Si vous voulez qu'elles disparaissent à
l'instant même, `emitterClear` les tue.
[source,lua]
----
emitterSetPosition(exhaust, shipX + SHIP_WIDTH / 2, shipY + SHIP_HEIGHT - 2)
----
C'est ainsi qu'un émetteur suit quelque chose. Un émetteur en deux dimensions
émet depuis un seul point, et ce point est là où vous l'avez mis en dernier,
alors vous le réglez à chaque image d'après ce qui bouge : la position d'un
sprite, le nœud d'un corps physique, la souris. Les nouvelles particules
apparaissent au nouvel endroit ; celles qui sont déjà en l'air restent là où
elles sont nées, ce qui est exactement ce qu'il faut pour un échappement, parce
qu'un échappement, ça reste derrière.
Un émetteur en trois dimensions est créé sur un nœud et suit ce nœud tout seul,
sans aucun appel dans votre mise à jour. Et quand vous voulez *vraiment* que
tout le nuage se déplace avec la chose -- un bouclier, une aura, le panache
d'un propulseur vu de l'extérieur -- `emitterSetLocal` active cela.
[source,lua]
----
emitterDraw(exhaust)
----
Les émetteurs en deux dimensions sont dessinés quand vous le demandez, une fois
par image, depuis `onOverlayUpdate`. L'ordre de vos appels à `emitterDraw` est
l'ordre dans lequel ils s'empilent. Oublier cet appel est l'erreur que tout le
monde fait une fois : l'émetteur continue de tourner, continue de puiser dans
sa réserve, et n'affiche rien.
Maintenant, les appels propres à l'explosion.
[source,lua]
----
emitterSetTexture(boom, sparkSprite)
----
Sans texture, chaque particule est une tache ronde et floue que le moteur
dessine lui-même ; c'est ce qu'utilise l'échappement, et c'est ce qu'il faut
pour une flamme. Avec une texture, chaque particule porte cette image -- ici
`star.png`, huit pixels sur huit. L'émetteur garde sa propre copie des pixels,
si bien que vous pouvez décharger le sprite ensuite et que les particules
continuent de marcher.
[source,lua]
----
emitterSetGravity(boom, 0, 160)
emitterSetDrag(boom, 0.9)
----
La gravité est une attraction constante, en unités de l'overlay par seconde et
par seconde, et elle est positive vers le bas pour la même raison que
l'échappement part avec un Y positif. Le freinage, c'est l'inverse : il retire
de la vitesse, un peu à chaque image, si bien que les étincelles partent fort
puis restent suspendues. Des étincelles avec de la gravité et sans freinage
retombent tout droit en pluie comme un feu d'artifice ; c'est le freinage qui
leur donne l'air de traverser de l'air.
[source,lua]
----
emitterSetSpin(boom, -240, 240)
emitterSetSize(boom, 10, 2, 0.4)
----
La rotation se compte en degrés par seconde, et une plage qui passe par zéro
vous donne des particules qui tournent dans chaque sens. Elle se voit sur une
texture et ne fait absolument rien à la tache intégrée, qui est ronde.
Le troisième nombre de `emitterSetSize` est la variation. Il multiplie les deux
tailles de chaque particule par un même facteur aléatoire -- ici n'importe où
entre six dixièmes et un et quatre dixièmes -- pour que les étincelles ne
soient pas toutes la même étoile à la même taille.
[source,lua]
----
emitterSetCollide(boom, COLLIDE_FLOOR, 0.35, 0.3, ground)
emitterSetTrail(boom, 4, 2)
----
`COLLIDE_FLOOR` pose un sol plat et invisible à travers le monde, à la hauteur
que vous indiquez, et les particules rebondissent dessus. Les deux nombres qui
précèdent la hauteur sont la part de vitesse qui survit à un rebond et la part
de vitesse latérale qui est gommée. Cela ne coûte rien, parce que c'est une
seule comparaison par particule, et cela donne aux débris l'air d'avoir
atterri quelque part plutôt que de traverser le monde en tombant. Dans un jeu
en trois dimensions, `COLLIDE_SCENE` fait plutôt rebondir les particules sur la
vraie géométrie, ce qui coûte beaucoup plus cher.
Une traînée garde les quelques dernières positions d'une particule et trace à
travers elles un ruban qui s'estompe. Quatre positions et deux unités de large
font de chaque étoile un court trait lumineux.
[source,lua]
----
emitterBurst(boom, 140)
----
Et voici l'autre façon de faire des particules. `emitterStart` est un
robinet ; `emitterBurst` est une poignée lancée d'un coup. L'émetteur n'a pas
besoin d'émettre en continu, et celui-ci ne le fait jamais. Tout le reste -- la
recette, la réserve, la position -- marche de la même façon.
Cette distinction vaut la peine d'être retenue. Tout ce qui est continu est un
flux que vous démarrez et arrêtez : un échappement, de la fumée, de la pluie,
une torche, une cascade. Tout ce qui arrive à un instant précis est une salve :
une explosion, une éclaboussure, un pas dans la poussière, un impact de balle,
un éclair de bouche. Bien des effets sont les deux, avec un émetteur ou deux :
un obus éclate, puis fume.
[#_how_many_is_too_many]
=== Combien, c'est trop
La réponse honnête, c'est que les particules coûtent peu à déplacer et cher à
regarder.
Déplacer dix mille particules, c'est de l'arithmétique, et un ordinateur
capable de faire tourner un jeu, quel qu'il soit, le fait sans s'en apercevoir.
Ce qui coûte, c'est le dessin, et ce que coûte le dessin, c'est à peu près le
nombre de pixels de l'écran que couvrent les particules, chevauchements
compris. Deux cents étincelles de huit pixels de large couvrent moins d'écran
qu'une seule bouffée de fumée qui le remplit. Voilà la règle : petites et
nombreuses, c'est presque gratuit ; grandes et superposées, ça ne l'est pas.
Le mélange additif est la sorte coûteuse, parce que rien de ce qu'il dessine ne
peut être sauté -- chaque couche s'ajoute à ce qui est dessous, alors la
machine les peint toutes. Une douzaine d'éclairs additifs plein écran les uns
sur les autres font ramper un Raspberry Pi 4, alors que dix mille minuscules
étincelles qui rebondissent ne le dérangent pas.
Donc : quelques milliers de petites particules, il n'y a pas de quoi
s'inquiéter. Avant d'aller au-delà, affichez le compte à l'écran et
surveillez-le.
[source,lua]
----
overlayPrint(2, 4, "Particles alive: " .. (emitterGetCount(exhaust) + emitterGetCount(boom)))
----
`emitterGetCount`, c'est ce qui vous permet de dimensionner une réserve
honnêtement plutôt qu'au jugé. Si le nombre reste pile à votre
`emitterSetMax`, votre réserve est pleine et le moteur refuse en silence de
fabriquer des particules que vous avez demandées. S'il n'en approche jamais,
vous réservez de la mémoire pour rien.
[#_try_it_25]
=== Essayez
. *Transformez l'échappement en fumée.* Changez
`emitterSetBlend(exhaust, PARTICLE_ADD)` en `PARTICLE_ALPHA`, réglez la
couleur sur un gris qui s'estompe jusqu'à un gris transparent, et faites-la
grossir au lieu de rétrécir avec `emitterSetSize(exhaust, 3, 14)`. Trois
lignes ont fait une tout autre machine.
. *Enlevez le freinage de l'explosion.* Supprimez la ligne `emitterSetDrag` et
comparez. Puis remettez le freinage et enlevez plutôt la gravité. L'un des
deux ressemble à un feu d'artifice et l'autre à un accident.
. *Élargissez l'échappement.* Mettez `emitterSetSpread(exhaust, 180)`. Le
vaisseau n'a plus l'air d'avoir un réacteur. L'ouverture travaille plus que
vous ne le pensez.
. *Lâchez-en mille d'un coup.* Changez `emitterBurst(boom, 140)` en
`emitterBurst(boom, 1000)` et regardez le compte à l'écran. Il n'atteindra
pas mille. Trouvez pourquoi à partir de `emitterSetMax`, puis agrandissez la
réserve et rendez les étincelles quatre fois plus grosses, et voyez ce que
cela fait à la fréquence d'images.
. *Déclenchez l'explosion là où le vaisseau n'est pas.* Mettez
`emitterSetPosition(boom, 40, 60)` dans `explode` à la place de la position du vaisseau. Un émetteur
est la description d'un feu, pas une chose qui vit quelque part, et vous
pouvez faire éclater le même en dix endroits dans la même image.
[#_break_it_on_purpose_24]
=== Cassez-le exprès
Les constantes -- `PARTICLE_ADD`, `COLLIDE_FLOOR`, `OVERLAY_UPDATED` -- sont
des noms que Singe a donnés à des nombres. Écrivez-en une un peu de travers et
Lua ne se plaint pas, parce qu'en Lua un nom qui n'a jamais reçu de valeur
n'est pas une erreur : c'est `nil`, la valeur qui veut dire « rien ici ».
Changez la ligne de mélange de l'échappement pour un nom qui a l'air tout aussi
plausible :
[source,lua]
----
emitterSetBlend(exhaust, PARTICLE_ADDITIVE)
----
Singe s'arrête avant que le jeu démarre, en donnant la ligne de votre
fichier où se trouve cet appel :
----
22:emitterSetBlend: Argument 2 must be a number.
----
Cette erreur n'a pas la même forme que les erreurs Lua de la leçon huit, parce
qu'elle vient du moteur et pas de Lua. C'est le numéro de ligne, puis l'appel
qui a protesté, puis la plainte. L'argument deux de `emitterSetBlend` est le
mode de mélange, et ce qui est arrivé, c'est `nil`, parce que
`PARTICLE_ADDITIVE` n'est pas un nom que quoi que ce soit ait jamais défini.
« Must be a number » veut presque toujours dire ceci : une constante mal
orthographiée, ou une variable que vous pensiez avoir réglée. Le remède, c'est
de vérifier le nom dans la Référence, qui l'écrit `PARTICLE_ADD`.
[#_what_you_learned_25]
=== Ce que vous avez appris
* Un émetteur contient la recette d'une sorte de particule et s'occupe lui-même
des particules, si bien que votre script n'en dessine jamais une seule.
* `emitterNew` sans argument crée un émetteur en deux dimensions, dans les
coordonnées de l'overlay ; avec un nœud, il en crée un en trois dimensions,
dans la scène.
* Chaque émetteur commence avec une recette complète, et chaque appel
`emitterSet...` en change une partie.
* Partout où un appel prend un minimum et un maximum, chaque particule tire sa
propre valeur au hasard entre les deux, et c'est de là que vient
l'irrégularité.
* La taille et la couleur se règlent à la naissance et à la mort, et la
particule glisse de l'une à l'autre au cours de sa vie.
* `emitterStart` émet en continu jusqu'à `emitterStop` ; `emitterBurst` lance
une poignée d'un coup. Les effets continus sont des flux, les effets
ponctuels sont des salves.
* `emitterStop` laisse les particules vivantes finir ; `emitterClear` les tue
tout de suite.
* Un émetteur en deux dimensions se déplace avec `emitterSetPosition` à chaque
image et se dessine avec `emitterDraw` à chaque image. Un émetteur en trois
dimensions suit son nœud et se dessine tout seul.
* Les particules coûtent ce qu'elles couvrent à l'écran, pas ce qu'elles
coûtent à déplacer, et le mélange additif est la sorte coûteuse.
* `emitterSetMax` plafonne la réserve, et `emitterGetCount` vous dit si vous en
avez deviné la bonne taille.
[#_next_time_25]
=== La prochaine fois
L'explosion a déplacé cent quarante choses sans qu'on lui dise où aucune
devait aller, mais c'étaient des étincelles, et les étincelles se moquent de ce
qu'elles percutent. La leçon vingt-six parle de quelque chose qui ne s'en moque
pas : un personnage qui doit aller d'ici à là-bas, avec un mur sur le chemin.