Lire des dialogues dans des interfaces 2D¶
Un Dialogue n'a pas besoin d'être joué comme une scène. Il peut aussi être lu par une
interface 2D de votre cru, quelle que soit sa forme : une lettre ou une page de terminal, un fil de
messagerie sur un téléphone, une popup avec un choix, la transcription d'une messagerie vocale, un
écran de verrouillage qui attend un code, pour n'en citer que quelques-unes. Cette page explique
comment : les helpers de lecture qui parcourent un Dialogue en dehors du Sequencer, le flag
Staging.Interface2D, les choix à saisie et le reply par défaut, ainsi que le lecteur 2D, les vues
et le device d'exemple livrés avec le showcase.
Pour la version jouée (stages, 3C, cinématiques, StartDialogue*), voir
Jouer des dialogues. Pour des exemples concrets dont vous inspirer, voir la
FAQ interfaces 2D.
Ce qui fait partie du produit, et ce qui est un exemple¶
| Couche | Où | Ce qu'elle vous apporte |
|---|---|---|
| Helpers de lecture | Produit, ULBRDialogueReadingLibrary (module LBRCore) |
La sémantique Libretto d'un Dialogue sans le Sequencer : quel sous-dialogue se joue, l'étape suivant une ligne, If/Else, Goto, replies valides, reply par défaut, saisie, effets de ligne, flags « done ». |
Flag Staging.Interface2D |
Produit | Marque un sous-dialogue comme lu par une interface 2D : aucune séquence ni aucun stage ne sont générés, les voix le sont toujours. |
| Lecteur 2D, contrat de vue, vues, device | Exemples, module LibrettoExamplesRuntime |
Une manière de construire des interfaces 2D sur les helpers. Lisez-la, copiez-la, remplacez-la : l'UI de votre jeu vous appartient. |
Les helpers sont la partie sur laquelle vous vous appuyez. Le lecteur et les vues d'exemple sont là pour montrer une implémentation complète, et sont faits pour être lus plutôt qu'étendus.
Le flag Staging.Interface2D¶
Cochez Libretto.Flags.Subdialogue.Staging.Interface2D sur chaque sous-dialogue lu par une
interface 2D.
- La génération l'ignore : pas de sous-séquence, pas de binding de stage, pas de caméra. Ses assets audio sont toujours générés (voix importées, text to speech), si bien qu'une messagerie vocale peut faire entendre une vraie voix.
- Un dialogue joué n'y entre jamais : une Dialogue Instance ne choisit pas un sous-dialogue
Interface2D, et un saut Sequencer vers l'un d'eux termine le dialogue. - Un dialogue dont tous les sous-dialogues sont
Interface2D(ouAudio2D) est générique : il n'a besoin d'aucune instance placée. - Comptage de mots : ses lignes sont exclues des mots parlés, et conservées dans les mots localisables.
- Il ne se combine pas avec
3C,CinematicDialogue,RandomBarksouAudio2Dsur le même sous-dialogue. La combinaison est signalée à la compilation du Dialogue, et l'asset se sauvegarde quand même.
Les flags enfants sont à vous de définir, un par type d'interface. Les exemples déclarent
Interface2D.Document, .Messaging, .Voicemail, .Popup et .Lock, plus le flag de ligne
Libretto.Flags.Lines.Interface2D.Title (la ligne utilisée comme titre de page). Filtrer sur
Interface2D accepte tous ses enfants.
Les helpers de lecture¶
ULBRDialogueReadingLibrary est une bibliothèque de fonctions Blueprint sans état (catégorie
Libretto ▸ Reading). Quelques règles valent pour toutes les fonctions :
- Une ligne est désignée par son index dans les tableaux de lignes du Dialogue.
ULBRDialogue::GetIndex(ID)convertit un ID de ligne en index. - Filtre de flags. Les fonctions qui prennent un
FlagFiltern'acceptent que les sous-dialogues qui portent ce flag (hiérarchiquement). Un filtre vide accepte tous les sous-dialogues. - Les conditions sont évaluées avec leur cache (
EvaluateWithCache, jamais forcé) : une condition avecbCacheSuccessoubCacheFailurefige son résultat la première fois qu'elle est affichée. Voir Conditions — cache. - Aucun flag « done » n'est levé pour vous. C'est votre interface qui décide quand une page
compte comme lue ou un fil comme terminé, et qui appelle
MarkSubdialogueDoneouMarkDialogueDone. - Des données invalides ne plantent jamais. Un dialogue nul ou un index hors limites renvoie un
résultat
Invalidet journalise un warning.
| Fonction | Renvoie | Rôle |
|---|---|---|
FindFirstPlayableSubdialogue(Dialogue, FlagFilter) |
Subdialogue | Le premier sous-dialogue, dans l'ordre de l'asset, qui passe le filtre et sa condition. |
GetPlayableSubdialogues(Dialogue, FlagFilter) |
Tableau | Tous les sous-dialogues jouables, dans l'ordre : une table des matières, la liste des fils d'un téléphone. |
GetSubdialogueEntry(Dialogue, Subdialogue, FlagFilter) |
FLBRReadStep |
La première étape d'un sous-dialogue (commentaires et lignes If sautés). |
GetNextStep(Dialogue, FromLineIndex, FlagFilter) |
FLBRReadStep |
L'étape après une ligne affichée, ou après un reply choisi (elle entre dans le corps du reply). Gère If/ElseIf/Else, la fin d'un reply, Goto -1 (fin), -2 (sous-dialogue jouable suivant) et Goto vers un ID. |
GetValidReplies(Dialogue, ChoiceIndex) |
Tableau de FLBRReplyOption |
Les replies à proposer : les replies DoOnce déjà choisis et ceux dont la condition Display échoue sont retirés ; bAvailable vaut false quand la condition Availability échoue (affichez-le grisé) ; bIsDefault marque le reply par défaut. |
GetDefaultReply(Dialogue, ChoiceIndex) |
Index de ligne | Le reply par défaut, s'il est encore valide. INDEX_NONE sinon. |
FindReplyMatchingInput(Dialogue, ChoiceIndex, Input, bIsDefault) |
Index de ligne | Le reply dont le texte correspond à ce que le joueur a saisi, ou le reply par défaut (bIsDefault vrai). Voir Saisie. |
RecordReplyChosen(Dialogue, ReplyIndex) |
— | Enregistre le reply dans les GPVars, exactement comme un dialogue joué. |
GetRetainedReply(Dialogue, ChoiceIndex, ReplyIndex) |
None / Single / Ambiguous |
Quel reply d'un choice a été choisi auparavant : aucun, exactement un, ou plusieurs. |
ApplyLineEffects(WorldContext, Dialogue, LineIndex) |
— | Applique les SetTags de la ligne, puis ses RemoveTags, puis ses actions. Toutes les actions s'exécutent, même si l'une d'elles lance un autre dialogue. |
MarkSubdialogueDone(Dialogue, Subdialogue) / MarkDialogueDone(Dialogue) |
— | Lève les mêmes flags « done » qu'un dialogue joué, si bien que ULBRDialogueDoneCondition les voit. |
Une étape (FLBRReadStep) vous dit quoi faire ensuite :
Kind |
Signification | Champs |
|---|---|---|
Line |
Afficher cette ligne (Text, Cinematic, Silence). |
LineIndex, Subdialogue |
Choice |
Présenter ce choice et attendre le joueur. | LineIndex, Subdialogue |
End |
La lecture s'arrête. | EndReason |
Subdialogue change quand un Goto passe dans un autre sous-dialogue. EndReason vaut
EndOfSubdialogue (Goto -1 ou fin naturelle), OutOfFilter (un saut vers un sous-dialogue qui ne
passe pas le filtre, comme un sous-dialogue 3D joué), InvalidDestination (un Goto vers un ID
inconnu), StepBudget (un cycle de Goto) ou Invalid.
La boucle de lecture¶
Avec ces helpers, un lecteur tient en une courte boucle. Voici ce que fait le lecteur d'exemple :
- Choisir le sous-dialogue :
FindFirstPlayableSubdialogue(Dialogue, Interface2D), ou celui que votre UI a choisi. - Obtenir sa première étape avec
GetSubdialogueEntry. - Tant que l'étape est une
Line: l'afficher, appelerApplyLineEffects, puisGetNextStepdepuis cette ligne. - Sur un
Choice: afficherGetValidReplieset attendre. Quand le joueur choisit un reply, appelerRecordReplyChosen, puisApplyLineEffectssur le reply, puis continuer avecGetNextStepdepuis le reply. - Sur un
End: fermer la page, le fil ou la popup.
Gardez les règles de Libretto hors de votre UI
Votre boucle ne devrait regarder que FLBRReadStep. Si vous vous surprenez à lire un chemin de
GPVar, le Type d'une ligne ou une condition directement, un helper fait déjà ce travail pour
vous.
Saisie et reply par défaut¶
Un écran de verrouillage, un mot de passe ou une énigme est un Choice dont les replies sont les
réponses acceptées. Le joueur tape, et FindReplyMatchingInput trouve le reply :
- il compare avec les replies proposés et disponibles, reply par défaut exclu ;
- les espaces en début et en fin sont ignorés, la casse aussi. Les accents comptent :
cafén'est pascafe; - le texte du reply est comparé dans la culture courante, si bien qu'un mot de passe traduit se tape dans la langue jouée ;
- quand rien ne correspond, il renvoie le reply par défaut avec
bIsDefaultvrai, ouINDEX_NONEs'il n'y en a pas.
Le reply par défaut (Default Reply sur un Reply) est le reply pris quand le joueur ne donne
aucune réponse valide : le temps d'un choix minuté s'écoule, ou la saisie ne correspond à rien.
Il peut donc aussi être posé sur un choix non minuté. Il y en a au plus un par Choice.

