OpenXR开发实战项目之VR BehaviorDesinger插件制作AI行为树

一、框架视图

二、关键代码

myCanSeeObject

using BehaviorDesigner.Runtime.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviorDesigner.Runtime;
//这个脚本是用来判断目标是否在视野内
public class myCanSeeObject : Conditional
{
    public Transform[] targets;
    public float filedOfViewAngle = 90;
    public float viewDistance = 7;

    public SharedTransform target;

    public override TaskStatus OnUpdate()
    {
        if (targets == null)
        {
            return TaskStatus.Failure;
        }

        foreach (var target in targets)
        {
            float distance = (target.position - transform.position).magnitude;
            float angle = Vector3.Angle(transform.forward, target.position - transform.position);
            if (distance < viewDistance && angle < filedOfViewAngle)
            {
                this.target.Value = target;
                return TaskStatus.Success;
            }
        }
        return TaskStatus.Failure;
    }
}

mySeek

using BehaviorDesigner.Runtime.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviorDesigner.Runtime;
//这个脚本的作用就是控制游戏物体到达目标位置
public class mySeek : Action //这个任务的调用都是由Behavior Desinger行为树去控制的
{
    public float speed;
    public SharedTransform target; //要到达的目标位置
    public float arriveDistance = 0.1f;
    private float sqrArriveDistance;
    public override void OnStart()
    {
        sqrArriveDistance = arriveDistance * arriveDistance;
    }
    //当进入到这个任务的时候,就会调用这个方法,一直到任务结束,会进行一些位置的计算
    //这个方法的调用频率,默认是和unity里面的帧率保持一致
    public override TaskStatus OnUpdate()
    {
        //安全校验
        if(target == null || target.Value == null)
        {
            return TaskStatus.Failure;
        }
        transform.LookAt(target.Value.position);//直接朝向目标位置
        transform.position = Vector3.MoveTowards(transform.position, target.Value.position, speed*Time.deltaTime);
        if((target.Value.position - transform.position).sqrMagnitude<arriveDistance * sqrArriveDistance)
        {
            return TaskStatus.Success; //如果距离目标位置的距离比较小,认为到达了目标
        }
        return TaskStatus.Running;
}
}


PerformInterruption

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Perform the actual interruption. This will immediately stop the specified tasks from running and will return success or failure depending on the value of interrupt success.")]
    [TaskIcon("{SkinColor}PerformInterruptionIcon.png")]
    public class PerformInterruption : Action
    {
        [Tooltip("The list of tasks to interrupt. Can be any number of tasks")]
        public Interrupt[] interruptTasks;
        [Tooltip("When we interrupt the task should we return a task status of success?")]
        public SharedBool interruptSuccess;

        public override TaskStatus OnUpdate()
        {
            // Loop through all of the tasks and fire an interruption. Once complete return success.
            for (int i = 0; i < interruptTasks.Length; ++i) {
                interruptTasks[i].DoInterrupt(interruptSuccess.Value ? TaskStatus.Success : TaskStatus.Failure);
            }
            return TaskStatus.Success;
        }

        public override void OnReset()
        {
            // Reset the properties back to their original values.
            interruptTasks = null;
            interruptSuccess = false;
        }
    }
}

RestartBehaviorTree

using UnityEngine;

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Restarts a behavior tree, returns success after it has been restarted.")]
    [TaskIcon("{SkinColor}RestartBehaviorTreeIcon.png")]
    public class RestartBehaviorTree : Action
    {
        [Tooltip("The GameObject of the behavior tree that should be restarted. If null use the current behavior")]
        public SharedGameObject behaviorGameObject;
        [Tooltip("The group of the behavior tree that should be restarted")]
        public SharedInt group;

        private Behavior behavior;

        public override void OnAwake()
        {
            var behaviorTrees = GetDefaultGameObject(behaviorGameObject.Value).GetComponents<Behavior>();
            if (behaviorTrees.Length == 1) {
                behavior = behaviorTrees[0];
            } else if (behaviorTrees.Length > 1) {
                for (int i = 0; i < behaviorTrees.Length; ++i) {
                    if (behaviorTrees[i].Group == group.Value) {
                        behavior = behaviorTrees[i];
                        break;
                    }
                }
                // If the group can't be found then use the first behavior tree
                if (behavior == null) {
                    behavior = behaviorTrees[0];
                }
            }
        }

        public override TaskStatus OnUpdate()
        {
            if (behavior == null) {
                return TaskStatus.Failure;
            }

            // Stop the behavior tree
            behavior.DisableBehavior();
            // Start the behavior tree back up
            behavior.EnableBehavior();
            // Return success
            return TaskStatus.Success;
        }

        public override void OnReset()
        {
            // Reset the properties back to their original values.
            behavior = null;
        }
    }
}

