├── .gitattributes
├── .gitignore
├── LICENSE
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This .gitignore file should be placed at the root of your Unity project directory
2 | #
3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
4 | #
5 | /[Ll]ibrary/
6 | /[Tt]emp/
7 | /[Oo]bj/
8 | /[Bb]uild/
9 | /[Bb]uilds/
10 | /[Ll]ogs/
11 | /[Mm]emoryCaptures/
12 |
13 | # Never ignore Asset meta data
14 | !/[Aa]ssets/**/*.meta
15 |
16 | # Uncomment this line if you wish to ignore the asset store tools plugin
17 | # /[Aa]ssets/AssetStoreTools*
18 |
19 | # TextMesh Pro files
20 | [Aa]ssets/TextMesh*Pro/
21 |
22 | # Autogenerated Jetbrains Rider plugin
23 | [Aa]ssets/Plugins/Editor/JetBrains*
24 |
25 | # Visual Studio cache directory
26 | .vs/
27 |
28 | # Gradle cache directory
29 | .gradle/
30 |
31 | # Autogenerated VS/MD/Consulo solution and project files
32 | ExportedObj/
33 | .consulo/
34 | *.csproj
35 | *.unityproj
36 | *.sln
37 | *.suo
38 | *.tmp
39 | *.user
40 | *.userprefs
41 | *.pidb
42 | *.booproj
43 | *.svd
44 | *.pdb
45 | *.mdb
46 | *.opendb
47 | *.VC.db
48 |
49 | # Unity3D generated meta files
50 | *.pidb.meta
51 | *.pdb.meta
52 | *.mdb.meta
53 |
54 | # Unity3D generated file on crash reports
55 | sysinfo.txt
56 |
57 | # Builds
58 | *.apk
59 | *.unitypackage
60 |
61 | # Crashlytics generated file
62 | crashlytics-build.properties
63 |
64 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Park June Chul
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | UnityHack
2 | ====
3 | A set of snippets to extend Unity Editor.
4 |
5 | Before you start
6 | ----
7 | All of snippets from this repository only work inside Unity Editor.
8 | You must place your code inside `Editor` directory to avoid build errors.
9 |
10 | Bootstrap your plugin
11 | ----
12 | It all begins with bootstrapping code. This step is pretty easy because Unity already has API for this.
13 | ```cs
14 | [InitializeOnLoadMethod]
15 | public static void Initialize() {
16 | /* ... Your code goes here ... */
17 | }
18 | ```
19 |
20 | Input
21 | ----
22 | Because of `Input` class does not work on Unity Editor, you have to write special code to handle it.
23 |
24 | __Handling global editor input__
25 | ```cs
26 | System.Reflection.FieldInfo info = typeof(EditorApplication).GetField("globalEventHandler", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
27 | EditorApplication.CallbackFunction value = (EditorApplication.CallbackFunction)info.GetValue(null);
28 | value += HandleUnityEvent;
29 | info.SetValue(null, value);
30 | ```
31 | ```cs
32 | static void HandleUnityEvent()
33 | {
34 | var e = Event.current;
35 |
36 | if (e.type == EventType.KeyDown && e.keyCode == KeyCode.LeftControl)
37 | {
38 | /* Do your stuffs here */
39 | }
40 | }
41 | ```
42 |
43 | __Handling scene window input__
44 | If you want to execute your code __ONLY__ inside the scene view, try this instead of `globalEventHandler`. It also seems less scary.
45 | ```cs
46 | SceneView.duringSceneGui += HandleSceneEvent;
47 | ```
48 | ```cs
49 | static void HandleSceneEvent() {
50 | var e = Event.current;
51 |
52 | if (e == null) return;
53 | if (e.type == EventType.KeyDown && e.keyCode == KeyCode.LeftControl)
54 | {
55 | /* Do your stuffs here */
56 | }
57 | }
58 | ```
59 |
60 |
61 | Property change detection
62 | ----
63 | There's no official API to get notification when your changed the property. However, this can be done with `Undo.postprocessModifications` since every changes will be recorded to `Undo`.
64 |
65 | ```cs
66 | Undo.postprocessModifications += OnPostProcessModifications;
67 | ```
68 | ```cs
69 | private static UndoPropertyModification[] OnPostProcessModifications(UndoPropertyModification[] propertyModifications)
70 | {
71 | foreach (var m in propertyModifications)
72 | {
73 | if (m.currentValue.target is Text textTarget) {
74 | if (m.currentValue.propertyPath == "m_FontData.m_FontSize") {
75 | /* User has changed `FontSize` from `Text` Component. */
76 | }
77 | }
78 | }
79 | }
80 | ```
81 |
82 | Rendering inside SceneView
83 | ----
84 | `OnDrawGizmo` is a traditional way to draw something inside SceneView. However, you must have a `GameObject` to receive `OnDrawGizmo` callback from Unity.
85 | You can also use `SceneView.duringSceneGui` to draw your stuffs without active `GameObject`.
86 |
87 | ```cs
88 | SceneView.duringSceneGui += OnDrawSceneGUI;
89 | ```
90 | ```cs
91 | static void OnDrawSceneGUI()
92 | {
93 | /* Do your stuffs! */
94 | }
95 | ```
96 |
97 | __Render Texts__
98 | There are APIs for drawing `Rect`, `Line`, `Polygon` and even `Texture`. But they don't have an API to render texts.
99 | You can render texts by below script:
100 | ```cs
101 | public static void DrawString(string text, Vector3 position, Color? color = null, bool showTexture = true, int offsetY = 0)
102 | {
103 | Handles.BeginGUI();
104 |
105 | var restoreColor = GUI.color;
106 | var viewSize = SceneView.currentDrawingSceneView.position;
107 | if (color.HasValue) GUI.color = color.Value;
108 | var view = SceneView.currentDrawingSceneView;
109 | Vector3 screenPos = view.camera.WorldToScreenPoint(
110 | position + view.camera.transform.forward);
111 | if (screenPos.y < 0 || screenPos.y > Screen.height || screenPos.x < 0 || screenPos.x > Screen.width || screenPos.z < 0)
112 | {
113 | GUI.color = restoreColor;
114 | Handles.EndGUI();
115 | return;
116 | }
117 | var style = new GUIStyle(GUI.skin.label);
118 | style.alignment = TextAnchor.MiddleCenter;
119 | style.fontSize = 30;
120 | Vector2 size = style.CalcSize(new GUIContent(text));
121 | GUI.Label(new Rect(screenPos.x - size.x/2, viewSize.height - screenPos.y - offsetY - size.y/2, size.x, size.y), text, style);
122 | GUI.color = restoreColor;
123 |
124 | Handles.EndGUI();
125 | }
126 | ```
127 |
128 | Detecting object creation
129 | ----
130 |
131 | __Detect GameObject creation__
132 | ```cs
133 | EditorApplication.hierarchyChanged += OnHierarchyChanged;
134 | ```
135 | ```cs
136 | private static GameObject lastChangedGO;
137 |
138 | private static void OnHierarchyChanged()
139 | {
140 | if (Selection.objects.Length == 0)
141 | return;
142 | var go = Selection.objects[0] as GameObject;
143 | if (go == null)
144 | return;
145 |
146 | if (go == lastChangedGO)
147 | return;
148 |
149 | lastChangedGO = go;
150 |
151 | /* Do your stuff! */
152 | }
153 | ```
154 |
155 | __Detect Component creation__
156 | ```cs
157 | private static GameObject lastChangedGO;
158 | private static int lastGOComponentCount;
159 |
160 | private static void OnHierarchyChanged()
161 | {
162 | if (Selection.objects.Length == 0)
163 | return;
164 | var go = Selection.objects[0] as GameObject;
165 | if (go == null)
166 | return;
167 |
168 | if (go != lastChangedGO)
169 | return;
170 | lastChangedGO = go;
171 |
172 | var comps = go.GetComponents();
173 | if (comps.Length == lastGOComponentCount)
174 | return;
175 | lastGOComponentCount = comps.Length;
176 |
177 | var addedComponent = comps.Last();
178 | /* Do your stuff! */
179 | }
180 | ```
181 |
182 | Drag and Drop
183 | ----
184 |
185 | __Asset drag detection__
186 | You can watch `BeginDrag` for your assets with below code:
187 | ```cs
188 | EditorApplication.update += OnUpdate;
189 | ```
190 | ```cs
191 | static void OnUpdate()
192 | {
193 | if (DragAndDrop.paths.Length > 0 &&
194 | !(Selection.activeObject is GameObject))
195 | {
196 | // DragAndDrop.objectReferences;
197 | }
198 | }
199 | ```
200 |
201 | Object Picking
202 | ----
203 | You can retrive gameObjects with a XY position using `HandleUtility.PickGameObject`. This is slightly better than `Physics.Raycast` since it does not require `Collider` on the GameObject.
204 |
205 | ```cs
206 | SceneView.duringSceneGui += HandleSceneEvents;
207 | ```
208 | ```cs
209 | static void HandleSceneEvents() {
210 | if (e == null) return;
211 | if (e.type == EventType.MouseDown) {
212 | var picked = HandleUtility.PickGameObject(
213 | Event.current.mousePosition, false);
214 | Debug.Log(picked);
215 | }
216 | }
217 | ```
218 |
219 | __Pick ALL objects with current mouse position__
220 | ```cs
221 | var ignoreList = new List();
222 | while (true)
223 | {
224 | var picked = HandleUtility.PickGameObject(
225 | Event.current.mousePosition, false,
226 | ignoreList.ToArray());
227 |
228 | if (picked == null)
229 | break;
230 |
231 | Debug.Log(picked);
232 |
233 | ignoreList.Add(picked);
234 | }
235 | ```
236 |
--------------------------------------------------------------------------------