Piège : un reply par défaut est DoOnce par défaut
Comme tout reply, un reply par défaut a bDoOnceReply coché par défaut. Une fois pris, il
n'est plus valide : le deuxième code faux ne trouve aucun repli, et FindReplyMatchingInput
renvoie INDEX_NONE. Pour un verrou ou une énigme que le joueur peut rater plusieurs fois,
décochez DoOnce sur le reply par défaut.
Le lecteur 2D d'exemple¶
Le module d'exemples livre un lecteur, ULBRInterface2DReader, et un contrat de vue,
ILBRInterface2DView. Le lecteur parcourt le Dialogue avec les helpers ; la vue affiche ce qu'elle
reçoit et décide du rythme (délais, « en train d'écrire… », une page affichée d'un coup).
Le lecteur avance jusqu'à la prochaine étape bloquante, un choice ou une fin. Chaque ligne est envoyée à la vue, puis ses effets sont appliqués dans la même frame. Si une action ferme la vue, la lecture s'arrête proprement.
Lecteur → vue (BlueprintNativeEvent) |
Envoyé quand |
|---|---|
EnterSubdialogue(Subdialogue) |
La lecture entre dans un sous-dialogue. Une vue racine choisit sa sous-vue d'après les flags. |
ShowText(Line) |
Une ligne est affichée. FLBRInterface2DLine porte Text, SpeakerName, Flags, TypedActions (indications de présentation, comme une image), Audio (jamais joué par le lecteur) et bReplay. |
PresentReplies(ChoiceIndex, Options, TimeLimit) |
Un choice attend. TimeLimit vaut 0 pour un choix non minuté. |
ReplyChosen(ReplyIndex, bReplay) |
Un reply a été choisi, p. ex. pour afficher le message envoyé. |
Unlocked() / NoMatch() |
La saisie correspond à une réponse / ne correspond à rien. |
Finished(Reason) |
La lecture s'est arrêtée d'elle-même. |
| Vue → lecteur | Effet |
|---|---|
Start(Dialogue, Subdialogue, View) |
Entre dans le sous-dialogue (le premier Interface2D jouable s'il est nul) et lit. |
Reconstruct(Dialogue, Subdialogue, View) |
Rejoue ce qui a déjà été lu, puis continue en direct. Voir Reconstruction. |
ChooseReply(ReplyIndex) |
Enregistre le reply, applique ses effets et lit son corps. |
SubmitInput(Text) |
Une réponse correcte envoie Unlocked, marque le sous-dialogue du verrou comme terminé et prend le reply. Le reply par défaut envoie NoMatch et est pris. Aucune correspondance du tout envoie NoMatch et représente le choice. |
TimeOut() |
Prend le reply par défaut s'il y en a un ; sinon le choice continue d'attendre. |
Stop() |
S'arrête sans Finished, p. ex. quand la vue se ferme. |
Le lecteur ne lève aucun flag « done », sauf pour un verrou ouvert. Ce sont les vues qui lèvent les autres, chacune selon son usage.
Actions typées : indications de présentation¶
Une ligne peut porter des actions qui ne font rien par elles-mêmes (DoAction est vide) mais
indiquent à la vue comment la présenter. La vue les lit dans TypedActions :
Interface2D Image(ULBRI2DImageAction) : une image affichée avec la ligne ;Interface2D Play Audio Subdialogue(ULBRI2DPlayAudioSubdialogueAction) : un sous-dialogueAudio2Ddu même Dialogue, joué quand le joueur appuie sur Play (une messagerie vocale).
Leurs propriétés ne sont pas editor-only : elles sont lues dans un jeu packagé.
Reconstruction¶
Un fil de messagerie montre ce qui a déjà été dit à chaque réouverture du téléphone. Libretto ne garde aucune transcription : le fil est reconstruit à partir des GPVars, qui enregistrent déjà chaque reply choisi.
Reconstruct lit le fil avec bReplay levé. Il n'applique aucun effet, ne lève aucun flag
« done » et ne joue aucun audio ; il envoie quand même les indications de présentation. À chaque
choice :
- si un seul reply a été choisi auparavant (
GetRetainedReplyrenvoieSingle) et que le choice n'a pas encore été rencontré pendant cette reconstruction, le lecteur envoieReplyChosen(bReplay)et le suit ; - sinon (jamais répondu, plusieurs replies choisis, ou rencontré une deuxième fois), la relecture s'arrête et le choice est présenté en direct.
Deux règles en découlent, et elles comptent quand vous écrivez un fil :
- Un fil ne passe pas deux fois par le même choice. Un fil qui reboucle sur un choice cesse d'être reconstruit au deuxième passage : le joueur y répond à nouveau.
- Les conditions d'un fil devraient être en cache. Une condition est réévaluée pendant la
reconstruction. Si son résultat a pu changer depuis la lecture du fil, le fil reconstruit prend
une autre branche. Cochez
bCacheSuccess/bCacheFailuresur les conditions qui décident de ce qu'un fil a déjà dit.
Les vues et le device d'exemple¶
Les vues sont des classes C++ UUserWidget qui construisent leur mise en page en code, dans
LibrettoExamplesRuntime/Interface2D/. Elles partagent une base, ULBRI2DView (cadre, titre,
bouton de fermeture, Open(Dialogue, Subdialogue), OnClosed, OnFinished).
| Vue | Usage | Marqué terminé par la vue |
|---|---|---|
ULBRI2DDocumentView |
Table des matières (les sous-dialogues Document, titrés par leur ligne Title), une page affichée d'un coup, des images. |
La page, à son ouverture. |
ULBRI2DPhoneView |
Entre sur le verrou s'il n'est pas encore ouvert, sur l'écran d'accueil sinon. Tuiles pour les fils et la messagerie vocale. | — |
ULBRI2DLockView |
Champ de saisie et retour NoMatch / Unlocked. |
Levé par le lecteur. |
ULBRI2DMessagingView |
Un fil au nom de son locuteur, reconstruit à l'ouverture, avec des délais de frappe. | Le fil, quand il se termine. |
ULBRI2DVoicemailView |
La transcription et un bouton Play. | La messagerie vocale, quand son audio se termine. |
ULBRI2DPopupView |
Un texte et un choix minuté. | Les sous-dialogues lus, à la fin. |
ALBRI2DDevice ouvre une vue sur un Dialogue. C'est un trigger de showcase utilisé avec la
touche Interact : il tient une référence directe au Dialogue (pour que le cook le suive) et une
View Class. C'est l'appelant qui choisit la vue : le Dialogue ne dit rien de la manière dont
il est affiché. Le device donne l'input à l'UI tant que la vue est ouverte, et le rend au jeu à sa
fermeture.
Sauvegarde¶
Tout ce dont une interface 2D a besoin pour se rouvrir dans le même état tient déjà dans deux objets : les GPVars (replies choisis, flags « done », vos propres tags) et l'historique des conditions (résultats de conditions figés). Sauvegardez les deux avec votre sauvegarde, et le document, les fils et le verrou reviennent tels que le joueur les a laissés. Voir Sauvegarder et restaurer l'état de Libretto.
Essayez : la station « 2D Interfaces »¶
La station « 2D Interfaces » de la map tutoriel L_LibrettoTutorial place les trois dialogues
d'exemple sur trois devices :
| Dialogue | Device | Montre |
|---|---|---|
D_I2D_Document |
Document | Des pages avec une image, un embranchement selon que le téléphone est déverrouillé, et un addendum qui apparaît une fois la messagerie vocale écoutée. |
D_I2D_Phone |
Téléphone | Un verrou (le code est sur le panneau de la station, un indice apparaît après trois codes faux), deux fils (le second apparaît une fois la messagerie vocale écoutée), une messagerie vocale avec son sous-dialogue Audio2D. |
D_I2D_Popup |
Terminal | Un premier avis avec un choix minuté, puis une page « bon retour » qui dépend du reply choisi. |

Ils se trouvent dans /Libretto/Examples/Interface2D/Dialogues/.
Packager une messagerie vocale
La messagerie vocale joue son audio comme un dialogue générique : aucune map ne le référence, donc le cooker n'atteint ni sa séquence générée ni ses sons. Ajoutez le dossier qui les contient à Additional Asset Directories to Cook, comme pour tout dialogue générique. Le document, les textes du téléphone et la popup n'ont besoin de rien : le device référence le Dialogue.
Voir aussi : FAQ interfaces 2D · Conditions · GPVars · Blueprint API