StackedAction

using UnityEngine;

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Allows multiple action tasks to be added to a single node.")]
    [TaskIcon("{SkinColor}StackedActionIcon.png")]
    public class StackedAction : Action
    {
        [InspectTask]
        public Action[] actions;
        public enum ComparisonType
        {
            Sequence,
            Selector
        }
        [Tooltip("Specifies if the tasks should be traversed with an AND (Sequence) or an OR (Selector).")]
        public ComparisonType comparisonType;
        [Tooltip("Should the tasks be labeled within the graph?")]
        public bool graphLabel;

        public override void OnAwake()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }

                actions[i].GameObject = gameObject;
                actions[i].Transform = transform;
                actions[i].Owner = Owner;
#if UNITY_EDITOR || DLL_RELEASE || DLL_DEBUG
                actions[i].NodeData = new NodeData();
#endif
                actions[i].OnAwake();
            }
        }

        public override void OnStart()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnStart();
            }
        }

        public override TaskStatus OnUpdate()
        {
            if (actions == null) {
                return TaskStatus.Failure;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                var executionStatus = actions[i].OnUpdate();
#if UNITY_EDITOR || DLL_RELEASE || DLL_DEBUG
                actions[i].NodeData.ExecutionStatus = executionStatus;
                if (actions[i].NodeData.ExecutionStatus == TaskStatus.Running) {
                    Debug.LogWarning("Warning: The action task returned a status of running when action tasks should only return success or failure.");
                }
#endif
                if (comparisonType == ComparisonType.Sequence && executionStatus == TaskStatus.Failure) {
                    return TaskStatus.Failure;
                } else if (comparisonType == ComparisonType.Selector && executionStatus == TaskStatus.Success) {
                    return TaskStatus.Success;
                }
            }
            return comparisonType == ComparisonType.Sequence ? TaskStatus.Success : TaskStatus.Failure;
        }

        public override void OnFixedUpdate()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnFixedUpdate();
            }
        }

        public override void OnLateUpdate()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnLateUpdate();
            }
        }

        public override void OnEnd()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnEnd();
            }
        }

        public override void OnTriggerEnter(Collider other)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnTriggerEnter(other);
            }
        }

        public override void OnTriggerEnter2D(Collider2D other)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnTriggerEnter2D(other);
            }
        }

        public override void OnTriggerExit(Collider other)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnTriggerExit(other);
            }
        }

        public override void OnTriggerExit2D(Collider2D other)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnTriggerExit2D(other);
            }
        }

        public override void OnCollisionEnter(Collision collision)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnCollisionEnter(collision);
            }
        }

        public override void OnCollisionEnter2D(Collision2D collision)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnCollisionEnter2D(collision);
            }
        }

        public override void OnCollisionExit(Collision collision)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnCollisionExit(collision);
            }
        }

        public override void OnCollisionExit2D(Collision2D collision)
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnCollisionExit2D(collision);
            }
        }

        public override string OnDrawNodeText()
        {
            if (actions == null || !graphLabel) {
                return string.Empty;
            }

            var text = string.Empty;
            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                if (!string.IsNullOrEmpty(text)) {
                    text += "\n";
                }
                text += actions[i].GetType().Name;
            }

            return text;
        }

        public override void OnReset()
        {
            if (actions == null) {
                return;
            }

            for (int i = 0; i < actions.Length; ++i) {
                if (actions[i] == null) {
                    continue;
                }
                actions[i].OnReset();
            }
        }
    }
}

