A game about forced loneliness, made by TACStudios
1using System;
2using System.Linq;
3using System.Collections.Generic;
4using UnityEngine;
5using UnityEditor;
6using UnityEditor.EditorTools;
7using UnityEditor.U2D.Common.Path.GUIFramework;
8using UnityObject = UnityEngine.Object;
9
10namespace UnityEditor.U2D.Common.Path
11{
12 internal static class PathEditorToolContents
13 {
14 internal static readonly GUIContent shapeToolIcon = IconContent("ShapeTool", "Start editing the Shape in the Scene View.");
15 internal static readonly GUIContent shapeToolPro = IconContent("ShapeToolPro", "Start editing the Shape in the Scene View.");
16
17 internal static GUIContent IconContent(string name, string tooltip = null)
18 {
19 return new GUIContent(AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.2d.common/Path/Editor/Handles/" + name + ".png"), tooltip);
20 }
21
22 public static GUIContent icon
23 {
24 get
25 {
26 if (EditorGUIUtility.isProSkin)
27 return shapeToolPro;
28
29 return shapeToolIcon;
30 }
31 }
32 }
33
34 internal interface IDuringSceneGuiTool
35 {
36 void DuringSceneGui(SceneView sceneView);
37 bool IsAvailable();
38 }
39
40 [InitializeOnLoad]
41 internal class EditorToolManager
42 {
43 private static List<IDuringSceneGuiTool> m_Tools = new List<IDuringSceneGuiTool>();
44
45 static EditorToolManager()
46 {
47 SceneView.duringSceneGui += DuringSceneGui;
48 }
49
50 internal static void Add(IDuringSceneGuiTool tool)
51 {
52 if (!m_Tools.Contains(tool) && tool is EditorTool)
53 m_Tools.Add(tool);
54 }
55
56 internal static void Remove(IDuringSceneGuiTool tool)
57 {
58 if (m_Tools.Contains(tool))
59 m_Tools.Remove(tool);
60 }
61
62 internal static bool IsActiveTool<T>() where T : EditorTool
63 {
64 return ToolManager.activeToolType.Equals(typeof(T));
65 }
66
67 internal static bool IsAvailable<T>() where T : EditorTool
68 {
69 var tool = GetEditorTool<T>();
70
71 if (tool != null)
72 return tool.IsAvailable();
73
74 return false;
75 }
76
77 internal static T GetEditorTool<T>() where T : EditorTool
78 {
79 foreach(var tool in m_Tools)
80 {
81 if (tool.GetType().Equals(typeof(T)))
82 return tool as T;
83 }
84
85 return null;
86 }
87
88 private static void DuringSceneGui(SceneView sceneView)
89 {
90 foreach (var tool in m_Tools)
91 {
92 if (tool.IsAvailable() && ToolManager.IsActiveTool(tool as EditorTool))
93 tool.DuringSceneGui(sceneView);
94 }
95 }
96 }
97
98 internal abstract class PathEditorTool<T> : EditorTool, IDuringSceneGuiTool where T : ScriptablePath
99 {
100 private Dictionary<UnityObject, T> m_Paths = new Dictionary<UnityObject, T>();
101 private IGUIState m_GUIState = new GUIState();
102 private Dictionary<UnityObject, PathEditor> m_PathEditors = new Dictionary<UnityObject, PathEditor>();
103 private Dictionary<UnityObject, SerializedObject> m_SerializedObjects = new Dictionary<UnityObject, SerializedObject>();
104 private MultipleEditablePathController m_Controller = new MultipleEditablePathController();
105 private PointRectSelector m_RectSelector = new PointRectSelector();
106 private bool m_IsActive = false;
107
108 public override bool gridSnapEnabled => true;
109
110 internal T[] paths
111 {
112 get { return m_Paths.Values.ToArray(); }
113 }
114
115 public override GUIContent toolbarIcon
116 {
117 get { return PathEditorToolContents.icon; }
118 }
119
120 public override bool IsAvailable()
121 {
122 return targets.Count() > 0;
123 }
124
125 public T GetPath(UnityObject targetObject)
126 {
127 var path = default(T);
128 m_Paths.TryGetValue(targetObject, out path);
129 return path;
130 }
131
132 public void SetPath(UnityObject target)
133 {
134 var path = GetPath(target);
135 path.localToWorldMatrix = Matrix4x4.identity;
136
137 var undoName = Undo.GetCurrentGroupName();
138 var serializedObject = GetSerializedObject(target);
139
140 serializedObject.UpdateIfRequiredOrScript();
141
142 SetShape(path, serializedObject);
143
144 Undo.SetCurrentGroupName(undoName);
145 }
146
147 private void RepaintInspectors()
148 {
149 var editorWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
150
151 foreach (var editorWindow in editorWindows)
152 {
153 if (editorWindow.titleContent.text == "Inspector")
154 editorWindow.Repaint();
155 }
156 }
157
158 private void OnEnable()
159 {
160 m_IsActive = false;
161 EditorToolManager.Add(this);
162
163 SetupRectSelector();
164 HandleActivation();
165
166 ToolManager.activeToolChanged += HandleActivation;
167 }
168
169 private void OnDestroy()
170 {
171 EditorToolManager.Remove(this);
172
173 ToolManager.activeToolChanged -= HandleActivation;
174 UnregisterCallbacks();
175 }
176
177 private void HandleActivation()
178 {
179 if (m_IsActive == false && ToolManager.IsActiveTool(this))
180 Activate();
181 else if (m_IsActive)
182 Deactivate();
183 }
184
185 private void Activate()
186 {
187 m_IsActive = true;
188 RegisterCallbacks();
189 InitializeCache();
190 OnActivate();
191 }
192
193 private void Deactivate()
194 {
195 OnDeactivate();
196 DestroyCache();
197 UnregisterCallbacks();
198 m_IsActive = false;
199 }
200
201 private void RegisterCallbacks()
202 {
203 UnregisterCallbacks();
204 Selection.selectionChanged += SelectionChanged;
205 EditorApplication.playModeStateChanged += PlayModeStateChanged;
206 Undo.undoRedoPerformed += UndoRedoPerformed;
207 }
208
209 private void UnregisterCallbacks()
210 {
211 Selection.selectionChanged -= SelectionChanged;
212 EditorApplication.playModeStateChanged -= PlayModeStateChanged;
213 Undo.undoRedoPerformed -= UndoRedoPerformed;
214 }
215
216 private void DestroyCache()
217 {
218 foreach (var pair in m_Paths)
219 {
220 var path = pair.Value;
221
222 if (path != null)
223 {
224 Undo.ClearUndo(path);
225 UnityObject.DestroyImmediate(path);
226 }
227 }
228 m_Paths.Clear();
229 m_Controller.ClearPaths();
230 m_PathEditors.Clear();
231 m_SerializedObjects.Clear();
232 }
233
234 private void UndoRedoPerformed()
235 {
236 ForEachTarget((target) =>
237 {
238 var path = GetPath(target);
239
240 if (!path.modified)
241 InitializePath(target);
242 });
243 }
244
245 private void SelectionChanged()
246 {
247 InitializeCache();
248 }
249
250 private void PlayModeStateChanged(PlayModeStateChange stateChange)
251 {
252 if (stateChange == PlayModeStateChange.EnteredEditMode)
253 EditorApplication.delayCall += () => { InitializeCache(); }; //HACK: At this point target is null. Let's wait to next frame to refresh.
254 }
255
256 private void SetupRectSelector()
257 {
258 m_RectSelector.onSelectionBegin = BeginSelection;
259 m_RectSelector.onSelectionChanged = UpdateSelection;
260 m_RectSelector.onSelectionEnd = EndSelection;
261 }
262
263 private void ForEachTarget(Action<UnityObject> action)
264 {
265 foreach(var target in targets)
266 {
267 if (target == null)
268 continue;
269
270 action(target);
271 }
272 }
273
274 private void InitializeCache()
275 {
276 m_Controller.ClearPaths();
277
278 ForEachTarget((target) =>
279 {
280 var path = GetOrCreatePath(target);
281 var pointCount = path.pointCount;
282
283 InitializePath(target);
284
285 if (pointCount != path.pointCount)
286 path.selection.Clear();
287
288 CreatePathEditor(target);
289
290 m_Controller.AddPath(path);
291 });
292 }
293
294 private void InitializePath(UnityObject target)
295 {
296 IShape shape = null;
297 ControlPoint[] controlPoints = null;
298
299 try
300 {
301 shape = GetShape(target);
302 controlPoints = shape.ToControlPoints();
303 }
304 catch (Exception e)
305 {
306 Debug.LogError(e.Message);
307 }
308
309 var path = GetPath(target);
310 path.Clear();
311
312 if (shape != null && controlPoints != null)
313 {
314 path.localToWorldMatrix = Matrix4x4.identity;
315 path.shapeType = shape.type;
316 path.isOpenEnded = shape.isOpenEnded;
317
318 foreach (var controlPoint in controlPoints)
319 path.AddPoint(controlPoint);
320 }
321
322 Initialize(path, GetSerializedObject(target));
323 }
324
325 private T GetOrCreatePath(UnityObject targetObject)
326 {
327 var path = GetPath(targetObject);
328
329 if (path == null)
330 {
331 path = ScriptableObject.CreateInstance<T>();
332 path.hideFlags = HideFlags.HideAndDontSave;
333 path.owner = targetObject;
334 m_Paths[targetObject] = path;
335 }
336
337 return path;
338 }
339
340 private PathEditor GetPathEditor(UnityObject target)
341 {
342 PathEditor pathEditor;
343 m_PathEditors.TryGetValue(target, out pathEditor);
344 return pathEditor;
345 }
346
347 private void CreatePathEditor(UnityObject target)
348 {
349 var pathEditor = new PathEditor();
350 pathEditor.controller = m_Controller;
351 pathEditor.drawerOverride = GetCustomDrawer(target);
352 m_PathEditors[target] = pathEditor;
353 }
354
355 private SerializedObject GetSerializedObject(UnityObject target)
356 {
357 var serializedObject = default(SerializedObject);
358
359 if (!m_SerializedObjects.TryGetValue(target, out serializedObject))
360 {
361 serializedObject = new SerializedObject(target);
362 m_SerializedObjects[target] = serializedObject;
363 }
364
365 return serializedObject;
366 }
367
368 void IDuringSceneGuiTool.DuringSceneGui(SceneView sceneView)
369 {
370 if (m_GUIState.eventType == EventType.Layout)
371 m_Controller.ClearClosestPath();
372
373 m_RectSelector.OnGUI();
374
375 bool changed = false;
376
377 ForEachTarget((target) =>
378 {
379 var path = GetPath(target);
380
381 if (path != null)
382 {
383 path.localToWorldMatrix = GetLocalToWorldMatrix(target);
384 path.forward = GetForward(target);
385 path.up = GetUp(target);
386 path.right = GetRight(target);
387 m_Controller.editablePath = path;
388
389 using (var check = new EditorGUI.ChangeCheckScope())
390 {
391 var pathEditor = GetPathEditor(target);
392 pathEditor.linearTangentIsZero = GetLinearTangentIsZero(target);
393 pathEditor.OnGUI();
394 OnCustomGUI(path);
395 changed |= check.changed;
396 }
397 }
398 });
399
400 if (changed)
401 {
402 SetShapes();
403 RepaintInspectors();
404 }
405 }
406
407 private void BeginSelection(ISelector<Vector3> selector, bool isAdditive)
408 {
409 m_Controller.RegisterUndo("Selection");
410
411 if (isAdditive)
412 {
413 ForEachTarget((target) =>
414 {
415 var path = GetPath(target);
416 path.selection.BeginSelection();
417 });
418 }
419 else
420 {
421 UpdateSelection(selector);
422 }
423 }
424
425 private void UpdateSelection(ISelector<Vector3> selector)
426 {
427 var repaintInspectors = false;
428
429 ForEachTarget((target) =>
430 {
431 var path = GetPath(target);
432
433 repaintInspectors |= path.Select(selector);
434 });
435
436 if (repaintInspectors)
437 RepaintInspectors();
438 }
439
440 private void EndSelection(ISelector<Vector3> selector)
441 {
442 ForEachTarget((target) =>
443 {
444 var path = GetPath(target);
445 path.selection.EndSelection(true);
446 });
447 }
448
449 internal void SetShapes()
450 {
451 ForEachTarget((target) =>
452 {
453 SetPath(target);
454 });
455 }
456
457 private Transform GetTransform(UnityObject target)
458 {
459 return (target as Component).transform;
460 }
461
462 private Matrix4x4 GetLocalToWorldMatrix(UnityObject target)
463 {
464 return GetTransform(target).localToWorldMatrix;
465 }
466
467 private Vector3 GetForward(UnityObject target)
468 {
469 return GetTransform(target).forward;
470 }
471
472 private Vector3 GetUp(UnityObject target)
473 {
474 return GetTransform(target).up;
475 }
476
477 private Vector3 GetRight(UnityObject target)
478 {
479 return GetTransform(target).right;
480 }
481
482 protected abstract IShape GetShape(UnityObject target);
483 protected virtual void Initialize(T path, SerializedObject serializedObject) { }
484 protected abstract void SetShape(T path, SerializedObject serializedObject);
485 protected virtual void OnActivate() { }
486 protected virtual void OnDeactivate() { }
487 protected virtual void OnCustomGUI(T path) { }
488 protected virtual bool GetLinearTangentIsZero(UnityObject target) { return false; }
489 protected virtual IDrawer GetCustomDrawer(UnityObject target) { return null; }
490 }
491}