
Big Tuto SFML 2 / Action-RPG 2 / Expert : Legends of Meruvia
Chapitre 4 : Créons une cinématique de A à Z
Tutoriel présenté par : Stéphane Barthélémy (Stephantasy)
Date d'écriture : 15 août 2016
Date de révision : -
Je ne sais pas pour vous, mais pour moi, la façon la plus efficace d’apprendre est par l’exemple. Nous allons donc créer une cinématique pas à pas. ![]()
1. Préparation*
Avant de commencer, vous devez avoir inséré la classe « Cinematic » dans votre programme. Pour cela, rien de plus simple, il suffit d’importer les fichiers « Cinematic.h » et « Cinematic.cpp » dans votre projet.
Ensuite, il faut modifier la classe « Main » tel qu'on l'a vu dans le chapitre 2.
Les différents accesseurs et modificateurs utilisés par la classe « Cinematic » ont été ajoutés.
Enfin, dans le dossier de votre projet (au même niveau que les dossiers « graphics », « music », etc.), créez deux nouveaux dossiers intitulés : « tools » et « scenarios ». Placez l’éditeur de scénarios (le fichier Excel) dans « tools ». Le dossier « scenarios » contiendra les scénarios créés.
*Cela concerne uniquement les personnes qui tentent d’insérer le gestionnaire de cinématique dans un projet personnel. Pour ceux qui ont téléchargé la version du Big Tuto ARPG au chapitre 2, tout est déjà inclus ! ![]()
2. Le scénario
Nous allons maintenant écrire notre scénario. Ouvrez l’éditeur de scénario prévu à cet effet ! ![]()
Voici notre histoire :
Notre personnage entre dans le magasin, heureux de pouvoir acheter un nouvel équipement. Moment de silence. Il semble n’y avoir personne. Le héros se alors dirige vers le comptoir, puis jette un œil aux quatre coins de la boutique. Personne. Il voit alors une porte au fond de la salle et se rue dessus, mais elle est fermée ! De colère, il la frappe de son épée. Rien n’y fait, elle reste close. Finalement, il reprend son sang-froid et décide qu’il reviendra plus tard.
Bon certes, cela ne casse pas des briques. Mais j’ai déjà vu des superproductions hollywoodiennes pires que ça ! ![]()
Première des choses, on donne un numéro à notre scénario :

À présent, il va falloir traduire notre texte en actions. Dans un premier temps, découpons notre texte en y associant des actions :
- Notre personnage entre dans le magasin, heureux de pouvoir acheter un nouvel équipement. => Dialogue* : « J’ai besoin d’une nouvelle épée ! »
- Moment de silence. => On baisse le volume de la musique
- Il semble n’y avoir personne. => Dialogue* : « Hum ? Il y a quelqu’un ? »
- Le héros se alors dirige vers le comptoir => Déplacement du héros vers le comptoir
- puis jette un œil aux quatre coins de la boutique. => Le héros se tourne dans toutes les directions
- Personne. => On attend
- Il voit alors une porte au fond de la salle => Emote* : « ! » (au-dessus de la tête du joueur) + Son
- et se rue dessus, => Le héros court vers la porte
- mais elle est fermée ! => Bruit de porte qui ne s’ouvre pas
- De colère, il la frappe de son épée. => Le héros attaque
- Rien n’y fait, elle reste close. => Rien
- Finalement, il reprend son sang-froid et décide qu’il reviendra plus tard. => Retour à la situation normal : Le héros marche vers le centre du magasin, on remonte le volume de la musique.
* Ces actions ne sont pas disponibles actuellement.
Maintenant que nous avons une bonne idée du déroulement du scénario, nous allons traduire les actions que nous avons définies par les actions de notre éditeur. Vous noterez au passage que c’est en réalisant cet exercice que l’on en découvre de nouvelles à créer (en l’occurrence « Dialogue » et « Emote »). ![]()
Mais pour le moment, nous nous contenterons d’associer les actions que nous avons à notre disposition. Nous allons donc volontairement ignorer celles non programmées. ![]()
En général, on commence toujours par un temps d’attente afin de permettre au joueur d’appréhender la scène qu’il voit pour la première fois. On écrit « Wait » dans la colonne « Action » et le temps d’Attente dans la colonne « Param1 ». Cette action n’a qu’un seul paramètre. La condition de « Wait » est toujours « 1 ».
Cela donne :