ParallelComplete

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Similar to the parallel selector task, except the parallel complete task will return the child status as soon as the child returns success or failure." + 
                     "The child tasks are executed simultaneously.")]
    [TaskIcon("{SkinColor}ParallelCompleteIcon.png")]
    public class ParallelComplete : Composite
    {
        // The index of the child that is currently running or is about to run.
        private int currentChildIndex;
        // The task status of every child task.
        private TaskStatus[] executionStatus;

        public override void OnAwake()
        {
            // Create a new task status array that will hold the execution status of all of the children tasks.
            executionStatus = new TaskStatus[children.Count];
        }

        public override void OnChildStarted(int childIndex)
        {
            // One of the children has started to run. Increment the child index and set the current task status of that child to running.
            currentChildIndex++;
            executionStatus[childIndex] = TaskStatus.Running;
        }

        public override bool CanRunParallelChildren()
        {
            // This task can run parallel children.
            return true;
        }

        public override int CurrentChildIndex()
        {
            return currentChildIndex;
        }

        public override bool CanExecute()
        {
            // We can continue executing if we have more children that haven't been started yet.
            return currentChildIndex < children.Count;
        }

        public override void OnChildExecuted(int childIndex, TaskStatus childStatus)
        {
            // One of the children has finished running. Set the task status.
            executionStatus[childIndex] = childStatus;
        }

        public override void OnConditionalAbort(int childIndex)
        {
            // Start from the beginning on an abort
            currentChildIndex = 0;
            for (int i = 0; i < executionStatus.Length; ++i) {
                executionStatus[i] = TaskStatus.Inactive;
            }
        }

        public override TaskStatus OverrideStatus(TaskStatus status)
        {
            if (currentChildIndex == 0) {
                return TaskStatus.Success;
            }
            // Return the child task's status as soon as a child task returns success or failure.
            for (int i = 0; i < currentChildIndex; ++i) {
                if (executionStatus[i] == TaskStatus.Success || executionStatus[i] == TaskStatus.Failure) {
                    return executionStatus[i];
                }
            }
            return TaskStatus.Running;
        }

        public override void OnEnd()
        {
            // Reset the execution status and the child index back to their starting values.
            for (int i = 0; i < executionStatus.Length; ++i) {
                executionStatus[i] = TaskStatus.Inactive;
            }
            currentChildIndex = 0;
        }
    }
}

RandomSelector

using UnityEngine;
using System.Collections.Generic;

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Similar to the selector task, the random selector task will return success as soon as a child task returns success.  " +
                     "The difference is that the random selector class will run its children in a random order. The selector task is deterministic " +
                     "in that it will always run the tasks from left to right within the tree. The random selector task shuffles the child tasks up and then begins " +
                     "execution in a random order. Other than that the random selector class is the same as the selector class. It will continue running tasks " +
                     "until a task completes successfully. If no child tasks return success then it will return failure.")]
    [TaskIcon("{SkinColor}RandomSelectorIcon.png")]
    public class RandomSelector : Composite
    {
        [Tooltip("Seed the random number generator to make things easier to debug")]
        public int seed = 0;
        [Tooltip("Do we want to use the seed?")]
        public bool useSeed = false;

        // A list of indexes of every child task. This list is used by the Fischer-Yates shuffle algorithm.
        private List<int> childIndexList = new List<int>();
        // The random child index execution order.
        private Stack<int> childrenExecutionOrder = new Stack<int>();
        // The task status of the last child ran.
        private TaskStatus executionStatus = TaskStatus.Inactive;

        public override void OnAwake()
        {
            // If specified, use the seed provided.
            if (useSeed) {
                Random.InitState(seed);
            }

            // Add the index of each child to a list to make the Fischer-Yates shuffle possible.
            childIndexList.Clear();
            for (int i = 0; i < children.Count; ++i) {
                childIndexList.Add(i);
            }
        }

        public override void OnStart()
        {
            // Randomize the indecies
            ShuffleChilden();
        }

        public override int CurrentChildIndex()
        {
            // Peek will return the index at the top of the stack.
            return childrenExecutionOrder.Peek();
        }

        public override bool CanExecute()
        {
            // Continue exectuion if no task has return success and indexes still exist on the stack.
            return childrenExecutionOrder.Count > 0 && executionStatus != TaskStatus.Success;
        }

        public override void OnChildExecuted(TaskStatus childStatus)
        {
            // Pop the top index from the stack and set the execution status.
            if (childrenExecutionOrder.Count > 0) {
                childrenExecutionOrder.Pop();
            }
            executionStatus = childStatus;
        }

        public override void OnConditionalAbort(int childIndex)
        {
            // Start from the beginning on an abort
            childrenExecutionOrder.Clear();
            executionStatus = TaskStatus.Inactive;
            ShuffleChilden();
        }

        public override void OnEnd()
        {
            // All of the children have run. Reset the variables back to their starting values.
            executionStatus = TaskStatus.Inactive;
            childrenExecutionOrder.Clear();
        }

        public override void OnReset()
        {
            // Reset the public properties back to their original values
            seed = 0;
            useSeed = false;
        }

        private void ShuffleChilden()
        {
            // Use Fischer-Yates shuffle to randomize the child index order.
            for (int i = childIndexList.Count; i > 0; --i) {
                int j = Random.Range(0, i);
                int index = childIndexList[j];
                childrenExecutionOrder.Push(index);
                childIndexList[j] = childIndexList[i - 1];
                childIndexList[i - 1] = index;
            }
        }
    }
}

