Aller au contenu

Dynamic Tags

De nombreuses fonctionnalités identifient les données au moyen de GameplayTags — dialogues, sous-dialogues, choix, réponses, ainsi que les métadonnées qui leur sont attachées (combien de fois un dialogue a été joué, si un choix a été sélectionné, etc.). Maintenir à la main une liste de tags parallèle duplique l'information : les fichiers .ini écrits manuellement se désynchronisent des assets, des fautes de frappe s'y glissent, et le packaging embarque des entrées obsolètes.

Les Dynamic Tags suppriment cette duplication. Plutôt que de stocker les tags de façon redondante, Libretto parcourt les données existantes au démarrage du projet et génère les tags à partir des assets eux-mêmes. Il existe une source de vérité unique — les assets — de sorte que les tags ne peuvent jamais se désynchroniser du contenu qu'ils décrivent.

Comment les tags sont générés

Au chargement du projet, le ULBRDialogueTagsSubsystem (module LBRCore) analyse chaque asset Dialogue, puis enregistre un tag pour le Dialogue et un pour chacun de ses sous-dialogues, choix et réponses. L'enregistrement de bas niveau est effectué par le ULBRDynamicTagsSubsystem (module LBRTags), qui détient les objets tag proprement dits.

Les tags générés suivent cette organisation :

Libretto.Dialogues.<DialogueName>
Libretto.Dialogues.<DialogueName>.<SubdialogueName>
Libretto.Dialogues.<DialogueName>.Choice_<ID>
Libretto.Dialogues.<DialogueName>.Choice_<ID>.Reply_<ID>

La racine (Libretto.Dialogues), le préfixe d'asset (D_) et les préfixes Choice_ / Reply_ sont des valeurs par défaut déclarées sur la classe ULBRDialogue et peuvent être surchargées par classe de base de Dialogue :

Propriété Valeur par défaut Rôle
TagRoot Libretto.Dialogues Racine sous laquelle chaque tag de dialogue est placé.
AssetPrefix D_ Préfixe de nom d'asset utilisé pour retrouver les assets Dialogue sur le disque.
ChoiceTagPrefix Choice_ Préfixe du tag propre à chaque Choice, suffixé par l'ID de la ligne.
ReplyTagPrefix Reply_ Préfixe du tag propre à chaque Reply, suffixé par l'ID de la ligne.
SelectorsTagRoot SubdialogueSelectors Sous-branche pour les sélecteurs de groupe partagés par plusieurs sous-dialogues.

Les tags de sous-dialogue font aussi office de sélecteur unique du sous-dialogue. Les sélecteurs de groupe — un tag partagé par plusieurs sous-dialogues, par exemple un groupe « Intro » — sont placés sous Libretto.Dialogues.<DialogueName>.SubdialogueSelectors.<Name>.

A generated Libretto.Dialogues subtree in the tag picker

Ce sont ces tags que le sous-système GPVar utilise comme clés pour ses métadonnées (nombre de lectures, nombre de sélections de choix, vérifications du type « ceci a-t-il déjà été joué »).

Builds packagés — le repli .ini

Ré-analyser chaque asset fonctionne bien dans l'éditeur et au moment du cook, mais un jeu packagé ne devrait pas ré-analyser les dialogues à l'exécution. Deux fichiers comblent cet écart :

  • Au moment du cook, les tags générés sont exportés dans Config/DynamicTags/DynamicTags.ini sous forme d'un bloc GameplayTagList standard. Dans un build packagé, l'enregistreur ajoute ce dossier comme chemin de recherche de tags (AddTagIniSearchPath), de sorte que les tags sont chargés depuis le .ini au lieu d'être régénérés en parcourant les assets.
  • À ses côtés, Config/LBRDynamicTags.ini stocke l'index tag-vers-dialogue (une section [TagsToDialogues] associant chaque tag racine de dialogue à son chemin d'asset), de sorte qu'un tag peut être résolu vers le Dialogue qu'il identifie sans tout charger.

Note sur les chemins

Les chemins de repli exacts ont été relevés dans les sources actuelles : la liste de tags exportée se trouve dans Config/DynamicTags/DynamicTags.ini et l'index tag-vers-dialogue dans Config/LBRDynamicTags.ini. Des brouillons antérieurs évoquaient un fichier par dialogue Config/Tags/Dialogues/<Name>.ini ; cette organisation n'est pas celle que le code produit aujourd'hui — fiez-vous aux deux chemins ci-dessus.

Limitations connues

Mécanisme fragile de FNativeGameplayTags

Le moteur n'offre aucun moyen supporté d'enregistrer une source de tags personnalisée à l'exécution, si bien que les Dynamic Tags détournent le mécanisme FNativeGameplayTag — chaque tag généré est adossé à une instance new FNativeGameplayTag(...) créée avec le token interne ENativeGameplayTagToken::PRIVATE_USE_MACRO_INSTEAD. Il s'agit d'un contournement délibéré d'une API normalement pilotée uniquement par les macros UE_DEFINE_GAMEPLAY_TAG, et il est sensible à la version du moteur : une future version d'Unreal pourrait durcir ou modifier ce chemin et imposer une refonte lors de la mise à niveau. Le cas échéant, le correctif propre serait une petite modification du moteur pour ajouter une source de tags dynamiques de première classe — ce que nous préférons éviter tant que l'approche actuelle tient.

Les tags doivent être enregistrés tôt

Une fois que le moteur positionne son flag interne bDoneAddingNativeTags, plus aucun tag dynamique ne peut être ajouté. La génération doit donc s'exécuter tôt dans l'initialisation, avant que ce flag ne soit posé. C'est géré en interne (l'analyseur s'accroche aux délégués de chargement de module / de cook), mais cela contraint le moment où les tags peuvent apparaître.

Ce n'est pas un workflow Blueprint

Les Dynamic Tags ne sont pas destinés à être pilotés depuis le Blueprint. Le point d'entrée d'enregistrement (RegisterTag) est techniquement BlueprintCallable mais est marqué « do not use unless you know what you are doing » — tout le système est censé s'exécuter automatiquement au chargement, il n'est donc pas exposé comme une API Blueprint générale. L'exposer plus largement serait simple mais n'a pas été nécessaire.

Coût à l'exécution

Analyser chaque dialogue au chargement est resté, en pratique, de l'ordre de quelques secondes même pour un jeu de grande envergure. Si cela devenait un jour un goulot d'étranglement, la réponse prévue est l'export au moment du cook décrit ci-dessus (sur lequel le repli s'appuie déjà) plutôt que de réduire l'analyse.