private void DrawMap() { Vector2 offset = this.offset + new Vector2(mapRect.width / 2, mapRect.height / 2); DrawGrid(20.0f * zoomLevel, 0.2f, Color.gray, offset); GUIStyle boxStyle = skin.GetStyle("boxStyle"); GUIStyle connectionStyle = skin.GetStyle("connectionStyle"); GUIStyle boolStyle = skin.GetStyle("boolStyle"); boxStyle.fontSize = boolStyle.fontSize = (int)(BOX_STYLE_FONT_SIZE * zoomLevel); for (int i = 0; i < allNodes.Length; i++) { // Get and set the position of the node Rect toDraw = allNodes[i].boundsInEditor; toDraw.position *= zoomLevel; toDraw.width *= zoomLevel; toDraw.height *= zoomLevel; toDraw.position += offset; // Draw the node on screen and color it accourding to the status it in Color oldColor = GUI.color; if (inPlayMode) // if in playmode color by current status { GUI.color = colorForStatus[allNodes[i].CurrentStatus]; } else if (allNodes[i] == selectedObject) { GUI.color = currentSelectedColor; } else if (allNodes[i].BreakPointEnabled)// if in editor mode color by breakpoint { GUI.color = breakPointEnabledColor; } else { GUI.color = breakPointDisabledColor; } GUIContent content = new GUIContent(allNodes[i].StringInEditor + "\n" + allNodes[i].name, allNodes[i].StringToolTip); GUI.Box(toDraw, content, allNodes[i].GetType().IsSubclassOf(typeof(BoolNode)) ? boolStyle : boxStyle); GUI.color = oldColor; // Get the connection boxes (parent/child connection sockets) GetConnectionBoxes(allNodes[i], out Rect upper, out Rect lower); // Set positions of the 2 connection boxes upper.width *= zoomLevel; upper.height *= zoomLevel; upper.position = (upper.position * zoomLevel) + offset; lower.width *= zoomLevel; lower.height *= zoomLevel; lower.position = (lower.position * zoomLevel) + offset; // Draw connection boxes if (rootNode != allNodes[i]) { GUI.Box(upper, "", connectionStyle); } if (allNodes[i].MaxNumberOfChildren != 0) { GUI.Box(lower, "", connectionStyle); } } // Draw the connections of each nodes with bezier curves for (int i = 0; i < allNodes.Length; i++) { BNode[] children = allNodes[i].Children; if (children == null) { continue; } for (int j = 0; j < children.Length; j++) { Vector2 from = (GetUpperMiddle(children[j]) * zoomLevel) + offset; Vector2 to = (GetLowerMiddle(allNodes[i]) * zoomLevel) + offset; Handles.DrawBezier(from, to, from + Vector2.down * 50f * zoomLevel, to - Vector2.down * 50f * zoomLevel, Color.black, null, 2f); } } // If the user is currently dragging a node, draw from the connecting node to the mouse cursor. if (currentDrag == DragType.NodeConnection) { BNode node = connectionConstructor.origin; Vector2 from = (GetUpperMiddle(node) * zoomLevel) + offset; Vector2 to = (connectionConstructor.mousePos * zoomLevel) + offset; Handles.DrawBezier(from, to, from + Vector2.down * 50f * zoomLevel, to - Vector2.down * 50f * zoomLevel, Color.black, null, 2f); } }
private void ProcessEvents(Event e) { if (!mapRect.Contains(e.mousePosition)) // if mouse not inside the middle map area { return; } bool used = true; Vector2 mousePos = (e.mousePosition - offset - mapRect.position - new Vector2(mapRect.width / 2, mapRect.height / 2)) / zoomLevel; switch (e.type) { case EventType.MouseDown: if (e.button == 0) // left click { dragged = false; currentDrag = DragType.Position; if (!GetNodeFromPosition(mousePos, out BNode node)) // is mouse hovering over box { break; } // check if the click was on one of the connection boxes if (inPlayMode == false && GetConnection(mousePos, node, out bool isTop) && isTop) { if (node == rootNode) { Debug.LogWarning("Root node cannot have parents! Change the root node first before adding new parents to this node!"); break; } currentDrag = DragType.NodeConnection; RemoveConnection(node); Vector3 connectionMiddlePos = isTop ? GetUpperMiddle(node) : GetLowerMiddle(node); connectionConstructor = new ConnectionConstructor(node, connectionMiddlePos, isTop); } else // click was not on the connection boxes { currentDrag = DragType.Node; clickedNode = node; } } break; case EventType.MouseDrag: if (e.button == 0) // left click { switch (currentDrag) { case DragType.None: goto MouseDragFinished; // skip the changed set to true flag case DragType.Node: if (inPlayMode == true) { break; } Vector2 toAdd = e.delta; Rect toChange = clickedNode.boundsInEditor; toChange.position += toAdd / zoomLevel; clickedNode.boundsInEditor = toChange; break; case DragType.Position: offset += e.delta; break; case DragType.NodeConnection: Vector2 nodeOffset = offset + new Vector2(mapRect.width / 2, mapRect.height / 2); BNode node = connectionConstructor.Value.origin; Vector2 from = (GetUpperMiddle(node)) + nodeOffset; Vector2 to = from - new Vector2(100, 100); Handles.DrawBezier(from, to, from + Vector2.down * 50f, to - Vector2.down * 50f, Color.black, null, 2f); break; } GUI.changed = true; dragged = true; MouseDragFinished :; } break; case EventType.MouseUp: if (e.button == 0) // left click { if (dragged == false) { if (clickedNode != null) { selectedObject = clickedNode; } } else // if dragged == true { if (currentDrag == DragType.Node) // Save the position of the dragged node { Rect toChange = clickedNode.boundsInEditor; ShapesUtil.RectRoundToNextInt(ref toChange); clickedNode.boundsInEditor = toChange; // The node was moved so the order of the parented children might be wrong if (GetParentNode(clickedNode, out BNode parent)) { SortChildren(parent); } AssetDatabase.SaveAssets(); } else if (currentDrag == DragType.NodeConnection) // try to connect the node to its new parent { if (GetNodeFromPosition(mousePos, out BNode newParent)) // if mouse is over hovering over a node { if (newParent == connectionConstructor.Value.origin) // if newParent is self { goto NodeConnectionFailed; } // if parent already has the maximum number of children if (newParent.MaxNumberOfChildren != -1 && newParent.Children.Length >= newParent.MaxNumberOfChildren) { Debug.LogWarning("Node has already the max number of children!"); goto NodeConnectionFailed; } BNode child = connectionConstructor.Value.origin; if (NodeIsChild(child, newParent)) // If the node is in any way already connected (no loops) { Debug.LogWarning("Node could not be connected. Connecting these nodes would result in a loop!"); goto NodeConnectionFailed; } // Check if the child node type is allowed on the parent Type[] allowedTypes = newParent.AllowedChildrenTypes; if (allowedTypes != null && allowedTypes.Length != 0) { bool isAllowed = false; for (int i = 0; i < allowedTypes.Length; i++) { if (child.GetType() == allowedTypes[i] || child.GetType().IsSubclassOf(allowedTypes[i])) { isAllowed = true; break; } } if (isAllowed == false) { Debug.LogWarning("The type " + child.GetType() + " is not allowed to be a child of this node."); goto NodeConnectionFailed; } } // Everything is fine, add the connection and sort the parents children AddToArray(newParent, child, true); SortChildren(newParent); NodeConnectionFailed :; } } // end if currentDrag is NodeConnection } // end if dragged dragged = false; currentDrag = DragType.None; clickedNode = null; connectionConstructor = null; } else if (e.button == 1) // right click { if (inPlayMode == true) { break; } if (!dragged) { if (GetNodeFromPosition(mousePos, out BNode deleteNode)) // is mouse hovering over node { // Show delete menu for node GenericMenu nodeMenu = new GenericMenu(); nodeMenu.AddItem(new GUIContent("Delete"), false, () => RemoveNode(deleteNode)); nodeMenu.ShowAsContext(); } else // mouse was not over node { // show menu for creating a new node GenericMenu nodeMenu = new GenericMenu(); for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; string nodeName = nodeTypes[i].Name; if (nodeName.EndsWith("node", StringComparison.OrdinalIgnoreCase)) { nodeName = nodeName.Substring(0, nodeName.Length - 4); } nodeMenu.AddItem(new GUIContent(nodeName), false, () => { BNode createNode = (BNode)CreateInstance(type); createNode.name = "New " + createNode.GetType().Name; createNode.boundsInEditor = new Rect(mousePos, new Vector2(80, 80)); AssetDatabase.AddObjectToAsset(createNode, tree.targetObject); AssetDatabase.SaveAssets(); }); } nodeMenu.ShowAsContext(); } } } break; case EventType.ScrollWheel: zoomLevel = Mathf.Clamp(zoomLevel - e.delta.y / 50, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); GUI.changed = true; break; default: used = false; break; } if (used) // consume event if used { e.Use(); } }
public ConnectionConstructor(BNode origin, Vector2 position, bool isTop) { this.origin = origin; this.position = position; this.isTop = isTop; }
private void ProcessEvents(Event e) { if (!mapRect.Contains(e.mousePosition)) // if mouse not inside the middle map area { return; } bool used = true; Vector2 mousePos = (e.mousePosition - offset - mapRect.position - new Vector2(mapRect.width / 2, mapRect.height / 2)) / zoomLevel; switch (e.type) { case EventType.MouseDown: if (e.button == 0) // left click { dragged = false; currentDrag = DragType.Position; if (!GetNodeFromPosition(mousePos, out BNode node)) // is mouse hovering over box { break; } // check if the click was on one of the connection boxes and the node was not the root node if (inPlayMode == false && GetConnection(mousePos, node, out bool isTop) && isTop && node != rootNode) { currentDrag = DragType.NodeConnection; RemoveConnection(node); Vector3 connectionMiddlePos = isTop ? GetUpperMiddle(node) : GetLowerMiddle(node); connectionConstructor = new ConnectionConstructor(node, connectionMiddlePos, isTop, mousePos); } else // click was not on the connection boxes { currentDrag = DragType.Node; nodeMover = new NodeMover(node, node.boundsInEditor.position - mousePos); } } break; case EventType.MouseDrag: if (e.button == 0) // left click { switch (currentDrag) { case DragType.None: goto MouseDragFinished; // skip the changed set to true flag case DragType.Node: if (inPlayMode == true) { break; } Rect toChange = nodeMover.node.boundsInEditor; Vector2 newPos = mousePos + nodeMover.clickedPosition; newPos /= SNAPPING_PIXELS; newPos.x = Mathf.Round(newPos.x); newPos.y = Mathf.Round(newPos.y); toChange.position = newPos * SNAPPING_PIXELS; nodeMover.node.boundsInEditor = toChange; break; case DragType.Position: offset += e.delta; break; case DragType.NodeConnection: connectionConstructor.mousePos = mousePos; break; } GUI.changed = true; MouseDragFinished: dragged = true; } break; case EventType.MouseUp: if (e.button == 0) // left click { if (dragged == false) { selectedObject = null; if (nodeMover != null) { selectedObject = nodeMover.node; } } else // if dragged == true { if (currentDrag == DragType.Node) // Save the position of the dragged node { Rect toChange = nodeMover.node.boundsInEditor; ShapesUtil.RectRoundToNextInt(ref toChange); nodeMover.node.boundsInEditor = toChange; // The node was moved so the order of the parented children might be wrong if (GetParentNode(nodeMover.node, out BNode parent)) { SortChildren(parent); } AssetDatabase.SaveAssets(); } else if (currentDrag == DragType.NodeConnection) // try to connect the node to its new parent { TryConnectNodeFromPosition(mousePos); } // end if currentDrag is NodeConnection } // end if dragged dragged = false; currentDrag = DragType.None; nodeMover = null; connectionConstructor = null; } else if (e.button == 1) // right click { if (inPlayMode == true) { break; } if (!dragged) { if (GetNodeFromPosition(mousePos, out BNode deleteNode)) // is mouse hovering over node { // Show delete menu for node GenericMenu nodeMenu = new GenericMenu(); nodeMenu.AddItem(new GUIContent("Delete"), false, () => { RemoveNode(deleteNode); Reload(); }); nodeMenu.ShowAsContext(); } else // mouse was not over node { // show menu for creating a new node GenericMenu nodeMenu = new GenericMenu(); for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; int maxNumberOfChilds = allNodesForTypes[i].MaxNumberOfChildren; string nodeName = nodeTypes[i].Name; if (nodeName.EndsWith("node", StringComparison.OrdinalIgnoreCase)) { nodeName = nodeName.Substring(0, nodeName.Length - 4); } string prefix; if (type.IsSubclassOf(typeof(BoolNode))) { prefix = NODES_INHERIT_FROM_BOOLNODE_STRING; } else if (maxNumberOfChilds == -1 || maxNumberOfChilds > 0) { prefix = NODES_WITH_CHILDREN_STRING; } else { prefix = NODES_WITH_NO_CHILDREN_STRING; } nodeMenu.AddItem(new GUIContent(prefix + "/" + nodeName, allNodesForTypes[i].StringToolTip), false, () => { BNode createNode = (BNode)CreateInstance(type); createNode.name = createNode.GetType().Name; createNode.boundsInEditor = new Rect(mousePos, new Vector2(80, 80)); AssetDatabase.AddObjectToAsset(createNode, tree.targetObject); AssetDatabase.SaveAssets(); Reload(); }); } nodeMenu.ShowAsContext(); } } } break; case EventType.ScrollWheel: zoomLevel = Mathf.Clamp(zoomLevel - e.delta.y / 50, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); GUI.changed = true; break; default: used = false; break; } if (used) // consume event if used { e.Use(); } }
public NodeMover(BNode node, Vector2 clickedPosition) { this.node = node; this.clickedPosition = clickedPosition; }
/// <summary> /// Draws the left side of the window. This side is for displaying debug window values, what the root node is /// and handles the adding, selction and removing of Values. /// </summary> private void DrawTreeInfo() { if (debug) { Vector2 changedOffset = offset / zoomLevel; Vector2 newOffset = EditorGUILayout.Vector2Field("Position:", changedOffset); if (changedOffset != newOffset) { offset = newOffset * zoomLevel; } zoomLevel = Mathf.Clamp(EditorGUILayout.FloatField("Zoomlevel", zoomLevel), MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); } lockTreeView = EditorGUILayout.Toggle("Lock Window to current Tree", lockTreeView); if (inPlayMode == false) { // Handle root node List <BNode> parentLessNodes = GetParentlessNodes(); // only nodes without parents can become root SerializedProperty rootNode = tree.FindProperty("root"); BNode previousRoot = rootNode.objectReferenceValue as BNode; string[] choices = new string[parentLessNodes.Count + 1]; choices[0] = "Null"; // If the root node should be set to nothing // fill all the choices with names of the nodes int index = 0; for (int i = 0; i < parentLessNodes.Count; i++) { choices[i + 1] = parentLessNodes[i].name; if (parentLessNodes[i] == previousRoot) { index = i + 1; } } // Update the new root node int newIndex = EditorGUILayout.Popup("root", index, choices); if (newIndex != index) { if (newIndex == 0) { rootNode.objectReferenceValue = null; } else { rootNode.objectReferenceValue = parentLessNodes[newIndex - 1]; } } this.rootNode = newIndex == 0 ? null : parentLessNodes[newIndex - 1]; // ---Handle root node tree.ApplyModifiedProperties(); // Add the Button for adding a new value if (GUILayout.Button("Add Value")) { GenericMenu valueMenu = new GenericMenu(); for (int i = 0; i < valueTypes.Length; i++) { Type type = valueTypes[i]; string name = valueTypes[i].Name; if (name.EndsWith("value", StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, name.Length - 5); } valueMenu.AddItem(new GUIContent(name), false, () => { Value newNode = (Value)CreateInstance(type); newNode.name = "New " + type.Name; AssetDatabase.AddObjectToAsset(newNode, tree.targetObject); selectedObject = newNode; AssetDatabase.SaveAssets(); }); } valueMenu.ShowAsContext(); } } // Display all values in a list for (int i = 0; i < allValues.Length; i++) { if (GUILayout.Button(allValues[i].name)) { selectedObject = allValues[i]; } } }