Sequence

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("The sequence task is similar to an \"and\" operation. It will return failure as soon as one of its child tasks return failure. " +
                     "If a child task returns success then it will sequentially run the next task. If all child tasks return success then it will return success.")]
    [TaskIcon("{SkinColor}SequenceIcon.png")]
    public class Sequence : Composite
    {
        // The index of the child that is currently running or is about to run.
        private int currentChildIndex = 0;
        // The task status of the last child ran.
        private TaskStatus executionStatus = TaskStatus.Inactive;

        public override int CurrentChildIndex()
        {
            return currentChildIndex;
        }

        public override bool CanExecute()
        {
            // We can continue to execuate as long as we have children that haven't been executed and no child has returned failure.
            return currentChildIndex < children.Count && executionStatus != TaskStatus.Failure;
        }

        public override void OnChildExecuted(TaskStatus childStatus)
        {
            // Increase the child index and update the execution status after a child has finished running.
            currentChildIndex++;
            executionStatus = childStatus;
        }

        public override void OnConditionalAbort(int childIndex)
        {
            // Set the current child index to the index that caused the abort
            currentChildIndex = childIndex;
            executionStatus = TaskStatus.Inactive;
        }

        public override void OnEnd()
        {
            // All of the children have run. Reset the variables back to their starting values.
            executionStatus = TaskStatus.Inactive;
            currentChildIndex = 0;
        }
    }
}

StackedConditional

using UnityEngine;

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Allows multiple conditional tasks to be added to a single node.")]
    [TaskIcon("{SkinColor}StackedConditionalIcon.png")]
    public class StackedConditional : Conditional
    {
        [InspectTask]
        public Conditional[] conditionals;
        public enum ComparisonType
        {
            Sequence,
            Selector
        }
        [Tooltip("Specifies if the tasks should be traversed with an AND (Sequence) or an OR (Selector).")]
        public ComparisonType comparisonType;
        [Tooltip("Should the tasks be labeled within the graph?")]
        public bool graphLabel;

        public override void OnAwake()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }

                conditionals[i].GameObject = gameObject;
                conditionals[i].Transform = transform;
                conditionals[i].Owner = Owner;
#if UNITY_EDITOR || DLL_RELEASE || DLL_DEBUG
                conditionals[i].NodeData = new NodeData();
