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);