TaskGuard
The task guard task is similar to a semaphore in multithreaded programming. The task guard task is there to ensure a limited resource is not being overused. For example, you may place a task guard above a task that plays an animation. Elsewhere within your behavior tree you may also have another task that plays a different animation but uses the same bones for that animation. Because of this you don't want that animation to play twice at the same time. Placing a task guard will let you specify how many times a particular task can be accessed at the same time. In the previous animation task example you would specify an access count of 1. With this setup the animation task can be only controlled by one task at a time. If the first task is playing the animation and a second task wants to control the animation as well, it will either have to wait or skip over the task completely.
The task guard task is similar to a semaphore in multithreaded programming. The task guard task is used to ensure that limited resources are not overused (a bit awkward, but it's easier to understand when thinking of multithreading). For example, you can decorate the task of playing animation with TaskGuard. Elsewhere in your behavior tree, you may also have another task, playing different animations, but using the same bones. Therefore, you do not want the animation to play twice at the same time. Using TaskGuard allows you to specify how many times a specific task can be accessed at the same time. In the previous animation task example [I don't see where the previous example is. The above English is copied from the official source code, but the description is relatively easy to understand], set the specified access count to 1. With this setting, animation tasks can only be controlled by one task at a time. If the first task is playing an animation and the second task wants to control the animation, you must wait or skip the task completely.
LinkTask, also known as refresh task, is used in the code. That is, a Task A can be accessed by Task B as a variable of another task B. Task A is the link task of task B. See details https://opsive.com/support/documentation/behavior-designer/referencing-tasks/
In fact, the implementation of TaskGuard is relatively simple, that is, use CanExecute to check whether the execution count is less than the upper limit. In the OnChildStarted function, the count of each Link Task is + 1 In OnEnd function - 1
Maybe it's because of the design. The official TaskGuard is implemented in this way. In fact, under the system of Behavior Tree, it is easy to realize similar functions with shared variable SharedXX or global variable. Even the counting excutingTask is also used as a shared variable
In TaskGurad, the maximum number of times is a shared variable, and the count is a local member variable. The purpose of sharing variables is achieved through OnChildStarted and OnEnd in disguise.
public class TaskGuard : Decorator { [Tooltip("The number of times the child tasks can be accessed by parallel tasks at once")] public SharedInt maxTaskAccessCount; [Tooltip("The linked tasks that also guard a task. If the task guard is not linked against any other tasks it doesn't have much purpose. Marked as LinkedTask to " + "ensure all tasks linked are linked to the same set of tasks")] [LinkedTask] public TaskGuard[] linkedTaskGuards = null; [Tooltip("If true the task will wait until the child task is available. If false then any unavailable child tasks will be skipped over")] public SharedBool waitUntilTaskAvailable; // The number of tasks that are currently using a particular task. private int executingTasks = 0; // True if the current task is executing. private bool executing = false; public override bool CanExecute() { // The child task can execute if the number of executing tasks is less than the maximum number of tasks allowed. return executingTasks < maxTaskAccessCount.Value && !executing; } public override void OnChildStarted() { // The child task has started to run. Increase the executing tasks counter and notify all of the other linked tasks. executingTasks++; executing = true; for (int i = 0; i < linkedTaskGuards.Length; ++i) { linkedTaskGuards[i].taskExecuting(true); } } public override TaskStatus OverrideStatus(TaskStatus status) { // return a running status if the children are currently waiting for a task to become available return (!executing && waitUntilTaskAvailable.Value) ? TaskStatus.Running : status; } public void taskExecuting(bool increase) { // A linked task is currently executing a task that is being guarded. If the task just started executing then increase will be true and if it is ending then // true will be false. executingTasks += (increase ? 1 : -1); } public override void OnEnd() { // The child task has been executed or skipped over. Only decrement the executing tasks count if the child task was being executed. Following that // notify all of the linked tasks that we are done executing. if (executing) { executingTasks--; for (int i = 0; i < linkedTaskGuards.Length; ++i) { linkedTaskGuards[i].taskExecuting(false); } executing = false; } } public override void OnReset() { // Reset the public properties back to their original values maxTaskAccessCount = null; linkedTaskGuards = null; waitUntilTaskAvailable = true; } }