From 580481142fb5f7f3a9d0510195c2299a287293a3 Mon Sep 17 00:00:00 2001 From: Michael Notarnicola Date: Sun, 11 Sep 2016 00:12:18 -0400 Subject: [PATCH 1/4] Switched to an abstract class based framework. Exposed name and last execution status for building visualization interfaces for trees. --- src/BehaviourTreeBuilder.cs | 33 ++++++++++++++---- src/BehaviourTreeNode.cs | 42 +++++++++++++++++++++++ src/IBehaviourTreeNode.cs | 18 ---------- src/IParentBehaviourTreeNode.cs | 18 ---------- src/Nodes/ActionNode.cs | 18 +++------- src/Nodes/InverterNode.cs | 37 ++++++++------------ src/Nodes/ParallelNode.cs | 36 ++++--------------- src/Nodes/SelectorNode.cs | 38 ++++---------------- src/Nodes/SequenceNode.cs | 38 ++++---------------- src/ParentBehaviourTreeNode.cs | 59 ++++++++++++++++++++++++++++++++ src/fluent-behaviour-tree.csproj | 4 +-- tests/InverterNodeTests.cs | 10 +++--- tests/ParallelNodeTests.cs | 20 +++++------ tests/SelectorNodeTests.cs | 16 ++++----- tests/SequenceNodeTests.cs | 16 ++++----- 15 files changed, 198 insertions(+), 205 deletions(-) create mode 100644 src/BehaviourTreeNode.cs delete mode 100644 src/IBehaviourTreeNode.cs delete mode 100644 src/IParentBehaviourTreeNode.cs create mode 100644 src/ParentBehaviourTreeNode.cs diff --git a/src/BehaviourTreeBuilder.cs b/src/BehaviourTreeBuilder.cs index fc8b9ac..4fec55d 100644 --- a/src/BehaviourTreeBuilder.cs +++ b/src/BehaviourTreeBuilder.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace FluentBehaviourTree { @@ -13,12 +11,12 @@ public class BehaviourTreeBuilder /// /// Last node created. /// - private IBehaviourTreeNode curNode = null; + private BehaviourTreeNode curNode = null; /// /// Stack node nodes that we are build via the fluent API. /// - private Stack parentNodeStack = new Stack(); + private Stack parentNodeStack = new Stack(); /// /// Create an action node. @@ -110,7 +108,7 @@ public BehaviourTreeBuilder Selector(string name) /// /// Splice a sub tree into the parent tree. /// - public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) + public BehaviourTreeBuilder Splice(BehaviourTreeNode subTree) { if (subTree == null) { @@ -129,12 +127,17 @@ public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) /// /// Build the actual tree. /// - public IBehaviourTreeNode Build() + public BehaviourTreeNode Build() { if (curNode == null) { throw new ApplicationException("Can't create a behaviour tree with zero nodes"); } + var curNodeTree = curNode as ParentBehaviourTreeNode; + if (curNodeTree != null) + { + BakeTreeRecursively(curNodeTree); + } return curNode; } @@ -146,5 +149,23 @@ public BehaviourTreeBuilder End() curNode = parentNodeStack.Pop(); return this; } + + /// + /// Bakes all of the tree execution information recursively. + /// + /// + private void BakeTreeRecursively(ParentBehaviourTreeNode curNodeTree) + { + for (int i = 0; i < curNodeTree.childCount; i++) + { + var child = curNodeTree[i]; + child.BakeData(); + var childAsParent = child as ParentBehaviourTreeNode; + if (childAsParent != null) + { + BakeTreeRecursively(childAsParent); + } + } + } } } diff --git a/src/BehaviourTreeNode.cs b/src/BehaviourTreeNode.cs new file mode 100644 index 0000000..2c3cf70 --- /dev/null +++ b/src/BehaviourTreeNode.cs @@ -0,0 +1,42 @@ +namespace FluentBehaviourTree +{ + /// + /// Interface for behaviour tree nodes. + /// + public abstract class BehaviourTreeNode + { + /// + /// The reference name for this node + /// + public string name { get; private set; } + /// + /// The result of the last execution + /// + public BehaviourTreeStatus lastExecutionStatus { get; private set; } + protected bool hasDataBeenBaked { get; private set; } + + public BehaviourTreeNode(string name) + { + this.name = name; + } + + /// + /// Update the time of the behaviour tree. + /// + public BehaviourTreeStatus Tick(TimeData time) + { + lastExecutionStatus = AbstractTick(time); + return lastExecutionStatus; + } + internal void BakeData() + { + if (!hasDataBeenBaked) + { + InternalBakeData(); + hasDataBeenBaked = true; + } + } + protected virtual void InternalBakeData() { } + protected abstract BehaviourTreeStatus AbstractTick(TimeData time); + } +} diff --git a/src/IBehaviourTreeNode.cs b/src/IBehaviourTreeNode.cs deleted file mode 100644 index 9114f9b..0000000 --- a/src/IBehaviourTreeNode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree -{ - /// - /// Interface for behaviour tree nodes. - /// - public interface IBehaviourTreeNode - { - /// - /// Update the time of the behaviour tree. - /// - BehaviourTreeStatus Tick(TimeData time); - } -} diff --git a/src/IParentBehaviourTreeNode.cs b/src/IParentBehaviourTreeNode.cs deleted file mode 100644 index cf5ea86..0000000 --- a/src/IParentBehaviourTreeNode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree -{ - /// - /// Interface for behaviour tree nodes. - /// - public interface IParentBehaviourTreeNode : IBehaviourTreeNode - { - /// - /// Add a child to the parent node. - /// - void AddChild(IBehaviourTreeNode child); - } -} diff --git a/src/Nodes/ActionNode.cs b/src/Nodes/ActionNode.cs index b07b47c..76ff1e9 100644 --- a/src/Nodes/ActionNode.cs +++ b/src/Nodes/ActionNode.cs @@ -1,33 +1,23 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace FluentBehaviourTree { /// /// A behaviour tree leaf node for running an action. /// - public class ActionNode : IBehaviourTreeNode + public class ActionNode : BehaviourTreeNode { - /// - /// The name of the node. - /// - private string name; - /// /// Function to invoke for the action. /// private Func fn; - - public ActionNode(string name, Func fn) + public ActionNode(string name, Func fn) : base(name) { - this.name=name; - this.fn=fn; + this.fn = fn; } - public BehaviourTreeStatus Tick(TimeData time) + protected override BehaviourTreeStatus AbstractTick(TimeData time) { return fn(time); } diff --git a/src/Nodes/InverterNode.cs b/src/Nodes/InverterNode.cs index 4fdd233..edcac1e 100644 --- a/src/Nodes/InverterNode.cs +++ b/src/Nodes/InverterNode.cs @@ -1,31 +1,28 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace FluentBehaviourTree { /// /// Decorator node that inverts the success/failure of its child. /// - public class InverterNode : IParentBehaviourTreeNode + public class InverterNode : ParentBehaviourTreeNode { - /// - /// Name of the node. - /// - private string name; - /// /// The child to be inverted. /// - private IBehaviourTreeNode childNode; - - public InverterNode(string name) - { - this.name = name; + private BehaviourTreeNode childNode { + get { + if (childCount == 0) + { + return null; + } + return this[0]; + } } - public BehaviourTreeStatus Tick(TimeData time) + public InverterNode(string name) : base(name) { } + + protected override BehaviourTreeStatus AbstractTick(TimeData time) { if (childNode == null) { @@ -47,17 +44,13 @@ public BehaviourTreeStatus Tick(TimeData time) } } - /// - /// Add a child to the parent node. - /// - public void AddChild(IBehaviourTreeNode child) + public override void AddChild(BehaviourTreeNode child) { - if (this.childNode != null) + if (childNode != null) { throw new ApplicationException("Can't add more than a single child to InverterNode!"); } - - this.childNode = child; + base.AddChild(child); } } } diff --git a/src/Nodes/ParallelNode.cs b/src/Nodes/ParallelNode.cs index 0021841..3148b4c 100644 --- a/src/Nodes/ParallelNode.cs +++ b/src/Nodes/ParallelNode.cs @@ -1,49 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// Runs childs nodes in parallel. /// - public class ParallelNode : IParentBehaviourTreeNode + public class ParallelNode : ParentBehaviourTreeNode { - /// - /// Name of the node. - /// - private string name; - - /// - /// List of child nodes. - /// - private List children = new List(); - /// /// Number of child failures required to terminate with failure. /// private int numRequiredToFail; - /// /// Number of child successess require to terminate with success. /// private int numRequiredToSucceed; - public ParallelNode(string name, int numRequiredToFail, int numRequiredToSucceed) + public ParallelNode(string name, int numRequiredToFail, int numRequiredToSucceed) : base(name) { - this.name = name; this.numRequiredToFail = numRequiredToFail; this.numRequiredToSucceed = numRequiredToSucceed; } - public BehaviourTreeStatus Tick(TimeData time) + protected override BehaviourTreeStatus AbstractTick(TimeData time) { var numChildrenSuceeded = 0; var numChildrenFailed = 0; - foreach (var child in children) + for (int i = 0; i < childCount; i++) { + var child = this[i]; var childStatus = child.Tick(time); switch (childStatus) { @@ -51,23 +35,15 @@ public BehaviourTreeStatus Tick(TimeData time) case BehaviourTreeStatus.Failure: ++numChildrenFailed; break; } } - if (numRequiredToSucceed > 0 && numChildrenSuceeded >= numRequiredToSucceed) { return BehaviourTreeStatus.Success; } - if (numRequiredToFail > 0 && numChildrenFailed >= numRequiredToFail) { return BehaviourTreeStatus.Failure; } - return BehaviourTreeStatus.Running; } - - public void AddChild(IBehaviourTreeNode child) - { - children.Add(child); - } } } diff --git a/src/Nodes/SelectorNode.cs b/src/Nodes/SelectorNode.cs index de576af..3be28c9 100644 --- a/src/Nodes/SelectorNode.cs +++ b/src/Nodes/SelectorNode.cs @@ -1,50 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// Selects the first node that succeeds. Tries successive nodes until it finds one that doesn't fail. /// - public class SelectorNode : IParentBehaviourTreeNode + public class SelectorNode : ParentBehaviourTreeNode { - /// - /// The name of the node. - /// - private string name; - - /// - /// List of child nodes. - /// - private List children = new List(); //todo: optimization, bake this to an array. - - public SelectorNode(string name) - { - this.name = name; - } + public SelectorNode(string name) : base(name) { } - public BehaviourTreeStatus Tick(TimeData time) + protected override BehaviourTreeStatus AbstractTick(TimeData time) { - foreach (var child in children) + for (int i = 0; i < childCount; i++) { + var child = this[i]; var childStatus = child.Tick(time); if (childStatus != BehaviourTreeStatus.Failure) { return childStatus; } } - return BehaviourTreeStatus.Failure; } - - /// - /// Add a child node to the selector. - /// - public void AddChild(IBehaviourTreeNode child) - { - children.Add(child); - } } } diff --git a/src/Nodes/SequenceNode.cs b/src/Nodes/SequenceNode.cs index e4394e8..fd0127d 100644 --- a/src/Nodes/SequenceNode.cs +++ b/src/Nodes/SequenceNode.cs @@ -1,50 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// Runs child nodes in sequence, until one fails. /// - public class SequenceNode : IParentBehaviourTreeNode + public class SequenceNode : ParentBehaviourTreeNode { - /// - /// Name of the node. - /// - private string name; - - /// - /// List of child nodes. - /// - private List children = new List(); //todo: this could be optimized as a baked array. - - public SequenceNode(string name) - { - this.name = name; - } + public SequenceNode(string name) : base(name) { } - public BehaviourTreeStatus Tick(TimeData time) + protected override BehaviourTreeStatus AbstractTick(TimeData time) { - foreach (var child in children) + for (int i = 0; i < childCount; i++) { + var child = this[i]; var childStatus = child.Tick(time); if (childStatus != BehaviourTreeStatus.Success) { return childStatus; } } - return BehaviourTreeStatus.Success; } - - /// - /// Add a child to the sequence. - /// - public void AddChild(IBehaviourTreeNode child) - { - children.Add(child); - } } } diff --git a/src/ParentBehaviourTreeNode.cs b/src/ParentBehaviourTreeNode.cs new file mode 100644 index 0000000..af8cebf --- /dev/null +++ b/src/ParentBehaviourTreeNode.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; + +namespace FluentBehaviourTree +{ + /// + /// Interface for behaviour tree nodes. + /// + public abstract class ParentBehaviourTreeNode : BehaviourTreeNode + { + /// + /// List of child nodes used for building nodes. + /// + private List _children = new List(); + /// + /// The baked array of children. + /// + private BehaviourTreeNode[] children = new BehaviourTreeNode[1]; + + public int childCount { + get { + if (hasDataBeenBaked) + { + return children.Length; + } + return _children.Count; + } + } + + public ParentBehaviourTreeNode(string name) : base(name) { } + + public BehaviourTreeNode this[int index] { + get { + if (hasDataBeenBaked) + { + return children[index]; + } + return _children[index]; + } + } + + /// + /// Add a child to the parent node. + /// + public virtual void AddChild(BehaviourTreeNode child) + { + if (!hasDataBeenBaked) + { + _children.Add(child); + } + } + + protected override void InternalBakeData() + { + base.InternalBakeData(); + children = _children.ToArray(); + _children.Clear(); + } + } +} diff --git a/src/fluent-behaviour-tree.csproj b/src/fluent-behaviour-tree.csproj index b92435e..381aed2 100644 --- a/src/fluent-behaviour-tree.csproj +++ b/src/fluent-behaviour-tree.csproj @@ -41,8 +41,8 @@ - - + + diff --git a/tests/InverterNodeTests.cs b/tests/InverterNodeTests.cs index c362eb7..c829152 100644 --- a/tests/InverterNodeTests.cs +++ b/tests/InverterNodeTests.cs @@ -34,7 +34,7 @@ public void inverts_success_of_child_node() var time = new TimeData(); - var mockChildNode = new Mock(); + var mockChildNode = new Mock(); mockChildNode .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); @@ -53,7 +53,7 @@ public void inverts_failure_of_child_node() var time = new TimeData(); - var mockChildNode = new Mock(); + var mockChildNode = new Mock(); mockChildNode .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); @@ -72,7 +72,7 @@ public void pass_through_running_of_child_node() var time = new TimeData(); - var mockChildNode = new Mock(); + var mockChildNode = new Mock(); mockChildNode .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running); @@ -89,10 +89,10 @@ public void adding_more_than_a_single_child_throws_exception() { Init(); - var mockChildNode1 = new Mock(); + var mockChildNode1 = new Mock(); testObject.AddChild(mockChildNode1.Object); - var mockChildNode2 = new Mock(); + var mockChildNode2 = new Mock(); Assert.Throws(() => testObject.AddChild(mockChildNode2.Object) ); diff --git a/tests/ParallelNodeTests.cs b/tests/ParallelNodeTests.cs index ac36360..576cf0a 100644 --- a/tests/ParallelNodeTests.cs +++ b/tests/ParallelNodeTests.cs @@ -26,7 +26,7 @@ public void runs_all_nodes_in_order() var callOrder = 0; - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running) @@ -35,7 +35,7 @@ public void runs_all_nodes_in_order() Assert.Equal(1, ++callOrder); }); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running) @@ -62,17 +62,17 @@ public void fails_when_required_number_of_children_fail() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); - var mockChild3 = new Mock(); + var mockChild3 = new Mock(); mockChild3 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running); @@ -95,17 +95,17 @@ public void succeeeds_when_required_number_of_children_succeed() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); - var mockChild3 = new Mock(); + var mockChild3 = new Mock(); mockChild3 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running); @@ -128,12 +128,12 @@ public void continues_to_run_if_required_number_children_neither_succeed_or_fail var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); diff --git a/tests/SelectorNodeTests.cs b/tests/SelectorNodeTests.cs index 97a5d60..824ee6e 100644 --- a/tests/SelectorNodeTests.cs +++ b/tests/SelectorNodeTests.cs @@ -24,12 +24,12 @@ public void runs_the_first_node_if_it_succeeds() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); testObject.AddChild(mockChild1.Object); testObject.AddChild(mockChild2.Object); @@ -47,12 +47,12 @@ public void stops_on_the_first_node_when_it_is_running() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); testObject.AddChild(mockChild1.Object); testObject.AddChild(mockChild2.Object); @@ -70,12 +70,12 @@ public void runs_the_second_node_if_the_first_fails() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); @@ -96,12 +96,12 @@ public void fails_when_all_children_fail() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); diff --git a/tests/SequenceNodeTests.cs b/tests/SequenceNodeTests.cs index 76bd000..12afd72 100644 --- a/tests/SequenceNodeTests.cs +++ b/tests/SequenceNodeTests.cs @@ -26,7 +26,7 @@ public void can_run_all_children_in_order() var callOrder = 0; - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success) @@ -35,7 +35,7 @@ public void can_run_all_children_in_order() Assert.Equal(1, ++callOrder); }); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success) @@ -62,12 +62,12 @@ public void when_first_child_is_running_second_child_is_supressed() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Running); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); testObject.AddChild(mockChild1.Object); testObject.AddChild(mockChild2.Object); @@ -85,12 +85,12 @@ public void when_first_child_fails_then_entire_sequence_fails() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); testObject.AddChild(mockChild1.Object); testObject.AddChild(mockChild2.Object); @@ -108,12 +108,12 @@ public void when_second_child_fails_then_entire_sequence_fails() var time = new TimeData(); - var mockChild1 = new Mock(); + var mockChild1 = new Mock(); mockChild1 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Success); - var mockChild2 = new Mock(); + var mockChild2 = new Mock(); mockChild2 .Setup(m => m.Tick(time)) .Returns(BehaviourTreeStatus.Failure); From 52edf7bfa3dbddbca288d1148275a1f8e6e24964 Mon Sep 17 00:00:00 2001 From: Michael Notarnicola Date: Sun, 11 Sep 2016 02:43:45 -0400 Subject: [PATCH 2/4] Passing in null to a leaf node will use the method name instead. lastExecStatus is Failure by default. --- src/BehaviourTreeBuilder.cs | 9 ++++++++- src/BehaviourTreeNode.cs | 4 ++++ src/ParentBehaviourTreeNode.cs | 3 +++ src/TimeData.cs | 11 +++-------- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/BehaviourTreeBuilder.cs b/src/BehaviourTreeBuilder.cs index 4fec55d..9377339 100644 --- a/src/BehaviourTreeBuilder.cs +++ b/src/BehaviourTreeBuilder.cs @@ -27,7 +27,10 @@ public BehaviourTreeBuilder Do(string name, Func { throw new ApplicationException("Can't create an unnested ActionNode, it must be a leaf node."); } - + if (string.IsNullOrEmpty(name)) + { + name = fn.Method.Name; + } var actionNode = new ActionNode(name, fn); parentNodeStack.Peek().AddChild(actionNode); return this; @@ -38,6 +41,10 @@ public BehaviourTreeBuilder Do(string name, Func /// public BehaviourTreeBuilder Condition(string name, Func fn) { + if (string.IsNullOrEmpty(name)) + { + name = fn.Method.Name; + } return Do(name, t => fn(t) ? BehaviourTreeStatus.Success : BehaviourTreeStatus.Failure); } diff --git a/src/BehaviourTreeNode.cs b/src/BehaviourTreeNode.cs index 2c3cf70..50f6e71 100644 --- a/src/BehaviourTreeNode.cs +++ b/src/BehaviourTreeNode.cs @@ -13,11 +13,15 @@ public abstract class BehaviourTreeNode /// The result of the last execution /// public BehaviourTreeStatus lastExecutionStatus { get; private set; } + /// + /// True if the Builder that made this node was built. + /// protected bool hasDataBeenBaked { get; private set; } public BehaviourTreeNode(string name) { this.name = name; + lastExecutionStatus = BehaviourTreeStatus.Failure; } /// diff --git a/src/ParentBehaviourTreeNode.cs b/src/ParentBehaviourTreeNode.cs index af8cebf..814b5a5 100644 --- a/src/ParentBehaviourTreeNode.cs +++ b/src/ParentBehaviourTreeNode.cs @@ -16,6 +16,9 @@ public abstract class ParentBehaviourTreeNode : BehaviourTreeNode /// private BehaviourTreeNode[] children = new BehaviourTreeNode[1]; + /// + /// The number of children added to this node + /// public int childCount { get { if (hasDataBeenBaked) diff --git a/src/TimeData.cs b/src/TimeData.cs index 058b2f6..a94b236 100644 --- a/src/TimeData.cs +++ b/src/TimeData.cs @@ -1,20 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// Represents time. Used to pass time values to behaviour tree nodes. /// public struct TimeData { + public float deltaTime; + public TimeData(float deltaTime) { this.deltaTime = deltaTime; } - - public float deltaTime; } } From 993e55eec9225b771101a9dd931e5acf60003846 Mon Sep 17 00:00:00 2001 From: Michael Notarnicola Date: Sun, 11 Sep 2016 04:44:55 -0400 Subject: [PATCH 3/4] Added hasExecuted to hint to visualizers how to show state. --- src/BehaviourTreeNode.cs | 9 +++++++++ src/BehaviourTreeStatus.cs | 9 ++------- src/ParentBehaviourTreeNode.cs | 9 ++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/BehaviourTreeNode.cs b/src/BehaviourTreeNode.cs index 50f6e71..0e62bc3 100644 --- a/src/BehaviourTreeNode.cs +++ b/src/BehaviourTreeNode.cs @@ -14,6 +14,10 @@ public abstract class BehaviourTreeNode /// public BehaviourTreeStatus lastExecutionStatus { get; private set; } /// + /// Used to determine if this node has been ticked. + /// + public bool hasExecuted { get; private set; } + /// /// True if the Builder that made this node was built. /// protected bool hasDataBeenBaked { get; private set; } @@ -30,8 +34,13 @@ public BehaviourTreeNode(string name) public BehaviourTreeStatus Tick(TimeData time) { lastExecutionStatus = AbstractTick(time); + hasExecuted = true; return lastExecutionStatus; } + public virtual void ResetLastExecStatus() + { + hasExecuted = false; + } internal void BakeData() { if (!hasDataBeenBaked) diff --git a/src/BehaviourTreeStatus.cs b/src/BehaviourTreeStatus.cs index 1d09083..91b15cf 100644 --- a/src/BehaviourTreeStatus.cs +++ b/src/BehaviourTreeStatus.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// The return type when invoking behaviour tree nodes. @@ -12,6 +7,6 @@ public enum BehaviourTreeStatus { Success, Failure, - Running + Running, } } diff --git a/src/ParentBehaviourTreeNode.cs b/src/ParentBehaviourTreeNode.cs index 814b5a5..25a9935 100644 --- a/src/ParentBehaviourTreeNode.cs +++ b/src/ParentBehaviourTreeNode.cs @@ -40,7 +40,14 @@ public BehaviourTreeNode this[int index] { return _children[index]; } } - + public override void ResetLastExecStatus() + { + base.ResetLastExecStatus(); + for (int i = 0; i < childCount; i++) + { + this[i].ResetLastExecStatus(); + } + } /// /// Add a child to the parent node. /// From 7bc6787250abb0828f7fa748a9b373639029097d Mon Sep 17 00:00:00 2001 From: Michael Notarnicola Date: Sun, 11 Sep 2016 21:03:02 -0400 Subject: [PATCH 4/4] Removed "baking". Internalized resetting hasExecuted status. Restored TimeData and BehaviourTreeStatus. --- src/BehaviourTreeBuilder.cs | 24 -------------------- src/BehaviourTreeNode.cs | 21 +++++------------- src/BehaviourTreeStatus.cs | 9 ++++++-- src/ParentBehaviourTreeNode.cs | 40 ++++++++++------------------------ src/TimeData.cs | 11 +++++++--- 5 files changed, 33 insertions(+), 72 deletions(-) diff --git a/src/BehaviourTreeBuilder.cs b/src/BehaviourTreeBuilder.cs index 9377339..1387fe8 100644 --- a/src/BehaviourTreeBuilder.cs +++ b/src/BehaviourTreeBuilder.cs @@ -121,7 +121,6 @@ public BehaviourTreeBuilder Splice(BehaviourTreeNode subTree) { throw new ArgumentNullException("subTree"); } - if (parentNodeStack.Count <= 0) { throw new ApplicationException("Can't splice an unnested sub-tree, there must be a parent-tree."); @@ -140,11 +139,6 @@ public BehaviourTreeNode Build() { throw new ApplicationException("Can't create a behaviour tree with zero nodes"); } - var curNodeTree = curNode as ParentBehaviourTreeNode; - if (curNodeTree != null) - { - BakeTreeRecursively(curNodeTree); - } return curNode; } @@ -156,23 +150,5 @@ public BehaviourTreeBuilder End() curNode = parentNodeStack.Pop(); return this; } - - /// - /// Bakes all of the tree execution information recursively. - /// - /// - private void BakeTreeRecursively(ParentBehaviourTreeNode curNodeTree) - { - for (int i = 0; i < curNodeTree.childCount; i++) - { - var child = curNodeTree[i]; - child.BakeData(); - var childAsParent = child as ParentBehaviourTreeNode; - if (childAsParent != null) - { - BakeTreeRecursively(childAsParent); - } - } - } } } diff --git a/src/BehaviourTreeNode.cs b/src/BehaviourTreeNode.cs index 0e62bc3..7a8c065 100644 --- a/src/BehaviourTreeNode.cs +++ b/src/BehaviourTreeNode.cs @@ -14,13 +14,9 @@ public abstract class BehaviourTreeNode /// public BehaviourTreeStatus lastExecutionStatus { get; private set; } /// - /// Used to determine if this node has been ticked. + /// Used to determine if this node has been ticked yet this iteration. /// public bool hasExecuted { get; private set; } - /// - /// True if the Builder that made this node was built. - /// - protected bool hasDataBeenBaked { get; private set; } public BehaviourTreeNode(string name) { @@ -33,23 +29,18 @@ public BehaviourTreeNode(string name) /// public BehaviourTreeStatus Tick(TimeData time) { + ResetLastExecStatus(); lastExecutionStatus = AbstractTick(time); hasExecuted = true; return lastExecutionStatus; } - public virtual void ResetLastExecStatus() + /// + /// Marks that this node hasn't executed yet. + /// + internal virtual void ResetLastExecStatus() { hasExecuted = false; } - internal void BakeData() - { - if (!hasDataBeenBaked) - { - InternalBakeData(); - hasDataBeenBaked = true; - } - } - protected virtual void InternalBakeData() { } protected abstract BehaviourTreeStatus AbstractTick(TimeData time); } } diff --git a/src/BehaviourTreeStatus.cs b/src/BehaviourTreeStatus.cs index 91b15cf..1d09083 100644 --- a/src/BehaviourTreeStatus.cs +++ b/src/BehaviourTreeStatus.cs @@ -1,4 +1,9 @@ -namespace FluentBehaviourTree +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FluentBehaviourTree { /// /// The return type when invoking behaviour tree nodes. @@ -7,6 +12,6 @@ public enum BehaviourTreeStatus { Success, Failure, - Running, + Running } } diff --git a/src/ParentBehaviourTreeNode.cs b/src/ParentBehaviourTreeNode.cs index 25a9935..ebdf899 100644 --- a/src/ParentBehaviourTreeNode.cs +++ b/src/ParentBehaviourTreeNode.cs @@ -8,39 +8,33 @@ namespace FluentBehaviourTree public abstract class ParentBehaviourTreeNode : BehaviourTreeNode { /// - /// List of child nodes used for building nodes. + /// List of child nodes /// - private List _children = new List(); - /// - /// The baked array of children. - /// - private BehaviourTreeNode[] children = new BehaviourTreeNode[1]; + private List children = new List(); /// /// The number of children added to this node /// public int childCount { get { - if (hasDataBeenBaked) - { - return children.Length; - } - return _children.Count; + return children.Count; } } public ParentBehaviourTreeNode(string name) : base(name) { } + /// + /// Retrieve a child node by index. + /// public BehaviourTreeNode this[int index] { get { - if (hasDataBeenBaked) - { - return children[index]; - } - return _children[index]; + return children[index]; } } - public override void ResetLastExecStatus() + /// + /// Marks that this node and all children have not execute yet. + /// + internal override void ResetLastExecStatus() { base.ResetLastExecStatus(); for (int i = 0; i < childCount; i++) @@ -53,17 +47,7 @@ public override void ResetLastExecStatus() /// public virtual void AddChild(BehaviourTreeNode child) { - if (!hasDataBeenBaked) - { - _children.Add(child); - } - } - - protected override void InternalBakeData() - { - base.InternalBakeData(); - children = _children.ToArray(); - _children.Clear(); + children.Add(child); } } } diff --git a/src/TimeData.cs b/src/TimeData.cs index a94b236..058b2f6 100644 --- a/src/TimeData.cs +++ b/src/TimeData.cs @@ -1,15 +1,20 @@ -namespace FluentBehaviourTree +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FluentBehaviourTree { /// /// Represents time. Used to pass time values to behaviour tree nodes. /// public struct TimeData { - public float deltaTime; - public TimeData(float deltaTime) { this.deltaTime = deltaTime; } + + public float deltaTime; } }