public void ShouldListenToEvents_WhenUsingChildBlackboard() { Blackboard rootBlackboard = new Blackboard(clock); Blackboard blackboard = new Blackboard(rootBlackboard, clock); // our mock nodes we want to query for status MockNode firstChild = new MockNode(false); // false -> fail when aborted MockNode secondChild = new MockNode(false); // conditions for each subtree that listen the BB for events BlackboardCondition firstCondition = new BlackboardCondition("branch1", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART, firstChild); BlackboardCondition secondCondition = new BlackboardCondition("branch2", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART, secondChild); // set up the tree Selector selector = new Selector(firstCondition, secondCondition); TestRoot behaviorTree = new TestRoot(blackboard, clock, selector); // intially we want to activate branch2 rootBlackboard.Set("branch2", true); // start the tree behaviorTree.Start(); // tick the timer to ensure the blackboard notifies the nodes clock.Update(0.1f); // verify the second child is running Assert.AreEqual(Node.State.INACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.ACTIVE, secondChild.CurrentState); // change keys so the first conditions get true, too rootBlackboard.Set("branch1", true); // tick the timer to ensure the blackboard notifies the nodes clock.Update(0.1f); // now we should be in branch1 Assert.AreEqual(Node.State.ACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, secondChild.CurrentState); }
public void ShouldNotActivateLowerPriorityBranchInCaseMultipleBranchesGetValid() { this.Timer = new Clock(); this.Blackboard = new Blackboard(Timer); // our mock nodes we want to query for status MockNode firstChild = new MockNode(false); // false -> fail when aborted MockNode secondChild = new MockNode(false); MockNode thirdChild = new MockNode(false); // coniditions for each subtree that listen the BB for events BlackboardCondition firstCondition = new BlackboardCondition("branch1", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART, firstChild); BlackboardCondition secondCondition = new BlackboardCondition("branch2", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART, secondChild); BlackboardCondition thirdCondtion = new BlackboardCondition("branch3", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART, thirdChild); // set up the tree Selector selector = new Selector(firstCondition, secondCondition, thirdCondtion); TestRoot behaviorTree = new TestRoot(Blackboard, Timer, selector); // intially we want to activate branch3 Blackboard.Set("branch3", true); // start the tree behaviorTree.Start(); // verify the third child is running Assert.AreEqual(Node.State.INACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, secondChild.CurrentState); Assert.AreEqual(Node.State.ACTIVE, thirdChild.CurrentState); Assert.AreEqual(0, firstChild.DebugNumStartCalls); Assert.AreEqual(0, secondChild.DebugNumStartCalls); Assert.AreEqual(1, thirdChild.DebugNumStartCalls); // change keys so the first & second conditions get true, too Blackboard.Set("branch1", true); Blackboard.Set("branch2", true); // still the third child should be active, as the blackboard didn't yet notifiy the nodes Assert.AreEqual(Node.State.INACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, secondChild.CurrentState); Assert.AreEqual(Node.State.ACTIVE, thirdChild.CurrentState); Assert.AreEqual(0, firstChild.DebugNumStartCalls); Assert.AreEqual(0, secondChild.DebugNumStartCalls); Assert.AreEqual(1, thirdChild.DebugNumStartCalls); // tick the timer to ensure the blackboard notifies the nodes Timer.Update(0.1f); // now we should be in branch1 Assert.AreEqual(Node.State.ACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, secondChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, thirdChild.CurrentState); Assert.AreEqual(1, firstChild.DebugNumStartCalls); Assert.AreEqual(0, secondChild.DebugNumStartCalls); Assert.AreEqual(1, thirdChild.DebugNumStartCalls); // disable first branch Blackboard.Set("branch1", false); Timer.Update(0.1f); // and now the second branch should be active Assert.AreEqual(Node.State.INACTIVE, firstChild.CurrentState); Assert.AreEqual(Node.State.ACTIVE, secondChild.CurrentState); Assert.AreEqual(Node.State.INACTIVE, thirdChild.CurrentState); Assert.AreEqual(1, firstChild.DebugNumStartCalls); Assert.AreEqual(1, secondChild.DebugNumStartCalls); Assert.AreEqual(1, thirdChild.DebugNumStartCalls); }
private void DrawNode(Node node, int depth, bool connected) { float tStopRequested = Mathf.Lerp(0.85f, 0.25f, 2.0f * (Time.time - node.DebugLastStopRequestAt)); float tStopped = Mathf.Lerp(0.85f, 0.25f, 2.0f * (Time.time - node.DebugLastStoppedAt)); bool inactive = node.CurrentState != Node.State.ACTIVE; float alpha = inactive ? Mathf.Max(0.35f, Mathf.Pow(tStopped, 1.5f)) : 1.0f; bool failed = (tStopped > 0.25f && tStopped < 1.0f && !node.DebugLastResult && inactive); bool stopRequested = (tStopRequested > 0.25f && tStopRequested < 1.0f && inactive); EditorGUILayout.BeginHorizontal(); { GUI.color = new Color(1f, 1f, 1f, alpha); string tagName; GUIStyle tagStyle = stopRequested ? nodeCapsuleStopRequested : (failed ? nodeCapsuleFailed : nodeCapsuleGray); bool drawLabel = !string.IsNullOrEmpty(node.Label); string label = node.Label; if (node is BlackboardCondition) { BlackboardCondition nodeBlackboardCond = node as BlackboardCondition; tagName = nodeBlackboardCond.Key + " " + operatorToString[nodeBlackboardCond.Operator] + " " + nodeBlackboardCond.Value; GUI.backgroundColor = new Color(0.9f, 0.9f, 0.6f); } else { if (node is Composite) { GUI.backgroundColor = new Color(0.3f, 1f, 0.1f); } if (node is Decorator) { GUI.backgroundColor = new Color(0.3f, 1f, 1f); } if (node is Task) { GUI.backgroundColor = new Color(0.5f, 0.1f, 0.5f); } if (node is ObservingDecorator) { GUI.backgroundColor = new Color(0.9f, 0.9f, 0.6f); } nameToTagString.TryGetValue(node.Name, out tagName); } if ((node is Container) && ((Container)node).Collapse) { if (!drawLabel) { drawLabel = true; label = tagName; } tagName = "..."; GUI.backgroundColor = new Color(0.4f, 0.4f, 0.4f); } if (string.IsNullOrEmpty(tagName)) { tagName = node.Name; } if (!drawLabel) { GUILayout.Label(tagName, tagStyle); } else { GUILayout.Label("(" + tagName + ") " + label, tagStyle); // Reset background color GUI.backgroundColor = Color.white; } GUILayout.FlexibleSpace(); // Draw Buttons if (node.CurrentState == Node.State.ACTIVE) { if (GUILayout.Button("stop", EditorStyles.miniButton)) { node.Stop(); } } else if (node is Root) { GUI.color = new Color(1f, 1f, 1f, 1f); if (GUILayout.Button("start", EditorStyles.miniButton)) { node.Start(); } GUI.color = new Color(1f, 1f, 1f, 0.3f); } // Draw Stats GUILayout.Label((node.DebugNumStoppedCalls > 0 ? node.DebugLastResult.ToString() : "") + " | " + node.DebugNumStartCalls + " , " + node.DebugNumStopCalls + " , " + node.DebugNumStoppedCalls, smallTextStyle); } EditorGUILayout.EndHorizontal(); // Draw the lines if (connected) { Rect rect = GUILayoutUtility.GetLastRect(); Handles.color = new Color(0f, 0f, 0f, 1f); Handles.BeginGUI(); float midY = 4 + (rect.yMin + rect.yMax) / 2f; Handles.DrawLine(new Vector2(rect.xMin - 5, midY), new Vector2(rect.xMin, midY)); Handles.EndGUI(); } }