#endif
                conditionals[i].OnAwake();
            }
        }

        public override void OnStart()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnStart();
            }
        }

        public override TaskStatus OnUpdate()
        {
            if (conditionals == null) {
                return TaskStatus.Failure;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                var executionStatus = conditionals[i].OnUpdate();
#if UNITY_EDITOR || DLL_RELEASE || DLL_DEBUG
                conditionals[i].NodeData.ExecutionStatus = executionStatus;
                if (conditionals[i].NodeData.ExecutionStatus == TaskStatus.Running) {
                    Debug.LogWarning("Warning: The conditional task returned a status of running when conditional tasks should only return success or failure.");
                }
#endif
                if (comparisonType == ComparisonType.Sequence && executionStatus == TaskStatus.Failure) {
                    return TaskStatus.Failure;
                } else if (comparisonType == ComparisonType.Selector && executionStatus == TaskStatus.Success) {
                    return TaskStatus.Success;
                }
            }
            return comparisonType == ComparisonType.Sequence ? TaskStatus.Success : TaskStatus.Failure;
        }

        public override void OnFixedUpdate()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnFixedUpdate();
            }
        }

        public override void OnLateUpdate()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnLateUpdate();
            }
        }

        public override void OnEnd()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnEnd();
            }
        }

        public override void OnTriggerEnter(Collider other)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnTriggerEnter(other);
            }
        }

        public override void OnTriggerEnter2D(Collider2D other)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnTriggerEnter2D(other);
            }
        }

        public override void OnTriggerExit(Collider other)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnTriggerExit(other);
            }
        }

        public override void OnTriggerExit2D(Collider2D other)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnTriggerExit2D(other);
            }
        }

        public override void OnCollisionEnter(Collision collision)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnCollisionEnter(collision);
            }
        }

        public override void OnCollisionEnter2D(Collision2D collision)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnCollisionEnter2D(collision);
            }
        }

        public override void OnCollisionExit(Collision collision)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnCollisionExit(collision);
            }
        }

        public override void OnCollisionExit2D(Collision2D collision)
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnCollisionExit2D(collision);
            }
        }

        public override string OnDrawNodeText()
        {
            if (conditionals == null || !graphLabel) {
                return string.Empty;
            }

            var text = string.Empty;
            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                if (!string.IsNullOrEmpty(text)) {
                    text += "\n";
                }
                text += conditionals[i].GetType().Name;
            }

            return text;
        }

        public override void OnReset()
        {
            if (conditionals == null) {
                return;
            }

            for (int i = 0; i < conditionals.Length; ++i) {
                if (conditionals[i] == null) {
                    continue;
                }
                conditionals[i].OnReset();
            }
        }
    }
}

ConditionalEvaluator

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("Evaluates the specified conditional task. If the conditional task returns success then the child task is run and the child status is returned. If the conditional task does not " +
                     "return success then the child task is not run and a failure status is immediately returned.")]
    [TaskIcon("{SkinColor}ConditionalEvaluatorIcon.png")]
    public class ConditionalEvaluator : Decorator
    {
        [Tooltip("Should the conditional task be reevaluated every tick?")]
        public SharedBool reevaluate;
        [InspectTask]
        [Tooltip("The conditional task to evaluate")]
        public Conditional conditionalTask;
        [Tooltip("Should the inspected conditional task be labeled within the graph?")]
        public bool graphLabel;

        // The status of the child after it has finished running.
        private TaskStatus executionStatus = TaskStatus.Inactive;
        private bool checkConditionalTask = true;
        private bool conditionalTaskFailed = false;

        public override void OnAwake()
        {
            if (conditionalTask != null) {
                conditionalTask.Owner = Owner;
                conditionalTask.GameObject = gameObject;
                conditionalTask.Transform = transform;
                conditionalTask.OnAwake();
            }
        }

        public override void OnStart()
        {
            if (conditionalTask != null) {
                conditionalTask.OnStart();
            }
        }

        public override bool CanExecute()
        {
            // CanExecute is called when checking the condition within a while loop so it will be called at least twice. Ensure the conditional task is checked only once
            if (checkConditionalTask) {
                checkConditionalTask = false;
                OnUpdate();
            }

            if (conditionalTaskFailed) {
                return false;
            }
            return executionStatus == TaskStatus.Inactive || executionStatus == TaskStatus.Running;
        }

        public override bool CanReevaluate()
        {
            return reevaluate.Value;
        }

        public override TaskStatus OnUpdate()
        {
            var childStatus = conditionalTask.OnUpdate();
            conditionalTaskFailed = conditionalTask == null || childStatus == TaskStatus.Failure;
            return childStatus;
        }

        public override void OnChildExecuted(TaskStatus childStatus)
        {
            // Update the execution status after a child has finished running.
            executionStatus = childStatus;
        }

        public override TaskStatus OverrideStatus()
        {
            // This version of OverrideStatus is called when the conditional evaluator fails reevaluation and has to stop all of its children.
            // Therefore, the return status will always be failure
            return TaskStatus.Failure;
        }

        public override TaskStatus OverrideStatus(TaskStatus status)
        {
            if (conditionalTaskFailed)
                return TaskStatus.Failure;
            return status;
        }

        public override void OnEnd()
        {
            // Reset the variables back to their starting values.
            executionStatus = TaskStatus.Inactive;
            checkConditionalTask = true;
            conditionalTaskFailed = false;
            if (conditionalTask != null) {
                conditionalTask.OnEnd();
            }
        }

        public override string OnDrawNodeText()
        {
            if (conditionalTask == null || !graphLabel) {
                return string.Empty;
            }

            return conditionalTask.GetType().Name;
        }

        public override void OnReset()
        {
            // Reset the public properties back to their original values.
            conditionalTask = null;
        }
    }
}

