Why is this conditional task rerunning on success?

poweremil

New member
I don't understand why this tree never enters the Send Event task even when Can Pass returns Success.

tree.png

Here's an image of the log showing it reaches the return TaskStatus.Success.

log.png

I have removed all irrelevant code and managed to track it down to the CanDoPass method. It works fine when this method is hardcoded to return true but when I have any kind of condition that takes more than one frame it fails, even this simple counter I have right now and I really can't figure out why.

Code:
public class CanPass : Conditional 
{
   public SharedFloat CheckInterval;

   private bool firstRun = true;
   private float lastTime;

   public override void OnReset()
   {
      CheckInterval = 1f;
   }

   public override void OnStart()
   {
      base.OnStart();
   
      if (firstRun)
      {
         lastTime = Time.time;
         firstRun = false;
      }
   }

   public override TaskStatus OnUpdate()
   {
      if (Time.time - lastTime < CheckInterval.Value)
      {
         return TaskStatus.Failure;
      }
   
      lastTime = Time.time;
      Debug.Log("CheckInterval time passed");
   
      if (!CanDoPass())
      {
         return TaskStatus.Failure;
      }
   
      Debug.Log("CanDoPass returned true");
      return TaskStatus.Success;
   }

   private int counter;

   private bool CanDoPass()
   {
      return ++counter > 1;
   }
}
 
If you enable logging on the behavior tree can you locate what is causing the sequence task from not progressing? My guess is that a different conditional abort is aborting it before it can start.
 
Edit: I managed to create a repro scene, and it seems to be the timer that bugs out. If I remove it or put 0 as the CheckInterval it seems to work just fine. The scene is Conditional_3.

Here's the log, it doesn't look like another task is aborting it?

log-2.png
 
Last edited by a moderator:
This forum can be read by anyone so please do not include a public link to paid assets. I have a copy of Behavior Designer so generally just sending the unique files is sufficient.

I think that I figured it out though. OnUpdate will run twice, once for the conditional abort and once when the task is entered. Because you are changing the task state within CanDoPass, the conditional abort will return success, but then when it enters the task it will return failure within the same tick. One workaround is to change CanDoPass so it increments the counter only when the task is within OnStart/OnEnd by using a flag.
 
Oh oops really sorry about linking the asset, I didn't think straight. I have also deleted the uploaded file in case someone stumbled across it.
 
Edit2: Feels like I'm spamming but I got it to work with just a single task which is much better.
I split it up into two scripts, one for just the timer so I can easily reuse it and changed from a counter to checking if a key is pressed which is much easier to test, should have thought about that to start with.

Posting it here in case somebody else needs something similar.

Code:
public class ConditionalTimer : Conditional
{
   public SharedFloat Interval;

   private float time;
   private bool hasReachedInterval;

   public override void OnReset()
   {
      Interval = 1f;
   }

   public override TaskStatus OnUpdate()
   {
      time += Time.deltaTime;
      hasReachedInterval = time > Interval.Value;

      return hasReachedInterval ? TaskStatus.Success : TaskStatus.Failure;
   }

   public override void OnEnd()
   {
      if (hasReachedInterval)
      {
         ResetTimer();
      }
   }

   protected void ResetTimer()
   {
      time = 0f;
      hasReachedInterval = false;
   }
}

Code:
public class CanPass : ConditionalTimer
{
   public override TaskStatus OnUpdate()
   {
      if (base.OnUpdate() == TaskStatus.Failure)
      {
         return TaskStatus.Failure;
      }

      if (HasSecondaryCondition())
      {
         return TaskStatus.Success;
      }

      ResetTimer();
      return TaskStatus.Failure;
   }

   private static bool HasSecondaryCondition()
   {
      return Input.GetKey(KeyCode.Space);
   }
}


Edit:
I managed to get it working as I want although I had to use multiple tasks to accomplish it. Now I jsut need to implement it into my actual project, fun.

CanPass now looks like this, Counter and Delta Time are now shared variables that are set other tasks.
Code:
public class CanPass : Conditional
{
   public SharedInt MinCounter;
   public SharedInt Counter;
   public SharedFloat CheckInterval;
   public SharedFloat DeltaTime;

   private float time;

   public override void OnReset()
   {
      base.OnReset();

      MinCounter = 1;
      Counter = 0;
      CheckInterval = 1f;
      DeltaTime = 0f;
   }

   public override TaskStatus OnUpdate()
   {
      if (!HasTimePassed())
      {
         return TaskStatus.Failure;
      }

      return !CanDoPass() ? TaskStatus.Failure : TaskStatus.Success;
   }

   private bool HasTimePassed()
   {
      return DeltaTime.Value > CheckInterval.Value;
   }

   private bool CanDoPass()
   {
      return Counter.Value > MinCounter.Value;
   }
}

It seems OnStart is only called once? I moved the counter increment to OnStart but it only increase once.

Code:
public class CanPass : Conditional
{
   public SharedFloat CheckInterval = 1;

   private float time;
   private int counter;

   public override void OnStart()
   {
      if (time == 0f)
      {
         time = Time.time;
      }
  
      counter++;
   }

   public override TaskStatus OnUpdate()
   {
      Debug.Log(counter);
  
      if (!HasTimePassed())
      {
         return TaskStatus.Failure;
      }

      if (!CanDoPass())
      {
         return TaskStatus.Failure;
      }

      time = Time.time;
      return TaskStatus.Success;
   }

   private bool HasTimePassed()
   {
      return Time.time - time > CheckInterval.Value;
   }

   private bool CanDoPass()
   {
      return counter > 1;
   }
}
 
Last edited:
Top