using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace Framework.Cinematics { /// /// Assists the user in editing tracks for individual characters in cinematics /// [CustomEditor(typeof(Track))] public class TrackEditor : Editor { /// Name of the layer on which the cinematics objects will reside private const string CinematicsLayerName = "Narration"; /// Creates a new ground assigned to the right layer and all [MenuItem("GameObject/Create Other/Track (Cinematics)")] public static void CreateTrack() { int layerIndex = LayerMask.NameToLayer(CinematicsLayerName); if(layerIndex == -1) { Debug.LogWarning( "Could not find the '" + CinematicsLayerName + "' layer. You should add " + "a custom layer with this name and configure Unity's physics settings so " + "that it doesn't collide with anything. Falling back to 'Default' layer " + "for this Ground object." ); layerIndex = 0; } GameObject trackGameObject = new GameObject("CinematicsTrack"); if(layerIndex != -1) { trackGameObject.layer = layerIndex; } // Create a new track with a single instruction as a starting point Track track = trackGameObject.AddComponent(); track.Instructions = new Instruction[1] { new Instruction() { Action = Action.Wait, Position = Vector3.zero, Duration = 0.1f, SpecialAnimationState = 0, Speed = 0.0f } }; } /// Called when the Unity editor draws the scene overlay GUI protected void OnSceneGUI() { drawWayPointHandles(); if(GUI.changed) { EditorUtility.SetDirty(target); } if(this.selectedInstructionIndex != -1) { Handles.BeginGUI(); Vector2 buttonPosition = HandleUtility.WorldToGUIPoint(getSelectedHandlePosition()); if(GUI.Button(new Rect(buttonPosition.x + 35, buttonPosition.y - 50, 30, 30), "+")) { insertNewInstruction(); } if(GUI.Button(new Rect(buttonPosition.x - 65, buttonPosition.y - 50, 30, 30), "-")) { removeCurrentInstruction(); } Handles.EndGUI(); } } /// Retrieves the position of the current selected handle /// The position of the currently selected handle private Vector3 getSelectedHandlePosition() { var track = (Track)target; if(this.selectedInstructionIndex == -1) { return track.transform.position; } else { Vector3 origin = track.GuessInitialPosition(); for(int index = 0; index < track.Instructions.Length; ++index) { if(isMovingInstruction(track.Instructions[index].Action)) { origin = track.transform.position + track.Instructions[index].Position; } if(index >= this.selectedInstructionIndex) { break; } } return origin; } } /// Removes the currently selected instruction from the track private void removeCurrentInstruction() { if(this.selectedInstructionIndex == -1) { return; } var track = (Track)target; Instruction[] previousInstructions = track.Instructions; track.Instructions = new Instruction[track.Instructions.Length - 1]; for(int index = 0; index < track.Instructions.Length; ++index) { int previousIndex = index; if(index >= this.selectedInstructionIndex) { ++previousIndex; } track.Instructions[index] = previousInstructions[previousIndex]; } if(this.selectedInstructionIndex >= track.Instructions.Length) { this.selectedInstructionIndex = track.Instructions.Length - 1; } } /// Inserts a new instruction behind the current selected instruction private void insertNewInstruction() { if(this.selectedInstructionIndex == -1) { return; // Or use last instruction? } var track = (Track)target; Instruction[] previousInstructions = track.Instructions; track.Instructions = new Instruction[track.Instructions.Length + 1]; for(int index = 0; index < track.Instructions.Length; ++index) { int previousIndex = index; if(index > this.selectedInstructionIndex) { --previousIndex; } track.Instructions[index] = previousInstructions[previousIndex]; } track.Instructions[this.selectedInstructionIndex + 1] = new Instruction() { Position = track.Instructions[this.selectedInstructionIndex].Position, Action = Action.Walk, Duration = 1.0f, Speed = 1.0f }; ++this.selectedInstructionIndex; } /// Draws the handles for the track's waypoints private void drawWayPointHandles() { var track = (Track)target; if(track.Instructions == null) { return; } Vector3 origin = track.GuessInitialPosition(); for(int index = 0; index < track.Instructions.Length; ++index) { Vector3 destination; if(isMovingInstruction(track.Instructions[index].Action)) { destination = track.transform.position + track.Instructions[index].Position; } else { destination = origin; } // Always draw the normal handle drawNormalHandle(index, destination); // Allow the user to move the currently selected height sample around if(index == this.selectedInstructionIndex) { drawMoveHandle(destination); } origin = destination; } } /// Draws a normal handle for a waypoint /// Index if the waypoint for which a handle will be drawn /// Absolute position of the waypoint private void drawNormalHandle(int index, Vector3 center) { bool wasHandleClicked = Handles.Button( center, Quaternion.identity, 0.1f, // size 0.15f, // pickSize Handles.CubeHandleCap ); if(wasHandleClicked) { this.selectedInstructionIndex = index; } } /// Draws a handle that supports moving for a waypoint /// Absolute position of the waypoint private void drawMoveHandle(Vector3 center) { var track = (Track)target; Vector3 position = track.transform.position; // Let Unity know that this object is being changed so it can take care // of recording its state in the undo history when moving ceases. Undo.RecordObject(track, "Move Cinematic Track Instruction"); // Draw the handle and update the height sample with the new position Vector3 newPosition = Handles.PositionHandle(center, Quaternion.identity); track.Instructions[this.selectedInstructionIndex].Position.x = newPosition.x - position.x; track.Instructions[this.selectedInstructionIndex].Position.y = newPosition.y - position.y; track.Instructions[this.selectedInstructionIndex].Position.z = newPosition.z - position.z; } /// /// Checks whether the specified instruction changes the agent's position /// /// Action that will be checked /// True if the action declares a movement instruction private bool isMovingInstruction(Action action) { return (action == Action.Walk) || (action == Action.Jump); } /// Index of the currently selected instruction, if any private int selectedInstructionIndex = -1; } } // namespace Framework.Cinematics