Skip to content

Commit f4dbb9f

Browse files
authored
Merge pull request #22 from Yecats/new-features
New features
2 parents 0250521 + c749fd7 commit f4dbb9f

File tree

12 files changed

+316
-14
lines changed

12 files changed

+316
-14
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,5 @@ sysinfo.txt
5858
# Crashlytics generated file
5959
crashlytics-build.properties
6060

61+
# Release zip files
62+
*.zip

com.wug.behaviortreevisualizer/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this package will be documented in this file.
44

5+
## [0.1.3] - 10-04-2020
6+
7+
* [#21](https://github.com/Yecats/UnityBehaviorTreeVisualizer/issues/21) You can now right click on a graph node to launch any of the scripts it represents.
8+
* [#18](https://github.com/Yecats/UnityBehaviorTreeVisualizer/issues/18) & [#15](https://github.com/Yecats/UnityBehaviorTreeVisualizer/issues/15) Added a hook so that you can request a redraw of the graph. Useful for dynamic trees or to add an inspector button (now included in the samples).
9+
* [#20](https://github.com/Yecats/UnityBehaviorTreeVisualizer/issues/20) Added a **Community Nodes** sample folder so that anyone can contribute behavior tree nodes! Also added a node - NavigateToDestination, which will take a GameObject or NavMeshAgent and position reference and handle movement.
10+
511
## [0.1.2] - 9-29-2020
612

713
* Fixed a bug that was causing the layout to be drawn incorrectly (no spaces between sections).

com.wug.behaviortreevisualizer/Editor/Scripts/BehaviorTreeGraphWindow.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
5+
using System.Runtime.CompilerServices;
56
using System.Threading.Tasks;
67
using UnityEditor;
78
using UnityEditor.Experimental.GraphView;
@@ -43,6 +44,29 @@ public static void Init()
4344
Instance.rootVisualElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(c_StylePath));
4445
}
4546

47+
/// <summary>
48+
/// Gives focus to the behavior tree window
49+
/// </summary>
50+
/// <param name="behaviorTree"></param>
51+
public static void DrawBehaviorTree(NodeBase behaviorTree, bool focusWindow)
52+
{
53+
// If this window is not open then nothing will be done
54+
if (!HasOpenInstances<BehaviorTreeGraphWindow>())
55+
{
56+
return;
57+
}
58+
59+
//focus the window if that's what is desired
60+
if (focusWindow)
61+
{
62+
FocusWindowIfItsOpen(typeof(BehaviorTreeGraphWindow));
63+
}
64+
65+
//Clears any existing nodes that have been drawn and loads the tree
66+
GraphView.ClearTree();
67+
GraphView.LoadBehaviorTree(behaviorTree);
68+
}
69+
4670
private void OnEnable()
4771
{
4872
//Loses reference for some reason

com.wug.behaviortreevisualizer/Editor/Scripts/Graph/BTNodeData.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ public enum DecoratorTypes
1919
[Serializable]
2020
public class BTGNodeData : GraphView.Node
2121
{
22-
public string Id;
23-
public Vector2 Position;
24-
public bool EntryPoint;
25-
public FullNodeInfo MainNodeDetails;
26-
private List<FullNodeInfo> m_DecoratorData;
22+
public string Id { get; private set; }
23+
public Vector2 Position { get; private set; }
24+
public bool EntryPoint { get; private set; }
25+
public FullNodeInfo MainNodeDetails { get; private set; }
26+
public List<FullNodeInfo> DecoratorData { get; private set; }
2727

28-
public GraphView.Port ParentPort;
28+
public GraphView.Port ParentPort { get; private set; }
2929

30-
public GraphView.Port InputPort;
30+
public GraphView.Port InputPort { get; private set; }
3131
public List<GraphView.Port> OutputPorts = new List<GraphView.Port>();
3232

3333
private VisualElement m_NodeBorder;
@@ -42,7 +42,7 @@ public class BTGNodeData : GraphView.Node
4242
public BTGNodeData(FullNodeInfo mainNodeDetails, bool entryPoint, GraphView.Port parentPort, List<FullNodeInfo> decoratorData)
4343
{
4444
MainNodeDetails = mainNodeDetails;
45-
m_DecoratorData = decoratorData;
45+
DecoratorData = decoratorData;
4646
MainNodeDetails.RunTimeNode.NodeStatusChanged += OnNodeStatusChanged;
4747

4848
title = MainNodeDetails.RunTimeNode.Name == null || MainNodeDetails.RunTimeNode.Name.Equals("") ? MainNodeDetails.RunTimeNode.GetType().Name : MainNodeDetails.RunTimeNode.Name;
@@ -74,9 +74,9 @@ public BTGNodeData(FullNodeInfo mainNodeDetails, bool entryPoint, GraphView.Port
7474
m_NodeTopMessageDecorator = GenerateStatusMessageLabel();
7575

7676
//Add the decorator icon
77-
if (m_DecoratorData != null)
77+
if (DecoratorData != null)
7878
{
79-
foreach (var decorator in m_DecoratorData)
79+
foreach (var decorator in DecoratorData)
8080
{
8181
decorator.RunTimeNode.NodeStatusChanged += OnNodeStatusChanged;
8282

@@ -96,9 +96,9 @@ public BTGNodeData(FullNodeInfo mainNodeDetails, bool entryPoint, GraphView.Port
9696
//Do an initial call to setup the style of the node in the event that it's already been running (pretty likely)
9797
OnNodeStatusChanged(MainNodeDetails.RunTimeNode);
9898

99-
if (m_DecoratorData != null)
99+
if (DecoratorData != null)
100100
{
101-
m_DecoratorData.ForEach(x => OnNodeStatusChanged(x.RunTimeNode));
101+
DecoratorData.ForEach(x => OnNodeStatusChanged(x.RunTimeNode));
102102
}
103103

104104
}

com.wug.behaviortreevisualizer/Editor/Scripts/Graph/BehaviorTreeGraphView.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.IO;
23
using System.Linq;
34
using System.Threading.Tasks;
45
using UnityEditor;
@@ -49,6 +50,52 @@ public BehaviorTreeGraphView()
4950

5051
}
5152

53+
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
54+
{
55+
base.BuildContextualMenu(evt);
56+
57+
if (evt.target is BTGNodeData)
58+
{
59+
BTGNodeData nodeData = (evt.target as BTGNodeData);
60+
61+
int countOfItems = 1;
62+
63+
for (int i = 0; i < nodeData.DecoratorData.Count; i++)
64+
{
65+
var name = nodeData.DecoratorData[0].RunTimeNode.GetType().Name;
66+
67+
evt.menu.InsertAction(0, $"Open {name}", (e) => { OpenFile($"{name}.cs"); });
68+
69+
countOfItems++;
70+
}
71+
72+
string nodeName = nodeData.MainNodeDetails.RunTimeNode.GetType().Name;
73+
evt.menu.InsertAction(0, $"Open {nodeName}", (e) => { OpenFile($"{nodeName}.cs"); });
74+
75+
evt.menu.InsertSeparator("", countOfItems);
76+
}
77+
}
78+
79+
/// <summary>
80+
/// Open a file at a specified location
81+
/// </summary>
82+
/// <param name="className">Class name with extension</param>
83+
private void OpenFile(string className)
84+
{
85+
86+
string[] res = Directory.GetFiles(Application.dataPath, className, SearchOption.AllDirectories);
87+
88+
if (res.Length == 0)
89+
{
90+
$"Unable to locate script path. Please file a bug at https://github.com/Yecats/UnityBehaviorTreeVisualizer".BTDebugLog();
91+
return;
92+
}
93+
94+
string path = res[0].Replace("\\", "/");
95+
96+
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(path, 1, 0);
97+
}
98+
5299
public void ClearTree()
53100
{
54101
m_Nodes.ForEach(x => x.parent.Remove(x));
@@ -120,7 +167,7 @@ public void DrawNodes(bool entryPoint, NodeBase currentNode, int columnIndex, Po
120167

121168
if (currentNode.ChildNodes.Count == 0)
122169
{
123-
$"Decorator ({currentNode.GetType().Name}) does not have any children. Nothing will be drawn".BTDebugLog();
170+
$"Decorator ({currentNode.GetType().Name}) does not have any children. Nothing will be drawn.".BTDebugLog();
124171
}
125172
else
126173
{
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using UnityEngine;
3+
using UnityEngine.AI;
4+
5+
namespace WUG.BehaviorTreeVisualizer.CommunityNodes
6+
{
7+
[Serializable]
8+
public class NavigateToDestination : Node
9+
{
10+
private Func<Vector3> m_DestinationCalculation;
11+
private Vector3 m_TargetDestination = Vector3.zero;
12+
private NavMeshAgent m_NavMeshAgent;
13+
private float m_MaxDistanceToSample = 1f;
14+
15+
/// <summary>
16+
/// Navigate a game object to a specific location using Unity's Navigation.
17+
/// Behavior will get a SamplePosition within 1 meter of the destination to ensure that the location
18+
/// provided can be navigated.
19+
/// </summary>
20+
/// <param name="navMeshAgent">NavMesh on the game object that will be moved</param>
21+
/// <param name="destination">Function that can be used to calculate the target destination. Must return a Vector3. </param>
22+
/// <param name="maxDistance">Max distance that NavMesh.SamplePosition should check for</param>
23+
public NavigateToDestination(NavMeshAgent navMeshAgent, Func<Vector3> destination, float maxDistance = 1f)
24+
{
25+
Name = "Navigate";
26+
m_NavMeshAgent = navMeshAgent;
27+
m_DestinationCalculation = destination;
28+
m_MaxDistanceToSample = maxDistance;
29+
}
30+
31+
/// <summary>
32+
/// Navigate a game object to a specific location using Unity's Navigation.
33+
/// Behavior will get a SamplePosition within 1f of the destination to ensure that the location
34+
/// provided can be navigated.
35+
/// </summary>
36+
/// <param name="gameObject">Game object that will be moved. Must have a NavMeshAgent on it</param>
37+
/// <param name="destination">Function that can be used to calculate the target destination. Must return a Vector3. </param>
38+
/// <param name="maxDistance">Max distance that NavMesh.SamplePosition should check for</param>
39+
public NavigateToDestination(GameObject gameObject, Func<Vector3> destination, float maxDistance = 1f)
40+
{
41+
Name = "Navigate";
42+
m_NavMeshAgent = gameObject.GetComponent<NavMeshAgent>();
43+
m_DestinationCalculation = destination;
44+
m_MaxDistanceToSample = maxDistance;
45+
}
46+
47+
48+
protected override void OnReset() { }
49+
50+
protected override NodeStatus OnRun()
51+
{
52+
//Make sure that a reference to NavMeshAgent and a valid destination exists
53+
if (m_NavMeshAgent == null)
54+
{
55+
StatusReason = "NavMeshAgent is null";
56+
return NodeStatus.Failure;
57+
}
58+
59+
//First Evaluation will get the official target destination and set it on the NavMeshAgent
60+
if (IsFirstEvaluation)
61+
{
62+
if (m_DestinationCalculation() == null)
63+
{
64+
StatusReason = "Unable to calculate a destination position. Did you pass a function in?";
65+
return NodeStatus.Failure;
66+
}
67+
68+
m_TargetDestination = m_DestinationCalculation();
69+
70+
if (m_TargetDestination == Vector3.zero)
71+
{
72+
StatusReason = "Unable to calculate a destination position. Does the function return a position?";
73+
return NodeStatus.Failure;
74+
}
75+
76+
NavMesh.SamplePosition(m_TargetDestination, out NavMeshHit hit, m_MaxDistanceToSample, 1);
77+
m_TargetDestination = hit.position;
78+
79+
m_NavMeshAgent.SetDestination(m_TargetDestination);
80+
StatusReason = $"Starting to navigate to {m_TargetDestination}";
81+
82+
return NodeStatus.Running;
83+
}
84+
85+
//Evaluate the remaining distance
86+
float distanceToTarget = Vector3.Distance(m_TargetDestination, m_NavMeshAgent.transform.position);
87+
88+
//Reached the destination
89+
if (distanceToTarget < .25f)
90+
{
91+
StatusReason = $"Navigation ended. " +
92+
$"\n - Evaluation Count: {EvaluationCount}. " +
93+
$"\n - Target Destination: {m_TargetDestination}" +
94+
$"\n - Distance to target: {Math.Round(distanceToTarget, 1)}";
95+
96+
return NodeStatus.Success;
97+
}
98+
99+
//Still moving
100+
StatusReason = $"Distance to target: {distanceToTarget}";
101+
return NodeStatus.Running;
102+
103+
}
104+
}
105+
}
106+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
# Community Nodes
3+
The goal of the Behavior Tree Visualizer tool is to make writing and debugging AI easier for all! To help with this, our hope is to facilitate a repository of Nodes that interact with various elements of Unity. A full list of the nodes can be found [here](https://github.com/Yecats/UnityBehaviorTreeVisualizer/wiki/Community-Nodes).
4+
5+
## Contribute
6+
Want to contribute a new community node or fix a bug? You can submit a pull request on the GitHub repository at https://github.com/Yecats/UnityBehaviorTreeVisualizer/. A few things to note:
7+
8+
1. Nodes should be in the **com.wug.behaviortreevisualizer\Samples~\Community Nodes** folder with a sub category folder (i.e. "Navigation").
9+
2. All community nodes must build off the **Standard Nodes** sample project. In other words, if you are creating a new **Decorator**, make sure it inherits from the `Decorator` node.
10+
3. Documentation, documentation, documentation! Make sure to add comments to explain what your node is doing, parameters that need to be passed, etc.
11+
12+
## Get Help
13+
If you encounter an issue with a community node, please reach out on GitHub and log an issue at https://github.com/Yecats/UnityBehaviorTreeVisualizer/issues or submit a pull request with the fix.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using UnityEditor;
7+
using UnityEngine;
8+
using WUG.BehaviorTreeVisualizer;
9+
10+
namespace Assets.Samples.Behavior_Tree_Visualizer__Beta_._0._1._2.NPC_Behavior_Tree___2019._4.Scripts.Editor
11+
{
12+
[CustomEditor(typeof(NonPlayerCharacter))]
13+
public class NonplayerCharacterEditor : UnityEditor.Editor
14+
{
15+
private NonPlayerCharacter m_NPC;
16+
17+
private void OnEnable()
18+
{
19+
m_NPC = target as NonPlayerCharacter;
20+
21+
EditorApplication.update += RedrawView;
22+
}
23+
24+
void RedrawView()
25+
{
26+
Repaint();
27+
}
28+
29+
public override void OnInspectorGUI()
30+
{
31+
DrawDefaultInspector();
32+
33+
if (GUILayout.Button("Draw Behavior Tree"))
34+
{
35+
m_NPC.ForceDrawingOfTree();
36+
}
37+
}
38+
}
39+
}

com.wug.behaviortreevisualizer/Samples~/NPC Sample 2019.4/Scripts/NonPlayerCharacter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,15 @@ private void GenerateBehaviorTree()
8080
new Idle()),
8181
new SetNavigationActivityTo(NavigationActivity.PickupItem)));
8282
}
83+
84+
public void ForceDrawingOfTree()
85+
{
86+
if (BehaviorTree == null)
87+
{
88+
$"Behavior tree is null - nothing to draw.".BTDebugLog();
89+
}
90+
91+
BehaviorTreeGraphWindow.DrawBehaviorTree(BehaviorTree, true);
92+
}
8393
}
8494
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using UnityEditor;
7+
using UnityEngine;
8+
using WUG.BehaviorTreeVisualizer;
9+
10+
namespace Assets.Samples.Behavior_Tree_Visualizer__Beta_._0._1._2.NPC_Behavior_Tree___2019._4.Scripts.Editor
11+
{
12+
[CustomEditor(typeof(NonPlayerCharacter))]
13+
public class NonplayerCharacterEditor : UnityEditor.Editor
14+
{
15+
private NonPlayerCharacter m_NPC;
16+
17+
private void OnEnable()
18+
{
19+
m_NPC = target as NonPlayerCharacter;
20+
21+
EditorApplication.update += RedrawView;
22+
}
23+
24+
void RedrawView()
25+
{
26+
Repaint();
27+
}
28+
29+
public override void OnInspectorGUI()
30+
{
31+
DrawDefaultInspector();
32+
33+
if (GUILayout.Button("Draw Behavior Tree"))
34+
{
35+
m_NPC.ForceDrawingOfTree();
36+
}
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)