ReturnSuccess

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("The return success task will always return success except when the child task is running.")]
    [TaskIcon("{SkinColor}ReturnSuccessIcon.png")]
    public class ReturnSuccess : Decorator
    {
        // The status of the child after it has finished running.
        private TaskStatus executionStatus = TaskStatus.Inactive;

        public override bool CanExecute()
        {
            // Continue executing until the child task returns success or failure.
            return executionStatus == TaskStatus.Inactive || executionStatus == TaskStatus.Running;
        }

        public override void OnChildExecuted(TaskStatus childStatus)
        {
            // Update the execution status after a child has finished running.
            executionStatus = childStatus;
        }

        public override TaskStatus Decorate(TaskStatus status)
        {
            // Return success even if the child task returned failure.
            if (status == TaskStatus.Failure) {
                return TaskStatus.Success;
            }
            return status;
        }

        public override void OnEnd()
        {
            // Reset the execution status back to its starting values.
            executionStatus = TaskStatus.Inactive;
        }
    }
}

UntilSuccess

namespace BehaviorDesigner.Runtime.Tasks
{
    [TaskDescription("The until success task will keep executing its child task until the child task returns success.")]
    [TaskIcon("{SkinColor}UntilSuccessIcon.png")]
    public class UntilSuccess : Decorator
    {
        // The status of the child after it has finished running.
        private TaskStatus executionStatus = TaskStatus.Inactive;

        public override bool CanExecute()
        {
            // Keep running until the child task returns success.
            return executionStatus == TaskStatus.Failure || executionStatus == TaskStatus.Inactive;
        }

        public override void OnChildExecuted(TaskStatus childStatus)
        {
            // Update the execution status after a child has finished running.
            executionStatus = childStatus;
        }

        public override void OnEnd()
        {
            // Reset the execution status back to its starting values.
            executionStatus = TaskStatus.Inactive;
        }
    }
}

GetAcceleration

using UnityEngine;
using UnityEngine.AI;

namespace BehaviorDesigner.Runtime.Tasks.Unity.UnityNavMeshAgent
{
    [TaskCategory("Unity/NavMeshAgent")]
    [TaskDescription("Gets the maximum acceleration of an agent as it follows a path, given in units / sec^2.. Returns Success.")]
    public class GetAcceleration : Action
    {
        [Tooltip("The GameObject that the task operates on. If null the task GameObject is used.")]
        public SharedGameObject targetGameObject;
        [SharedRequired]
        [Tooltip("The NavMeshAgent acceleration")]
        public SharedFloat storeValue;

        // cache the navmeshagent component
        private NavMeshAgent navMeshAgent;
        private GameObject prevGameObject;

        public override void OnStart()
        {
            var currentGameObject = GetDefaultGameObject(targetGameObject.Value);
            if (currentGameObject != prevGameObject) {
                navMeshAgent = currentGameObject.GetComponent<NavMeshAgent>();
                prevGameObject = currentGameObject;
            }
        }

        public override TaskStatus OnUpdate()
        {
            if (navMeshAgent == null) {
                Debug.LogWarning("NavMeshAgent is null");
                return TaskStatus.Failure;
            }

            storeValue.Value = navMeshAgent.acceleration;

            return TaskStatus.Success;
        }

        public override void OnReset()
        {
            targetGameObject = null;
            storeValue = 0;
        }
    }
}

三、效果展示



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容