A game about forced loneliness, made by TACStudios
at master 288 lines 10 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5using UnityEngine.Timeline; 6 7namespace UnityEditor.Timeline 8{ 9 enum ManipulateEdges 10 { 11 Left, 12 Right, 13 Both 14 } 15 16 class SnapEngine 17 { 18 static readonly float k_MagnetInfluenceInPixels = 10.0f; 19 20 class SnapInfo 21 { 22 public double time { get; set; } 23 24 public bool showSnapHint { get; set; } 25 26 public bool IsInInfluenceZone(double currentTime, WindowState state) 27 { 28 var pos = state.TimeToPixel(currentTime); 29 var magnetPos = state.TimeToPixel(time); 30 31 return Math.Abs(pos - magnetPos) < k_MagnetInfluenceInPixels; 32 } 33 } 34 35 struct TimeBoundaries 36 { 37 public TimeBoundaries(double l, double r) 38 { 39 left = l; 40 right = r; 41 } 42 43 public readonly double left; 44 public readonly double right; 45 46 public TimeBoundaries Translate(double d) 47 { 48 return new TimeBoundaries(left + d, right + d); 49 } 50 } 51 52 public static bool displayDebugLayout; 53 54 readonly IAttractable m_Attractable; 55 readonly IAttractionHandler m_AttractionHandler; 56 readonly ManipulateEdges m_ManipulateEdges; 57 58 readonly WindowState m_State; 59 60 double m_GrabbedTime; 61 TimeBoundaries m_GrabbedTimes; 62 63 TimeBoundaries m_CurrentTimes; 64 65 readonly List<SnapInfo> m_Magnets = new List<SnapInfo>(); 66 67 bool m_SnapEnabled; 68 69 public SnapEngine(IAttractable attractable, IAttractionHandler attractionHandler, ManipulateEdges manipulateEdges, WindowState state, 70 Vector2 mousePosition, IEnumerable<ISnappable> snappables = null) 71 { 72 m_Attractable = attractable; 73 m_ManipulateEdges = manipulateEdges; 74 75 m_AttractionHandler = attractionHandler; 76 m_State = state; 77 78 m_CurrentTimes = m_GrabbedTimes = new TimeBoundaries(m_Attractable.start, m_Attractable.end); 79 m_GrabbedTime = m_State.PixelToTime(mousePosition.x); 80 81 // Add Time zero as Magnet 82 AddMagnet(0.0, true, state); 83 84 // Add current Time as Magnet 85 // case1157280 only add current time as magnet if visible 86 if (TimelineWindow.instance.currentMode.ShouldShowTimeCursor(m_State)) 87 AddMagnet(state.editSequence.time, true, state); 88 89 if (state.IsEditingASubTimeline()) 90 { 91 // Add start and end of evaluable range as Magnets 92 // This includes the case where the master timeline has a fixed length 93 var range = state.editSequence.GetEvaluableRange(); 94 AddMagnet(range.start, true, state); 95 AddMagnet(range.end, true, state); 96 } 97 else if (state.masterSequence.asset.durationMode == TimelineAsset.DurationMode.FixedLength) 98 { 99 // Add end sequence Time as Magnet 100 AddMagnet(state.masterSequence.asset.duration, true, state); 101 } 102 103 104 if (snappables == null) 105 snappables = GetVisibleSnappables(m_State); 106 107 foreach (var snappable in snappables) 108 { 109 if (!attractable.ShouldSnapTo(snappable)) 110 continue; 111 112 var edges = snappable.SnappableEdgesFor(attractable, manipulateEdges); 113 foreach (var edge in edges) 114 AddMagnet(edge.time, edge.showSnapHint, state); 115 } 116 } 117 118 public static IEnumerable<ISnappable> GetVisibleSnappables(WindowState state) 119 { 120 Rect rect = TimelineWindow.instance.state.timeAreaRect; 121 rect.height = float.MaxValue; 122 return state.spacePartitioner.GetItemsInArea<ISnappable>(rect).ToArray(); 123 } 124 125 void AddMagnet(double magnetTime, bool showSnapHint, WindowState state) 126 { 127 var magnet = m_Magnets.FirstOrDefault(m => m.time.Equals(magnetTime)); 128 if (magnet == null) 129 { 130 if (IsMagnetInShownArea(magnetTime, state)) 131 m_Magnets.Add(new SnapInfo { time = magnetTime, showSnapHint = showSnapHint }); 132 } 133 else 134 { 135 magnet.showSnapHint |= showSnapHint; 136 } 137 } 138 139 static bool IsMagnetInShownArea(double time, WindowState state) 140 { 141 var shownArea = state.timeAreaShownRange; 142 return time >= shownArea.x && time <= shownArea.y; 143 } 144 145 SnapInfo GetMagnetAt(double time) 146 { 147 return m_Magnets.FirstOrDefault(m => m.time.Equals(time)); 148 } 149 150 SnapInfo ClosestMagnet(double time) 151 { 152 SnapInfo candidate = null; 153 var min = double.MaxValue; 154 foreach (var magnetInfo in m_Magnets) 155 { 156 var m = Math.Abs(magnetInfo.time - time); 157 if (m < min) 158 { 159 candidate = magnetInfo; 160 min = m; 161 } 162 } 163 164 if (candidate != null && candidate.IsInInfluenceZone(time, m_State)) 165 return candidate; 166 167 return null; 168 } 169 170 public void Snap(Vector2 currentMousePosition, EventModifiers modifiers) 171 { 172 var d = m_State.PixelToTime(currentMousePosition.x) - m_GrabbedTime; 173 174 m_CurrentTimes = m_GrabbedTimes.Translate(d); 175 176 bool isLeft = m_ManipulateEdges == ManipulateEdges.Left || m_ManipulateEdges == ManipulateEdges.Both; 177 bool isRight = m_ManipulateEdges == ManipulateEdges.Right || m_ManipulateEdges == ManipulateEdges.Both; 178 179 bool attracted = false; 180 181 m_SnapEnabled = modifiers == ManipulatorsUtils.actionModifier ? !m_State.edgeSnaps : m_State.edgeSnaps; 182 183 if (m_SnapEnabled) 184 { 185 SnapInfo leftActiveMagnet = null; 186 SnapInfo rightActiveMagnet = null; 187 188 if (isLeft) 189 leftActiveMagnet = ClosestMagnet(m_CurrentTimes.left); 190 191 if (isRight) 192 rightActiveMagnet = ClosestMagnet(m_CurrentTimes.right); 193 194 if (leftActiveMagnet != null || rightActiveMagnet != null) 195 { 196 attracted = true; 197 198 bool leftAttraction = false; 199 200 if (rightActiveMagnet == null) 201 { 202 // Attracted by a left magnet only. 203 leftAttraction = true; 204 } 205 else 206 { 207 if (leftActiveMagnet != null) 208 { 209 // Attracted by both magnets, choose the closest one. 210 var leftDistance = Math.Abs(leftActiveMagnet.time - m_CurrentTimes.left); 211 var rightDistance = Math.Abs(rightActiveMagnet.time - m_CurrentTimes.right); 212 213 leftAttraction = leftDistance <= rightDistance; 214 } 215 // else, Attracted by right magnet only 216 } 217 218 if (leftAttraction) 219 { 220 m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Left, leftActiveMagnet.time); 221 } 222 else 223 { 224 m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Right, rightActiveMagnet.time); 225 } 226 } 227 } 228 229 if (!attracted) 230 { 231 var time = isLeft ? m_CurrentTimes.left : m_CurrentTimes.right; 232 233 time = TimeReferenceUtility.SnapToFrameIfRequired(time); 234 235 m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.None, time); 236 } 237 } 238 239 public void OnGUI(bool showLeft = true, bool showRight = true) 240 { 241 if (displayDebugLayout) 242 { 243 // Display Magnet influence zone 244 foreach (var m in m_Magnets) 245 { 246 var window = TimelineWindow.instance; 247 var rect = new Rect(m_State.TimeToPixel(m.time) - k_MagnetInfluenceInPixels, window.state.timeAreaRect.yMax, 2f * k_MagnetInfluenceInPixels, m_State.windowHeight); 248 EditorGUI.DrawRect(rect, new Color(1f, 0f, 0f, 0.4f)); 249 } 250 251 // Display Cursor position 252 var mousePos = Event.current.mousePosition; 253 var time = m_State.PixelToTime(mousePos.x); 254 var p = new Vector2(m_State.TimeToPixel(time), TimelineWindow.instance.state.timeAreaRect.yMax); 255 var s = new Vector2(1f, m_State.windowHeight); 256 EditorGUI.DrawRect(new Rect(p, s), Color.blue); 257 258 p = new Vector2(m_State.TimeToPixel(m_GrabbedTime), TimelineWindow.instance.state.timeAreaRect.yMax); 259 s = new Vector2(1f, m_State.windowHeight); 260 EditorGUI.DrawRect(new Rect(p, s), Color.red); 261 262 p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.left), TimelineWindow.instance.state.timeAreaRect.yMax); 263 s = new Vector2(1f, m_State.windowHeight); 264 EditorGUI.DrawRect(new Rect(p, s), Color.yellow); 265 266 p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.right), TimelineWindow.instance.state.timeAreaRect.yMax); 267 EditorGUI.DrawRect(new Rect(p, s), Color.yellow); 268 } 269 270 if (m_SnapEnabled) 271 { 272 if (showLeft) 273 DrawMagnetLineAt(m_Attractable.start); 274 275 if (showRight) 276 DrawMagnetLineAt(m_Attractable.end); 277 } 278 } 279 280 void DrawMagnetLineAt(double time) 281 { 282 var magnet = GetMagnetAt(time); 283 284 if (magnet != null && magnet.showSnapHint) 285 Graphics.DrawLineAtTime(m_State, magnet.time, Color.white); 286 } 287 } 288}