/// <summary> /// Deep copies the tree. /// Make sure that the original behaviour tree has its pre-orders calculated. /// </summary> /// <param name="originalBT"></param> /// <returns></returns> public static BehaviourTree Clone(BehaviourTree originalBT) { var cloneBt = Instantiate(originalBT); if (originalBT._blackboard) { cloneBt._blackboard = Instantiate(originalBT._blackboard); } cloneBt.allNodes.Clear(); Action <BehaviourNode> copier = (originalNode) => { var nodeCopy = Instantiate(originalNode); // Linke the root copy. if (originalBT.Root == originalNode) { cloneBt.Root = nodeCopy; } // Nodes will be added in pre-order. nodeCopy.ClearTree(); nodeCopy.Tree = cloneBt; }; // Traversing in tree order will make sure that the runtime tree has its nodes properly sorted // in pre-order and will also make sure that dangling nodes are left out (unconnected nodes from the editor). TreeIterator <BehaviourNode> .Traverse(originalBT.Root, copier); // At this point the clone BT has its children in pre order order // and the original BT has pre-order indices calculated for each node. // // RELINK children and parent associations of the cloned nodes. // The clone node count is <= original node count because the editor may have dangling nodes. int maxCloneNodeCount = cloneBt.allNodes.Count; for (int i = 0; i < maxCloneNodeCount; ++i) { BehaviourNode originalNode = originalBT.allNodes[i]; BehaviourNode originalParent = originalNode.Parent; if (originalParent) { BehaviourNode copyNode = GetInstanceVersion(cloneBt, originalNode); BehaviourNode copyParent = GetInstanceVersion(cloneBt, originalParent); copyParent.ForceSetChild(copyNode); } } for (int i = 0; i < maxCloneNodeCount; ++i) { cloneBt.allNodes[i].OnCopy(); } IncludeTreeReferences(cloneBt); return(cloneBt); }
/// <summary> /// Ticks the iterator. /// </summary> public void Update() { CallOnEnterOnQueuedNodes(); TickBranch(); int index = _traversal.Peek(); BehaviourNode node = _tree.allNodes[index]; LastStatusReturned = node.Run(); #if UNITY_EDITOR node.SetStatusEditor(LastStatusReturned); #endif if (LastStatusReturned != BehaviourNode.Status.Running) { PopNode(); CallOnChildExit(node); } if (_traversal.Count == 0) { OnDone(); } }
/// <summary> /// Tells the iterator to abort the current running subtree and jump to the aborter. /// </summary> /// <param name="aborter"></param> public void OnAbort(ConditionalAbort aborter) { BehaviourNode parent = aborter.Parent; int terminatingIndex = BehaviourNode.kInvalidOrder; if (parent) { terminatingIndex = parent.preOrderIndex; } // If an abort node is the root, then we need to empty the entire traversal. // We can achieve this by setting the terminating index to the invalid index, which is an invalid index // and will empty the traversal. while (_traversal.Count != 0 && _traversal.Peek() != terminatingIndex) { StepBackAbort(); } // Only composite nodes need to worry about which of their subtrees fired an abort. if (parent.MaxChildCount() > 1) { parent.OnAbort(aborter); } // Any requested traversals are cancelled on abort. _requestedTraversals.Clear(); Traverse(aborter); }
/// <summary> /// Ticks the iterator. /// </summary> public void Update() { CallOnEnterOnQueuedNodes(); int index = traversal.Peek(); BehaviourNode node = tree.Nodes[index]; BehaviourNode.Status s = node.Run(); LastExecutedStatus = s; #if UNITY_EDITOR node.StatusEditorResult = (BehaviourNode.StatusEditor)s; #endif if (s != BehaviourNode.Status.Running) { PopNode(); OnChildExit(node, s); } if (traversal.Count == 0) { OnDone(); } }
/// <summary> /// Test if the aborter may abort the node. /// Make sure that the node orders are pre-computed before calling this function. /// This method is mainly used by the editor. /// </summary> /// <param name="aborter">The node to perform the abort.</param> /// <param name="node">The node that gets aborted.</param> /// <returns></returns> public static bool IsAbortable(ConditionalAbort aborter, BehaviourNode node) { // This makes sure that dangling nodes do not show that they can abort nodes under main tree. if (aborter.preOrderIndex == kInvalidOrder) { return(false); } // Parallel subtrees cannot abort each other. if (aborter.Parent && aborter.Parent is Parallel) { return(false); } switch (aborter.abortType) { case AbortType.LowerPriority: return (!BehaviourTree.IsUnderSubtree(aborter, node) && BehaviourTree.IsUnderSubtree(aborter.Parent, node) && aborter.Priority() > GetSubtree(aborter.Parent, node).Priority()); // Self aborts always interrupt, regardless of the condition. case AbortType.Self: return(BehaviourTree.IsUnderSubtree(aborter, node)); case AbortType.Both: return (BehaviourTree.IsUnderSubtree(aborter, node) || (BehaviourTree.IsUnderSubtree(aborter.Parent, node) && aborter.Priority() > GetSubtree(aborter.Parent, node).Priority())); } return(false); }
/// <summary> /// Adds a child if it is parentless. /// </summary> /// <param name="child"></param> public sealed override void AddChild(BehaviourNode child) { if (child == null) { Debug.LogWarning("Child is null"); return; } if (child == this) { Debug.LogWarning("A child cannot be its own child."); return; } if (child.Parent == null) { child.Parent = this; child._indexOrder = _children.Count; _children.Add(child); } else { Debug.LogWarning("Composite node attempted to parent a child that already has a set parent."); } }
private void CallOnChildEnter(BehaviourNode node) { if (node.Parent) { node.Parent.OnChildEnter(node._indexOrder); } }
/// <summary> /// Removes the child from its children, if it is the parent of the child. /// </summary> /// <param name="child"></param> public sealed override void RemoveChild(BehaviourNode child) { if (child == null) { return; } // Assure that this child was actually parented to this composite node. if (child.Parent == this) { // Forget about this child. bool bRemoved = _children.Remove(child); // If removed then we unparent the child. if (bRemoved) { child._indexOrder = 0; child._parent = null; UpdateIndexOrders(); } // BIG ERROR. This should not happen. // The theory is that the only way for a child to have its parent set if it was null // which gets handled internally by the standard node types: Composite and Decorator. else { const string msg1 = "Error on CompositeNode.Remove(child). "; const string msg2 = "A child was parented to a composite node but was not found in the children list. "; const string msg3 = "This should not have happend."; Debug.LogError(String.Concat(msg1, msg2, msg3)); } } }
public void Interrupt(BehaviourNode subroot, bool bFullInterrupt = false) { // Interrupt this subtree. subroot.Iterator.StepBackInterrupt(subroot, bFullInterrupt); // Look for parallel nodes under the subroot. // Since the parallel count is usually small, we // can just do a linear iteration to interrupt multiple // parallel nodes. for (int pIndex = 0; pIndex < _parallelNodeCount; ++pIndex) { Parallel p = _parallelNodes[pIndex]; if (IsUnderSubtree(subroot, p)) { for (int itrIndex = 0; itrIndex < p.ChildCount(); ++itrIndex) { BehaviourIterator itr = p.GetIterator(itrIndex); // Only interrupt running iterators. if (itr.IsRunning) { // Get the child of the parallel node, and interrupt the child subtree. int childIndex = itr.FirstInTraversal; BehaviourNode firstNode = allNodes[childIndex]; itr.StepBackInterrupt(firstNode.Parent, bFullInterrupt); } } } } }
private void OnChildExit(BehaviourNode node, BehaviourNode.Status s) { if (node.Parent) { node.Parent.OnChildExit(node.indexOrder, s); LastChildExitStatus = s; } }
private void OnChildEnter(BehaviourNode node) { if (node.Parent) { LastChildExitStatus = null; node.Parent.OnChildEnter(node.indexOrder); } }
/// <summary> /// Gets the subtree that is running under a parent. /// This does not work directly under parallel nodes since they use their own iterator. /// </summary> /// <param name="parent"></param> /// <returns></returns> public BehaviourNode GetRunningSubtree(BehaviourNode parent) { int parentIndexInTraversal = GetIndexInTraversal(parent); int subtreeIndexInTraversal = parentIndexInTraversal + 1; int subtreePreOrder = _traversal.GetValue(subtreeIndexInTraversal); return(_tree.allNodes[subtreePreOrder]); }
/// <summary> /// <para>Set the child for the decorator node.</para> /// <para> /// This should be called <b>once</b> when the tree is being built, /// before Tree Start() and never during Tree Update() /// </para> /// </summary> public void SetChild(BehaviourNode node) { child = node; if (child != null) { child.Parent = this; child.indexOrder = 0; } }
private static BehaviourNode GetSubtree(BehaviourNode parent, BehaviourNode grandchild) { BehaviourNode sub = grandchild; while (sub.Parent != parent) { sub = sub.Parent; } return(sub); }
private void CallOnEnterOnQueuedNodes() { // Make sure to call on enter on any queued new traversals. while (requestedTraversals.Count != 0) { int i = requestedTraversals.Dequeue(); BehaviourNode node = tree.Nodes[i]; node.OnEnter(); OnChildEnter(node); } }
/// <summary> /// Tests if node is under the root tree. /// </summary> /// <param name="root"></param> /// <param name="node"></param> /// <returns></returns> public static bool IsUnderSubtree(BehaviourNode root, BehaviourNode node) { // Assume that this is the root of the tree root. // This would happen when checking IsUnderSubtree(node.parent, other) if (root == null) { return(true); } return(root.PostOrderIndex > node.PostOrderIndex && root.PreOrderIndex < node.PreOrderIndex); }
/// <summary> /// DANGER! /// Directly sets the child (at its relative index. /// This is used to help clone nodes. /// </summary> /// <param name="child"></param> public sealed override void ForceSetChild(BehaviourNode child) { child.ClearParent(); child.Parent = this; // Do not bother with unsetting the original child's parent. // The original child is already properly setup in its tree. // This is used when trying to build a tree copy, so we can // simply set a new child at that index. _children[child.ChildOrder] = child; }
/// <summary> /// Requests the iterator to traverse a new node. /// </summary> /// <param name="next"></param> public void Traverse(BehaviourNode next) { int index = next.preOrderIndex; traversal.Push(index); requestedTraversals.Enqueue(index); #if UNITY_EDITOR next.StatusEditorResult = BehaviourNode.StatusEditor.Running; #endif }
private void stepBackAbort() { int index = _traversal.Pop(); BehaviourNode node = _tree.allNodes[index]; node.OnExit(); #if UNITY_EDITOR node.SetStatusEditor(BehaviourNode.StatusEditor.Aborted); #endif }
// Editor only helper method. // Clears all nodes, children, and sets the tree reference to null. public void ClearStructure() { foreach (BehaviourNode node in allNodes) { node.ClearChildren(); node.ClearTree(); } allNodes.Clear(); _root = null; }
private BehaviourNode PreOrderNext() { BehaviourNode current = traversal.Pop(); for (int i = current.ChildCount() - 1; i >= 0; --i) { BehaviourNode child = current.GetChildAt(i); traversal.Push(child); } return(current); }
/// <summary> /// DANGER! /// Directly sets the child. /// This is used to help clone nodes. /// </summary> /// <param name="child"></param> public sealed override void ForceSetChild(BehaviourNode child) { child.ClearParent(); // Do not bother with unsetting the original child's parent. // The original child is already properly setup in its tree. // This is used when trying to build a tree copy, so we can // simply null the reference and set a new child. _child = null; AddChild(child); }
/// <summary> /// Sums the utility values of the traversed branch. /// </summary> /// <param name="root"></param> /// <param name="initial"></param> /// <returns></returns> public float SumUtility(BehaviourNode root, float initial = 0f) { traversal.Push(root); while (HasNext()) { var node = Next(); initial += node.UtilityValue(); } traversal.Clear(); return(initial); }
/// <summary> /// Gets the maximum maximum for the traversed branch. /// </summary> /// <param name="root"></param> /// <param name="initial"></param> /// <note>int.MinValue is used because that is lowest possible pre order priority value.</note> /// <returns></returns> public float MaxUtility(BehaviourNode root, float initial = int.MinValue) { traversal.Push(root); while (HasNext()) { var node = Next(); initial = Math.Max(initial, node.UtilityValue()); } traversal.Clear(); return(initial); }
/// <summary> /// Requests the iterator to traverse a new node. /// </summary> /// <param name="next"></param> public void Traverse(BehaviourNode next) { int index = next.preOrderIndex; _traversal.Push(index); _requestedTraversals.Enqueue(index); LastStatusReturned = BehaviourNode.Status.Running; #if UNITY_EDITOR next.SetStatusEditor(BehaviourNode.Status.Running); #endif }
private void ClearChildrenStructure(BehaviourNode node) { if (node.IsComposite()) { var composite = node as Composite; composite.SetChildren(new BehaviourNode[] { }); } else if (node.IsDecorator()) { var decorator = node as Decorator; decorator.SetChild(null); } }
private static void GetCompositeParent( BehaviourNode child, out BehaviourNode compositeParent, out int branchIndex) { compositeParent = child.Parent; branchIndex = child.indexOrder; while (compositeParent && !compositeParent.IsComposite()) { branchIndex = compositeParent.indexOrder; compositeParent = compositeParent.Parent; } }
/// <summary> /// Removes the child, if it is the parent of the child. /// </summary> /// <param name="child"></param> public sealed override void RemoveChild(BehaviourNode child) { // Cannot be null and child must match. if (_child == null || _child != child) { return; } // Unparent the child. _child._parent = null; // This decorator forgets about its child. _child = null; }
private void CallOnChildExit(BehaviourNode node) { // If this is not a root node, then notify the parent about the child finishing. if (_traversal.Count > 0) { node.Parent.OnChildExit(node._indexOrder, LastStatusReturned); } // If this was a subtree under a parallel node, then notify its parent. else if (node.Parent && _tree.IsParallelNode(node.Parent)) { node.Parent.OnChildExit(node._indexOrder, LastStatusReturned); } }
private BehaviourNode PopNode() { int index = traversal.Pop(); BehaviourNode node = tree.Nodes[index]; if (node.IsComposite()) { for (int i = 0; i < node.ChildCount(); i++) { node.GetChildAt(i).OnCompositeParentExit(); } } node.OnExit(); return(node); }