-> Attente pendant 500 millisecondes.
Ensuite, on baisse le volume de la musique. Notez que l’on met la condition à 0, car on n’a pas besoin d’attendre que ce soit fait (et on ne veut pas) pour poursuivre le scénario. Donc l’action est « Volume Music ».
Cette action a trois paramètres :
- Ce que l’on fait du volume : Augmenter ou Diminuer
- De quel manière : Direct ou Progressif
- Jusqu’à quelle valeur
On obtient :

-> On diminue progressivement le volume de la musique jusqu’à 0.
À présent, on déplace notre héros vers le comptoir et on attend un peu. Nous allons contrôler un Sprite donc l’action « Sprite Control » semble toute indiquée !
Ensuite on désigne le Sprite que l’on va contrôler; il s’agit de « Player ». On indique qu’il doit monter, avec « Go_Up », en marchant « Walk », jusqu’au comptoir, c'est-à-dire jusqu’à la position y = 230.

-> On déplace le héros vers le haut en marchant jusqu’à la position y = 230
-> On attend 500 millisecondes
Bon, je pense que vous avez compris le principe, alors voici le résultat de notre scénario
:

Ce scénario étant très simple, vous le comprendrez aisément. Je vais quand même souligner quelques points importants.
D’abord, vous remarquerez qu’il y a de nombreux « Wait » dans le tableau. Ils sont indispensables, car ce sont eux qui donnent le tempo. Sans cela, le scénario se déroulerait très vite et vous n’auriez pas le temps de comprendre ce qu’il se passe ! ![]()
Deuxièmement, nous avons ajouté un SKIP* à la fin du scénario. Ce dernier offre au joueur la possibilité d’interrompre la cinématique à tout moment. Ainsi, on prend soin de remonter le volume de la musique si cela arrive. Dans ce scénario, la position du héros importe peu et on le laissera là où il se trouve au moment du Skip. Mais ce ne sera pas toujours le cas, pensez-y ! ![]()
* Référez-vous aux détails du Skip plus haut pour son fonctionnement.
Pour être certain de ne pas faire d’erreur de syntaxe, il est préférable de faire des copier-coller entre les tables d’actions et de paramètres et la zone d’édition. Ne vous préoccupez pas des couleurs, elles disparaîtront lors de la validation du scénario.
Les valeurs x et y pour le positionnement du héros ne tombent malheureusement pas du ciel ! Pour les trouver, j’ai lancé le jeu depuis Visual Studio et placé le héros à l’endroit désiré. Ensuite, je pose un point d’arrêt (avec la touche F9) dans la fonction « Player ::draw(…) » à la ligne « hero.setPosition(Vector2f(x - map.getStartX(), y - map.getStartY())); » pour récupérer les valeurs x et y.
3. Exporter le fichier
Ça y est, notre scénario est écrit.
Mais avant de pouvoir le tester dans notre jeu, il va falloir effectuer quelques manipulations. ![]()
a) Validation
Nous allons commencer par valider notre scénario. Appuyez sur le bouton
pour vous assurer que tout est correct. S’il y a des erreurs, les cellules concernées apparaîtront en rouge. Si elles sont dans la colonne action, c’est un problème de syntaxe. Si c’est dans les colonnes de paramètres, c’est soit une erreur de syntaxe, soit que la valeur entrée n’est pas celle attendue.
b) Double Save !
Pour ne pas perdre notre travail, nous allons sauvegarder tout cela en appuyant sur le bouton
. La première fois qu’on sauvegarde un scénario, un nouvel onglet est créé. Ensuite, l’éditeur demandera confirmation pour écraser les anciennes données.
Attention ! Il faut également sauvegarder le fichier Excel !
c) Exporter
Maintenant nous allons créer le fichier de sortie qui sera lu par le gestionnaire de cinématique. Appuyez sur le bouton
. Il faut sauvegarder le fichier dans le dossier « scenarios ». N’oubliez pas de choisir Texte (DOS) (*.txt) pour le type de fichier ! ![]()
Et voilà, on en a fini avec l’éditeur ! ![]()
4. Test
Lancez le jeu. Vous voilà dans la première Map. Au-dessus du héros se trouve un magasin. Entrez-y. La cinématique commence, sortez le pop-corn et profitez du spectacle ! ![]()
Vous remarquerez quand même quelque chose de terrible : la cinématique est diablement plus courte que le temps qu’il nous a fallu pour la créer ! ![]()
N.B. : Il faut aussi préciser le numéro de la cinématique à jouer dans le level editor en éditant la map du magasin. ![]()
5. VOTRE gestionnaire de cinématiques
Inclure des cinématiques dans son jeu, c'est cool.
Cela permet d’enrichir l’expérience du joueur en l’immergeant plus profondément dans l’histoire que votre jeu raconte.
D’ailleurs, une cinématique, c’est votre histoire et vous allez peut-être vouloir créer des scènes prenantes, émouvantes, voire impressionnantes !
Cependant, il y a fort à parier que vous allez rapidement vous sentir à l’étroit avec cet outil, car il n’offre que peu de contrôles. ![]()
Bien qu’on puisse aisément étoffer notre gestionnaire pour qu’il offre davantage de possibilités (ce qui sera peut-être fait avec l’avancée du tutoriel ARPG
), il y aura toujours une fonctionnalité qui sera propre à VOTRE jeu. Ainsi, je vais vous montrer comment modifier cet outil afin que vous puissiez l’adapter à vos besoins.
a. Avant de commencer
En cas de modification, sachez qu’il est préférable de garder les choses le plus simple possible. Cela permet de modifier l’outil aisément et de rendre le débogage plus facile. Il serait, par exemple, long et fastidieux de développer un éditeur et un gestionnaire de cinématiques qui soient en mesure de détecter et de gérer toutes les erreurs.
Aussi, vous êtes maître du scénario que vous créez et il est préférable de vous assurer que vous demandez des actions réalisables. Si vous demandez au Sprite « Player » de se déplacer vers la droite jusqu’à une valeur plus grande que la limite de l’écran, il ne faudra pas s’étonner de voir le joueur marcher indéfiniment à droite de l’écran… ![]()
b. Modifier la classe « Cinematic »
Nous allons prendre pour exemple l’ajout de la fonction « PlaySound ».
La toute première chose à faire est de voir comment on peut introduire cette action dans notre moteur de jeu. Pour l’occasion, nous sommes chanceux, car la classe « Sounds » possède déjà une fonction « PlaySoundFx(int type) » prenant en paramètre le numéro du son à jouer.
Il ne reste qu’à modifier la classe « Cinematic ».
Dans la définition de classe, ajoutons le champ « Action_PlaySound » à l’énumérateur « ActionType », juste avant le Skip.
Dans la classe, il va falloir ajouter deux choses :
- La possibilité de lire cette action,
- La possibilité d’exécuter cette action.
Dans la fonction « readActions() », ajoutons notre nouveau cas au « Switch ». Cette action n’ayant rien de particulier, on y inclut les éléments de base, soit le chargement de l’action et la gestion de la condition.
|
// Joue un son
case Action_PlaySound:
{
// On charge l'action à exécuter et on indique qu'elle n'est pas remplie
ExecActions tmp = { _activeStep, false };
_execActions.push_back(tmp);
if (_actions[_activeStep].condition == Condition_Final) {
_previousStep = _activeStep;
}
else {
_activeStep++;
}
}break;
|
Dans la fonction « executeActions() », ajoutons également notre nouveau cas au « Switch ». Là encore, rien de compliqué, on ne fait qu’appeler la méthode « PlaySoundFx » :
|
// On joue un son
case Action_PlaySound:
{
_sounds.PlaySoundFx(std::stoi(_actions[_execActions[i].step].param1));
// On valide la condition
_execActions[i].isConditionMeet = true;
}break;
|
Et voilà, c’est tout pour la classe « Cinematic ».
D’ailleurs, vous n’aurez jamais rien d’autre à modifier dans cette classe (hormis peut-être l’ajout de variables privées au besoin). Car vous ne faites qu’ajouter une action, vous ne modifiez pas la mécanique! ![]()
Par contre, il n’en sera pas de même pour le moteur du jeu. Dans notre exemple, nous n’avons rien eu à ajouter, mais ce ne sera pas souvent le cas. Par exemple, pour le contrôle du joueur, j’ai dû ajouter quatre accesseurs et deux mutateurs.
À présent, passons à la modification de l’éditeur.
c. Modifier l’éditeur
c1) La feuille Excel
Là encore, il va falloir déclarer notre nouvelle action. Pour cela, rien de plus simple, on ajoute « Sound FX » dans la table des actions. À noter que le nom est à votre convenance. ![]()

