/// <summary>The primary method for drawing a Rail component on the scene view</summary> /// <param name="rail">The rail to draw</param> /// <param name="handleSize">Handle size</param> /// <param name="snapSize">Snap Size</param> /// <param name="snapActive">Indicates whether positioning should snap to a grid</param> /// <param name="nodeRemovalActive">Indicates whether nodes are able to be removed</param> /// <param name="drawEditorHandles">Indicates whether interactive handles should be drawn</param> public static void DrawRailOnScene(Rail rail, float handleSize, Vector3 snapSize, bool snapActive = false, bool nodeRemovalActive = false, bool drawEditorHandles = false) { Handles.color = EditorPreferences.GetColor32(EditorPreferences.RailColorKey, EditorPreferences.RailColorValue); var nodes = rail.Nodes; // Editor Handles if (drawEditorHandles) { if (nodeRemovalActive && nodes.Count > 2) { DrawRailNodeRemoveButtons(rail, handleSize); Handles.color = EditorPreferences.GetColor32(EditorPreferences.RailColorKey, EditorPreferences.RailColorValue); } else { DrawRailNodeInsertButtons(rail, handleSize); DrawRailNodeAddButton(rail, snapActive, handleSize, snapSize.x); DrawRailNodeInsertAtStartButton(rail, snapActive, handleSize, snapSize.x); DrawRailNodeFreeMoveHandles(rail, handleSize, snapSize); } } // Node Index Labels Vector2 nodeIndexLabelOffset = new Vector2(-handleSize * 0.2f, handleSize * 1.2f); for (int j = 0; j < nodes.Count; j++) { DrawTextAtPosition(j.ToString(), nodes[j].Position + nodeIndexLabelOffset, Handles.color, false); } // Rail Name Label DrawTextAtPosition(rail.gameObject.name, nodes[0].Position, Handles.color, true); }
/// <summary>Moves the Scene View to the specified rail</summary> /// <param name="rail">The rail to find in Scene</param> private void FindInScene(Rail rail) { if (rail.Nodes.Count > 1 && SceneView.lastActiveSceneView != null) { float sceneViewHeight = 0f; float sceneViewPadding = 1.5f; float railDisplacementX = Mathf.Abs(rail.Nodes[0].Position.x - rail.Nodes[rail.Nodes.Count - 1].Position.x); float railDisplacementY = Mathf.Abs(rail.Nodes[0].Position.y - rail.Nodes[rail.Nodes.Count - 1].Position.y); if (railDisplacementX != 0f || railDisplacementY != 0f) { if (railDisplacementX / SceneView.lastActiveSceneView.camera.aspect > railDisplacementY) { sceneViewHeight = railDisplacementX / SceneView.lastActiveSceneView.camera.aspect; } else { sceneViewHeight = railDisplacementY; } if (sceneViewHeight != 0) { SceneView.lastActiveSceneView.size = sceneViewHeight * sceneViewPadding; } } SceneView.lastActiveSceneView.pivot = (rail.Nodes[0].Position + rail.Nodes[rail.Nodes.Count - 1].Position) * 0.5f; } }
/// <summary>Destroys a game object with the specified Rail component attached</summary> /// <param name="rail">The Rail component whose game object to destroy</param> private void RemoveRail(Rail rail) { if (EditorUtility.DisplayDialog("Remove Rail?", "Do you really want to remove " + rail.gameObject.name + " from the scene?", "Remove", "Cancel")) { _currentRailIndex = -1; GUIUtilities.RemoveChildObject(rail.gameObject); } }
/// <summary>Adds a rail node to the end of the specified rail</summary> /// <param name="rail">The rail to add a node to</param> /// <param name="nodePosition">The position of the node to add</param> public static void AddRailNode(Rail rail, Vector2 nodePosition) { Undo.RecordObject(rail, "Add Node"); var node = new RailNode() { Position = nodePosition }; rail.Nodes.Add(node); }
/// <summary>Inserts a rail node on the specified rail</summary> /// <param name="rail">The rail to insert a node on</param> /// <param name="nodeIndex">The index of the node to insert</param> /// <param name="nodePosition">The position of the node to insert</param> public static void InsertRailNode(Rail rail, int nodeIndex, Vector2 nodePosition) { Undo.RecordObject(rail, "Insert Node"); var nodes = rail.Nodes; var node = new RailNode() { Position = nodePosition }; nodes.Insert(nodeIndex, node); }
/// <summary>The primary method that moves the camera. This method is called by the selected Update Method.</summary> private void UpdatePosition() { CheckForScreenSizeChange(); // Check if on a rail _currentRail = GetCurrentRail(); if (!_staticPosition) { // Return if no available targets if (Target == null && (_currentRail == null || _currentRail.Target == null)) { return; } // Get intended position without offset _intendedPositionWithoutOffset = GetIntendedPositionWithoutOffset(); // Get intended position with offset if (OffsetX != 0f || OffsetY != 0f) { _intendedPosition = GetIntendedPosition(); } else { _intendedPosition = _intendedPositionWithoutOffset; } } // Smooth if (!_resetSmoothing && (SmoothX > 0f || SmoothY > 0f)) { _smoothedPosition = GetSmoothedPosition(); } else { _smoothedPosition = _intendedPosition; if (_resetSmoothing) { _resetSmoothing = false; } } // Correct the Smoothed Position with Bounds if (BoundX > 0f || BoundY > 0f) { _smoothedPosition = GetBoundPosition(); } // Set camera position SetPosition(_smoothedPosition); }
/// <summary>Draws buttons used for inserting nodes on the rail</summary> /// <param name="rail">The rail to insert nodes on</param> /// <param name="handleSize">Handle size</param> public static void DrawRailNodeInsertButtons(Rail rail, float handleSize) { var buttonSize = 0.25f * handleSize; var nodes = rail.Nodes; for (int i = 1; i < nodes.Count; i++) { var buttonPosition = (nodes[i].Position + nodes[i - 1].Position) * 0.5f; if (Handles.Button(buttonPosition, Quaternion.identity, buttonSize, buttonSize, Handles.RectangleHandleCap)) { InsertRailNode(rail, i, buttonPosition); } } }
/// <summary>Draws buttons that remove nodes from the specified Rail</summary> /// <param name="rail">The rail to draw handles for</param> /// <param name="handleSize">Handle size</param> public static void DrawRailNodeRemoveButtons(Rail rail, float handleSize) { Handles.color = Color.red; var buttonSize = 0.5f * handleSize; var nodes = rail.Nodes; for (int i = 0; i < rail.Nodes.Count; i++) { if (Handles.Button(nodes[i].Position, Quaternion.identity, buttonSize, buttonSize, Handles.DotHandleCap)) { RemoveRailNode(rail, i); return; } } }
/// <summary>Draws Free Move Handles for the specified rail</summary> /// <param name="rail">The rail to draw handles for</param> /// <param name="handleSize">Handle size</param> /// <param name="snapSize">Snap size</param> public static void DrawRailNodeFreeMoveHandles(Rail rail, float handleSize, Vector3 snapSize) { var moveHandleSize = 0.5f * handleSize; var nodes = rail.Nodes; for (int i = 0; i < nodes.Count; i++) { Vector2 newNodePosition = Handles.FreeMoveHandle(nodes[i].Position, Quaternion.identity, moveHandleSize, snapSize, Handles.RectangleHandleCap); if (nodes[i].Position != newNodePosition) { Undo.RecordObject(rail, "Move Node"); nodes[i].Position = newNodePosition; } } }
/// <summary>Sets the currently displayed rail and text in the main editor</summary> /// <param name="railManager">The RailManager component selected in the Inspector</param> private void InitializeMainEditor(RailManager railManager) { // Default values var heading = "NO RAILS FOUND"; Rail currentRail = null; // If a rail is supposed to be displayed if (_currentRailIndex != -1 && _rails[_currentRailIndex] != null) { currentRail = _rails[_currentRailIndex]; heading = currentRail.gameObject.name.ToUpper(); } // If no rail is displayed, but rails are available else if (_rails.Count > 0) { heading = "SELECT RAIL TO EDIT"; } DrawMainEditor(currentRail, heading); }
/// <summary>Draws a button used for inserting a node at the start of the rail</summary> /// <param name="rail">The rail to insert a node at the start of</param> /// <param name="snapActive">Indicates whether positioning should snap to a grid</param> /// <param name="handleSize">Handle size</param> public static void DrawRailNodeInsertAtStartButton(Rail rail, bool snapActive, float handleSize, float snapSize) { var buttonSize = 0.25f * handleSize; var buttonOffsetVector = handleSize * 1.5f; if (snapActive) { buttonOffsetVector = Mathf.Round(buttonOffsetVector / snapSize) * snapSize; if (buttonOffsetVector < snapSize) { buttonOffsetVector = snapSize; } } var buttonPosition = rail.Nodes[0].Position - new Vector2(buttonOffsetVector, buttonOffsetVector); if (Handles.Button(buttonPosition, Quaternion.identity, buttonSize, buttonSize, Handles.RectangleHandleCap)) { InsertRailNode(rail, 0, buttonPosition); } }
/// <summary>Creates Buttons for organising a rail's nodes</summary> /// <param name="rail">The rail whose nodes are being edited</param> /// <param name="i">Index of the node to be edited</param> private void NodeEditorButtons(Rail rail, int i) { var nodes = rail.Nodes; // Shift Up _content = new GUIContent("\u25b2", "Shift up"); if (GUILayout.Button(_content, EditorStyles.miniButtonLeft, GUILayout.Width(20f))) { if (i > 0) { Undo.RecordObject(rail, "Shift Node Up"); var node = nodes[i]; nodes.RemoveAt(i); nodes.Insert(i - 1, node); _repaintScene = true; } } // Shift Down _content = new GUIContent("\u25bc", "Shift down"); if (GUILayout.Button(_content, EditorStyles.miniButtonMid, GUILayout.Width(20f))) { if (i < nodes.Count - 1) { Undo.RecordObject(rail, "Shift Node Down"); var node = nodes[i]; nodes.RemoveAt(i); nodes.Insert(i + 1, node); _repaintScene = true; } } // Insert _content = new GUIContent("\u002b", "Insert"); if (GUILayout.Button(_content, EditorStyles.miniButtonMid, GUILayout.Width(20f))) { Vector2 newNodePos; if (i == 0) { newNodePos = nodes[i].Position - new Vector2(2f, 2f); } else { newNodePos = (nodes[i].Position + nodes[i - 1].Position) * 0.5f; } GUIUtilities.InsertRailNode(rail, i, newNodePos); _repaintScene = true; } // Remove _content = new GUIContent("\u2212", "Remove"); if (GUILayout.Button(_content, EditorStyles.miniButtonRight, GUILayout.Width(20f))) { if (nodes.Count > 2) { GUIUtilities.RemoveRailNode(rail, i); _repaintScene = true; } } }
/// <summary>Displays a list of nodes for the current rail</summary> /// <param name="rail">The Rail component whose nodes to display</param> private void ListRailNodes(Rail rail) { List <RailNode> nodes = rail.Nodes; // Column Labels EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Node #", GUILayout.Width(60f)); EditorGUILayout.LabelField("Position", GUILayout.MinWidth(105f)); _content = new GUIContent("FX: Threshold", "Increased velocity transition effect\n\u2611 to invert"); EditorGUILayout.LabelField(_content, GUILayout.MinWidth(156f), GUILayout.MaxWidth(156f)); EditorGUILayout.EndHorizontal(); for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; EditorGUILayout.BeginHorizontal(); // Row Label EditorGUILayout.LabelField("Node " + i.ToString(), GUILayout.Width(60f)); // Position var newPosition = EditorGUILayout.Vector2Field(GUIContent.none, node.Position, GUILayout.MinWidth(105f)); if (node.Position != newPosition) { Undo.RecordObject(rail, "Move Node"); node.Position = newPosition; } // Disables editing of the final node's Threshold FX as the value produces no effect if (i == nodes.Count - 1) { GUI.enabled = false; } var newInvertThreshold = EditorGUILayout.Toggle(GUIContent.none, node.InvertThreshold, GUILayout.Width(12f)); if (node.InvertThreshold != newInvertThreshold) { Undo.RecordObject(rail, "Invert Node Threshold"); node.InvertThreshold = newInvertThreshold; } var newThreshold = EditorGUILayout.Slider(GUIContent.none, node.Threshold, 0f, 1f, GUILayout.Width(50f)); if (node.Threshold != newThreshold) { Undo.RecordObject(rail, "Change Node Threshold"); node.Threshold = newThreshold; } if (i == nodes.Count - 1) { GUI.enabled = true; } // Buttons for shifting, inserting, and removing nodes NodeEditorButtons(rail, i); EditorGUILayout.EndHorizontal(); } // Button to add node _content = new GUIContent("\u002b", "Add node"); if (GUILayout.Button(_content)) { Vector2 newNodePos; if (nodes.Count > 0) { newNodePos = nodes[nodes.Count - 1].Position + new Vector2(2f, 2f); } else { newNodePos = GUIUtilities.GetRoundedScenePivot(); } GUIUtilities.AddRailNode(rail, newNodePos); _repaintScene = true; } }
/// <summary>Draws the main editor used for editing rail values</summary> /// <param name="rail">The Rail component whose values to display</param> /// <param name="heading">The string to display in the EditorHeader</param> private void DrawMainEditor(Rail rail, string heading) { // Default values used when rail == null. Color currentColor = Color.grey; Transform currentTarget = null; RailOrientation currentOrientation = RailOrientation.Horizontal; float currentLeadIn = 0f; float currentTrailOut = 0f; if (rail != null) { currentTarget = rail.Target; currentOrientation = rail.Orientation; currentLeadIn = rail.LeadIn; currentTrailOut = rail.TrailOut; } else { GUI.enabled = false; } // Header - displays name of rail being edited GUIUtilities.EditorHeader(heading); EditorGUILayout.Space(); // Target _content = new GUIContent("Target", "The target the camera follows while on the rail. If null, the Railcam 2D target is used by default"); var newTarget = EditorGUILayout.ObjectField(_content, currentTarget, typeof(Transform), true); if (rail != null) { if (rail.Target != (Transform)newTarget) { Undo.RecordObject(rail, "Change Rail Target"); rail.Target = (Transform)newTarget; } } EditorGUILayout.Space(); // Orientation _content = new GUIContent("Orientation", "The axis used to calculate camera position while on the rail"); var newOrientation = EditorGUILayout.EnumPopup(_content, currentOrientation); if (rail != null) { if (rail.Orientation != (RailOrientation)newOrientation) { Undo.RecordObject(rail, "Change Rail Orientation"); rail.Orientation = (RailOrientation)newOrientation; } } EditorGUILayout.Space(); // Lead-In _content = new GUIContent("FX: Lead-In", "The camera leads with reduced velocity between the first two nodes"); var newLeadIn = EditorGUILayout.Slider(_content, currentLeadIn, 0f, 1f); if (rail != null) { if (rail.LeadIn != newLeadIn) { Undo.RecordObject(rail, "Change Rail Lead-In"); rail.LeadIn = newLeadIn; _repaintScene = true; } } // Trail-Out _content = new GUIContent("FX: Trail-Out", "The camera trails with reduced velocity between the final two nodes"); var newTrailOut = EditorGUILayout.Slider(_content, currentTrailOut, 0f, 1f); if (rail != null) { if (rail.TrailOut != newTrailOut) { Undo.RecordObject(rail, "Change Rail Trail-Out"); rail.TrailOut = newTrailOut; _repaintScene = true; } } EditorGUILayout.Space(); // Rail Nodes if (rail != null) { ListRailNodes(rail); } }
/// <summary>Removes a rail node from the specified rail</summary> /// <param name="rail">The rail to remove the node from</param> /// <param name="nodeIndex">The index of the node to remove</param> public static void RemoveRailNode(Rail rail, int nodeIndex) { Undo.RecordObject(rail, "Remove Node"); rail.Nodes.RemoveAt(nodeIndex); }