Aller au contenu

Conditions

Une condition est un petit Blueprint (ou une classe C++) réutilisable qui évalue une situation et répond à une unique question par oui ou non au runtime. Questions typiques :

  • Le joueur a-t-il déjà parlé à ce personnage ?
  • A-t-il trouvé telle pièce d'un puzzle ?
  • Son personnage possède-t-il assez d'une certaine compétence ?

Les conditions sont le ciment qui rend les dialogues réactifs. Dans le Dialogue Editor, elles verrouillent les lignes de Reply, les branches If / ElseIf et des subdialogues entiers — mais la même classe est tout aussi utilisable depuis n'importe quel Blueprint (interactions, triggers, logique de gameplay).

Ce qu'est une condition

La classe de base de toute condition est ULBRCondition (module LBRConditions). Pour créer la vôtre, dérivez-en — ou d'une classe qui en dérive déjà — et implémentez la logique d'évaluation.

Tip

De nombreux besoins sont déjà couverts par les conditions prêtes à l'emploi bâties sur le système des GPVars — vérifiez-les avant d'écrire une nouvelle classe. Voir GPVars & Conditions.

ULBRCondition est marquée Abstract, Blueprintable, DefaultToInstanced et EditInlineNew, si bien que vous pouvez la sous-classer en Blueprint et qu'elle est destinée à être instanciée partout où elle est utilisée (plus de détails sous Conditions instanciées).

Écrire une condition

Surchargez Evaluate — un BlueprintNativeEvent qui renvoie le résultat du test. En Blueprint, vous implémentez l'événement Evaluate ; en C++, vous surchargez Evaluate_Implementation(). La déclaration de base est purement virtuelle, si bien que toute condition concrète doit en fournir une.

// C++ signature to override
virtual bool Evaluate_Implementation() override;

Un Blueprint de condition surchargeant Evaluate

Evaluate ne prend aucun paramètre et renvoie un bool. Elle est protected : les appelants ne l'invoquent pas directement — ils passent par EvaluateWithCache, qui décide s'il faut exécuter Evaluate ou non.

Libellés dans l'éditeur

Pour qu'une condition se lise clairement dans le Dialogue Editor, surchargez les hooks de libellé. Ils font apparaître une courte description sur la ligne qui porte la condition :

