diff --git a/src/BehaviourTreeBuilder.cs b/src/BehaviourTreeBuilder.cs index fc8b9ac..1387fe8 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. @@ -29,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; @@ -40,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); } @@ -110,13 +115,12 @@ 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) { throw new ArgumentNullException("subTree"); } - if (parentNodeStack.Count <= 0) { throw new ApplicationException("Can't splice an unnested sub-tree, there must be a parent-tree."); @@ -129,7 +133,7 @@ public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) /// /// Build the actual tree. /// - public IBehaviourTreeNode Build() + public BehaviourTreeNode Build() { if (curNode == null) { diff --git a/src/BehaviourTreeNode.cs b/src/BehaviourTreeNode.cs new file mode 100644 index 0000000..7a8c065 --- /dev/null +++ b/src/BehaviourTreeNode.cs @@ -0,0 +1,46 @@ +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; } + /// + /// Used to determine if this node has been ticked yet this iteration. + /// + public bool hasExecuted { get; private set; } + + public BehaviourTreeNode(string name) + { + this.name = name; + lastExecutionStatus = BehaviourTreeStatus.Failure; + } + + /// + /// Update the time of the behaviour tree. + /// + public BehaviourTreeStatus Tick(TimeData time) + { + ResetLastExecStatus(); + lastExecutionStatus = AbstractTick(time); + hasExecuted = true; + return lastExecutionStatus; + } + /// + /// Marks that this node hasn't executed yet. + /// + internal virtual void ResetLastExecStatus() + { + hasExecuted = false; + } + 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..ebdf899 --- /dev/null +++ b/src/ParentBehaviourTreeNode.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; + +namespace FluentBehaviourTree +{ + /// + /// Interface for behaviour tree nodes. + /// + public abstract class ParentBehaviourTreeNode : BehaviourTreeNode + { + /// + /// List of child nodes + /// + private List children = new List(); + + /// + /// The number of children added to this node + /// + public int childCount { + get { + return children.Count; + } + } + + public ParentBehaviourTreeNode(string name) : base(name) { } + + /// + /// Retrieve a child node by index. + /// + public BehaviourTreeNode this[int index] { + get { + return children[index]; + } + } + /// + /// Marks that this node and all children have not execute yet. + /// + internal override void ResetLastExecStatus() + { + base.ResetLastExecStatus(); + for (int i = 0; i < childCount; i++) + { + this[i].ResetLastExecStatus(); + } + } + /// + /// Add a child to the parent node. + /// + public virtual void AddChild(BehaviourTreeNode child) + { + children.Add(child); + } + } +} 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);