Help with creating a "Cooldown Timer" task

Austin

New member
I am trying to create a cooldown task. When the task is first visited in the behavior tree it should execute its child as normally, then if the child's TaskStatus was a success and/or failure, a cooldown timer should begin and the cooldown task should not allow its child to execute again, returning TaskStatus.Failure, until the timer reaches zero. Here is what I have right now:

public class CooldownTimer : Decorator
{
public float cooldownTime;
public bool cooldownStartOnSuccess;
public bool cooldownStartOnFailure;

private float cooldownStart;

public override bool CanExecute() {
return (Time.time - cooldownStart >= cooldownTime) ? true : false;
}

public override void OnChildExecuted(TaskStatus childStatus) {
if (cooldownStartOnSuccess && childStatus == TaskStatus.Success) cooldownStart = Time.time;
if (cooldownStartOnFailure && childStatus == TaskStatus.Failure) cooldownStart = Time.time;
}
}

And here is what my behavior tree looks like right now:

EnemyAi.png

I am trying to get it so that when the enemy spots a player it will chase it until the player is within shooting range and unobstructed by any obstacles then fire a number of shots, and try to close the distance after until it is ready to fire another volley when the cooldown ends. However I'm getting some unexpected problems:

After the enemy fires a volley at the player and the cooldown timer starts, the enemy does not enter the Ai Pursue state and close the distance to the target while the timer counts down.

CooldownStarted.png

Then there are cases where the enemy is shooting at the player but the player moves out of range or sight, and the enemy pursues the player but never tries shooting again even when the player comes back into sight and range. Here's the state of the behavior tree at one of those times.
UnknownCaseA.png
I'm burnt out on trying to figure out where I went wrong right now, and I'm having trouble finding any further documentation on the base class Decorator that might help me understand where my errors lie. I'm sure there's something simple I'm forgetting; I only just started using Behavior Designer about a week ago and there's a lot I still don't fully understand. If someone sees what I'm missing from my custom task please let me know, or better yet, if you have your own solution to building a cooldown timer please do share.
 

Justin

Administrator
Staff member
I really like the idea of a cooldown decorator so can take a look at adding it to the next version. What I've found a lot of people doing for cooldown is using a self conditional abort on a custom conditional task. Something like this:

------------- Sequence (Self abort)
--CooldownConditional ---- OtherTasks

This should make it easy to just have to override OnUpdate with the timer. You'd reset the timer within OnStart.
 

Austin

New member
Thanks for the advice. I ended up doing something just like that, except I split my solution into two parts to get closer to the behavior I was going for:
C#:
public class CooldownStart : Action
{
    public float time;

    public override void OnStart() {
        time = Time.time;
    }
}

public class CooldownCheck : Conditional
{
    public float cooldownTime;
    public CooldownStart cooldownStart; // referenced task

    public override TaskStatus OnUpdate() {
        return (Time.time - cooldownStart.time >= cooldownTime) ? TaskStatus.Success : TaskStatus.Failure;
    }
}
Now my tree is looking like this:
tree.png
My ai works now; it fires a volley of three shots at the player and has to cool down for a few second before it can fire again, closing the distance while the time counts down. I'd still like one day to figure out how to get my first attempt at using a Decorator to work or see it in the next version like you say, but in the meantime I'll do this for now.
 

Justin

Administrator
Staff member
Just giving you a heads up that a cooldown task will be included in version 1.7 :)
 

Austin

New member
Thanks. I'll keep an eye out for the new version; looking forward to testing the new cooldown task.
 

fsq19474

New member
Hello there. I recently found myself stuck in the same question, and I've tried out the new Cooldown task but seems like it's still away from the goal here.

IIUC, the Cooldown task is a decorator, and is blocking while in CD, which means:
1. It won't trigger conditional abort when under a Composite, since only Conditional tasks can do that.
2. The tree is blocking at this node, the AI can't do other things like Pursue. I tried to put Attack and Pursue under a Parallel, but that lets the enemy Attack and Pursue at the same time.

A workaround I can think of is to create a SharedFloat to record last attack finish, and use a custom Conditional to check CD, like what mentioned in #2. But this creates a bunch of new variables only to handle CD, which looks cumbersome.

I guess what I'm looking for here is something like a ConditionalEvaluator with Cooldown, and can be re-evaluated and abort low-priority childs. I've also thought about incorporate CD in the Action task itself. But again Actions can't abort other children when CD is over.

I'm relatively new to game programming and behavior trees tho, I might have missed something. Any suggestions are appreciated, thanks!
 

Justin

Administrator
Staff member
This will require an extension to the cooldown task. I can add it to my list of new features to add but if you want to work on it before I can get to it I would duplicate the existing cooldown task and start to modify it with those new features.
 

fsq19474

New member
That sounds awesome, thanks!
Would you mind elaborating little bit about the correct way of implementing this? The most straight forward way I guess is to
1. Let new Cooldown trigger conditional abort in Composite ancestor , which I'm not sure if there is an existing API
2. Let task return Failure instead of Running while in CD. (This change looks straightforward in current code).

Thanks again for the prompt response.
 

Justin

Administrator
Staff member
Conditional aborts are not able to trigger any other task type so that would be a major change. There's not the API in order to support this. Your approach of having a new conditional task check the cooldown is probably the most straight forward, and can be implemented with the existing API. You can reference other tasks within a task so through code you could get a reference to the cooldown from your own conditional task. To do this you can use the BehaviorTree.FindTask method. You should not need any new shared variables.
 

fsq19474

New member
Conditional aborts are not able to trigger any other task type so that would be a major change. There's not the API in order to support this. Your approach of having a new conditional task check the cooldown is probably the most straight forward, and can be implemented with the existing API. You can reference other tasks within a task so through code you could get a reference to the cooldown from your own conditional task. To do this you can use the BehaviorTree.FindTask method. You should not need any new shared variables.
Got it, thank you so much!
 
Top