Another approach to Conditions

I feel like the system for the Conditions with the additions of Disjunction and Conjuction is …a bit over-engineered.

It is flexible but to actually use it in the Unity interface is just a chore. The Unity GUI is not built to handle such incapsulation.
And I feel like 95 % of the cases where I want to manipulate the dialogues by conditions, can be handle by one condition or two max. Beyond that, it is make more sens (for me) to give a new Dialogue, or another Quest to the NPC.
And to write the predicate type, and the name of the quests, or the items each time, it is not scallable for a full game. Every one make typos, and it will be very easy to make numerous one with so many copy pasting / writing.

So I make three changes.

  • The “condition” field on DIalogueNode become a List of Condition instead of a single one.
  • In Condition predicate become a Enum instead of a string. It gain its one file, so we can use it in others classes.
  • In Condition parameters become … an array of ScriptableObject. In case of QuestList what are we trying to do with the parameters ? To obtain a Quest by its name. Same for Inventory with InventoryItem.
    And both Quest and InventoryItem are ScriptableObject. So we don’t have to write the name, we directly search them !
namespace RPG.Core
{
    public enum PredicateType
    {
        HasQuest,
        CompletedQuest,
        ObjectiveCompleted,
        HasInventoryItem,
    }
}

    public class Condition
    {
        [SerializeField] PredicateType predicate;
        [NonReorderable] [SerializeField] ScriptableObject[] parameters;
  }

namespace RPG.Core
{
    public interface IPredicateEvaluator
    {
        bool? Evaluate(PredicateType predicate, ScriptableObject[] parameters);
    }
}



    public class DialogueNode : ScriptableObject
    {      
  [NonReorderable] [SerializeField] private List<Condition> conditions = new List<Condition>();

        // Will return true if ALL the conditions are fulfilled.
        public bool CheckCondition(IEnumerable<IPredicateEvaluator> evaluators)
        {

            foreach (Condition condition in conditions)
            {
                bool result = condition.Check(evaluators);
                if (!result) return false;
            }

            return true;
        }
}

PlayerConversant

        private IEnumerable<DialogueNode> FilterOnCondition(IEnumerable<DialogueNode> inputNode)
        {
            foreach (var node in inputNode)
            {
                if (node.CheckCondition(GetEvaluators()))
                {
                    yield return node;
                }
            }
        }

Inventory

        public bool? Evaluate(PredicateType predicate, ScriptableObject[] parameters)
        {
            InventoryItem item = parameters[0] as InventoryItem;
            if (item == null) return null;
            switch (predicate)
            {
                case PredicateType.HasInventoryItem:
                    return HasItem(item);
            }
            return null;
        }

It is a less flexible system, but it feels much more conveniant to use.
I have lost the possibility (for now) to use the predicate “ObjectiveCompleted” but I think it is doable to get it back.
Probably by having three parameters in Evaluate. Something like this maybe

bool? Evaluate(PredicateType predicate, ScriptableObject object, string[] parameters);

public class Condition
{
     [SerializeField] PredicateType predicate;
      [NonReorderable] [SerializeField] ScriptableObject object;
      [NonReorderable] [SerializeField] string[] parameters;
}
1 Like

I also tried to reduce the choices for

ScriptableObject[] parameters;

because here we can reference every Scriptable object, such as Dialogue, and even DialogueNode.
I implemented an Interface IConditionSearchable, put it on InventoryItem and Quest, and tried then

IConditionSearchable[] parameters;

But the field does not appear anymore in the editor . :sob:
Another approach will be to create a Class ConditionSearchable, extending the ScriptableObject and then to have the others two extend it.
But I would like to make it work with an Interface.
If anyone else is interested, this thread exist : How to expose a field of type Interface in the inspector? - Unity Answers

Have you taken a look at this thread? It uses much of the same logic as the original, but works around the string issue by constraining the choices to lists derived from the assets and even handles specific quest objectives.

1 Like

Thanks you Brian, I will definitively take a look at this.
I already had a previous introduction to PropertyDrawer but without a proper … application and experience with it, I already forgot what I knew.
This sound like a perfect opportunity to dive deeper into it.
Thanks.

Privacy & Terms