Par contre, sa position dans la table est importante ! Il est impératif que l’ordre des actions dans la table soit identique à celui de l’énumérateur « ActionType » de la classe « Cinematic » !
Concernant les paramètres, il va falloir trouver un moyen de savoir à quel son correspond quel numéro. Pour cela, nous allons utiliser l’énumérateur de la classe « Sounds »

Là encore, il est impératif que l’ordre des sons dans la table soit identique à celui de l’énumérateur « soundFxName» de la classe « Sounds » !
On n’est pas obligé de nommer les sons « Bumper », « Destroy », etc., dans la table des paramètres. On pourrait directement utiliser le nombre correspondant à sa position dans l’énumérateur (Exemple 3 pour « Star »). Cela permet surtout de rendre notre scénario plus lisible.
c2) La macro
Après avoir modifié la feuille Excel, il va falloir modifier la macro de validation. Rassurez-vous, tout comme pour la classe « Cinematic », la modification se limitera à l’ajout d’un nom dans un énumérateur et d’un cas dans un Switch ! ![]()
Lancez l’éditeur de macro avec « Alt-F11 ». Là, ouvrez le module « ValidParameters ».

Dans le haut de la macro, ajouter l’action à l’énumérateur :
|
' Liste des actions Private Enum eActionName Wait = 0 SpriteControl = 1 LoadMap = 2 PlayMusic = 3 VolumeMusic = 4 PlaySound = 5 Skip = 99 |
Ensuite, ajoutez le cas de notre action dans le Switch. Ce que l’on fait dans cette partie du code, c’est de valider que les paramètres, entrés dans la zone d’édition, sont bons. Si vous ne souhaitez pas modifier ce code, voir la note un peu plus bas.
Bien que cela puisse paraitre un peu barbare, c’est en réalité assez simple. Il y a trois parties :
- Vérification du premier paramètre : « Nom du son lu » (détails plus bas*)
- On affecte un « - » aux trois autres paramètres, car ils ne servent à rien
- On contrôle que la condition soit égale à 0 ou à 1
Il est possible de faire un copier-coller des autres cas au besoin, car le fonctionnement reste généralement le même.
*Concernant le code de vérification du premier paramètre, voici ce que cela fait :
Set tmpRange = paramRange.Find(leRange(1, COL_PARAM1).Value, LookAt:=xlWhole)
On cherche si le nom entré dans la colonne Param1 dans la zone d’édition se trouve dans la table des paramètres. Ensuite on teste :
- Si le champ dans la zone d’édition est vide => erreur
- Si le résultat de la recherche est nul => erreur
- Si on n’a pas trouvé de correspondance => erreur
- Sinon, on mémorise la valeur correspondant au nom du son (exemple 3 pour Star)
Si vous ne vous sentez pas de modifier le code de la macro, voici une méthode plus simple.
Vous n’êtes pas obligé de faire le contrôle de validité de la zone d’édition. Cependant, l’éditeur vous dira toujours que votre scénario est bon, ce qui sera peut-être faux. Il vous faudra être plus vigilant lors de la création du scénario et vous aurez probablement plus d’erreurs lors de la lecture de la cinématique.
Cependant, il vous faudra toujours ajouter l’action à l’énumérateur. Mais ça, c’est facile !
Pour le cas du Switch, vous pouvez simplement copier les lignes suivantes :
Case Nom_Action*
Dim i As Integer
For i = ARR_PARAM1 To ARR_PARAM4
If (leRange(1, i + 1).Value = "") Then
TABLEAU_SCENARIO(nbLigne - 1, i) = "-"
Else
TABLEAU_SCENARIO(nbLigne - 1, i) = leRange(1, i + 1).Value
End If
Next
'Condition
If (leRange(1, COL_COND).Value <> "0" And leRange(1, COL_COND).Value <> "1") Then
leRange(1, COL_COND).Interior.Color = 255
ValidParameters = 2
Else
TABLEAU_SCENARIO(nbLigne - 1, ARR_COND) = leRange(1, COL_COND).Value
End If
*Mettre le nom de l’action déclarée dans l’énumérateur.
d. Conseils
Lors de modification/ajout, attention à bien faire correspondre les données extraites de l’éditeur avec le gestionnaire de cinématique du jeu ! En particulier :
L’ordre des actions :
- « ActionType » dans « Cinematic » doit être égal à « eActionName » dans la macro.
Le contenu des paramètres :
- Parfois des String, parfois des Int… Une erreur fera forcément crasher le jeu... ![]()
6. Bug
Le bug le plus fréquemment rencontré est qu’une action ne rencontre jamais sa condition. Le programme se retrouve alors bloqué dans la fonction « executeActions() » ad vitam aeternam ! ![]()
Exemple concret :
1. Déplacer le joueur vers la droite jusqu’à x = 500.
2. À x = 350, il y a un buisson.
3. Le héros est coincé sur l’obstacle et n’avance plus.
4. Il n’atteint jamais x = 500.
Être bloqué indéfiniment dans une boucle est une des pires choses dans un programme, cela ne devrait jamais arriver. Cependant, il est assez difficile de s’en protéger dans notre cas, car toutes les conditions sont différentes et il n’est pas simple de savoir quand interrompre notre boucle, car on soupçonne une erreur. ![]()
En réalité, cela ne devrait se produire que durant la création de la cinématique. Car une fois qu’elle est au point, l’enchainement des actions se fait de manière séquentielle et rien ne peut venir bouleverser ce mécanisme (rappelez-vous que le gestionnaire est indépendant). ![]()
Ainsi, l’erreur vient toujours du scénario dont il vous appartient de vous assurer que sa réalisation est sans encombre. (Ou alors, revoyez votre Level Design !
)
7. Mot de la fin
C’est fini. Ouf ! ![]()
Cela représente trois gros chapitres ! En réalité, les choses sont plus simples qu’elles n’y paraissent. C’est assez long à présenter et à expliquer, mais vous avez pu constater que les mécanismes sont triviaux. Et c’est d’ailleurs l’objectif ! ![]()
En voici le résumé :
• Un éditeur pour nous aider à créer un scénario composé d’une suite d’actions ; un fichier est généré.
• Un gestionnaire qui lit le fichier et exécute les actions dans l’ordre. Notre cinématique est jouée à l’écran.
A bientôt ! ![]()

English
Français