Override Affiché sur
GetLabel La ligne Reply (le libellé générique).
GetLabelSuccess La ligne If (ce que signifie la branche de succès).
GetLabelFail La ligne Else (ce que signifie la branche d'échec).

Chacun est un BlueprintNativeEvent renvoyant un FText ; par défaut, ils renvoient un texte vide.

Opérateurs intégrés

LBRConditions fournit des conditions opérateurs composables afin que vous puissiez construire des expressions sans écrire de code. Chacune prend d'autres conditions comme opérandes (instanciés) :

Condition Nom d'affichage Résultat
ULBRConditionAnd AND vrai lorsque tous les opérandes sont vrais
ULBRConditionOr OR vrai lorsque au moins un opérande est vrai
ULBRConditionXOr XOR vrai lorsque exactement un opérande est vrai
ULBRConditionNot NOT inverse son unique Condition
ULBRConditionFalse FALSE toujours faux
ULBRConditionEmpty — toujours vrai (la condition neutre)

Les opérateurs AND / OR / NOT imbriquant des conditions

Le cache succès/échec

Évaluer une condition peut être coûteux, et la même condition peut être vérifiée à répétition. Chaque ULBRCondition peut donc mémoriser son résultat d'évaluation et éviter de ré-exécuter Evaluate. Le résultat est rangé dans l'historique des conditions, qui est global : il survit aux changements de niveau et peut être sauvegardé.

Deux flags, situés dans la section advanced de la propriété, activent la mise en cache :

Flag Effet
bCacheSuccess Une fois que la condition s'évalue à true, ce résultat est mis en cache et réutilisé — plus aucune vérification.
bCacheFailure Une fois que la condition s'évalue à false, ce résultat est mis en cache et réutilisé.

Activez l'un, l'autre, ou les deux. Les deux valent false par défaut (toujours ré-évaluer).

Les propriétés advanced bCacheSuccess / bCacheFailure

Warning

La mise en cache suppose que la réponse ne changera pas. Ne l'activez que pour des conditions dont le résultat est stable une fois atteint (par exemple, « le joueur a terminé le tutoriel ») — faute de quoi la valeur périmée en cache sera servie indéfiniment.

Appeler une condition depuis un Blueprint

Depuis n'importe quel Blueprint, vérifiez que la condition est valide, puis appelez EvaluateWithCache :

  • Avec bForceEvaluation = false (la valeur par défaut), elle renvoie le résultat en cache lorsqu'il est valide, sinon elle exécute Evaluate, met le résultat en cache si le flag pertinent est activé, et le renvoie.
  • Avec bForceEvaluation = true, elle ignore le cache et exécute toujours Evaluate.

Le node EvaluateWithCache dans un graphe Blueprint

Trois helpers vous permettent d'inspecter et de gérer le cache manuellement :

Fonction Rôle
IsStoredResultValid() Renvoie false si la condition n'a jamais été évaluée, a été invalidée, ou si ses flags de cache ne couvrent plus le résultat mémorisé.
GetStoredResult() Renvoie le résultat mémorisé (sans évaluer), ou false s'il n'y a pas de résultat mémorisé valide.
InvalidateLastResult() Retire le résultat mémorisé de l'historique pour que le prochain EvaluateWithCache ré-exécute Evaluate.

L'historique des conditions

Les résultats en cache vivent dans ULBRConditionHistorySubsystem, un engine subsystem : un registre global pour toute la session de jeu, avec la même durée de vie que les GPVars. Chaque entrée a pour clé le chemin du sous-objet condition, et ce registre est la seule source du cache : une condition ne garde aucun résultat de son côté. Dans l'éditeur, l'historique est vidé au début et à la fin de chaque session PIE.

Un résultat mémorisé n'est utilisé que tant que les flags de la condition le couvrent : si vous décochez bCacheSuccess sur une condition, son true mémorisé est ignoré et la condition est réévaluée.

Depuis un Blueprint :

Fonction Rôle
Add(Path, bResult) Mémorise un résultat pour un chemin.
Remove(Path) Retire l'entrée d'un chemin.
CheckHistory(Path) 1 = vrai mémorisé, 0 = faux mémorisé, -1 = aucune entrée. Un false mémorisé ne se confond jamais avec une entrée absente.
ClearConditionsOf(Owner) Retire les entrées de toutes les conditions contenues dans Owner (un dialogue, un acteur, un package). Purger D_Foo laisse D_Foo2 intact.
ClearAll() Vide l'historique.

En C++, FindConditionResult(Path, bOutResult) renvoie false s'il n'y a pas d'entrée ; bOutResult n'a de sens que si elle renvoie true.

Pour sauvegarder l'historique, sérialisez le subsystem avec une archive SaveGame, en plus de ULBRGPVarSubsystem. Charger une sauvegarde remplace tout l'historique.

Piège

L'historique est global et rien n'est purgé automatiquement, pas même au déchargement d'un niveau : le plugin ne peut pas savoir si une condition doit rester figée quand le joueur revient. Les résultats des conditions portées par des acteurs de niveau s'accumulent donc tant que vous ne les purgez pas. Appelez ClearConditionsOf (ou ClearAll) sur ce dont vous n'avez plus besoin.

Piège

La clé est le chemin de la condition. Renommer ou déplacer un asset perd l'historique de ses conditions. Il n'y a pas de redirect.

Conditions instanciées & ConditionContainer

Une condition est un UObject, si bien qu'une propriété qui se contente de la référencer est nulle jusqu'à ce que vous lui assigniez une instance. Il existe deux façons d'obtenir une condition qui crée et possède son instance automatiquement.

1 — le spécificateur Instanced (C++). Marquez une propriété ULBRCondition* comme Instanced et l'éditeur vous laissera choisir une sous-classe et l'éditer en ligne, en créant le sous-objet pour vous :

UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "Conditions")
TObjectPtr<ULBRCondition> MyCondition;

C'est exactement ainsi que les conditions opérateurs contiennent leurs opérandes (ULBRConditionOperator::Operands, ULBRConditionNot::Condition).

2 — FLBRConditionContainer (aucun C++ requis). Pour le cas courant, utilisez le type de struct prêt à l'emploi — une fine enveloppe dont l'unique champ Condition est déjà configuré comme Instanced :

USTRUCT(BlueprintType)
struct FLBRConditionContainer
{
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = "Conditions")
    TObjectPtr<ULBRCondition> Condition;
};

Exposez une propriété FLBRConditionContainer et vous obtenez l'édition de condition en ligne dans le details panel, prête à l'emploi.

Un FLBRConditionContainer édité en ligne dans le details panel

Conditions sur les lignes de dialogue

Dans le Dialogue Editor, les conditions apparaissent à trois endroits :

  • Les lignes de Reply portent deux conditions :
    • Display Condition — si fausse, la Reply n'est ni proposée ni affichée.
    • Availability Condition — si fausse, la Reply est affichée mais grisée et non sélectionnable.
  • Les lignes If / ElseIf utilisent une Success Condition — si vraie, le joueur entre dans cette branche ; sinon le ElseIf/Else suivant est évalué.
  • Les Subdialogues portent une Condition qui autorise, ou non, le subdialogue à se jouer. À l'entrée, les subdialogues candidats sont évalués dans l'ordre et le premier dont le nom correspond et dont la condition passe est joué.

Une condition Display / Availability sur une ligne de Reply

Les overrides GetLabel / GetLabelSuccess / GetLabelFail décrits ci-dessus sont ce qui affiche un résumé lisible directement sur ces lignes. Voir la page Dialogue Editor pour le workflow d'écriture.

Vérifications « dialogue joué »

Pour tester « ce dialogue/subdialogue a-t-il été joué », utilisez la condition dédiée ULBRDialogueDoneCondition (dans LBRCore) plutôt qu'une condition sur chemin de GPVar écrite à la main. Une variante comptée, ULBRDialogueDoneCountedCondition, compare combien de fois un dialogue ou un subdialogue a été joué. Voir GPVars & Conditions.