/// <summary> /// Splits a clip into two separate ones at the specified time. /// </summary> /// <param name="splitPoint">The time at which to split the clip.</param> /// <param name="track">The track the clip is sitting on.</param> /// <param name="clip">The clip to split.</param> /// <returns>The new clip.</returns> public static CutsceneClip SplitClipAtTime(float splitPoint, CutsceneTrack track, CutsceneClip clip) { CutsceneClip newClip = clip.GetCopy(); // Make sure the clip actually spans over the split point if (splitPoint < clip.timelineStart || splitPoint > clip.timelineStart + clip.duration) { EDebug.Log("Cutscene Editor: cannot split clip; clip does not contain the split point"); return null; } clip.SetOutPoint(clip.inPoint + (splitPoint - clip.timelineStart)); newClip.SetInPoint(clip.outPoint); newClip.SetTimelineStart(splitPoint); track.clips.Add(newClip); Event.current.Use(); EDebug.Log("Cutscene Editor: splitting clip at time " + splitPoint); return newClip; }
/// <summary> /// Splits a clip into two separate ones. /// </summary> /// <param name="track">The track the clip is sitting on.</param> /// <param name="clip">The clip to split.</param> /// <param name="mousePosition">The position of the mouse when the split operation occurred.</param> /// <returns>The new clip.</returns> CutsceneClip SplitClip(CutsceneTrack track, CutsceneClip clip, Vector2 mousePosition) { float splitPoint = mousePosition.x / ed.timelineZoom; return CutsceneTimeline.SplitClipAtTime(splitPoint, track, clip); }
/// <summary> /// Displays visual tracks upon which clips sit. /// </summary> void DisplayTrack(Rect rect, CutsceneTrack track) { GUI.enabled = track.enabled; for (int i = track.clips.Count - 1; i >= 0; i--) { DisplayClip(rect, track, track.clips[i]); } GUI.enabled = true; // Handle clicks Vector2 mousePos = Event.current.mousePosition; if (Event.current.type == EventType.MouseDown && rect.Contains(mousePos)) { switch (Event.current.button) { case 0: // Left mouse button ed.selectedTrack = track; ed.selectedClip = null; break; case 1: // Right mouse button EditorUtility.DisplayPopupMenu(new Rect(mousePos.x, mousePos.y, 0, 0), "CONTEXT/CutsceneTrack/", new MenuCommand(track)); Event.current.Use(); break; default: break; } Event.current.Use(); } }
/// <summary> /// Displays a clip. /// </summary> /// <param name="trackRect">The Rect of the track the clip sits on.</param> /// <param name="track">The track the clip sits on.</param> /// <param name="clip">The clip to display.</param> void DisplayClip(Rect trackRect, CutsceneTrack track, CutsceneClip clip) { const float trimWidth = 5f; GUIStyle clipStyle = ed.style.GetStyle("Selected Clip"); // Set the clip style if this isn't the selected clip (selected clips all share the same style) if (clip != ed.selectedClip) { switch (clip.type) { case Cutscene.MediaType.Shots: clipStyle = ed.style.GetStyle("Shot Clip"); break; case Cutscene.MediaType.Actors: clipStyle = ed.style.GetStyle("Actor Clip"); break; case Cutscene.MediaType.Audio: clipStyle = ed.style.GetStyle("Audio Clip"); break; default: // Cutscene.MediaType.Subtitles clipStyle = ed.style.GetStyle("Subtitle Clip"); break; } } Rect rect = new Rect((trackRect.x + clip.timelineStart) * ed.timelineZoom, trackRect.y + 1, clip.duration * ed.timelineZoom, clipStyle.fixedHeight); GUI.BeginGroup(rect, clipStyle); GUIContent clipLabel = new GUIContent(clip.name, "Clip: " + clip.name + "\nDuration: " + clip.duration + "\nTimeline start: " + clip.timelineStart + "\nTimeline end: " + (clip.timelineStart + clip.duration)); Rect clipLabelRect = new Rect(clipStyle.contentOffset.x, 0, rect.width - clipStyle.contentOffset.x, rect.height); GUI.Label(clipLabelRect, clipLabel); GUI.EndGroup(); // Handle mouse clicks Vector2 mousePos = Event.current.mousePosition; if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition)) { switch (Event.current.button) { case 0: // Left mouse button ed.selectedClip = clip; ed.selectedTrack = track; break; case 1: // Right mouse button EditorUtility.DisplayPopupMenu(new Rect(mousePos.x, mousePos.y, 0, 0), "CONTEXT/CutsceneClip/", new MenuCommand(clip)); Event.current.Use(); break; default: break; } } if (clip.setToDelete) { ed.selectedTrack.clips.Remove(clip); return; } // Don't allow actions to be performed on the clip if the track is disabled or locked if (!track.enabled || track.locked) { return; } switch (ed.currentTool) { case Tool.MoveResize: // Define edit areas, adding custom cursors when hovered over // Move Rect move = new Rect(rect.x + trimWidth, rect.y, rect.width - (2 * trimWidth), rect.height); EditorGUIUtility.AddCursorRect(move, MouseCursor.SlideArrow); // Resize left Rect resizeLeft = new Rect(rect.x, rect.y, trimWidth, rect.height); EditorGUIUtility.AddCursorRect(resizeLeft, MouseCursor.ResizeHorizontal); // Resize right Rect resizeRight = new Rect(rect.xMax - trimWidth, rect.y, trimWidth, rect.height); EditorGUIUtility.AddCursorRect(resizeRight, MouseCursor.ResizeHorizontal); if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition)) { ed.dragClip = clip; // Move if (move.Contains(Event.current.mousePosition)) { ed.dragEvent = DragEvent.Move; EDebug.Log("Cutscene Editor: starting clip move"); // Resize left } else if (resizeLeft.Contains(Event.current.mousePosition)) { ed.dragEvent = DragEvent.ResizeLeft; EDebug.Log("Cutscene Editor: starting clip resize left"); // Resize right } else if (resizeRight.Contains(Event.current.mousePosition)) { ed.dragEvent = DragEvent.ResizeRight; EDebug.Log("Cutscene Editor: starting clip resize right"); } Event.current.Use(); } else if (Event.current.type == EventType.MouseDrag && ed.dragClip == clip) { float shift = Event.current.delta.x / ed.timelineZoom; switch (ed.dragEvent) { case DragEvent.Move: float newPos = clip.timelineStart + shift; // Left collisions CutsceneClip leftCollision = track.ContainsClipAtTime(newPos, clip); if (leftCollision != null) { newPos = leftCollision.timelineStart + leftCollision.duration; } // Right collisions CutsceneClip rightCollision = track.ContainsClipAtTime(newPos + clip.duration, clip); if (rightCollision != null) { newPos = rightCollision.timelineStart - clip.duration; } if (newPos + clip.duration > ed.scene.duration) { newPos = ed.scene.duration - clip.duration; } clip.SetTimelineStart(newPos); break; case DragEvent.ResizeLeft: clip.SetTimelineStart(clip.timelineStart + shift); clip.SetInPoint(clip.inPoint + shift); // TODO Improve collision behaviour CutsceneClip leftResizeCollision = track.ContainsClipAtTime(clip.timelineStart, clip); if (leftResizeCollision != null) { clip.SetTimelineStart(leftResizeCollision.timelineStart + leftResizeCollision.duration); } break; case DragEvent.ResizeRight: float newOut = clip.outPoint + shift; // Right collisions CutsceneClip rightResizeCollision = track.ContainsClipAtTime(clip.timelineStart + clip.duration + shift, clip); if (rightResizeCollision != null) { newOut = rightResizeCollision.timelineStart - clip.timelineStart + clip.inPoint; } clip.SetOutPoint(newOut); break; default: break; } Event.current.Use(); } else if (Event.current.type == EventType.MouseUp) { ed.dragClip = null; Event.current.Use(); } break; case Tool.Scissors: // TODO Switch to something better than the text cursor, if possible EditorGUIUtility.AddCursorRect(rect, MouseCursor.Text); if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition)) { SplitClip(track, clip, Event.current.mousePosition); Event.current.Use(); } break; default: break; } }
/// <summary> /// Deletes a track. /// </summary> /// <param name="track">The track to delete.</param> /// <returns>True if the track was successfully deleted, false otherwise.</returns> static bool DeleteTrack(CutsceneTrack track) { bool delete = true; // Display a dialog to prevent accidental deletions if (EditorPrefs.GetBool("Cutscene Warn Before Delete", true)) { delete = EditorUtility.DisplayDialog("Delete Track", "Are you sure you wish to delete this track? Changes cannot be undone.", "Delete", "Cancel"); } if (delete) { DestroyImmediate(track); } return delete; }