"Multi Conditional" that evaluates several conditionals at once

drewv

New member
Here is the problem I attempted to solve:
1624553656502.png

I have a rough solution working but was hoping for some recommendations on how this could be improved, as I think it's a useful feature. I am aware that this problem can be solved by creating a new conditional that is just a composite of the conditionals I want to be evaluated at the same time. But, if possible, I want to avoid making these kinds of redundant composite conditionals.

To solve this problem I created a custom conditional task called "multi conditional" that can reference as many different conditionals as desired and only returns success if all of the conditionals returned success. There is also an option to invert each referenced conditional individually if desired. This way you can observe several conditionals together when using conditional aborts. So far it has worked, but feels a little janky. Here is the code for this new conditional:

C#:
[TaskCategory("NPC/Conditional")]
    [TaskDescription("The MultiConditional evaluates multiple conditionals at once and only returns True if all conditionals are true.")]
    public class MultiConditional : Conditional
    {
        public Conditional[] condtionalTasks;
        public bool[] inversionFlags;
 
        public override void OnAwake()
        {
            if (condtionalTasks.Length > 0)
            {
                foreach (var conditional in condtionalTasks)
                {
                    conditional.Owner = Owner;
                    conditional.GameObject = gameObject;
                    conditional.Transform = transform;
                    conditional.OnAwake();
                }
            }
        }
 
        public override void OnStart()
        {
            if (condtionalTasks.Length > 0)
            {
                foreach (var conditional in condtionalTasks)
                {
                    conditional.OnStart();
                }
            }
        }
 
        public override TaskStatus OnUpdate()
        {
            //initialize as a failure, all input conditionals must return true for this task to return success.
            var status = TaskStatus.Failure;
            if (condtionalTasks.Length > 0)
            {
                for (var i = 0; i < condtionalTasks.Length; i++)
                {
                    var conditional = condtionalTasks[i];
                    status = conditional.OnUpdate();
                    if (inversionFlags[i])
                        status = InvertStatus(status);
 
                    //if any single conditional in this array return failure we want the entire multiconditional to return failure.
                    if (status == TaskStatus.Failure)
                        return TaskStatus.Failure;
                }
 
                return status;
            }
            return TaskStatus.Failure;
        }
 
        public override void OnEnd()
        {
            if (condtionalTasks.Length > 0)
            {
                foreach (var conditional in condtionalTasks)
                {
                    conditional.OnEnd();
                }
            }
        }
 
        public override void OnReset() => condtionalTasks = new Conditional[0];
 
        private TaskStatus InvertStatus(TaskStatus inputStatus)
        {
            var invertedStatus = TaskStatus.Inactive;
           
            if (inputStatus == TaskStatus.Failure)
                invertedStatus = TaskStatus.Success;
            else if (inputStatus == TaskStatus.Success)
                invertedStatus = TaskStatus.Failure;
 
            return invertedStatus;
        }
    }

And here is a picture of what it looks like within the tree (more for example than an actual use-case) Here I have the multi conditional evaluating a condtional called 'see player' and also evaluating an inverted conditional 'is within radius'. In this case the low priority abort on the combat sequence task is only activated if both 'see player' returns success and the inverted 'is within radius' returns success.
1624554206899.png
1624554116782.png

As you can see, the conditionals are not attached to the tree however it still works because of the references linking them to the multiconditional. I was curious if anyone had some ideas for how they would implement this kind of custom task so that the conditionals it evaluates are connected to the tree and any other possible improvements. One rough area with this implementation is that I have to manually set the size of the "inversion flags" array. I created it this way because I have very limited experience with scripting custom editors, and had no clue how to resize this array without access to any kind of "OnValidate" function.

Any thoughts or suggestions would be greatly appreciated, thank you!
 
The jank continues! lol. Here's a way to make sure the conditionals referenced by the multi conditional are actually attached to the tree.1624572360755.png
The sequence that always returns success holds the conditionals referenced by the multi conditional, but the always false conditional at the start of the sequence makes sure that the conditionals are only ever evaluated through the multi conditional. This is a pretty roundabout way of doing things but now everything is attached to the main tree which is a necessity if you were to ever regenerate the tree or use this within an external tree.

It does feel like I'm kind of going against the grain in this implementation, although if there was a way to reference tasks without the task actually having to be in the tree, that would be perfect I think. I wanted something similar to how the already existing task "Conditional Evaluator" references a conditional, however that only seems to work with a single conditional and not an array of conditionals.

ALSO... I wanted to thank you Justin for making such a cool asset! Your responses on the forums and also the documentation have been tremendously helpful in getting started with behavior designer, and I'm having so much fun with it :)
 
Last edited:
Happy to hear that you're enjoying Behavior Designer!

I like your creativity :) Right now this scenario requires two separate conditional tasks or a hack like you implemented. In version 2 I have added the ability to stack tasks into a single node, and when I implement conditional aborts I'll take this into account.

You mentioning the conditional evaluator may be a good interum solution until I can get version 2 out (version 2 uses DOTS so it'll still be awhile). I'll try to add it to the next update!
 
We'll be buying V2 in a heartbeat! Working in a team right now that's making a game with some DOTS and Unity Netcode which also uses ECS. I know we'd love to get the AI on board with that! It's a bummer there are no milestones for DOTS on Unity's 2021 roadmap.
 
Top