This commit is contained in:
2020-07-04 14:41:25 +08:00
parent 70c346d2c1
commit a8f02e4da5
3748 changed files with 587372 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 73525b0fba0614973bb8c2d52480c0a0
folderAsset: yes
timeCreated: 1501226182
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: cac08ba45fa2741ac964cc42d466ff1f
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
namespace Hivemind {
[CustomEditor(typeof(BehaviorTreeAgent))]
public class BTAgentInspector : Editor {
private BTEditorManager _manager;
public void OnEnable() {
BehaviorTreeAgent btAgent = target as BehaviorTreeAgent;
BehaviorTree bt = null;
if (btAgent.behaviorTree == null) {
btAgent.Awake();
}
bt = btAgent.behaviorTree;
if (bt != null) {
_manager = BTEditorManager.Manager;
if (!_manager) {
_manager = BTEditorManager.CreateInstance(bt, btAgent.btAsset);
} else {
_manager.behaviorTree = bt;
}
_manager.btInspector = this;
_manager.inspectedAgent = btAgent;
}
}
public void OnDisable() {
if (!EditorApplication.isPlaying && _manager != null) {
DestroyImmediate (_manager);
} else if (_manager != null) {
_manager.behaviorTree = null;
}
}
public override void OnInspectorGUI() {
DrawDefaultInspector();
if (_manager.selectedNode != null) {
EditorGUILayout.LabelField("Last Status: " + _manager.selectedNode.lastStatus.ToString());
}
BehaviorTreeAgent btAgent = target as BehaviorTreeAgent;
if (btAgent.context != null) {
EditorGUILayout.LabelField("Current Context");
foreach (KeyValuePair<string, object> item in btAgent.context.All) {
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(item.Key);
EditorGUILayout.LabelField(item.Value.ToString ());
EditorGUILayout.EndHorizontal();
}
}
if (GUILayout.Button ("Show Behavior Tree editor")) {
BTEditorWindow.ShowWindow ();
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 718ad4335bf544699b489e985a0e8d59
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,293 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.IO;
using Coolape;
namespace Hivemind
{
/*
* This manager is created by the BTInspector, the custom inspector for BehaviorTree assets.
* Every time the inspector receives the OnEnable message, a fresh manager is created, and
* destroyed when the inspector receives the OnDisable message.
*
* Its main responsibility is to serve as the sole route for BehaviorTree manipulation.
*
* It achieves that by exposing a selected BehaviorTree (determined by the current BehaviorTree
* being inspected in BTInspector) to the BTEditorWindow
*
* BTEditorWindow manages its own sub systems to provide editing functionality, and forwards
* all actual manipulations to this manager.
*
* Creation of new BehaviorTrees are also handled here.
*/
public class BTEditorManager : ScriptableObject
{
public Editor btInspector;
public Editor nodeInspector;
public BehaviorTreeAgent inspectedAgent;
public BTEditorWindow editorWindow;
public Node selectedNode;
public BehaviorTree _behaviorTree;
public BehaviorTree behaviorTree {
get { return _behaviorTree; }
set {
_behaviorTree = value;
if (_behaviorTree != null) {
_behaviorTree.nodeWillTick = OnNodeWillTick;
_behaviorTree.nodeDidTick = OnNodeDidTick;
}
}
}
public BTAsset btAsset;
public void OnNodeWillTick (Node node)
{
if (inspectedAgent.debugMode) {
Debug.Log (node.GetType ().Name);
}
}
public void OnNodeDidTick (Node node, Status result)
{
if (editorWindow != null)
editorWindow.Repaint ();
}
public static BTEditorManager Manager { get; private set; }
public static BTEditorManager CreateInstance (BehaviorTree bt, BTAsset asset)
{
if (Manager == null) {
Manager = (BTEditorManager)ScriptableObject.CreateInstance (typeof(BTEditorManager));
Manager.behaviorTree = bt;
Manager.btAsset = asset;
}
return Manager;
}
// Lifecycle
public void OnEnable ()
{
hideFlags = HideFlags.HideAndDontSave;
}
public void OnDestroy ()
{
Manager = null;
DestroyImmediate (behaviorTree);
}
// Asset management ------------------------------------------------------------------------------------------------------------------------------------
[MenuItem ("Coolape/New Behavior Tree", false, 3)]
static void CreateNewBehaviorTree (MenuCommand menuCommand)
{
// string path = AssetDatabase.GetAssetPath(Selection.activeObject);
// if (path == "")
// path = "Assets";
// else if (Path.GetExtension(path) != "")
// path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
string basePath = PStr.b ().a ("Assets/").a (CLPathCfg.self.basePath).a ("/xRes/carbonEvent/").e ();
string fullPath = EditorUtility.SaveFilePanel ("Save Carbon Event", basePath, "New Behavior Tree", "asset");
if (string.IsNullOrEmpty (fullPath)) {
return;
}
// fullPath = AssetDatabase.GenerateUniqueAssetPath (fullPath);
fullPath = fullPath.Substring(fullPath.IndexOf("/Assets/") + 1);
BehaviorTree bt = ScriptableObject.CreateInstance<BehaviorTree> ();
Root root = ScriptableObject.CreateInstance<Root> ();
root.editorPosition = new Vector2 (0, 0);
bt.SetRoot (root);
BTAsset btAsset = ScriptableObject.CreateInstance<BTAsset> ();
btAsset.Serialize (bt);
AssetDatabase.CreateAsset (btAsset, fullPath);
AssetDatabase.Refresh ();
EditorUtility.FocusProjectWindow ();
Selection.activeObject = btAsset;
}
public void Dirty ()
{
if (editorWindow != null)
editorWindow.Repaint ();
btAsset.Serialize (behaviorTree);
EditorUtility.SetDirty (btAsset);
}
// Behavior Tree manipulation --------------------------------------------------------------------------------------------------------------------------
public void Add (Node parent, Vector2 position, string nodeType)
{
Node node = null;
switch (nodeType) {
case "Action":
node = behaviorTree.CreateNode<NodeAction> ();
break;
case "Branch":
node = behaviorTree.CreateNode<NodeBranch> ();
break;
case "Together":
node = behaviorTree.CreateNode<NodeTogether> ();
break;
// case "Inverter":
// node = behaviorTree.CreateNode<Inverter>();
// break;
// case "Parallel":
// node = behaviorTree.CreateNode<Parallel>();
// break;
// case "RandomSelector":
// node = behaviorTree.CreateNode<RandomSelector>();
// break;
// case "Repeater":
// node = behaviorTree.CreateNode<Repeater>();
// break;
// case "Selector":
// node = behaviorTree.CreateNode<Selector>();
// break;
// case "Sequence":
// node = behaviorTree.CreateNode<Sequence>();
// break;
// case "Succeeder":
// node = behaviorTree.CreateNode<Succeeder>();
// break;
// case "UntilSucceed":
// node = behaviorTree.CreateNode<UntilSucceed>();
// break;
}
behaviorTree.nodes.Add (node);
if (parent != null && parent.CanConnectChild) {
if (parent.ChildCount > 0) {
Node lastSibling = parent.Children [parent.ChildCount - 1];
node.editorPosition = lastSibling.editorPosition + new Vector2 (GridRenderer.step.x * 10, 0);
} else {
node.editorPosition = new Vector2 (parent.editorPosition.x, parent.editorPosition.y + GridRenderer.step.y * 10);
}
parent.ConnectChild (node);
// SortChildren(parent);
} else {
float xOffset = position.x % GridRenderer.step.x;
float yOffset = position.y % GridRenderer.step.y;
float xoffset1 = editorWindow.view.scrollPoint.x;
float yoffset1 = editorWindow.view.scrollPoint.y;
node.editorPosition = new Vector2 (position.x - xOffset +xoffset1, position.y - yOffset+yoffset1);
}
Dirty ();
// Select the newly added node
if (editorWindow != null)
editorWindow.view.SelectNode (node);
}
public void Connect (Node parent, Node child)
{
if (parent == child)
return;
if (parent.CanConnectChild) {
parent.ConnectChild (child);
// SortChildren(parent);
Dirty ();
} else {
Debug.LogWarning (string.Format ("{0} can't accept child {1}", parent, child));
}
}
public void ConnectKill (Node parent, Node child)
{
if (parent == child)
return;
parent.ConnectKillChild (child);
Dirty ();
}
public void ConnectLeft (NodeBranch parent, Node child)
{
if (parent == child)
return;
if (parent.CanConnectChild) {
parent.ConnectLeftChild (child);
// SortChildren(parent);
Dirty ();
} else {
Debug.LogWarning (string.Format ("{0} can't accept child {1}", parent, child));
}
}
public void ConnectLeftKill (NodeBranch parent, Node child)
{
if (parent == child)
return;
parent.ConnectKillLeftChild (child);
Dirty ();
}
public void ConnectRight (NodeBranch parent, Node child)
{
if (parent == child)
return;
if (parent.CanConnectChild) {
parent.ConnectRightChild (child);
// SortChildren(parent);
Dirty ();
} else {
Debug.LogWarning (string.Format ("{0} can't accept child {1}", parent, child));
}
}
public void ConnectRightKill (NodeBranch parent, Node child)
{
if (parent == child)
return;
parent.ConnectKillRightChild (child);
Dirty ();
}
public void Unparent (Node node)
{
node.Unparent ();
Dirty ();
}
public void DisconnectAll (Node node)
{
node.Disconnect ();
Dirty ();
}
public void Delete (Node node)
{
node.Disconnect ();
behaviorTree.nodes.Remove (node);
DestroyImmediate (node, true);
Dirty ();
}
public void SetEditorPosition (Node node, Vector2 position)
{
node.editorPosition = position;
// SortChildren(node.parent);
Dirty ();
}
// private void SortChildren(Node parent) {
// Composite parentComposite = parent as Composite;
// if (parentComposite != null)
// parentComposite.SortChildren();
// }
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 73316990941e844698e84ea9cb88a931
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,513 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Collections;
using Coolape;
using System.Reflection;
using System;
using System.Linq;
namespace Hivemind
{
public class MenuAction
{
public string nodeType;
public Vector2 position;
public Node node;
public MenuAction (Node nodeVal)
{
node = nodeVal;
}
public MenuAction (Node nodeVal, Vector2 positionVal, string nodeTypeVal)
{
node = nodeVal;
position = positionVal;
nodeType = nodeTypeVal;
}
}
public class BTEditorWindow : EditorWindow
{
public View view;
private GUIStyle _noSelectionStyle;
private bool _debugMode;
[MenuItem ("Window/Behavior Tree Editor")]
public static void ShowWindow ()
{
BTEditorWindow editor = EditorWindow.GetWindow<BTEditorWindow> ();
editor.minSize = new Vector2 (480, 360);
editor.title = "Behavior Tree";
}
void OnSelectionChange ()
{
Repaint ();
}
void OnEnable ()
{
_noSelectionStyle = new GUIStyle ();
_noSelectionStyle.fontSize = 24;
_noSelectionStyle.alignment = TextAnchor.MiddleCenter;
if (BTEditorManager.Manager != null)
BTEditorManager.Manager.editorWindow = this;
}
void OnDisable ()
{
if (BTEditorManager.Manager)
BTEditorManager.Manager.editorWindow = null;
}
void OnDestroy ()
{
if (BTEditorManager.Manager)
BTEditorManager.Manager.editorWindow = null;
}
void OnGUI ()
{
if (BTEditorManager.Manager != null && BTEditorManager.Manager.behaviorTree != null) {
BTEditorManager.Manager.editorWindow = this;
if (view == null)
view = new View (this);
if (view.nodeInspector != null)
view.nodeInspector.OnInspectorGUI ();
bool viewNeedsRepaint = view.Draw (position);
if (viewNeedsRepaint)
Repaint ();
view.ResizeCanvas ();
} else {
GUI.Label (new Rect (0, 0, position.width, position.height), "No Behavior Tree selected", _noSelectionStyle);
}
}
public void ShowContextMenu (Vector2 point, Node node)
{
// if (Application.isPlaying) {
// return;
// }
var menu = new GenericMenu ();
if (node == null || node.CanConnectChild) {
// Add new node
string addMsg = (node == null ? "Add" : "Add Child") + "/";
// List all available node subclasses
int length = BehaviorTree.NodeTypes.Length;
for (int i = 0; i < length; i++) {
menu.AddItem (
new GUIContent (addMsg + BehaviorTree.NodeTypes [i]),
false,
Add,
new MenuAction (node, point, BehaviorTree.NodeTypes [i])
);
}
} else {
menu.AddDisabledItem (new GUIContent ("Add"));
}
int menuType = 0;
int selectedNodesCount = view.selectedNodes.Count;
if (selectedNodesCount == 0) {
menuType = 0;
} else if (selectedNodesCount == 1) {
menuType = 0;
} else if (selectedNodesCount == 2) {
menuType = 2;
} else {
menuType = 3;
}
if (menuType == 2) {
menu.AddItem (new GUIContent ("Disconnect From Selected Nodes"), false, DisconnectSelectedNodes, new MenuAction (node));
menu.AddSeparator ("");
menu.AddItem (new GUIContent ("Delete Selected Nodes"), false, DeleteSelectNodes, new MenuAction (node));
} else if (menuType == 3) {
//empty
menu.AddItem (new GUIContent ("Delete Selected Nodes"), false, DeleteSelectNodes, new MenuAction (node));
} else {
if (node == null) {
menu.AddSeparator ("");
menu.AddItem (new GUIContent ("Save"), false, Save, null);
menu.AddItem (new GUIContent ("Refresh Nodes Index"), false, RefreshNodesIndex, null);
menu.AddItem (new GUIContent ("Genrate Node State"), false, GenerateNodeState, null);
menu.AddItem (new GUIContent ("Export Data"), false, exportData, null);
}
menu.AddSeparator ("");
// Node actions
if (node != null) {
// Connect/Disconnect Parent
if (!(node is Root)) {
menu.AddItem (new GUIContent ("Disconnect from All Parent"), false, Unparent, new MenuAction (node));
menu.AddItem (new GUIContent ("Disconnect All"), false, DisconnectAll, new MenuAction (node));
menu.AddItem (new GUIContent ("Connect to Parent"), false, ConnectParent, new MenuAction (node));
}
menu.AddSeparator ("");
// Connect Child
if (node.CanConnectChild) {
if (node is NodeBranch) {
menu.AddItem (new GUIContent ("Connect to Left Child"), false, ConnectLeftChild, new MenuAction (node));
menu.AddItem (new GUIContent ("Connect to Left Kill Child"), false, ConnectLeftKillChild, new MenuAction (node));
menu.AddItem (new GUIContent ("Connect to Right Child"), false, ConnectRightChild, new MenuAction (node));
menu.AddItem (new GUIContent ("Connect to Right Kill Child"), false, ConnectRightKillChild, new MenuAction (node));
} else {
menu.AddItem (new GUIContent ("Connect to Child"), false, ConnectChild, new MenuAction (node));
menu.AddItem (new GUIContent ("Connect to Kill Child"), false, ConnectKillChild, new MenuAction (node));
}
} else {
menu.AddDisabledItem (new GUIContent ("Connect to Child"));
}
menu.AddSeparator ("");
// Deleting
if (node is Root)
menu.AddDisabledItem (new GUIContent ("Delete"));
else
menu.AddItem (new GUIContent ("Delete"), false, Delete, new MenuAction (node));
}
}
menu.DropDown (new Rect (point.x, point.y, 0, 0));
}
// Context Menu actions
public void Add (object userData)
{
MenuAction menuAction = userData as MenuAction;
BTEditorManager.Manager.Add (menuAction.node, menuAction.position, menuAction.nodeType);
view.ResizeCanvas ();
Repaint ();
}
public void Unparent (object userData)
{
MenuAction menuAction = userData as MenuAction;
BTEditorManager.Manager.Unparent (menuAction.node);
Repaint ();
}
public void DisconnectAll (object userData)
{
MenuAction menuAction = userData as MenuAction;
BTEditorManager.Manager.DisconnectAll (menuAction.node);
Repaint ();
}
public void ConnectParent (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectParent (menuAction.node);
}
public void ConnectChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectChild (menuAction.node);
}
public void ConnectKillChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectKillChild (menuAction.node);
}
public void ConnectLeftChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectLeftChild (menuAction.node);
}
public void ConnectLeftKillChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectLeftKillChild (menuAction.node);
}
public void ConnectRightChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectRightChild (menuAction.node);
}
public void ConnectRightKillChild (object userData)
{
MenuAction menuAction = userData as MenuAction;
view.ConnectRightKillChild (menuAction.node);
}
public void Delete (object userData)
{
MenuAction menuAction = userData as MenuAction;
BTEditorManager.Manager.Delete (menuAction.node);
Repaint ();
}
public void Save (object userData)
{
if (BTEditorManager.Manager != null
&& BTEditorManager.Manager.btAsset != null
&& BTEditorManager.Manager.behaviorTree != null) {
BTEditorManager.Manager.btAsset.Serialize (BTEditorManager.Manager.behaviorTree);
EditorUtility.SetDirty (BTEditorManager.Manager.btAsset);
}
AssetDatabase.Refresh ();
AssetDatabase.SaveAssets ();
Debug.Log ("Save");
}
public void DisconnectSelectedNodes (object userData)
{
if (view.selectedNodes.Count != 2)
return;
Node node1 = view.selectedNodes [0];
Node node2 = view.selectedNodes [1];
node1.DisconnectChild (node2);
node1.DisconnectKillChild (node2);
node1.Unparent (node2);
node2.DisconnectChild (node1);
node2.DisconnectKillChild (node1);
node2.Unparent (node1);
if (node1 is NodeBranch) {
((NodeBranch)node1).DisconnectLeftChild (node2);
((NodeBranch)node1).DisconnectRightChild (node2);
((NodeBranch)node1).DisconnectKillLeftChild (node2);
((NodeBranch)node1).DisconnectKillRightChild (node2);
}
if (node2 is NodeBranch) {
((NodeBranch)node2).DisconnectLeftChild (node1);
((NodeBranch)node2).DisconnectRightChild (node1);
((NodeBranch)node2).DisconnectKillLeftChild (node1);
((NodeBranch)node2).DisconnectKillRightChild (node1);
}
Repaint ();
// Save (userData);
}
public void DeleteSelectNodes (object userData)
{
for (int i = view.selectedNodes.Count - 1; i >= 0; i--) {
BTEditorManager.Manager.Delete (view.selectedNodes [i]);
}
view.selectedNodes.Clear ();
Repaint ();
// Save (userData);
}
public void RefreshNodesIndex (object userData)
{
Root node = BTEditorManager.Manager.behaviorTree.rootNode;
node.index = 0;
node.maxIndex = 0;
foreach (Node n in node.behaviorTree.nodes) {
if (n != node) {
node.maxIndex++;
n.index = node.begainIndex + node.maxIndex;
}
}
// Save (userData);
}
public void GenerateNodeState (object userData)
{
List<Node> nodes = BTEditorManager.Manager.behaviorTree.nodes;
Hashtable procedMap = new Hashtable ();
for (int i = 0; i < nodes.Count; i++) {
doGenerateNodeState (nodes [i], ref procedMap);
}
}
public void doGenerateNodeState (Node node, ref Hashtable map)
{
if (MapEx.getBool (map, node) || node == node.behaviorTree.rootNode) {
return;
}
map [node] = true;
int mainType = 14;
string pubStr = "";
if (node.behaviorTree.rootNode.attr != null) {
pubStr = mainType + "/" + ((MyRoot)(node.behaviorTree.rootNode.attr)).fubenID + "/";
} else {
pubStr = mainType + "/" + 0 + "/";
}
//Condition
if (node.parents != null && node.parents.Count > 0) {
node.condition = "{";
if ((node.parents.Count == 1 && (node.parents [0]) is Root)) {
node.condition += "{\"" + pubStr + node.index + "/" + 0 + "\"}";
} else if (node.parents.Contains (node.behaviorTree.rootNode)) {
node.condition += "{\"" + pubStr + node.index + "/" + 0 + "\",\"" + pubStr + node.index + "/" + 1 + "\"}";
} else {
node.condition += "{\"" + pubStr + node.index + "/" + 1 + "\"}";
}
if (node is NodeTogether) {
Node parent = null;
for (int i = 0; i < node.parents.Count; i++) {
parent = node.parents [i];
if (parent is Root) {
} else if (parent is NodeBranch) {
if (((NodeBranch)parent).inLeft (node)) {
node.condition += ",{\"" + pubStr + (-node.parents [i].index) + "/" + 1 + "\"}";
}
if (((NodeBranch)parent).inRight (node)) {
node.condition += ",{\"" + pubStr + (-node.parents [i].index) + "/" + 2 + "\"}";
}
} else {
node.condition += ",{\"" + pubStr + (-node.parents [i].index) + "/" + 1 + "\"}";
}
}
}
node.condition += "}";
} else {
node.condition = "{{\"" + pubStr + node.index + "/" + 0 + "\"}}";
}
//result
if (node is NodeBranch) {
NodeBranch branch = node as NodeBranch;
node.result1 = getResultWithSubNodes (branch, branch.ChildrenLeft, branch.KillLeftNodes);
node.result2 = getResultWithSubNodes (branch, branch.ChildrenRight, branch.KillRightNodes);
} else {
node.result1 = getResultWithSubNodes (node, node.Children, node.KillNodes);
node.result2 = node.result1;
}
}
public string getNodeResult (Node node, Node child)
{
string ret = "";
if (node is NodeBranch) {
if (((NodeBranch)node).inLeft (child)) {
if (child is NodeTogether) {
ret = -node.index + "/" + 1;
} else {
ret = child.index + "/" + 1;
}
}
if (((NodeBranch)node).inRight (child)) {
if (child is NodeTogether) {
ret = -node.index + "/" + 2;
} else {
ret = child.index + "/" + 1;
}
}
} else {
if (child is NodeTogether) {
ret = -node.index + "/" + 1;
} else {
ret = child.index + "/" + 1;
}
}
return ret;
}
public string getResultWithSubNodes (Node node, List<Node>children, List<Node>killChildren)
{
int mainType = 14;
string pubStr = "";
if (node.behaviorTree.rootNode.attr != null) {
pubStr = mainType + "/" + ((MyRoot)(node.behaviorTree.rootNode.attr)).fubenID + "/";
} else {
pubStr = mainType + "/" + 0 + "/";
}
string result = "{";
if (!node.canMultTimes) {
result = result + "\"" + pubStr + node.index + "/" + 9 + "\"";
}
if ((node is NodeAction) && ((((NodeAction)node).action) is MyAction)) {
int triggerType = ((MyAction)(((NodeAction)node).action)).triggerType;
if (node.canMultTimes && triggerType == 1) {
result = result + "\"" + pubStr + node.index + "/" + 9 + "\"";
Debug.LogError ("Node index=["+ node.index +"] have logic error, beacause it can multTimes and it triggered auto");
}
}
Node child = null;
for (int i = 0; i < children.Count; i++) {
child = children [i];
if (result.Length == 1) {
result = result + "\"" + pubStr + children [i].index + "/" + 1 + "\"";
} else {
result = result + ",\"" + pubStr + children [i].index + "/" + 1 + "\"";
}
if (children [i] is NodeTogether) {
result = result + ",\"" + pubStr + getNodeResult (node, children [i]) + "\"";
}
}
for (int i = 0; i < killChildren.Count; i++) {
if (killChildren [i] == null)
continue;
if (result.Length == 1) {
result = result + "\"" + pubStr + killChildren [i].index + "/" + 9 + "\"";
} else {
result = result + ",\"" + pubStr + killChildren [i].index + "/" + 9 + "\"";
}
result = result + ",\"" + pubStr + (-killChildren [i].index) + "/" + 9 + "\"";
}
result = result + "}";
if (result.Equals ("{}")) {
result = "";
}
return result;
}
//会调用加了【ExportHiveData】的函数
public void exportData (object userData)
{
GenerateNodeState (userData);
List<Node> nodes = BTEditorManager.Manager.behaviorTree.nodes;
callCustomExportHiveData (nodes);
EditorUtility.DisplayDialog ("Alert", "Finished!", "Okay");
}
static IEnumerable<MethodInfo> exportHiveDataMethodList = (from type in XLua.Utils.GetAllTypes ()
from method in type.GetMethods (BindingFlags.Static | BindingFlags.Public)
where method.IsDefined (typeof(ExportHiveDataAttribute), false)
select method);
static void callCustomExportHiveData (List<Node> nodes)
{
if (exportHiveDataMethodList.Count<MethodInfo> () == 0) {
Debug.LogError ("There is no customer export data function");
return;
}
foreach (var method in exportHiveDataMethodList) {
method.Invoke (null, new object[] { nodes });
}
}
}
public class ExportHiveDataAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6176253aeb08d47ec96605fb529f2f54
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,301 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Hivemind
{
public class HivemindInspector : Editor
{
private static GUIStyle _titleStyle;
private static GUIStyle _subtitleStyle;
public static GUIStyle TitleStyle {
get {
if (_titleStyle == null) {
_titleStyle = new GUIStyle ();
_titleStyle.fontSize = 18;
}
return _titleStyle;
}
}
public static GUIStyle SubtitleStyle {
get {
if (_subtitleStyle == null) {
_subtitleStyle = new GUIStyle ();
_subtitleStyle.fontSize = 15;
}
return _subtitleStyle;
}
}
}
[CustomEditor (typeof(BTAsset))]
public class BTInspector : HivemindInspector
{
private BTEditorManager manager;
public void OnEnable ()
{
BTAsset btAsset = (BTAsset)serializedObject.targetObject;
BehaviorTree bt = btAsset.Deserialize ();
manager = BTEditorManager.CreateInstance (bt, btAsset);
manager.btInspector = this;
}
public void OnDisable ()
{
DestroyImmediate (manager);
}
public override void OnInspectorGUI ()
{
if (BTEditorManager.Manager.nodeInspector == null) {
EditorGUILayout.LabelField ("Behavior Tree", TitleStyle);
if (manager.behaviorTree.nodes.Count > 2)
EditorGUILayout.LabelField (string.Format ("{0} nodes", manager.behaviorTree.nodes.Count - 1));
else if (manager.behaviorTree.nodes.Count == 2)
EditorGUILayout.LabelField ("Empty");
else
EditorGUILayout.LabelField ("1 node");
EditorGUILayout.Space ();
if (GUILayout.Button ("Show Behavior Tree editor")) {
BTEditorWindow.ShowWindow ();
}
} else {
manager.nodeInspector.OnInspectorGUI ();
}
if (GUI.changed) {
manager.Dirty ();
}
}
}
[CustomEditor (typeof(Node), true)]
public class BTNodeInspector : HivemindInspector
{
public override void OnInspectorGUI ()
{
Node node = (Node)serializedObject.targetObject;
if (node is Root) {
DrawInspector ((Root)node);
} else if (node is NodeBranch) {
DrawInspector ((NodeBranch)node);
} else if (node is NodeAction) {
DrawInspector ((NodeAction)node);
}
// else if (node is Selector) { DrawInspector ((Selector) node); }
// else if (node is Sequence) { DrawInspector ((Sequence) node); }
// else if (node is Parallel) { DrawInspector ((Parallel) node); }
// else if (node is Repeater) { DrawInspector ((Repeater) node); }
// else if (node is RandomSelector) { DrawInspector ((RandomSelector) node); }
// else if (node is UntilSucceed) { DrawInspector ((UntilSucceed) node); }
// else if (node is Inverter) { DrawInspector ((Inverter) node); }
// else if (node is Succeeder) { DrawInspector ((Succeeder) node); }
if (GUI.changed) {
BTEditorManager.Manager.Dirty ();
}
}
// private int IndexOf (string[] array, string target)
// {
// int length = array.Length;
// for (var i = 0; i < length; i++) {
// if (array [i] == target)
// return i;
// }
// return 0;
// }
public void DrawInspectorBase (Node node)
{
ECLEditorUtl.BeginContents ();
{
EditorGUILayout.LabelField ("State values");
EditorGUILayout.TextField (new GUIContent ("Conditon"), node.condition);
EditorGUILayout.TextField (new GUIContent ("Result1"), node.result1);
EditorGUILayout.TextField (new GUIContent ("Result2"), node.result2);
}
ECLEditorUtl.EndContents ();
}
public void DrawInspector (Root node)
{
EditorGUILayout.LabelField (new GUIContent ("Root"), TitleStyle);
EditorGUILayout.Space ();
node.begainIndex = EditorGUILayout.IntField (new GUIContent ("Begain Index"), node.begainIndex);
node.monoScript = (MonoScript)EditorGUILayout.ObjectField ("Root Script", node.monoScript, typeof(MonoScript), false);
if (node.monoScript != null) {
if (!node.monoScript.GetClass ().IsSubclassOf (typeof(ActionBase))) {
EditorGUILayout.HelpBox (string.Format ("{0} is not a subclass of Hivemind.ActionBase", node.monoScript.GetClass ().ToString ()), MessageType.Warning);
// action.monoScript = null;
} else {
}
}
if (node.attr != null) {
ECLEditorUtl.BeginContents ();
{
EditorGUILayout.LabelField ("Event Content");
node.attr.DrawInspector (node);
}
ECLEditorUtl.EndContents ();
}
EditorGUILayout.Space ();
if (GUILayout.Button ("Refresh Nodes Index")) {
node.index = 0;
node.maxIndex = 0;
foreach (Node n in node.behaviorTree.nodes) {
if (n != node) {
node.maxIndex++;
n.index = node.begainIndex + node.maxIndex;
}
}
}
}
public void DrawInspector (NodeAction node)
{
EditorGUILayout.LabelField (new GUIContent ("Action"), TitleStyle);
EditorGUILayout.Space ();
node.desc = EditorGUILayout.TextField (new GUIContent ("Desc"), node.desc);
node.canMultTimes = EditorGUILayout.Toggle("Run MultTimes", node.canMultTimes);
// MonoScript selection field
NodeAction action = (NodeAction)serializedObject.targetObject;
action.monoScript = (MonoScript)EditorGUILayout.ObjectField ("Action Script", action.monoScript, typeof(MonoScript), false);
if (action.monoScript != null) {
if (!action.monoScript.GetClass ().IsSubclassOf (typeof(ActionBase))) {
EditorGUILayout.HelpBox (string.Format ("{0} is not a subclass of Hivemind.ActionBase", action.monoScript.GetClass ().ToString ()), MessageType.Warning);
// action.monoScript = null;
} else {
}
}
if (action.action != null) {
ECLEditorUtl.BeginContents ();
{
EditorGUILayout.LabelField ("Event Content");
action.action.DrawInspector (node);
}
ECLEditorUtl.EndContents ();
}
EditorGUILayout.Space ();
DrawInspectorBase (node);
serializedObject.ApplyModifiedProperties ();
}
// private object DrawParamControl(System.Type type, string label, object value) {
// if (type == typeof(string)) {
// return EditorGUILayout.TextField(label, (string) value);
// }
// else if (type == typeof(float)) {
// return EditorGUILayout.FloatField(label, (float) value);
// }
// return null;
// }
// public void DrawInspector(Selector node) {
// EditorGUILayout.LabelField(new GUIContent("Selector"), TitleStyle);
// EditorGUILayout.Space ();
// node.rememberRunning = EditorGUILayout.Toggle("Remember running child", node.rememberRunning);
// string message = "The Selector node ticks its children sequentially from left to right, until one of them returns SUCCESS, RUNNING or ERROR, at which point it returns that state. If all children return the failure state, the priority also returns FAILURE.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// message = "If \"Remember running chikd\" is on, when a child returns RUNNING the Selector will remember that child, and in future ticks it will skip directly to that child until it returns something other than RUNNING.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// }
//
// public void DrawInspector(Sequence node) {
// EditorGUILayout.LabelField(new GUIContent("Sequence"), TitleStyle);
// EditorGUILayout.Space ();
// node.rememberRunning = EditorGUILayout.Toggle("Remember running child", node.rememberRunning);
// string message = "The Sequence node ticks its children sequentially from left to right, until one of them returns FAILURE, RUNNING or ERROR, at which point the Sequence returns that state. If all children return the success state, the sequence also returns SUCCESS.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// message = "If \"Remember running child\" is on, when a child returns RUNNING the Sequence will remember that child, and in future ticks it will skip directly to that child until it returns something other than RUNNING.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// }
//
// public void DrawInspector(Parallel node) {
// EditorGUILayout.LabelField(new GUIContent("Parallel"), TitleStyle);
// EditorGUILayout.Space ();
//
//
// node.strategy = (Parallel.ResolutionStrategy) EditorGUILayout.EnumPopup("Return Strategy", node.strategy);
// string message = "The parallel node ticks all children sequentially from left to right, regardless of their return states. It returns SUCCESS if the number of succeeding children is larger than a local constant S, FAILURE if the number of failing children is larger than a local constant F or RUNNING otherwise.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
//
// public void DrawInspector(Repeater node) {
// EditorGUILayout.LabelField(new GUIContent("Repeater"), TitleStyle);
// EditorGUILayout.Space ();
// string message = "Repeater decorator sends the tick signal to its child every time that its child returns a SUCCESS or FAILURE.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
//
// public void DrawInspector(Inverter node) {
// EditorGUILayout.LabelField(new GUIContent("Inveter"), TitleStyle);
// EditorGUILayout.Space ();
// string message = "Like the NOT operator, the inverter decorator negates the result of its child node, i.e., SUCCESS state becomes FAILURE, and FAILURE becomes SUCCESS. RUNNING or ERROR states are returned as is.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
//
// public void DrawInspector(Succeeder node) {
// EditorGUILayout.LabelField(new GUIContent("Succeeder"), TitleStyle);
// EditorGUILayout.Space ();
// string message = "Succeeder always returns a SUCCESS, no matter what its child returns.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
//
// public void DrawInspector(UntilSucceed node) {
// EditorGUILayout.LabelField(new GUIContent("Repeat Until Succeed"), TitleStyle);
// EditorGUILayout.Space ();
// string message = "This decorator keeps calling its child until the child returns a SUCCESS value. When this happen, the decorator return a SUCCESS state.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
//
// public void DrawInspector(RandomSelector node) {
// EditorGUILayout.LabelField(new GUIContent("Random Selector"), TitleStyle);
// EditorGUILayout.Space ();
// if (node.ChildCount > 0) {
// float chance = 100f / node.ChildCount;
// EditorGUILayout.LabelField(new GUIContent("Each child has a " + chance.ToString ("F1") + "% chance of being selected."), SubtitleStyle);
// }
// string message = "The Random Selector sends the tick signal to one of its children, selected at random, and returns the state returned by that child.";
// EditorGUILayout.HelpBox(message, MessageType.Info);
// EditorGUILayout.HelpBox("Not yet implemented!", MessageType.Error);
// }
public void DrawError ()
{
EditorGUILayout.LabelField ("Selected node is invalid");
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b96f652a0ef1a488fa6d57ad4e74bd74
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace Hivemind {
public class GridRenderer {
Texture2D gridTex;
public static int width { get { return 120; } }
public static int height { get { return 120; } }
public static Vector2 step { get { return new Vector2(width / 10, height / 10); } }
void GenerateGrid() {
gridTex = new Texture2D(width, height);
gridTex.hideFlags = HideFlags.DontSave;
Color bg = new Color(0.365f, 0.365f, 0.365f);
Color dark = new Color(0.278f, 0.278f, 0.278f);
Color light = new Color(0.329f, 0.329f, 0.329f);
Color darkX = new Color(0.216f, 0.216f, 0.216f);
Color lightX = new Color(0.298f, 0.298f, 0.298f);
for (int x = 0; x < width; x ++) {
for (int y = 0; y < height; y ++) {
if (x == 0 && y == 0)
gridTex.SetPixel(x, y, darkX);
else if (x == 0 || y == 0)
gridTex.SetPixel(x, y, dark);
else if (x % step.x == 0 && y % step.y == 0)
gridTex.SetPixel(x, y, lightX);
else if (x % step.x == 0 || y % step.y == 0)
gridTex.SetPixel(x, y, light);
else
gridTex.SetPixel(x, y, bg);
}
}
gridTex.Apply();
}
public void Draw(Vector2 scrollPoint, Rect canvas) {
if (!gridTex) GenerateGrid ();
float yOffset = scrollPoint.y % gridTex.height;
float yStart = scrollPoint.y - yOffset;
float yEnd = scrollPoint.y + canvas.height + yOffset;
float xOffset = scrollPoint.x % gridTex.width;
float xStart = scrollPoint.x - xOffset;
float xEnd = scrollPoint.x + canvas.width + xOffset;
for (float x = xStart; x < xEnd; x += gridTex.width) {
for (float y = yStart; y < yEnd; y += gridTex.height) {
GUI.DrawTexture(new Rect(x, y, gridTex.width, gridTex.height), gridTex);
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 022fe7acf7ac94fd29ae89afc1feb0d8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,281 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
namespace Hivemind
{
public class NodeRenderer
{
Texture2D nodeTexture;
Texture2D nodeDebugTexture;
Texture2D shadowTexture;
Color edgeColor = Color.white;
Color shadowColor = new Color (0f, 0f, 0f, 0.15f);
// Selection
Texture2D selectionTexture;
Color selColor = new Color (1f, .78f, .353f);
float selMargin = 2f;
float selWidth = 2f;
private Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D> ();
public static float Width { get { return GridRenderer.step.x * 6; } }
public static float Height { get { return GridRenderer.step.y * 6; } }
public void Draw (Node node, bool selected)
{
float shadowOffset = 3;
Vector2 offset = new Vector2 (shadowOffset, shadowOffset);
// Edge
for (int i = 0; i < node.parents.Count; i++) {
if (node.parents [i] is NodeBranch) {
Vector2 startPos = node.parents [i].editorPosition;
if (((NodeBranch)(node.parents [i])).inLeft (node)) {
startPos.x -= (NodeRenderer.Width / 4);
// Shadow
DrawEdge (startPos + offset, node.editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (startPos, node.editorPosition, Width, Height, edgeColor);
}
startPos = node.parents [i].editorPosition;
if (((NodeBranch)(node.parents [i])).inRight (node)) {
startPos.x += (NodeRenderer.Width / 4);
// Shadow
DrawEdge (startPos + offset, node.editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (startPos, node.editorPosition, Width, Height, edgeColor);
}
} else {
// Shadow
DrawEdge (node.parents [i].editorPosition + offset, node.editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (node.parents [i].editorPosition, node.editorPosition, Width, Height, edgeColor);
}
}
//Kill nodes
if (node is NodeBranch) {
NodeBranch branch = node as NodeBranch;
for (int i = 0; i < branch.KillLeftNodes.Count; i++) {
Vector2 startPos = node.editorPosition;
startPos.x -= (NodeRenderer.Width / 4);
// Shadow
DrawEdge (startPos + offset, branch.KillLeftNodes [i].editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (startPos, branch.KillLeftNodes [i].editorPosition, Width, Height, Color.red);
}
for (int i = 0; i < branch.KillRightNodes.Count; i++) {
if (branch.KillRightNodes [i] == null)
continue;
Vector2 startPos = node.editorPosition;
startPos.x += (NodeRenderer.Width / 4);
// Shadow
DrawEdge (startPos + offset, branch.KillRightNodes [i].editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (startPos, branch.KillRightNodes [i].editorPosition, Width, Height, Color.red);
}
} else {
for (int i = 0; i < node.KillNodes.Count; i++) {
// Shadow
DrawEdge (node.editorPosition + offset, node.KillNodes [i].editorPosition + offset, Width, Height, shadowColor);
// Line
DrawEdge (node.editorPosition, node.KillNodes [i].editorPosition, Width, Height, Color.red);
}
}
// Node Shadow
Rect nodeRect = new Rect (node.editorPosition.x, node.editorPosition.y, Width, Height);
Rect shadowRect = new Rect (nodeRect.x + shadowOffset, nodeRect.y + shadowOffset, nodeRect.width, nodeRect.height);
if (shadowTexture == null) {
shadowTexture = new Texture2D (1, 1);
shadowTexture.hideFlags = HideFlags.DontSave;
shadowTexture.SetPixel (0, 0, shadowColor);
shadowTexture.Apply ();
}
GUI.DrawTexture (shadowRect, shadowTexture);
// Node
if (nodeTexture == null) {
Color colA = new Color (0.765f, 0.765f, 0.765f);
Color colB = new Color (0.886f, 0.886f, 0.886f);
nodeTexture = new Texture2D (1, (int)Height);
nodeTexture.hideFlags = HideFlags.DontSave;
for (int y = 0; y < Height; y++) {
nodeTexture.SetPixel (0, y, Color.Lerp (colA, colB, (float)y / 75));
}
nodeTexture.Apply ();
}
// Node Debug
if (nodeDebugTexture == null) {
Color colA = new Color (1.000f, 0.796f, 0.357f);
Color colB = new Color (0.894f, 0.443f, 0.008f);
nodeDebugTexture = new Texture2D (1, (int)Height);
nodeDebugTexture.hideFlags = HideFlags.DontSave;
for (int y = 0; y < Height; y++) {
nodeDebugTexture.SetPixel (0, y, Color.Lerp (colA, colB, (float)y / 75));
}
nodeDebugTexture.Apply ();
}
if (node.behaviorTree.debugMode && node.behaviorTree.currentNode == node) {
GUI.DrawTexture (nodeRect, nodeDebugTexture);
} else {
GUI.DrawTexture (nodeRect, nodeTexture);
}
// Icons
DrawNodeIcon (nodeRect, node);
GUI.color = Color.black;
EditorGUI.LabelField (new Rect (nodeRect.x, nodeRect.y + 58, nodeRect.width, nodeRect.height), node.index.ToString ());
GUI.color = Color.white;
DrawMuttimesIcon (nodeRect, node);
// Debug status
// DrawStatusIcon (nodeRect, node);
// Action title
if (node is NodeAction) {
// Node title
string title = node.desc;
// if (((Action)node).methodInfo != null)
// title = ((Action)node).methodName;
// else
// title = "";
title = title.Replace (".", ".\n");
Vector2 textSize = GUI.skin.label.CalcSize (new GUIContent (title));
float x = node.editorPosition.x + (Width / 2) - (textSize.x / 2) - 6;
Rect titleRect = new Rect (x, node.editorPosition.y + Height, textSize.x + 10, textSize.y);
EditorGUI.DropShadowLabel (titleRect, new GUIContent (title));
}
// Selection highlight
if (selected) {
if (selectionTexture == null) {
selectionTexture = new Texture2D (1, 1);
selectionTexture.hideFlags = HideFlags.DontSave;
selectionTexture.SetPixel (0, 0, selColor);
selectionTexture.Apply ();
}
float mbOffset = selMargin + selWidth; // Margin + Border offset
GUI.DrawTexture (new Rect (nodeRect.x - mbOffset, nodeRect.y - mbOffset, nodeRect.width + mbOffset * 2, selWidth), selectionTexture); // Top
GUI.DrawTexture (new Rect (nodeRect.x - mbOffset, nodeRect.y - selMargin, selWidth, nodeRect.height + selMargin * 2), selectionTexture); // Left
GUI.DrawTexture (new Rect (nodeRect.x + nodeRect.width + selMargin, nodeRect.y - selMargin, selWidth, nodeRect.height + selMargin * 2), selectionTexture); // Right
GUI.DrawTexture (new Rect (nodeRect.x - mbOffset, nodeRect.y + nodeRect.height + selMargin, nodeRect.width + mbOffset * 2, selWidth), selectionTexture); // Top
}
}
private void DrawMuttimesIcon (Rect nodeRect, Node node)
{
if (node.canMultTimes) {
if (!textures.ContainsKey ("canMultTimes")) {
Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath<Texture2D> ("Assets/CoolapeFrame/Hivemind/EditorResources/Status/Running.png");
if (tex == null) {
// Debug.LogWarning (status + ".png not found");
return;
}
tex.hideFlags = HideFlags.DontSave;
textures.Add ("canMultTimes", tex);
}
Rect statusRect = new Rect (nodeRect.x, nodeRect.y, 32f, 32f);
GUI.DrawTexture (statusRect, textures ["canMultTimes"]);
}
}
private void DrawStatusIcon (Rect nodeRect, Node node)
{
EditorGUI.LabelField (new Rect (nodeRect.x, nodeRect.y + 58f, nodeRect.width, nodeRect.height), node.lastTick.ToString ());
if (node.lastStatus != null && BTEditorManager.Manager.behaviorTree.TotalTicks == node.lastTick) {
string status = node.lastStatus.ToString ();
if (!textures.ContainsKey (status)) {
Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath<Texture2D> ("Assets/CoolapeFrame/Hivemind/EditorResources/Status/" + status + ".png");
if (tex == null) {
Debug.LogWarning (status + ".png not found");
return;
}
tex.hideFlags = HideFlags.DontSave;
textures.Add (status, tex);
}
Rect statusRect = new Rect (nodeRect.x, nodeRect.y, 32f, 32f);
GUI.DrawTexture (statusRect, textures [status]);
}
}
private void DrawNodeIcon (Rect nodeRect, Node node)
{
int width = NearestPowerOfTwo (nodeRect.width);
int height = NearestPowerOfTwo (nodeRect.height);
float xOffset = (nodeRect.width - width) / 2;
float yOffset = (nodeRect.height - height) / 2;
Rect iconRect = new Rect (nodeRect.x + xOffset, nodeRect.y + yOffset, width, height);
string nodeName = node.GetType ().Name;
// if (node is Sequence && ((Sequence) node).rememberRunning) nodeName = "MemSequence";
// if (node is Selector && ((Selector) node).rememberRunning) nodeName = "MemSelector";
if (!textures.ContainsKey (nodeName)) {
Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath<Texture2D> ("Assets/CoolapeFrame/Hivemind/EditorResources/Nodes/" + nodeName + ".png");
if (tex == null) {
Debug.LogWarning (nodeName + ".png not found");
return;
}
tex.hideFlags = HideFlags.DontSave;
textures.Add (nodeName, tex);
}
GUI.DrawTexture (iconRect, textures [nodeName]);
}
int NearestPowerOfTwo (float value)
{
int result = 1;
do {
result = result << 1;
} while (result << 1 < value);
return result;
}
public static void DrawEdge (Vector2 start, Vector2 end, float width, float height, Color color)
{
float offset = width / 2;
Vector3 startPos = new Vector3 (start.x + offset, start.y + height, 0);
Vector3 endPos = new Vector3 (end.x + offset, end.y, 0);
Vector3 startTan = startPos + Vector3.up * GridRenderer.step.x * 2;
Vector3 endTan = endPos + Vector3.down * GridRenderer.step.x * 2;
Handles.DrawBezier (startPos, endPos, startTan, endTan, color, null, 4);
// Handles.ArrowCap(0, endPos, Quaternion.FromToRotation(new Vector3(0, 0, 1), new Vector3(0, 0, 1)), 154);
Handles.color = color;
Handles.DrawSolidDisc (endPos, new Vector3 (0, 0, 1), 5);
Handles.color = Color.white;
}
public Rect rectForNode (Node node, Vector2 offset)
{
return new Rect (node.editorPosition.x - offset.x, node.editorPosition.y - offset.y, Width, Height);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: dfe6e92e7477f475e985f16aef49f009
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,488 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
namespace Hivemind
{
enum Mode
{
NodeAction,
CanvasAction,
DragNode,
PanCanvas,
ConnectParent,
ConnectChild,
ConnectKillChild,
ConnectLeftChild,
ConnectLeftKillChild,
ConnectRightChild,
ConnectRightKillChild,
InvokeMenu,
None
}
public class View
{
GridRenderer gridRenderer;
Rect canvas;
public Vector2 scrollPoint = Vector2.zero;
NodeRenderer nodeRenderer;
public Editor nodeInspector;
BTEditorWindow editorWindow;
Mode currentMode = Mode.None;
Node contextNode;
Vector2 initialMousePosition = Vector2.zero;
public List<Node> selectedNodes = new List<Node> ();
Vector2 nodeActionOffset = Vector2.zero;
public View (BTEditorWindow owner)
{
editorWindow = owner;
canvas = new Rect (0, 0, owner.position.width, owner.position.height);
}
void DrawNodes (List<Node> nodes)
{
if (nodeRenderer == null)
nodeRenderer = new NodeRenderer ();
int count = nodes.Count;
for (int i = 0; i < count; i++) {
if (nodes [i] != null) {
nodeRenderer.Draw (nodes [i], selectedNodes.Contains (nodes [i]));
}
}
}
public bool Draw (Rect position)
{
bool needsRepaint = HandleMouseEvents (position, BTEditorManager.Manager.behaviorTree.nodes);
scrollPoint = GUI.BeginScrollView (new Rect (0, 0, position.width, position.height), scrollPoint, canvas);
if (gridRenderer == null)
gridRenderer = new GridRenderer ();
gridRenderer.Draw (scrollPoint, canvas);
DrawNodes (BTEditorManager.Manager.behaviorTree.nodes);
if (currentMode == Mode.ConnectChild || currentMode == Mode.ConnectParent
|| currentMode == Mode.ConnectLeftChild || currentMode == Mode.ConnectRightChild) {
DrawConnectionLine (Color.white);
needsRepaint = true;
} else if (currentMode == Mode.ConnectKillChild
|| currentMode == Mode.ConnectRightKillChild
|| currentMode == Mode.ConnectLeftKillChild) {
DrawConnectionLine (Color.red);
needsRepaint = true;
}
GUI.EndScrollView ();
return needsRepaint;
}
void DrawConnectionLine (Color color)
{
Vector3 startPos = Vector3.zero;
Vector3 startTan = Vector3.zero;
Vector3 endPos = new Vector3 (Event.current.mousePosition.x, Event.current.mousePosition.y, 0);
Vector3 endTan = Vector3.zero;
if (currentMode == Mode.ConnectParent) {
startPos = new Vector3 (contextNode.editorPosition.x + (NodeRenderer.Width / 2), contextNode.editorPosition.y, 0);
startTan = startPos + Vector3.down * GridRenderer.step.x * 2;
endTan = endPos + Vector3.up * GridRenderer.step.x * 2;
} else if (currentMode == Mode.ConnectChild || currentMode == Mode.ConnectKillChild) {
startPos = new Vector3 (contextNode.editorPosition.x + (NodeRenderer.Width / 2), contextNode.editorPosition.y + NodeRenderer.Height, 0);
startTan = startPos + Vector3.up * GridRenderer.step.x * 2;
endTan = endPos + Vector3.down * GridRenderer.step.x * 2;
} else if (currentMode == Mode.ConnectLeftChild || currentMode == Mode.ConnectLeftKillChild) {
startPos = new Vector3 (contextNode.editorPosition.x + (NodeRenderer.Width / 2) - (NodeRenderer.Width / 4), contextNode.editorPosition.y + NodeRenderer.Height, 0);
startTan = startPos + Vector3.up * GridRenderer.step.x * 2;
endTan = endPos + Vector3.down * GridRenderer.step.x * 2;
} else if (currentMode == Mode.ConnectRightChild || currentMode == Mode.ConnectRightKillChild) {
startPos = new Vector3 (contextNode.editorPosition.x + (NodeRenderer.Width / 2) + (NodeRenderer.Width / 4), contextNode.editorPosition.y + NodeRenderer.Height, 0);
startTan = startPos + Vector3.up * GridRenderer.step.x * 2;
endTan = endPos + Vector3.down * GridRenderer.step.x * 2;
}
Handles.DrawBezier (startPos, endPos, startTan, endTan, color, null, 4);
// Handles.ArrowCap(0, endPos, Quaternion.FromToRotation(startPos, endPos), 54);
Handles.DrawWireCube (endPos, Vector3.one * 10);
}
// Returns true if needs a repaint
bool HandleMouseEvents (Rect position, List<Node> nodes)
{
// MouseDown //
// Identify the control being clicked
if (Event.current.type == EventType.MouseDown) {
// Do nothing for MouseDown on the horizontal scrollbar, if present
if (canvas.width > position.width && Event.current.mousePosition.y >= position.height - 20) {
currentMode = Mode.None;
}
// Do nothing for MouseDown on the vertical scrollbar, if present
else if (canvas.height > position.height && Event.current.mousePosition.x >= position.width - 20) {
currentMode = Mode.None;
}
// MouseDown in the canvas, check if in a node or on background
else {
// Store the mouse position
initialMousePosition = Event.current.mousePosition;
// Loop through nodes and check if their rects contain the mouse position
for (int i = 0; i < nodes.Count; i++) {
if (nodes [i] != null && nodeRenderer.rectForNode (nodes [i], scrollPoint).Contains (Event.current.mousePosition)) {
// Connect a parent to a child
if (contextNode is NodeBranch) {
if (currentMode == Mode.ConnectLeftChild) {
BTEditorManager.Manager.ConnectLeft ((NodeBranch)contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
} else if (currentMode == Mode.ConnectLeftKillChild) {
BTEditorManager.Manager.ConnectLeftKill ((NodeBranch)contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
} else if (currentMode == Mode.ConnectRightChild) {
BTEditorManager.Manager.ConnectRight ((NodeBranch)contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
} else if (currentMode == Mode.ConnectRightKillChild) {
BTEditorManager.Manager.ConnectRightKill ((NodeBranch)contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
} else if (currentMode == Mode.ConnectParent) {
BTEditorManager.Manager.Connect (nodes [i], contextNode);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
}
// Perform a node action at key up
else {
currentMode = Mode.NodeAction;
contextNode = nodes [i];
nodeActionOffset = Event.current.mousePosition - nodes [i].editorPosition;
}
} else {
if (currentMode == Mode.ConnectChild) {
BTEditorManager.Manager.Connect (contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
} else if (currentMode == Mode.ConnectKillChild) {
BTEditorManager.Manager.ConnectKill (contextNode, nodes [i]);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
}
// Connect a child to a parent
else if (currentMode == Mode.ConnectParent) {
BTEditorManager.Manager.Connect (nodes [i], contextNode);
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
break;
}
// Perform a node action at key up
else {
currentMode = Mode.NodeAction;
contextNode = nodes [i];
nodeActionOffset = Event.current.mousePosition - nodes [i].editorPosition;
}
}
}
}
// Cancel the connection
if (currentMode == Mode.ConnectParent || currentMode == Mode.ConnectChild
|| currentMode == Mode.ConnectLeftChild || currentMode == Mode.ConnectRightChild
|| currentMode == Mode.ConnectKillChild || currentMode == Mode.ConnectLeftKillChild
|| currentMode == Mode.ConnectRightKillChild) {
editorWindow.wantsMouseMove = false;
currentMode = Mode.None;
}
// MouseDown on the canvas background enables panning the view
if (currentMode == Mode.None) {
currentMode = Mode.CanvasAction;
}
}
}
// Mouse Up //
// MouseUp resets the current interaction mode to None
if (Event.current.type == EventType.MouseUp) {
// Select node
if (currentMode == Mode.NodeAction && Event.current.button == 0) {
currentMode = Mode.None;
SelectNode (contextNode);
return true;
}
// Deselect node
else if (currentMode == Mode.CanvasAction && Event.current.button == 0) {
SelectNode (null);
currentMode = Mode.None;
return true;
}
// Context Menu
else if (Event.current.button == 1) {
if (currentMode == Mode.NodeAction) {
editorWindow.ShowContextMenu (Event.current.mousePosition, contextNode);
} else if (currentMode == Mode.CanvasAction) {
editorWindow.ShowContextMenu (Event.current.mousePosition, null);
}
currentMode = Mode.None;
}
// Resize canvas after a drag
else if (currentMode == Mode.DragNode) {
ResizeCanvas ();
currentMode = Mode.None;
return true;
} else {
currentMode = Mode.None;
}
}
// Mouse Drag //
if (Event.current.type == EventType.MouseDrag && Event.current.button == 0) {
// Switch to Pan mode
if (currentMode == Mode.CanvasAction) {
currentMode = Mode.PanCanvas;
}
// Switch to node dragging mode
if (currentMode == Mode.NodeAction && contextNode != null) {
float deltaX = Mathf.Abs (Event.current.mousePosition.x - initialMousePosition.x);
float deltaY = Mathf.Abs (Event.current.mousePosition.y - initialMousePosition.y);
// Ignore mouse drags inside nodes lesser than the grid step. These would be rounded,
// and make selecting a node slightly more difficult.
if (deltaX >= GridRenderer.step.x || deltaY >= GridRenderer.step.y) {
currentMode = Mode.DragNode;
}
}
// Pan if the mouse drag initiated by MouseDown outside any windows
if (currentMode == Mode.PanCanvas) {
scrollPoint.x += -Event.current.delta.x;
scrollPoint.y += -Event.current.delta.y;
currentMode = Mode.PanCanvas;
return true;
}
// Drag a node
if (currentMode == Mode.DragNode) {
Vector2 newPositionAbs = Event.current.mousePosition - nodeActionOffset;
float x = newPositionAbs.x - (newPositionAbs.x % GridRenderer.step.x);
float y = newPositionAbs.y - (newPositionAbs.y % GridRenderer.step.y);
DragNode (contextNode, new Vector2 (x, y));
currentMode = Mode.DragNode;
return true;
}
}
return false;
}
public void ResizeCanvas ()
{
Rect newCanvas = new Rect (0, 0, editorWindow.position.width, editorWindow.position.height);
foreach (Node node in BTEditorManager.Manager.behaviorTree.nodes) {
float xOffset = node.editorPosition.x + NodeRenderer.Width + GridRenderer.step.x * 2;
if (xOffset > newCanvas.width) {
newCanvas.width = xOffset;
}
float yOffset = node.editorPosition.y + NodeRenderer.Height + GridRenderer.step.y * 2;
if (yOffset > newCanvas.height) {
newCanvas.height = yOffset;
}
canvas = newCanvas;
}
}
public void SelectNode (Node node)
{
if (node == null) {
selectedNodes.Clear ();
} else {
if (Event.current != null && (Event.current.command || Event.current.control)) {
if (!selectedNodes.Contains (node)) {
selectedNodes.Add (node);
}
} else {
selectedNodes.Clear ();
selectedNodes.Add (node);
}
}
Editor nodeInspector = Editor.CreateEditor (node);
if (nodeInspector != null) {
BTEditorManager.Manager.nodeInspector = nodeInspector;
BTEditorManager.Manager.nodeInspector.Repaint ();
} else if (BTEditorManager.Manager.btInspector != null) {
BTEditorManager.Manager.nodeInspector = null;
BTEditorManager.Manager.btInspector.Repaint ();
}
}
private void DragNode (Node node, Vector2 newPosition)
{
// if (Application.isPlaying) {
// return;
// }
Hashtable map = new Hashtable ();
doDragNode (node, newPosition, ref map);
map.Clear ();
map = null;
}
private void doDragNode (Node node, Vector2 newPosition, ref Hashtable map)
{
Node _node;
map [node] = true;
if (Event.current.shift) {
if (node.ChildCount > 0) {
for (int i = 0; i < node.ChildCount; i++) {
_node = node.Children [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
}
for (int i = 0; i < node.KillNodes.Count; i++) {
_node = node.KillNodes [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
if (node is NodeBranch) {
for (int i = 0; i < ((NodeBranch)node).ChildrenLeft.Count; i++) {
_node = ((NodeBranch)node).ChildrenLeft [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
for (int i = 0; i < ((NodeBranch)node).KillLeftNodes.Count; i++) {
_node = ((NodeBranch)node).KillLeftNodes [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
for (int i = 0; i < ((NodeBranch)node).ChildrenRight.Count; i++) {
_node = ((NodeBranch)node).ChildrenRight [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
for (int i = 0; i < ((NodeBranch)node).KillRightNodes.Count; i++) {
_node = ((NodeBranch)node).KillRightNodes [i];
Vector2 childOffset = _node.editorPosition - node.editorPosition;
Vector2 newChildPosition = newPosition + childOffset;
if (map [_node] == null) {
doDragNode (_node, newChildPosition, ref map);
}
}
}
}
BTEditorManager.Manager.SetEditorPosition (node, newPosition);
}
public void ConnectParent (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectParent;
}
public void ConnectChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectChild;
}
public void ConnectKillChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectKillChild;
}
public void ConnectLeftChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectLeftChild;
}
public void ConnectLeftKillChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectLeftKillChild;
}
public void ConnectRightChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectRightChild;
}
public void ConnectRightKillChild (Node node)
{
editorWindow.wantsMouseMove = true;
contextNode = node;
currentMode = Mode.ConnectRightKillChild;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e0a34463203824fe3a3b375ee3725023
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: