void _PickSector(SECTR_Portal myPortal) { HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); if (Event.current.type == EventType.MouseMove) { _ComputeCursorVert(); } else if (Event.current.type == EventType.MouseUp && Event.current.button == 0 && !Event.current.alt && !Event.current.control) { SECTR_Sector sector = _GetSectorFromSelection(); if (sector) { SECTR_Undo.Record(myPortal, "Assign Sector to Portal."); if (pickBack) { myPortal.BackSector = sector; } else { myPortal.FrontSector = sector; } EditorUtility.SetDirty(myPortal); pickFront = false; pickBack = false; _EndSelection(); } } else if (Event.current.type == EventType.KeyUp && Event.current.keyCode == KeyCode.Escape) { pickBack = false; pickFront = false; _EndSelection(); } }
protected static GameObject CreateTriggerFromPortal(SECTR_Portal portal, string newName) { GameObject newGameObject = null; if (portal) { newGameObject = new GameObject(newName); BoxCollider newCollider = newGameObject.AddComponent <BoxCollider>(); newCollider.isTrigger = true; newGameObject.transform.position = portal.transform.position; newGameObject.transform.rotation = portal.transform.rotation; Vector3 newSize = Vector3.Scale(portal.HullMesh ? portal.HullMesh.bounds.size : Vector3.one, portal.transform.lossyScale); float maxSize = Mathf.Max(newSize.x, Mathf.Max(newSize.y, newSize.z)); if (Mathf.Abs(newSize.x) < 0.001f) { newSize.x = maxSize; } else if (Mathf.Abs(newSize.y) < 0.001f) { newSize.y = maxSize; } else { newSize.z = maxSize; } newCollider.size = newSize; } return(newGameObject); }
public static void CreateTriggerLoader() { string newObjectName = "SECTR Trigger Loader"; string undoName = "Created " + newObjectName; GameObject newGameObject; SECTR_Portal selectedPortal = Selection.activeGameObject ? Selection.activeGameObject.GetComponent <SECTR_Portal>() : null; if (selectedPortal) { newGameObject = CreateTriggerFromPortal(selectedPortal, newObjectName); SECTR_TriggerLoader newLoader = newGameObject.AddComponent <SECTR_TriggerLoader>(); if (selectedPortal.FrontSector) { newLoader.Sectors.Add(selectedPortal.FrontSector); } if (selectedPortal.BackSector) { newLoader.Sectors.Add(selectedPortal.BackSector); } } else { newGameObject = CreateGameObject(newObjectName); BoxCollider newCollider = newGameObject.AddComponent <BoxCollider>(); newCollider.isTrigger = true; newGameObject.AddComponent <SECTR_TriggerLoader>(); } SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
/// Informs the Sector that a Portal is connected into it. /// Should only be called by SECTR_Portal. public void Register(SECTR_Portal portal) { // Should this be an assert? Probably? if (!portals.Contains(portal)) { portals.Add(portal); } }
void _DrawViewportGUI(SECTR_Portal myPortal) { Handles.BeginGUI(); float width = 400; if (pickBack || pickFront) { float height = 100; GUI.Box(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height), "Selecting " + (pickBack ? "back" : "front") + " Sector of " + myPortal.name + ".\n" + (_GetSectorFromSelection() != null ? "Left Click to select." : "") + "\nEsc to cancel.", boxStyle); } else if (createHull || myPortal.ForceEditHull) { float height = 100; string returnText = ""; if (newHullVerts.Count >= 3) { returnText = "Return to complete."; } else if (newHullVerts.Count == 0 && myPortal.ForceEditHull) { returnText = "Return to create empty portal."; } GUI.Box(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height), "Drawing geometry for " + myPortal.name + ".\n" + (closesetVertIsValid ? "Left Click to add vert. " : "") + returnText + "\nEsc to cancel.", boxStyle); } else if (Selection.gameObjects.Length == 1) { float height = 100; GUILayout.BeginArea(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height)); if (GUILayout.Button(new GUIContent(myPortal.HullMesh ? "Redraw Portal" : "Draw Portal", "Lets you (re)create the geometry for this Portal"), buttonStyle)) { createHull = true; } GUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("Pick Front Sector", "Provides an in-viewport interface for picking the front Sector."), buttonStyle)) { pickFront = true; } if (GUILayout.Button(new GUIContent("Swap Sectors", "Swaps the front and back Sectors, in case they are backwards."), buttonStyle)) { _SwapSectors(myPortal); } if (GUILayout.Button(new GUIContent("Pick Back Sector", "Provides an in-viewport interface for picking the front Sector."), buttonStyle)) { pickBack = true; } GUILayout.EndHorizontal(); GUILayout.EndArea(); } Handles.EndGUI(); }
void _SwapSectors(SECTR_Portal myPortal) { SECTR_Undo.Record(myPortal, "Swap Portal Sectors"); SECTR_Sector oldFront = myPortal.FrontSector; SECTR_Sector oldBack = myPortal.BackSector; myPortal.FrontSector = null; myPortal.BackSector = null; myPortal.FrontSector = oldBack; myPortal.BackSector = oldFront; EditorUtility.SetDirty(myPortal); }
public static void CreatePortal() { string newName = "SECTR Portal"; string undoName = "Create " + newName; GameObject newGameObject = CreateGameObject(newName); SECTR_Portal newPortal = newGameObject.AddComponent <SECTR_Portal>(); newPortal.ForceEditHull = true; newPortal.CenterOnEdit = true; SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
void _DrawSectorLinks(SECTR_Portal myPortal) { nullStyle.normal.textColor = SECTR_Portal.FrontAnchorColor; Handles.Label(myPortal.FrontAnchorPosition, "F", nullStyle); nullStyle.normal.textColor = SECTR_Portal.BackAnchorColor; Handles.Label(myPortal.BackAnchorPosition, "B", nullStyle); if (myPortal.FrontSector != null) { Handles.color = SECTR_Portal.FrontAnchorColor; Handles.DrawLine(myPortal.FrontAnchorPosition, myPortal.FrontSector.TotalBounds.center); } if (myPortal.BackSector != null) { Handles.color = SECTR_Portal.BackAnchorColor; Handles.DrawLine(myPortal.BackAnchorPosition, myPortal.BackSector.TotalBounds.center); } }
public override void OnInspectorGUI() { SECTR_Portal myPortal = (SECTR_Portal)target; SECTR_Sector newFront = ObjectField <SECTR_Sector>("Front Sector", "Reference to the Sector on the front side of this Portal", myPortal.FrontSector, true); SECTR_Sector newBack = ObjectField <SECTR_Sector>("Back Sector", "Reference to the Sector on the back side of this Portal", myPortal.BackSector, true); // Only apply changes if things are actually different. // Note that the code below duplicates some functionality from SECTR_Portal's // accessors, but I can'f figure out any other way to get the SerializedProperty // multi-select compatable Undo to work... if (myPortal.FrontSector != newFront || myPortal.BackSector != newBack) { serializedObject.Update(); if (myPortal.FrontSector != newFront) { if (myPortal.FrontSector) { myPortal.FrontSector.Deregister(myPortal); } frontProp.objectReferenceValue = newFront; if (myPortal.FrontSector) { myPortal.FrontSector.Register(myPortal); } } if (myPortal.BackSector != newBack) { if (myPortal.BackSector) { myPortal.BackSector.Deregister(myPortal); } backProp.objectReferenceValue = newBack; if (myPortal.BackSector) { myPortal.BackSector.Register(myPortal); } } serializedObject.ApplyModifiedProperties(); } base.OnInspectorGUI(); }
protected override void OnDrawGizmosSelected() { Bounds bounds = TotalBounds; Gizmos.color = SectorColor; Gizmos.DrawWireCube(bounds.center, bounds.size); if (enabled) { // Render links to neighbor Sectors. Gizmos.color = SECTR_Portal.ActivePortalColor; int numNeighbors = portals.Count; for (int neighborIndex = 0; neighborIndex < numNeighbors; ++neighborIndex) { SECTR_Portal portal = portals[neighborIndex]; Gizmos.DrawLine(TotalBounds.center, portal.Center); } Gizmos.color = Color.red; List <SECTR_Member.Child> sharedChildren = GetSharedChildren(); int numSharedChildren = sharedChildren.Count; for (int childIndex = 0; childIndex < numSharedChildren; ++childIndex) { SECTR_Member.Child child = sharedChildren[childIndex]; Bounds totalChildBounds = new Bounds(child.gameObject.transform.position, Vector3.zero); if (child.renderer) { totalChildBounds.Encapsulate(child.rendererBounds); } if (child.light) { totalChildBounds.Encapsulate(child.lightBounds); } if (child.terrain) { totalChildBounds.Encapsulate(child.terrainBounds); } Gizmos.DrawWireCube(totalChildBounds.center, totalChildBounds.size); } } }
protected static GameObject CreateDoor <T>(string newName) where T : SECTR_Door { string undoName = "Create " + newName; GameObject newGameObject; SECTR_Portal selectedPortal = Selection.activeGameObject ? Selection.activeGameObject.GetComponent <SECTR_Portal>() : null; if (selectedPortal) { newGameObject = CreateTriggerFromPortal(selectedPortal, newName); T newDoor = newGameObject.AddComponent <T>(); newDoor.Portal = selectedPortal; } else { newGameObject = CreateGameObject(newName); BoxCollider newCollider = newGameObject.AddComponent <BoxCollider>(); newCollider.isTrigger = true; newGameObject.AddComponent <T>(); } SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; return(newGameObject); }
public void OnSceneGUI() { SECTR_Portal myPortal = (SECTR_Portal)target; if (boxStyle == null) { boxStyle = new GUIStyle(GUI.skin.box); boxStyle.alignment = TextAnchor.UpperCenter; boxStyle.fontSize = 15; } if (buttonStyle == null) { buttonStyle = new GUIStyle(GUI.skin.button); buttonStyle.alignment = TextAnchor.UpperCenter; buttonStyle.fontSize = 12; } if (nullStyle == null) { nullStyle = new GUIStyle(); } // Viewport GUI Drawing _DrawViewportGUI(myPortal); // Input if (pickBack || pickFront) { _PickSector(myPortal); } else if ((createHull || myPortal.ForceEditHull) && !Application.isPlaying) { _EditHull(myPortal); } // Input may destroy this object. if (target == null) { return; } // Viewport 3D drawing if (!createHull && !myPortal.ForceEditHull) { _DrawSectorLinks(myPortal); } if (createHull || myPortal.ForceEditHull) { _DrawHullEditor(myPortal); } else if (pickBack || pickFront) { Handles.color = pickFront ? SECTR_Portal.FrontAnchorColor : SECTR_Portal.BackAnchorColor; Handles.DrawSolidDisc(closestVert, lastHit.normal, .1f); SECTR_Sector sector = _GetSectorFromSelection(); if (sector != null) { Handles.Label(closestVert, sector.name); } } }
/// Informs the Sector that a Portal is no longer connected into it. /// Should only be called by SECTR_Portal. public void Deregister(SECTR_Portal portal) { // Assert that we're contained? portals.Remove(portal); }
/// Informs the Sector that a Portal is connected into it. /// Should only be called by SECTR_Portal. public void Register(SECTR_Portal portal) { // Should this be an assert? Probably? if(!portals.Contains(portal)) { portals.Add(portal); } }
/// Finds the shortest path through the portal graph between two points. /// The start and end points must currently be within Sector in the graph. /// <param name="path">List into which search results will be written.</param> /// <param name="start">The world space position at which to start the search.</param> /// <param name="goal">The world space goal of the search.</param> /// <param name="stopFlags">Flag set to test at each SECTR_Portal. A failed test will stop the traversal.</param> /// <returns>A list of nodes from the Start to the Goal. Empty if there is no path.</returns> public static void FindShortestPath(ref List <Node> path, Vector3 start, Vector3 goal, SECTR_Portal.PortalFlags stopFlags) { // This is an implementation of a basic A* search. // Implementation is optimized through use of a priority queue open list // and a dictionary for the closed list. path.Clear(); openSet.Clear(); closedSet.Clear(); // Get the list of starting portals, all of which will be pushed on to the open set. // There may be multiple candidate Sectors becuase the bounding boxes may overlap. SECTR_Sector.GetContaining(ref initialSectors, start); SECTR_Sector.GetContaining(ref goalSectors, goal); int numInitialSectors = initialSectors.Count; for (int initialSectorIndex = 0; initialSectorIndex < numInitialSectors; ++initialSectorIndex) { SECTR_Sector sector = initialSectors[initialSectorIndex]; if (goalSectors.Contains(sector)) { Node newElement = new Node(); newElement.Sector = sector; path.Add(newElement); return; } int numPortals = sector.Portals.Count; for (int portalIndex = 0; portalIndex < numPortals; ++portalIndex) { SECTR_Portal portal = sector.Portals[portalIndex]; if ((portal.Flags & stopFlags) == 0) { Node newElement = new Node(); newElement.Portal = portal; newElement.Sector = sector; newElement.ForwardTraversal = sector == portal.FrontSector; newElement.Cost = Vector3.Magnitude(start - portal.transform.position); float estimate = Vector3.Magnitude(goal - portal.transform.position); newElement.CostPlusEstimate = newElement.Cost + estimate; openSet.Enqueue(newElement); } } } // Time to do some A*... while (openSet.Count > 0) { Node current = openSet.Dequeue(); SECTR_Sector sector = current.ForwardTraversal ? current.Portal.BackSector : current.Portal.FrontSector; if (!sector) { continue; } // If the current element Sector contains the goal point, we're done. // NOTE: I *think* it's correct to end here but we should prove that // this is correct even strange connections of concave Sector. if (goalSectors.Contains(sector)) { Node.ReconstructPath(path, current); break; } int numPortals = sector.Portals.Count; for (int portalIndex = 0; portalIndex < numPortals; ++portalIndex) { SECTR_Portal portal = sector.Portals[portalIndex]; if (portal != current.Portal && (portal.Flags & stopFlags) == 0) { // Create a new SearchElement for this neighbor. Node neighborElement = new Node(); neighborElement.Parent = current; neighborElement.Portal = portal; neighborElement.Sector = sector; neighborElement.ForwardTraversal = sector == portal.FrontSector; neighborElement.Cost = current.Cost + Vector3.Magnitude(neighborElement.Portal.transform.position - current.Portal.transform.position); float estimate = Vector3.Magnitude(goal - neighborElement.Portal.transform.position); neighborElement.CostPlusEstimate = neighborElement.Cost + estimate; // If the closed list already contains this portal, // and that version is closer than us, we'll skip this node. Node closedElement = null; closedSet.TryGetValue(neighborElement.Portal, out closedElement); if (closedElement != null && closedElement.CostPlusEstimate < neighborElement.CostPlusEstimate) { continue; } // Check to see if the neighbor is already on the open list. Node openElement = null; for (int i = 0; i < openSet.Count; ++i) { if (openSet[i].Portal == neighborElement.Portal) { openElement = openSet[i]; break; } } // Skip this neighbor if the open neighbor is better than us. if (openElement != null && openElement.CostPlusEstimate < neighborElement.CostPlusEstimate) { continue; } // Add this neighbor to the open list. openSet.Enqueue(neighborElement); } } // Once all neighbors are considered, put this node onto the close list. if (!closedSet.ContainsKey(current.Portal)) { closedSet.Add(current.Portal, current); } } }
void _PickSector(SECTR_Portal myPortal) { HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); if(Event.current.type == EventType.mouseMove) { _ComputeCursorVert(); } else if(Event.current.type == EventType.mouseUp && Event.current.button == 0 && !Event.current.alt && !Event.current.control) { SECTR_Sector sector = _GetSectorFromSelection(); if(sector) { SECTR_Undo.Record(myPortal, "Assign Sector to Portal."); if(pickBack) { myPortal.BackSector = sector; } else { myPortal.FrontSector = sector; } EditorUtility.SetDirty(myPortal); pickFront = false; pickBack = false; _EndSelection(); } } else if(Event.current.type == EventType.keyUp && Event.current.keyCode == KeyCode.Escape) { pickBack = false; pickFront = false; _EndSelection(); } }
void _DrawViewportGUI(SECTR_Portal myPortal) { Handles.BeginGUI(); float width = 400; if(pickBack || pickFront) { float height = 100; GUI.Box(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height), "Selecting " + (pickBack ? "back" : "front") + " Sector of " + myPortal.name + ".\n" + (_GetSectorFromSelection() != null ? "Left Click to select." : "") + "\nEsc to cancel.", boxStyle); } else if(createHull || myPortal.ForceEditHull) { float height = 100; string returnText = ""; if(newHullVerts.Count >= 3) { returnText = "Return to complete."; } else if(newHullVerts.Count == 0 && myPortal.ForceEditHull) { returnText = "Return to create empty portal."; } GUI.Box(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height), "Drawing geometry for " + myPortal.name + ".\n" + (closesetVertIsValid ? "Left Click to add vert. " : "") + returnText + "\nEsc to cancel.", boxStyle); } else if(Selection.gameObjects.Length == 1) { float height = 100; GUILayout.BeginArea(new Rect((Screen.width * 0.5f) - (width * 0.5f), Screen.height - height, width, height)); if(GUILayout.Button(new GUIContent(myPortal.HullMesh ? "Redraw Portal" : "Draw Portal", "Lets you (re)create the geometry for this Portal"), buttonStyle)) { createHull = true; } GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Pick Front Sector", "Provides an in-viewport interface for picking the front Sector."), buttonStyle)) { pickFront = true; } if(GUILayout.Button(new GUIContent("Swap Sectors", "Swaps the front and back Sectors, in case they are backwards."), buttonStyle)) { _SwapSectors(myPortal); } if(GUILayout.Button(new GUIContent("Pick Back Sector", "Provides an in-viewport interface for picking the front Sector."), buttonStyle)) { pickBack = true; } GUILayout.EndHorizontal(); GUILayout.EndArea(); } Handles.EndGUI(); }
void _DrawSectorLinks(SECTR_Portal myPortal) { nullStyle.normal.textColor = SECTR_Portal.FrontAnchorColor; Handles.Label(myPortal.FrontAnchorPosition, "F", nullStyle); nullStyle.normal.textColor = SECTR_Portal.BackAnchorColor; Handles.Label(myPortal.BackAnchorPosition, "B", nullStyle); if(myPortal.FrontSector != null) { Handles.color = SECTR_Portal.FrontAnchorColor; Handles.DrawLine(myPortal.FrontAnchorPosition, myPortal.FrontSector.TotalBounds.center); } if(myPortal.BackSector != null) { Handles.color = SECTR_Portal.BackAnchorColor; Handles.DrawLine(myPortal.BackAnchorPosition, myPortal.BackSector.TotalBounds.center); } }
/// Generates a List of nodes that is a depth-first traversal of /// walk of sector graph from the specified root. /// <param name="nodes">List into which walk results will be written.</param> /// <param name="root">The Sector at which to start the traversal.</param> /// <param name="stopFlags">Flag set to test at each SECTR_Portal. A failed test will stop the traversal.</param> /// <param name="maxDepth">The depth into the graph at which to end the traversal. -1 means no limit.</param> /// <returns>A List of Nodes in depth-first traveral order.</returns> public static void DepthWalk(ref List <Node> nodes, SECTR_Sector root, SECTR_Portal.PortalFlags stopFlags, int maxDepth) { nodes.Clear(); if (root == null) { return; } else if (maxDepth == 0) { Node node = new Node(); node.Sector = root; nodes.Add(node); return; } // We only want to visit each Sector once, so we'll mark them // in order to avoid cycles. int numSectors = SECTR_Sector.All.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector.All[sectorIndex].Visited = false; } // Use a stack for the search, to keep implementation similar // to the breadth first search above. Stack <Node> nodeStack = new Stack <Node>(numSectors); Node rootNode = new Node(); rootNode.Sector = root; rootNode.Depth = 1; nodeStack.Push(rootNode); root.Visited = true; int exploredNodes = 0; while (nodeStack.Count > 0) { Node nextNode = nodeStack.Pop(); nodes.Add(nextNode); ++exploredNodes; if (maxDepth < 0 || nextNode.Depth <= maxDepth) { int numPortals = nextNode.Sector.Portals.Count; for (int portalIndex = 0; portalIndex < numPortals; ++portalIndex) { SECTR_Portal portal = nextNode.Sector.Portals[portalIndex]; if (portal && (portal.Flags & stopFlags) == 0) { SECTR_Sector neighborSector = portal.FrontSector == nextNode.Sector ? portal.BackSector : portal.FrontSector; if (neighborSector && !neighborSector.Visited) { Node neighborNode = new Node(); neighborNode.Parent = nextNode; neighborNode.Sector = neighborSector; neighborNode.Portal = portal; neighborNode.Depth = nextNode.Depth + 1; nodeStack.Push(neighborNode); neighborSector.Visited = true; } } } } } }