├── .gitattributes ├── .gitignore ├── Common ├── BetterList.cs ├── BetterList.cs.meta ├── CommonHelper.cs ├── CommonHelper.cs.meta ├── ContextMenu.cs ├── ContextMenu.cs.meta ├── Decorate.cs ├── Decorate.cs.meta ├── LayoutInfo.cs ├── LayoutInfo.cs.meta ├── PathSaver.cs ├── PathSaver.cs.meta ├── ReloadLayoutOnExitGame.cs ├── ReloadLayoutOnExitGame.cs.meta ├── ReopenLayoutOnExitGame.cs ├── ReopenLayoutOnExitGame.cs.meta ├── UIEditorHelper.cs ├── UIEditorHelper.cs.meta ├── UILayoutTool.cs └── UILayoutTool.cs.meta ├── Configure.cs ├── Editor ├── CustomInspectors.cs ├── CustomInspectors.cs.meta ├── FindReferences.cs ├── FindReferences.cs.meta ├── PrefabWin.cs ├── PrefabWin.cs.meta ├── SceneEditor.cs └── SceneEditor.cs.meta ├── README.md └── Res ├── Canvas.prefab ├── Canvas.prefab.meta ├── Decorate.prefab ├── Decorate.prefab.meta ├── Preview.meta └── Preview └── _.gitkeep /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/Resources* 7 | Assets/UIEditor/Res/Preview/ 8 | /Assets/AssetStoreTools* 9 | 10 | 11 | # Visual Studio 2015 cache directory 12 | /.vs/ 13 | 14 | # Autogenerated VS/MD/Consulo solution and project files 15 | ExportedObj/ 16 | .consulo/ 17 | *.csproj 18 | *.unityproj 19 | *.sln 20 | *.suo 21 | *.tmp 22 | *.user 23 | *.userprefs 24 | *.pidb 25 | *.booproj 26 | *.svd 27 | *.pdb 28 | 29 | # Unity3D generated meta files 30 | *.pidb.meta 31 | 32 | # Unity3D Generated File On Crash Reports 33 | sysinfo.txt 34 | 35 | # Builds 36 | *.apk 37 | *.unitypackage 38 | -------------------------------------------------------------------------------- /Common/BetterList.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------- 2 | // NGUI: Next-Gen UI kit 3 | // Copyright © 2011-2017 Tasharen Entertainment Inc 4 | //------------------------------------------------- 5 | 6 | using UnityEngine; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | 10 | /// 11 | /// This improved version of the System.Collections.Generic.List that doesn't release the buffer on Clear(), 12 | /// resulting in better performance and less garbage collection. 13 | /// PRO: BetterList performs faster than List when you Add and Remove items (although slower if you remove from the beginning). 14 | /// CON: BetterList performs worse when sorting the list. If your operations involve sorting, use the standard List instead. 15 | /// 16 | 17 | public class BetterList 18 | { 19 | #if UNITY_FLASH 20 | 21 | List mList = new List(); 22 | 23 | /// 24 | /// Direct access to the buffer. Note that you should not use its 'Length' parameter, but instead use BetterList.size. 25 | /// 26 | 27 | public T this[int i] 28 | { 29 | get { return mList[i]; } 30 | set { mList[i] = value; } 31 | } 32 | 33 | /// 34 | /// Compatibility with the non-flash syntax. 35 | /// 36 | 37 | public List buffer { get { return mList; } } 38 | 39 | /// 40 | /// Direct access to the buffer's size. Note that it's only public for speed and efficiency. You shouldn't modify it. 41 | /// 42 | 43 | public int size { get { return mList.Count; } } 44 | 45 | /// 46 | /// For 'foreach' functionality. 47 | /// 48 | 49 | public IEnumerator GetEnumerator () { return mList.GetEnumerator(); } 50 | 51 | /// 52 | /// Clear the array by resetting its size to zero. Note that the memory is not actually released. 53 | /// 54 | 55 | public void Clear () { mList.Clear(); } 56 | 57 | /// 58 | /// Clear the array and release the used memory. 59 | /// 60 | 61 | public void Release () { mList.Clear(); } 62 | 63 | /// 64 | /// Add the specified item to the end of the list. 65 | /// 66 | 67 | public void Add (T item) { mList.Add(item); } 68 | 69 | /// 70 | /// Insert an item at the specified index, pushing the entries back. 71 | /// 72 | 73 | public void Insert (int index, T item) 74 | { 75 | if (index > -1 && index < mList.Count) mList.Insert(index, item); 76 | else mList.Add(item); 77 | } 78 | 79 | /// 80 | /// Returns 'true' if the specified item is within the list. 81 | /// 82 | 83 | public bool Contains (T item) { return mList.Contains(item); } 84 | 85 | /// 86 | /// Return the index of the specified item. 87 | /// 88 | 89 | public int IndexOf (T item) { return mList.IndexOf(item); } 90 | 91 | /// 92 | /// Remove the specified item from the list. Note that RemoveAt() is faster and is advisable if you already know the index. 93 | /// 94 | 95 | public bool Remove (T item) { return mList.Remove(item); } 96 | 97 | /// 98 | /// Remove an item at the specified index. 99 | /// 100 | 101 | public void RemoveAt (int index) { mList.RemoveAt(index); } 102 | 103 | /// 104 | /// Remove an item from the end. 105 | /// 106 | 107 | public T Pop () 108 | { 109 | if (buffer != null && size != 0) 110 | { 111 | T val = buffer[mList.Count - 1]; 112 | mList.RemoveAt(mList.Count - 1); 113 | return val; 114 | } 115 | return default(T); 116 | } 117 | 118 | /// 119 | /// Mimic List's ToArray() functionality, except that in this case the list is resized to match the current size. 120 | /// 121 | 122 | public T[] ToArray () { return mList.ToArray(); } 123 | 124 | /// 125 | /// List.Sort equivalent. 126 | /// 127 | 128 | public void Sort (System.Comparison comparer) { mList.Sort(comparer); } 129 | 130 | #else 131 | 132 | /// 133 | /// Direct access to the buffer. Note that you should not use its 'Length' parameter, but instead use BetterList.size. 134 | /// 135 | 136 | public T[] buffer; 137 | 138 | /// 139 | /// Direct access to the buffer's size. Note that it's only public for speed and efficiency. You shouldn't modify it. 140 | /// 141 | 142 | public int size = 0; 143 | 144 | /// 145 | /// For 'foreach' functionality. 146 | /// 147 | 148 | [DebuggerHidden] 149 | [DebuggerStepThrough] 150 | public IEnumerator GetEnumerator () 151 | { 152 | if (buffer != null) 153 | { 154 | for (int i = 0; i < size; ++i) 155 | { 156 | yield return buffer[i]; 157 | } 158 | } 159 | } 160 | 161 | /// 162 | /// Convenience function. I recommend using .buffer instead. 163 | /// 164 | 165 | [DebuggerHidden] 166 | public T this[int i] 167 | { 168 | get { return buffer[i]; } 169 | set { buffer[i] = value; } 170 | } 171 | 172 | /// 173 | /// Helper function that expands the size of the array, maintaining the content. 174 | /// 175 | 176 | void AllocateMore () 177 | { 178 | T[] newList = (buffer != null) ? new T[Mathf.Max(buffer.Length << 1, 32)] : new T[32]; 179 | if (buffer != null && size > 0) buffer.CopyTo(newList, 0); 180 | buffer = newList; 181 | } 182 | 183 | /// 184 | /// Trim the unnecessary memory, resizing the buffer to be of 'Length' size. 185 | /// Call this function only if you are sure that the buffer won't need to resize anytime soon. 186 | /// 187 | 188 | void Trim () 189 | { 190 | if (size > 0) 191 | { 192 | if (size < buffer.Length) 193 | { 194 | T[] newList = new T[size]; 195 | for (int i = 0; i < size; ++i) newList[i] = buffer[i]; 196 | buffer = newList; 197 | } 198 | } 199 | else buffer = null; 200 | } 201 | 202 | /// 203 | /// Clear the array by resetting its size to zero. Note that the memory is not actually released. 204 | /// 205 | 206 | public void Clear () { size = 0; } 207 | 208 | /// 209 | /// Clear the array and release the used memory. 210 | /// 211 | 212 | public void Release () { size = 0; buffer = null; } 213 | 214 | /// 215 | /// Add the specified item to the end of the list. 216 | /// 217 | 218 | public void Add (T item) 219 | { 220 | if (buffer == null || size == buffer.Length) AllocateMore(); 221 | buffer[size++] = item; 222 | } 223 | 224 | /// 225 | /// Insert an item at the specified index, pushing the entries back. 226 | /// 227 | 228 | public void Insert (int index, T item) 229 | { 230 | if (buffer == null || size == buffer.Length) AllocateMore(); 231 | 232 | if (index > -1 && index < size) 233 | { 234 | for (int i = size; i > index; --i) buffer[i] = buffer[i - 1]; 235 | buffer[index] = item; 236 | ++size; 237 | } 238 | else Add(item); 239 | } 240 | 241 | /// 242 | /// Returns 'true' if the specified item is within the list. 243 | /// 244 | 245 | public bool Contains (T item) 246 | { 247 | if (buffer == null) return false; 248 | for (int i = 0; i < size; ++i) if (buffer[i].Equals(item)) return true; 249 | return false; 250 | } 251 | 252 | /// 253 | /// Return the index of the specified item. 254 | /// 255 | 256 | public int IndexOf (T item) 257 | { 258 | if (buffer == null) return -1; 259 | for (int i = 0; i < size; ++i) if (buffer[i].Equals(item)) return i; 260 | return -1; 261 | } 262 | 263 | /// 264 | /// Remove the specified item from the list. Note that RemoveAt() is faster and is advisable if you already know the index. 265 | /// 266 | 267 | public bool Remove (T item) 268 | { 269 | if (buffer != null) 270 | { 271 | EqualityComparer comp = EqualityComparer.Default; 272 | 273 | for (int i = 0; i < size; ++i) 274 | { 275 | if (comp.Equals(buffer[i], item)) 276 | { 277 | --size; 278 | buffer[i] = default(T); 279 | for (int b = i; b < size; ++b) buffer[b] = buffer[b + 1]; 280 | buffer[size] = default(T); 281 | return true; 282 | } 283 | } 284 | } 285 | return false; 286 | } 287 | 288 | /// 289 | /// Remove an item at the specified index. 290 | /// 291 | 292 | public void RemoveAt (int index) 293 | { 294 | if (buffer != null && index > -1 && index < size) 295 | { 296 | --size; 297 | buffer[index] = default(T); 298 | for (int b = index; b < size; ++b) buffer[b] = buffer[b + 1]; 299 | buffer[size] = default(T); 300 | } 301 | } 302 | 303 | /// 304 | /// Remove an item from the end. 305 | /// 306 | 307 | public T Pop () 308 | { 309 | if (buffer != null && size != 0) 310 | { 311 | T val = buffer[--size]; 312 | buffer[size] = default(T); 313 | return val; 314 | } 315 | return default(T); 316 | } 317 | 318 | /// 319 | /// Mimic List's ToArray() functionality, except that in this case the list is resized to match the current size. 320 | /// 321 | 322 | public T[] ToArray () { Trim(); return buffer; } 323 | 324 | //class Comparer : System.Collections.IComparer 325 | //{ 326 | // public System.Comparison func; 327 | // public int Compare (object x, object y) { return func((T)x, (T)y); } 328 | //} 329 | 330 | //Comparer mComp = new Comparer(); 331 | 332 | /// 333 | /// List.Sort equivalent. Doing Array.Sort causes GC allocations. 334 | /// 335 | 336 | //public void Sort (System.Comparison comparer) 337 | //{ 338 | // if (size > 0) 339 | // { 340 | // mComp.func = comparer; 341 | // System.Array.Sort(buffer, 0, size, mComp); 342 | // } 343 | //} 344 | 345 | /// 346 | /// List.Sort equivalent. Manual sorting causes no GC allocations. 347 | /// 348 | 349 | [DebuggerHidden] 350 | [DebuggerStepThrough] 351 | public void Sort (CompareFunc comparer) 352 | { 353 | int start = 0; 354 | int max = size - 1; 355 | bool changed = true; 356 | 357 | while (changed) 358 | { 359 | changed = false; 360 | 361 | for (int i = start; i < max; ++i) 362 | { 363 | // Compare the two values 364 | if (comparer(buffer[i], buffer[i + 1]) > 0) 365 | { 366 | // Swap the values 367 | T temp = buffer[i]; 368 | buffer[i] = buffer[i + 1]; 369 | buffer[i + 1] = temp; 370 | changed = true; 371 | } 372 | else if (!changed) 373 | { 374 | // Nothing has changed -- we can start here next time 375 | start = (i == 0) ? 0 : i - 1; 376 | } 377 | } 378 | } 379 | } 380 | 381 | /// 382 | /// Comparison function should return -1 if left is less than right, 1 if left is greater than right, and 0 if they match. 383 | /// 384 | 385 | public delegate int CompareFunc (T left, T right); 386 | #endif 387 | } 388 | -------------------------------------------------------------------------------- /Common/BetterList.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c80989c78df03344839803db8983eb9 3 | timeCreated: 1520392679 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/CommonHelper.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using UnityEngine; 3 | using System.Diagnostics; 4 | using System; 5 | 6 | namespace U3DExtends { 7 | public class CommonHelper { 8 | //生成parent下的唯一控件名 9 | public static string GenerateUniqueName(GameObject parent, string type) 10 | { 11 | var widgets = parent.GetComponentsInChildren(); 12 | int test_num = 1; 13 | string test_name = type+"_"+test_num; 14 | RectTransform uiBase = null; 15 | int prevent_death_count = 0;//防止死循环 16 | do { 17 | test_name = type+"_"+test_num; 18 | uiBase = System.Array.Find(widgets, p => p.gameObject.name==test_name); 19 | test_num = test_num + UnityEngine.Random.Range(1, (prevent_death_count+1)*2); 20 | if (prevent_death_count++ >= 100) 21 | break; 22 | } while (uiBase != null); 23 | 24 | return test_name; 25 | } 26 | 27 | static public bool IsPointInRect(Vector3 mouse_abs_pos, RectTransform trans) 28 | { 29 | if (trans != null) 30 | { 31 | float l_t_x = trans.position.x; 32 | float l_t_y = trans.position.y; 33 | float r_b_x = l_t_x + trans.sizeDelta.x; 34 | float r_b_y = l_t_y - trans.sizeDelta.y; 35 | if (mouse_abs_pos.x >= l_t_x && mouse_abs_pos.y <= l_t_y && mouse_abs_pos.x <= r_b_x && mouse_abs_pos.y >= r_b_y) 36 | { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | public static Color StringToColor(string hexString) 44 | { 45 | int start_index = 2; 46 | if (hexString.Length == 8) 47 | start_index = 2; 48 | else if (hexString.Length == 6) 49 | start_index = 0; 50 | byte r = byte.Parse(hexString.Substring(start_index, 2), System.Globalization.NumberStyles.HexNumber); 51 | byte g = byte.Parse(hexString.Substring(start_index+2, 2), System.Globalization.NumberStyles.HexNumber); 52 | byte b = byte.Parse(hexString.Substring(start_index+4, 2), System.Globalization.NumberStyles.HexNumber); 53 | return new Color(r / 255.0f, g / 255.0f, b / 255.0f); 54 | } 55 | 56 | public static string ColorToString(Color color) 57 | { 58 | string hexString = ""; 59 | string color_hex = "00"; 60 | color_hex = String.Format("{0:x}", (int)Mathf.Floor(color.r * 255)); 61 | if (color_hex.Length < 2) 62 | color_hex = "0" + color_hex; 63 | hexString = hexString + color_hex; 64 | color_hex = String.Format("{0:x}", (int)Mathf.Floor(color.g * 255)); 65 | if (color_hex.Length < 2) 66 | color_hex = "0" + color_hex; 67 | hexString = hexString + color_hex; 68 | color_hex = String.Format("{0:x}", (int)Mathf.Floor(color.b * 255)); 69 | if (color_hex.Length < 2) 70 | color_hex = "0" + color_hex; 71 | hexString = hexString + color_hex; 72 | //UnityEngine.Debug.Log("hexString :" + hexString); 73 | return hexString; 74 | } 75 | 76 | public static Process ProcessCommand(string command, string argument) 77 | { 78 | UnityEngine.Debug.Log(string.Format("command : {0} argument{1}", command, argument)); 79 | ProcessStartInfo start = new ProcessStartInfo(command); 80 | start.Arguments = argument; 81 | start.CreateNoWindow = true; 82 | start.ErrorDialog = true; 83 | start.UseShellExecute = false; 84 | 85 | if (start.UseShellExecute) 86 | { 87 | start.RedirectStandardOutput = false; 88 | start.RedirectStandardError = false; 89 | start.RedirectStandardInput = false; 90 | } 91 | else 92 | { 93 | start.RedirectStandardOutput = true; 94 | start.RedirectStandardError = true; 95 | start.RedirectStandardInput = true; 96 | start.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8; 97 | start.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8; 98 | } 99 | 100 | Process p = Process.Start(start); 101 | //string output = p.StandardOutput.ReadToEnd(); 102 | //p.WaitForExit(); 103 | return p; 104 | } 105 | 106 | //获取文件名 107 | public static string GetFileNameByPath(string path) 108 | { 109 | path = path.Replace("\\", "/"); 110 | int last_gang_index = path.LastIndexOf("/"); 111 | if (last_gang_index == -1) 112 | return ""; 113 | last_gang_index++; 114 | string name = path.Substring(last_gang_index, path.Length - last_gang_index); 115 | int last_dot_index = name.LastIndexOf('.'); 116 | if (last_dot_index == -1) 117 | return ""; 118 | name = name.Substring(0, last_dot_index); 119 | return name; 120 | } 121 | 122 | //获取文件类型后缀 123 | public static string GetFileTypeSuffixByPath(string path) 124 | { 125 | path = path.Replace("\\", "/"); 126 | int last_dot_index = path.LastIndexOf('.'); 127 | if (last_dot_index == -1) 128 | return ""; 129 | last_dot_index++; 130 | string type_str = path.Substring(last_dot_index, path.Length - last_dot_index); 131 | return type_str; 132 | } 133 | } 134 | } 135 | #endif -------------------------------------------------------------------------------- /Common/CommonHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c451c114c4e790f4ebc16668e49140c1 3 | timeCreated: 1520392679 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/ContextMenu.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using UnityEngine; 3 | using UnityEditor; 4 | using System.Collections.Generic; 5 | 6 | namespace U3DExtends { 7 | static public class ContextMenu 8 | { 9 | static List mEntries = new List(); 10 | static GenericMenu mMenu; 11 | 12 | static public void AddItem(string item, bool isChecked, GenericMenu.MenuFunction callback) 13 | { 14 | if (callback != null) 15 | { 16 | if (mMenu == null) mMenu = new GenericMenu(); 17 | int count = 0; 18 | 19 | for (int i = 0; i < mEntries.Count; ++i) 20 | { 21 | string str = mEntries[i]; 22 | if (str == item) ++count; 23 | } 24 | mEntries.Add(item); 25 | 26 | if (count > 0) item += " [" + count + "]"; 27 | mMenu.AddItem(new GUIContent(item), isChecked, callback); 28 | } 29 | else AddDisabledItem(item); 30 | } 31 | 32 | static public void AddItemWithArge(string item, bool isChecked, GenericMenu.MenuFunction2 callback, object arge) 33 | { 34 | if (callback != null) 35 | { 36 | if (mMenu == null) mMenu = new GenericMenu(); 37 | int count = 0; 38 | 39 | for (int i = 0; i < mEntries.Count; ++i) 40 | { 41 | string str = mEntries[i]; 42 | if (str == item) ++count; 43 | } 44 | mEntries.Add(item); 45 | 46 | if (count > 0) item += " [" + count + "]"; 47 | mMenu.AddItem(new GUIContent(item), isChecked, callback, arge); 48 | } 49 | else AddDisabledItem(item); 50 | } 51 | 52 | static public void Show() 53 | { 54 | if (mMenu != null) 55 | { 56 | mMenu.ShowAsContext(); 57 | mMenu = null; 58 | mEntries.Clear(); 59 | } 60 | } 61 | 62 | //增加几种对齐菜单 63 | static public void AddAlignMenu() 64 | { 65 | AddItem("对齐/左对齐 ←", false, AlignTool.AlignInHorziontalLeft); 66 | AddItem("对齐/右对齐 →", false, AlignTool.AlignInHorziontalRight); 67 | AddItem("对齐/上对齐 ↑", false, AlignTool.AlignInVerticalUp); 68 | AddItem("对齐/下对齐 ↓", false, AlignTool.AlignInVerticalDown); 69 | AddItem("对齐/水平均匀 |||", false, AlignTool.UniformDistributionInHorziontal); 70 | AddItem("对齐/垂直均匀 ☰", false, AlignTool.UniformDistributionInVertical); 71 | AddItem("对齐/一样大 ■", false, AlignTool.ResizeMax); 72 | AddItem("对齐/一样小 ●", false, AlignTool.ResizeMin); 73 | } 74 | 75 | //增加层次菜单 76 | static public void AddPriorityMenu() 77 | { 78 | AddItem("层次/最里层 ↑↑↑", false, PriorityTool.MoveToTopWidget); 79 | AddItem("层次/最外层 ↓↓↓", false, PriorityTool.MoveToBottomWidget); 80 | AddItem("层次/往里挤 ↑", false, PriorityTool.MoveUpWidget); 81 | AddItem("层次/往外挤 ↓", false, PriorityTool.MoveDownWidget); 82 | } 83 | 84 | //增加UI控件菜单 85 | static public void AddUIMenu() 86 | { 87 | AddItem("添加控件/Empty", false, UIEditorHelper.CreateEmptyObj); 88 | AddItem("添加控件/Image", false, UIEditorHelper.CreateImageObj); 89 | AddItem("添加控件/RawImage", false, UIEditorHelper.CreateRawImageObj); 90 | AddItem("添加控件/Button", false, UIEditorHelper.CreateButtonObj); 91 | AddItem("添加控件/Text", false, UIEditorHelper.CreateTextObj); 92 | AddItem("添加控件/HScroll", false, UIEditorHelper.CreateHScrollViewObj); 93 | AddItem("添加控件/VScroll", false, UIEditorHelper.CreateVScrollViewObj); 94 | } 95 | 96 | //增加UI组件菜单 97 | static public void AddUIComponentMenu() 98 | { 99 | AddItem("添加组件/Image", false, UIEditorHelper.AddImageComponent); 100 | //AddItem("添加组件/RawImage", false, UIEditorHelper.CreateRawImageObj); 101 | //AddItem("添加组件/Button", false, UIEditorHelper.CreateButtonObj); 102 | //AddItem("添加组件/Text", false, UIEditorHelper.CreateTextObj); 103 | AddItem("添加组件/HLayout", false, UIEditorHelper.AddHorizontalLayoutComponent); 104 | AddItem("添加组件/VLayout", false, UIEditorHelper.AddVerticalLayoutComponent); 105 | AddItem("添加组件/GridLayout", false, UIEditorHelper.AddGridLayoutGroupComponent); 106 | 107 | } 108 | 109 | //增加显示隐藏菜单 110 | static public void AddShowOrHideMenu() 111 | { 112 | bool hasHideWidget = false; 113 | foreach (var item in Selection.gameObjects) 114 | { 115 | if (!item.activeSelf) 116 | { 117 | hasHideWidget = true; 118 | break; 119 | } 120 | } 121 | if (hasHideWidget) 122 | AddItem("显示", false, UILayoutTool.ShowAllSelectedWidgets); 123 | else 124 | AddItem("隐藏", false, UILayoutTool.HideAllSelectedWidgets); 125 | } 126 | 127 | static public void AddCommonItems(GameObject[] targets) 128 | { 129 | if (targets == null || targets.Length <= 0) 130 | { 131 | AddItem("新建", false, UIEditorHelper.CreatNewLayoutForMenu); 132 | AddItem("打开界面", false, UIEditorHelper.LoadLayout); 133 | AddItem("打开文件夹", false, UIEditorHelper.LoadLayoutWithFolder); 134 | } 135 | if (targets != null && targets.Length > 0) 136 | { 137 | AddItem("保存", false, UIEditorHelper.SaveLayoutForMenu); 138 | AddItem("另存为", false, UIEditorHelper.SaveAnotherLayoutContextMenu); 139 | AddItem("重新加载", false, UIEditorHelper.ReLoadLayoutForMenu); 140 | 141 | AddSeparator("///"); 142 | AddItem("复制选中控件名", false, UIEditorHelper.CopySelectWidgetName); 143 | 144 | //如果选中超过1个节点的话 145 | if (targets.Length > 1) 146 | { 147 | AddAlignMenu(); 148 | AddItem("同流合污", false, UILayoutTool.MakeGroup); 149 | } 150 | AddSeparator("///"); 151 | if (targets.Length == 1) 152 | { 153 | AddUIMenu(); 154 | AddUIComponentMenu(); 155 | AddPriorityMenu(); 156 | 157 | if (UIEditorHelper.IsNodeCanDivide(targets[0])) 158 | AddItem("分道扬镖", false, UILayoutTool.UnGroup); 159 | Decorate uiBase = targets[0].GetComponent(); 160 | if (uiBase != null) 161 | { 162 | if (uiBase.gameObject.hideFlags == HideFlags.NotEditable) 163 | { 164 | AddItem("解锁", false, UIEditorHelper.UnLockWidget); 165 | } 166 | else 167 | { 168 | AddItem("锁定", false, UIEditorHelper.LockWidget); 169 | } 170 | } 171 | } 172 | 173 | AddShowOrHideMenu(); 174 | 175 | AddSeparator("///"); 176 | 177 | AddItem("添加参考图", false, UIEditorHelper.CreateDecorate); 178 | if (targets.Length == 1 && targets[0].transform.childCount > 0) 179 | AddItem("优化层级", false, UILayoutTool.OptimizeBatchForMenu); 180 | 181 | } 182 | AddItem("排序所有界面", false, UILayoutTool.ResortAllLayout); 183 | AddItem("清空界面", false, UIEditorHelper.ClearAllCanvas); 184 | } 185 | 186 | static public void AddSeparator(string path) 187 | { 188 | if (mMenu == null) mMenu = new GenericMenu(); 189 | 190 | if (Application.platform != RuntimePlatform.OSXEditor) 191 | mMenu.AddSeparator(path); 192 | } 193 | 194 | static public void AddDisabledItem(string item) 195 | { 196 | if (mMenu == null) mMenu = new GenericMenu(); 197 | mMenu.AddDisabledItem(new GUIContent(item)); 198 | } 199 | } 200 | } 201 | #endif -------------------------------------------------------------------------------- /Common/ContextMenu.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6fe4cd445ce309344932a7f21c4e0e4e 3 | timeCreated: 1520818956 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/Decorate.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace U3DExtends 6 | { 7 | [RequireComponent(typeof(RectTransform))] 8 | [ExecuteInEditMode] 9 | public class Decorate : MonoBehaviour 10 | { 11 | string spr_path = ""; 12 | [SerializeField] 13 | [HideInInspector] 14 | private Image _image; 15 | 16 | Vector3 _lastPos = new Vector3(-1, -1); 17 | Vector2 _lastSize = Vector2.zero; 18 | 19 | public string SprPath 20 | { 21 | get { return spr_path; } 22 | set 23 | { 24 | LoadSpr(value); 25 | } 26 | } 27 | 28 | public void LoadSpr(string path) 29 | { 30 | InitComponent(); 31 | if (spr_path != path) 32 | { 33 | spr_path = path; 34 | _image.sprite = UIEditorHelper.LoadSpriteInLocal(path); 35 | _image.SetNativeSize(); 36 | gameObject.name = CommonHelper.GetFileNameByPath(path); 37 | //Debug.Log("_image.sprite :" + (_image.sprite != null).ToString()); 38 | } 39 | } 40 | 41 | protected void Start() 42 | { 43 | InitComponent(); 44 | } 45 | 46 | protected void InitComponent() 47 | { 48 | if (_image == null) 49 | _image = GetComponent(); 50 | } 51 | 52 | public bool IsChangedTrans() 53 | { 54 | RectTransform curTrans = transform as RectTransform; 55 | if (curTrans.localPosition == _lastPos && curTrans.sizeDelta == _lastSize) 56 | return false; 57 | else 58 | return true; 59 | } 60 | 61 | public void SaveTrans() 62 | { 63 | RectTransform rectTrans = transform as RectTransform; 64 | _lastPos = rectTrans.localPosition; 65 | _lastSize = rectTrans.sizeDelta; 66 | } 67 | 68 | } 69 | } 70 | #endif -------------------------------------------------------------------------------- /Common/Decorate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 533a76e193efebc48ab9061535a0b110 3 | timeCreated: 1521105297 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/LayoutInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhaopen/UGUI-Editor/3144f295379d0f7e7c2f1dd1492657bc128bb5f9/Common/LayoutInfo.cs -------------------------------------------------------------------------------- /Common/LayoutInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 85543a2dadd029c42a60f1b3eb371827 3 | timeCreated: 1531211532 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Common/PathSaver.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using UnityEditor; 3 | 4 | //路径保存器,记录上次打开的路径,不同项目的不同用处路径都分开保存 5 | namespace U3DExtends 6 | { 7 | public enum PathType { 8 | //OpenLayout,//打开布局时默认打开的文件夹路径//和Save用同个会方便点 9 | SaveLayout,//保存布局时默认打开的文件夹路径 10 | OpenDecorate,//选择参考图时默认打开的文件夹路径 11 | PrefabTool,//Prefab界面用的 12 | } 13 | public class PathSaver { 14 | private volatile static PathSaver _instance = null; 15 | private static readonly object lockHelper = new object(); 16 | private PathSaver() { } 17 | public static PathSaver GetInstance() 18 | { 19 | if(_instance == null) 20 | { 21 | lock(lockHelper) 22 | { 23 | if(_instance == null) 24 | _instance = new PathSaver(); 25 | } 26 | } 27 | return _instance; 28 | } 29 | 30 | public string GeDefaultPath(PathType type) 31 | { 32 | return ""; 33 | } 34 | 35 | public string GetLastPath(PathType type) 36 | { 37 | return EditorPrefs.GetString("PathSaver_" + U3DExtends.Configure.ProjectUUID + "_" + type.ToString(), GeDefaultPath(type)); 38 | } 39 | 40 | public void SetLastPath(PathType type, string path) 41 | { 42 | if (path == "") 43 | return; 44 | path = System.IO.Path.GetDirectoryName(path); 45 | EditorPrefs.SetString("PathSaver_" + U3DExtends.Configure.ProjectUUID + "_" + type.ToString(), path); 46 | } 47 | 48 | } 49 | } 50 | #endif -------------------------------------------------------------------------------- /Common/PathSaver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5a399f9e8d9b3ec48ae81f9a2c031840 3 | timeCreated: 1520819465 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/ReloadLayoutOnExitGame.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhaopen/UGUI-Editor/3144f295379d0f7e7c2f1dd1492657bc128bb5f9/Common/ReloadLayoutOnExitGame.cs -------------------------------------------------------------------------------- /Common/ReloadLayoutOnExitGame.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e567ee5ac5ca9b4897ca6ca15bb480b 3 | timeCreated: 1531359091 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Common/ReopenLayoutOnExitGame.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace U3DExtends 5 | { 6 | public class ReopenLayoutOnExitGame : MonoBehaviour 7 | { 8 | #if UNITY_EDITOR 9 | // class ReopenInfo 10 | // { 11 | // string path; 12 | // Vector3 pos; 13 | // } 14 | private static ReopenLayoutOnExitGame Instance; 15 | 16 | private static Dictionary layout_open_in_playmode = new Dictionary(); 17 | private bool isRunningGame = false; 18 | 19 | public static void RecordOpenLayout(string path, Vector3 pos) 20 | { 21 | Debug.Log("record : "+path+" pos:"+pos.ToString()); 22 | if (Instance != null && Instance.isRunningGame && path!="") 23 | { 24 | layout_open_in_playmode.Add(path, pos); 25 | } 26 | } 27 | 28 | private void Start() 29 | { 30 | Instance = this; 31 | // hadSaveOnRunTime = false; 32 | Debug.Log("Start"); 33 | isRunningGame = true; 34 | } 35 | 36 | private void OnDisable() { 37 | // Debug.Log("disable"); 38 | Instance = null; 39 | } 40 | 41 | private void OnTransformChildrenChanged() { 42 | Debug.Log("OnTransformChildrenChanged"); 43 | List wait_delete_key = new List(); 44 | foreach (var item in layout_open_in_playmode) 45 | { 46 | bool had_find = false; 47 | for (int i = 0; i < transform.childCount; i++) 48 | { 49 | LayoutInfo info = transform.GetChild(i).GetComponent(); 50 | if (info && info.LayoutPath == item.Key) 51 | { 52 | had_find = true; 53 | break; 54 | } 55 | } 56 | if (!had_find) 57 | { 58 | wait_delete_key.Add(item.Key); 59 | } 60 | } 61 | foreach (var item in wait_delete_key) 62 | { 63 | layout_open_in_playmode.Remove(item); 64 | } 65 | } 66 | 67 | private void OnApplicationQuit() 68 | { 69 | Debug.Log("OnApplicationQuit"); 70 | isRunningGame = false; 71 | if (layout_open_in_playmode.Count>0 && U3DExtends.Configure.ReloadLayoutOnExitGame) 72 | { 73 | System.Action p = null; 74 | p = new System.Action((UnityEditor.PlayModeStateChange c) => { 75 | foreach (var item in layout_open_in_playmode) 76 | { 77 | // Debug.Log("item.Key : "+item.Key); 78 | Transform layout = UIEditorHelper.LoadLayoutByPath(item.Key); 79 | if (layout != null) 80 | { 81 | layout.localPosition = item.Value; 82 | } 83 | } 84 | layout_open_in_playmode.Clear(); 85 | UnityEditor.EditorApplication.playModeStateChanged -= p; 86 | }); 87 | UnityEditor.EditorApplication.playModeStateChanged += p; 88 | } 89 | } 90 | #endif 91 | } 92 | } -------------------------------------------------------------------------------- /Common/ReopenLayoutOnExitGame.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eae15c9b8c4e77d46bf3ff140ad806f7 3 | timeCreated: 1540622474 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Common/UIEditorHelper.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using UnityEngine.UI; 8 | 9 | namespace U3DExtends 10 | { 11 | public static class UIEditorHelper 12 | { 13 | public static void SetImageByPath(string assetPath, Image image, bool isNativeSize = true) 14 | { 15 | Object newImg = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, typeof(Sprite)); 16 | Undo.RecordObject(image, "Change Image");//有了这句才可以用ctrl+z撤消此赋值操作 17 | image.sprite = newImg as Sprite; 18 | if (isNativeSize) 19 | image.SetNativeSize(); 20 | EditorUtility.SetDirty(image); 21 | } 22 | 23 | [MenuItem("Edit/Copy Names " + Configure.ShortCut.CopyNodesName, false, 2)] 24 | public static void CopySelectWidgetName() 25 | { 26 | string result = ""; 27 | foreach (var item in Selection.gameObjects) 28 | { 29 | string item_name = item.name; 30 | Transform root_trans = item.transform.parent; 31 | while (root_trans != null && root_trans.GetComponent() == null) 32 | { 33 | if (root_trans.parent != null && root_trans.parent.GetComponent() == null) 34 | item_name = root_trans.name + "/" + item_name; 35 | else 36 | break; 37 | root_trans = root_trans.parent; 38 | } 39 | result = result + "\"" + item_name + "\","; 40 | } 41 | 42 | //复制到系统全局的粘贴板上 43 | GUIUtility.systemCopyBuffer = result; 44 | Debug.Log("Copy Nodes Name Succeed!"); 45 | } 46 | 47 | public static Transform GetRootLayout(Transform trans) 48 | { 49 | Transform result = null; 50 | Canvas canvas = trans.GetComponentInParent(); 51 | if (canvas != null) 52 | { 53 | foreach (var item in canvas.transform.GetComponentsInChildren()) 54 | { 55 | if (item.GetComponent() == null && canvas.transform != item) 56 | { 57 | result = item; 58 | break; 59 | } 60 | } 61 | } 62 | return result; 63 | } 64 | 65 | static public GameObject GetUITestRootNode() 66 | { 67 | GameObject testUI = GameObject.Find(Configure.UITestNodeName); 68 | if (!testUI) 69 | { 70 | testUI = new GameObject(Configure.UITestNodeName, typeof(RectTransform)); 71 | RectTransform trans = testUI.GetComponent(); 72 | trans.position = Configure.UITestNodePos; 73 | trans.sizeDelta = Configure.UITestNodeSize; 74 | testUI.AddComponent(); 75 | } 76 | return testUI; 77 | } 78 | 79 | static public Transform GetContainerUnderMouse(Vector3 mouse_abs_pos, GameObject ignore_obj = null) 80 | { 81 | GameObject testUI = UIEditorHelper.GetUITestRootNode(); 82 | List list = new List(); 83 | Canvas[] containers = Transform.FindObjectsOfType(); 84 | Vector3[] corners = new Vector3[4]; 85 | foreach (var item in containers) 86 | { 87 | if (ignore_obj == item.gameObject || item.transform.parent != testUI.transform) 88 | continue; 89 | RectTransform trans = item.transform as RectTransform; 90 | if (trans != null) 91 | { 92 | //获取节点的四个角的世界坐标,分别按顺序为左下左上,右上右下 93 | trans.GetWorldCorners(corners); 94 | if (mouse_abs_pos.x >= corners[0].x && mouse_abs_pos.y <= corners[1].y && mouse_abs_pos.x <= corners[2].x && mouse_abs_pos.y >= corners[3].y) 95 | { 96 | list.Add(trans); 97 | } 98 | } 99 | } 100 | if (list.Count <= 0) 101 | return null; 102 | list.Sort((RectTransform a, RectTransform b) => { return (a.GetSiblingIndex() == b.GetSiblingIndex()) ? 0 : ((a.GetSiblingIndex() < b.GetSiblingIndex()) ? 1 : -1); } 103 | ); 104 | return GetRootLayout(list[0]); 105 | } 106 | 107 | public static GameObject CreatNewLayout(bool isNeedLayout = true) 108 | { 109 | GameObject testUI = UIEditorHelper.GetUITestRootNode(); 110 | 111 | string file_path = Path.Combine(Configure.ResAssetsPath, "Canvas.prefab"); 112 | file_path = FileUtil.GetProjectRelativePath(file_path); 113 | GameObject layout_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath(file_path, typeof(UnityEngine.Object)) as GameObject; 114 | GameObject layout = GameObject.Instantiate(layout_prefab) as GameObject; 115 | layout.transform.SetParent(testUI.transform); 116 | Vector3 last_pos = layout.transform.localPosition; 117 | layout.transform.localPosition = new Vector3(last_pos.x, last_pos.y, 0); 118 | if (!isNeedLayout) 119 | { 120 | Transform child = layout.transform.Find("Layout"); 121 | // layout.transform.DetachChildren(); 122 | if (child!=null) 123 | Undo.DestroyObjectImmediate(child.gameObject); 124 | } 125 | 126 | Selection.activeGameObject = layout; 127 | RectTransform trans = layout.transform as RectTransform; 128 | SceneView.lastActiveSceneView.MoveToView(trans); 129 | return layout; 130 | } 131 | 132 | public static bool SelectPicForDecorate(Decorate decorate) 133 | { 134 | if (decorate != null) 135 | { 136 | string default_path = PathSaver.GetInstance().GetLastPath(PathType.OpenDecorate); 137 | string spr_path = EditorUtility.OpenFilePanel("加载外部图片", default_path, ""); 138 | if (spr_path.Length > 0) 139 | { 140 | decorate.SprPath = spr_path; 141 | PathSaver.GetInstance().SetLastPath(PathType.OpenDecorate, spr_path); 142 | return true; 143 | } 144 | } 145 | return false; 146 | } 147 | 148 | public static Decorate CreateEmptyDecorate(Transform parent) 149 | { 150 | string file_path = Path.Combine(Configure.ResAssetsPath, "Decorate.prefab"); 151 | file_path = FileUtil.GetProjectRelativePath(file_path); 152 | GameObject decorate_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath(file_path, typeof(UnityEngine.Object)) as GameObject; 153 | GameObject decorate = GameObject.Instantiate(decorate_prefab) as GameObject; 154 | decorate.transform.SetParent(parent); 155 | RectTransform rectTrans = decorate.transform as RectTransform; 156 | rectTrans.SetAsFirstSibling(); 157 | rectTrans.localPosition = Vector3.zero; 158 | rectTrans.localScale = Vector3.one; 159 | Decorate decor = rectTrans.GetComponent(); 160 | return decor; 161 | } 162 | 163 | public static void CreateDecorate() 164 | { 165 | if (Selection.activeTransform != null) 166 | { 167 | Canvas canvas = Selection.activeTransform.GetComponentInParent(); 168 | if (canvas != null) 169 | { 170 | Decorate decor = CreateEmptyDecorate(canvas.transform); 171 | Selection.activeTransform = decor.transform; 172 | 173 | if (Configure.OpenSelectPicDialogWhenAddDecorate) 174 | { 175 | bool isSucceed = UIEditorHelper.SelectPicForDecorate(decor); 176 | if (!isSucceed) 177 | GameObject.DestroyImmediate(decor.gameObject); 178 | } 179 | } 180 | } 181 | } 182 | 183 | // [MenuItem("UIEditor/清空界面 " + Configure.ShortCut.ClearAllCanvas)] 184 | public static void ClearAllCanvas() 185 | { 186 | bool isDeleteAll = EditorUtility.DisplayDialog("警告", "是否清空掉所有界面?", "干!", "不了"); 187 | if (isDeleteAll) 188 | { 189 | GameObject test = GameObject.Find(Configure.UITestNodeName); 190 | if (test != null) 191 | { 192 | LayoutInfo[] allLayouts = test.transform.GetComponentsInChildren(true); 193 | foreach (var item in allLayouts) 194 | { 195 | Undo.DestroyObjectImmediate(item.gameObject); 196 | } 197 | // GameObject.DestroyImmediate(test); 198 | } 199 | } 200 | } 201 | 202 | public static void LoadLayoutWithFolder() 203 | { 204 | string default_path = PathSaver.GetInstance().GetLastPath(PathType.SaveLayout); 205 | string select_path = EditorUtility.OpenFolderPanel("Open Layout", default_path, ""); 206 | PathSaver.GetInstance().SetLastPath(PathType.SaveLayout, select_path); 207 | if (select_path.Length > 0) 208 | { 209 | string[] file_paths = Directory.GetFiles(select_path, "*.prefab"); 210 | foreach (var path in file_paths) 211 | { 212 | LoadLayoutByPath(path); 213 | } 214 | } 215 | UILayoutTool.ResortAllLayout(); 216 | } 217 | 218 | private static GameObject GetLoadedLayout(string layoutPath) 219 | { 220 | GameObject testUI = UIEditorHelper.GetUITestRootNode(); 221 | if (testUI != null) 222 | { 223 | LayoutInfo[] layoutInfos = testUI.GetComponentsInChildren(true); 224 | foreach (var item in layoutInfos) 225 | { 226 | if (item.LayoutPath == layoutPath) 227 | return item.gameObject; 228 | } 229 | } 230 | return null; 231 | } 232 | 233 | //从界面的Canvas里取到真实的界面prefab 234 | public static Transform GetRealLayout(GameObject anyObj) 235 | { 236 | LayoutInfo layoutInfo = anyObj.GetComponentInParent(); 237 | Transform real_layout = null; 238 | if (layoutInfo == null) 239 | return real_layout; 240 | if (layoutInfo.LayoutPath != string.Empty) 241 | { 242 | string just_name = System.IO.Path.GetFileNameWithoutExtension(layoutInfo.LayoutPath); 243 | for (int i = 0; i < layoutInfo.transform.childCount; i++) 244 | { 245 | Transform child = layoutInfo.transform.GetChild(i); 246 | if (child.name.StartsWith(just_name)) 247 | { 248 | real_layout = child; 249 | break; 250 | } 251 | } 252 | } 253 | else 254 | { 255 | //界面是新建的,未保存过的情况下取其子节点 256 | Canvas layout = anyObj.GetComponentInParent(); 257 | for (int i = 0; i < layout.transform.childCount; i++) 258 | { 259 | Transform child = layout.transform.GetChild(i); 260 | if (child.GetComponent() != null) 261 | continue; 262 | 263 | real_layout = child.transform; 264 | break; 265 | } 266 | } 267 | return real_layout; 268 | } 269 | 270 | public static void DelayReLoadLayout(GameObject o, bool isQuiet) 271 | { 272 | System.Action p = null; 273 | p = new System.Action((PlayModeStateChange c) => { 274 | Debug.Log("reload !"); 275 | ReLoadLayout(o, isQuiet); 276 | UnityEditor.EditorApplication.playModeStateChanged -= p; 277 | }); 278 | UnityEditor.EditorApplication.playModeStateChanged += p; 279 | } 280 | 281 | public static void ReLoadLayout(GameObject o, bool isQuiet) 282 | { 283 | GameObject saveObj = o == null ? Selection.activeGameObject : (o as GameObject); 284 | if (saveObj == null) 285 | return; 286 | LayoutInfo layoutInfo = saveObj.GetComponentInParent(); 287 | if (layoutInfo != null && layoutInfo.LayoutPath != string.Empty) 288 | { 289 | bool is_reopen = isQuiet || EditorUtility.DisplayDialog("警告", "是否重新加载?", "来吧", "不了"); 290 | if (is_reopen) 291 | { 292 | string just_name = System.IO.Path.GetFileNameWithoutExtension(layoutInfo.LayoutPath); 293 | Transform real_layout = GetRealLayout(layoutInfo.gameObject); 294 | 295 | if (real_layout) 296 | { 297 | string select_path = FileUtil.GetProjectRelativePath(layoutInfo.LayoutPath); 298 | Object prefab = AssetDatabase.LoadAssetAtPath(select_path, typeof(Object)); 299 | GameObject new_view = PrefabUtility.InstantiateAttachedAsset(prefab) as GameObject; 300 | new_view.transform.SetParent(layoutInfo.transform); 301 | new_view.transform.localPosition = real_layout.localPosition; 302 | new_view.transform.localScale = Vector3.one; 303 | new_view.name = just_name; 304 | PrefabUtility.DisconnectPrefabInstance(new_view);//链接中的话删里面的子节点时会报警告,所以还是一直失联的好,保存时直接覆盖pref 305 | Undo.DestroyObjectImmediate(real_layout.gameObject); 306 | Debug.Log("Reload Layout Succeed!"); 307 | layoutInfo.ApplyConfig(select_path); 308 | } 309 | } 310 | } 311 | else 312 | Debug.Log("Try to reload unsaved layout failed"); 313 | } 314 | 315 | public static Transform LoadLayoutByPath(string select_path) 316 | { 317 | //Debug.Log("select_path : "+select_path); 318 | GameObject new_layout = CreatNewLayout(false); 319 | new_layout.transform.localPosition = new Vector3(new_layout.transform.localPosition.x, new_layout.transform.localPosition.y, 0); 320 | LayoutInfo layoutInfo = new_layout.GetComponent(); 321 | layoutInfo.LayoutPath = select_path; 322 | if (!File.Exists(select_path)) 323 | { 324 | Debug.Log("UIEditorHelper:LoadLayoutByPath cannot find layout file:"+select_path); 325 | return null; 326 | } 327 | string asset_relate_path = select_path; 328 | if (!select_path.StartsWith("Assets/")) 329 | asset_relate_path = FileUtil.GetProjectRelativePath(select_path); 330 | 331 | Object prefab = AssetDatabase.LoadAssetAtPath(asset_relate_path, typeof(Object)); 332 | GameObject new_view = PrefabUtility.InstantiateAttachedAsset(prefab) as GameObject; 333 | new_view.transform.SetParent(new_layout.transform); 334 | new_view.transform.localPosition = Vector3.zero; 335 | new_view.transform.localScale = Vector3.one; 336 | string just_name = System.IO.Path.GetFileNameWithoutExtension(asset_relate_path); 337 | new_view.name = just_name; 338 | new_layout.gameObject.name = just_name + "_Canvas"; 339 | PrefabUtility.DisconnectPrefabInstance(new_view);//链接中的话删里面的子节点时会报警告,所以还是一直失联的好,保存时直接覆盖prefab就行了 340 | //打开界面时,从项目临时文件夹找到对应界面的参照图配置,然后生成参照图 341 | layoutInfo.ApplyConfig(asset_relate_path); 342 | ReopenLayoutOnExitGame.RecordOpenLayout(select_path, new_layout.transform.localPosition); 343 | return new_layout.transform; 344 | } 345 | 346 | //[MenuItem("UIEditor/加载界面 " + Configure.ShortCut.LoadUIPrefab, false, 1)] 347 | public static void LoadLayout() 348 | { 349 | string default_path = PathSaver.GetInstance().GetLastPath(PathType.SaveLayout); 350 | string select_path = EditorUtility.OpenFilePanel("Open Layout", default_path, "prefab"); 351 | PathSaver.GetInstance().SetLastPath(PathType.SaveLayout, select_path); 352 | if (select_path.Length > 0) 353 | { 354 | //检查是否已打开同名界面 355 | GameObject loaded_layout = GetLoadedLayout(select_path); 356 | if (loaded_layout!=null) 357 | { 358 | bool is_reopen = EditorUtility.DisplayDialog("警告", "已打开同名界面,是否重新加载?", "来吧", "不了"); 359 | if (is_reopen) 360 | { 361 | //Undo.DestroyObjectImmediate(loaded_layout); 362 | ReLoadLayout(loaded_layout, true); 363 | } 364 | return; 365 | } 366 | LoadLayoutByPath(select_path); 367 | } 368 | } 369 | 370 | //[MenuItem("UIEditor/Operate/锁定")] 371 | public static void LockWidget() 372 | { 373 | if (Selection.gameObjects.Length > 0) 374 | { 375 | Selection.gameObjects[0].hideFlags = HideFlags.NotEditable; 376 | } 377 | } 378 | 379 | //[MenuItem("UIEditor/Operate/解锁")] 380 | public static void UnLockWidget() 381 | { 382 | if (Selection.gameObjects.Length > 0) 383 | { 384 | Selection.gameObjects[0].hideFlags = HideFlags.None; 385 | } 386 | } 387 | 388 | //是否支持解体 389 | public static bool IsNodeCanDivide(GameObject obj) 390 | { 391 | if (obj == null) 392 | return false; 393 | return obj.transform != null && obj.transform.childCount > 0 && obj.GetComponent() == null && obj.transform.parent != null && obj.transform.parent.GetComponent() == null; 394 | } 395 | 396 | public static bool SaveTextureToPNG(Texture inputTex, string save_file_name) 397 | { 398 | RenderTexture temp = RenderTexture.GetTemporary(inputTex.width, inputTex.height, 0, RenderTextureFormat.ARGB32); 399 | Graphics.Blit(inputTex, temp); 400 | bool ret = SaveRenderTextureToPNG(temp, save_file_name); 401 | RenderTexture.ReleaseTemporary(temp); 402 | return ret; 403 | 404 | } 405 | 406 | //将RenderTexture保存成一张png图片 407 | public static bool SaveRenderTextureToPNG(RenderTexture rt, string save_file_name) 408 | { 409 | RenderTexture prev = RenderTexture.active; 410 | RenderTexture.active = rt; 411 | Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); 412 | png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); 413 | byte[] bytes = png.EncodeToPNG(); 414 | string directory = Path.GetDirectoryName(save_file_name); 415 | if (!Directory.Exists(directory)) 416 | Directory.CreateDirectory(directory); 417 | FileStream file = File.Open(save_file_name, FileMode.Create); 418 | BinaryWriter writer = new BinaryWriter(file); 419 | writer.Write(bytes); 420 | file.Close(); 421 | Texture2D.DestroyImmediate(png); 422 | png = null; 423 | RenderTexture.active = prev; 424 | return true; 425 | 426 | } 427 | 428 | public static Texture2D LoadTextureInLocal(string file_path) 429 | { 430 | //创建文件读取流 431 | FileStream fileStream = new FileStream(file_path, FileMode.Open, FileAccess.Read); 432 | fileStream.Seek(0, SeekOrigin.Begin); 433 | //创建文件长度缓冲区 434 | byte[] bytes = new byte[fileStream.Length]; 435 | //读取文件 436 | fileStream.Read(bytes, 0, (int)fileStream.Length); 437 | //释放文件读取流 438 | fileStream.Close(); 439 | fileStream.Dispose(); 440 | fileStream = null; 441 | 442 | //创建Texture 443 | int width = 300; 444 | int height = 372; 445 | Texture2D texture = new Texture2D(width, height); 446 | texture.LoadImage(bytes); 447 | return texture; 448 | } 449 | 450 | private static Vector2 HalfVec = new Vector2(0.5f, 0.5f); 451 | //加载外部资源为Sprite 452 | public static Sprite LoadSpriteInLocal(string file_path) 453 | { 454 | if (!File.Exists(file_path)) 455 | { 456 | Debug.Log("LoadSpriteInLocal() cannot find sprite file : " + file_path); 457 | return null; 458 | } 459 | Texture2D texture = LoadTextureInLocal(file_path); 460 | //创建Sprite 461 | Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), UIEditorHelper.HalfVec); 462 | return sprite; 463 | } 464 | 465 | public static Texture GetAssetPreview(GameObject obj) 466 | { 467 | GameObject canvas_obj = null; 468 | GameObject clone = GameObject.Instantiate(obj); 469 | Transform cloneTransform = clone.transform; 470 | bool isUINode = false; 471 | if (cloneTransform is RectTransform) 472 | { 473 | //如果是UGUI节点的话就要把它们放在Canvas下了 474 | canvas_obj = new GameObject("render canvas", typeof(Canvas)); 475 | Canvas canvas = canvas_obj.GetComponent(); 476 | cloneTransform.SetParent(canvas_obj.transform); 477 | cloneTransform.localPosition = Vector3.zero; 478 | 479 | canvas_obj.transform.position = new Vector3(-1000, -1000, -1000); 480 | canvas_obj.layer = 21;//放在21层,摄像机也只渲染此层的,避免混入了奇怪的东西 481 | isUINode = true; 482 | } 483 | else 484 | cloneTransform.position = new Vector3(-1000, -1000, -1000); 485 | 486 | Transform[] all = clone.GetComponentsInChildren(); 487 | foreach (Transform trans in all) 488 | { 489 | trans.gameObject.layer = 21; 490 | } 491 | 492 | Bounds bounds = GetBounds(clone); 493 | Vector3 Min = bounds.min; 494 | Vector3 Max = bounds.max; 495 | GameObject cameraObj = new GameObject("render camera"); 496 | 497 | Camera renderCamera = cameraObj.AddComponent(); 498 | renderCamera.backgroundColor = new Color(0.8f, 0.8f, 0.8f, 1f); 499 | renderCamera.clearFlags = CameraClearFlags.Color; 500 | renderCamera.cameraType = CameraType.Preview; 501 | renderCamera.cullingMask = 1 << 21; 502 | if (isUINode) 503 | { 504 | cameraObj.transform.position = new Vector3((Max.x + Min.x) / 2f, (Max.y + Min.y) / 2f, cloneTransform.position.z-100); 505 | Vector3 center = new Vector3(cloneTransform.position.x+0.01f, (Max.y + Min.y) / 2f, cloneTransform.position.z);//+0.01f是为了去掉Unity自带的摄像机旋转角度为0的打印,太烦人了 506 | cameraObj.transform.LookAt(center); 507 | 508 | renderCamera.orthographic = true; 509 | float width = Max.x - Min.x; 510 | float height = Max.y - Min.y; 511 | float max_camera_size = width > height ? width : height; 512 | renderCamera.orthographicSize = max_camera_size / 2;//预览图要尽量少点空白 513 | } 514 | else 515 | { 516 | cameraObj.transform.position = new Vector3((Max.x + Min.x) / 2f, (Max.y + Min.y) / 2f, Max.z + (Max.z - Min.z)); 517 | Vector3 center = new Vector3(cloneTransform.position.x+0.01f, (Max.y + Min.y) / 2f, cloneTransform.position.z); 518 | cameraObj.transform.LookAt(center); 519 | 520 | int angle = (int)(Mathf.Atan2((Max.y - Min.y) / 2, (Max.z - Min.z)) * 180 / 3.1415f * 2); 521 | renderCamera.fieldOfView = angle; 522 | } 523 | RenderTexture texture = new RenderTexture(128, 128, 0, RenderTextureFormat.Default); 524 | renderCamera.targetTexture = texture; 525 | 526 | Undo.DestroyObjectImmediate(cameraObj); 527 | Undo.PerformUndo();//不知道为什么要删掉再Undo回来后才Render得出来UI的节点,3D节点是没这个问题的,估计是Canvas创建后没那么快有效? 528 | renderCamera.RenderDontRestore(); 529 | RenderTexture tex = new RenderTexture(128, 128, 0, RenderTextureFormat.Default); 530 | Graphics.Blit(texture, tex); 531 | 532 | Object.DestroyImmediate(canvas_obj); 533 | Object.DestroyImmediate(cameraObj); 534 | return tex; 535 | } 536 | 537 | public static Bounds GetBounds(GameObject obj) 538 | { 539 | Vector3 Min = new Vector3(99999, 99999, 99999); 540 | Vector3 Max = new Vector3(-99999, -99999, -99999); 541 | MeshRenderer[] renders = obj.GetComponentsInChildren(); 542 | if (renders.Length > 0) 543 | { 544 | for (int i = 0; i < renders.Length; i++) 545 | { 546 | if (renders[i].bounds.min.x < Min.x) 547 | Min.x = renders[i].bounds.min.x; 548 | if (renders[i].bounds.min.y < Min.y) 549 | Min.y = renders[i].bounds.min.y; 550 | if (renders[i].bounds.min.z < Min.z) 551 | Min.z = renders[i].bounds.min.z; 552 | 553 | if (renders[i].bounds.max.x > Max.x) 554 | Max.x = renders[i].bounds.max.x; 555 | if (renders[i].bounds.max.y > Max.y) 556 | Max.y = renders[i].bounds.max.y; 557 | if (renders[i].bounds.max.z > Max.z) 558 | Max.z = renders[i].bounds.max.z; 559 | } 560 | } 561 | else 562 | { 563 | RectTransform[] rectTrans = obj.GetComponentsInChildren(); 564 | Vector3[] corner = new Vector3[4]; 565 | for (int i = 0; i < rectTrans.Length; i++) 566 | { 567 | //获取节点的四个角的世界坐标,分别按顺序为左下左上,右上右下 568 | rectTrans[i].GetWorldCorners(corner); 569 | if (corner[0].x < Min.x) 570 | Min.x = corner[0].x; 571 | if (corner[0].y < Min.y) 572 | Min.y = corner[0].y; 573 | if (corner[0].z < Min.z) 574 | Min.z = corner[0].z; 575 | 576 | if (corner[2].x > Max.x) 577 | Max.x = corner[2].x; 578 | if (corner[2].y > Max.y) 579 | Max.y = corner[2].y; 580 | if (corner[2].z > Max.z) 581 | Max.z = corner[2].z; 582 | } 583 | } 584 | 585 | Vector3 center = (Min + Max) / 2; 586 | Vector3 size = new Vector3(Max.x - Min.x, Max.y - Min.y, Max.z - Min.z); 587 | return new Bounds(center, size); 588 | } 589 | 590 | //[MenuItem("UIEditor/另存为 ")] 591 | public static void SaveAnotherLayoutMenu() 592 | { 593 | if (Selection.activeGameObject == null) 594 | { 595 | EditorUtility.DisplayDialog("Warning", "I don't know which prefab you want to save", "Ok"); 596 | return; 597 | } 598 | LayoutInfo layout = Selection.activeGameObject.GetComponentInParent(); 599 | if (layout != null) 600 | { 601 | GameObject editingView = layout.EditingView; 602 | if (editingView != null) 603 | { 604 | UIEditorHelper.SaveAnotherLayout(layout.GetComponent(), editingView.transform); 605 | } 606 | } 607 | // for (int i = 0; i < layout.transform.childCount; i++) 608 | // { 609 | // Transform child = layout.transform.GetChild(i); 610 | // if (child.GetComponent() != null) 611 | // continue; 612 | // GameObject child_obj = child.gameObject; 613 | // //Debug.Log("child type :" + PrefabUtility.GetPrefabType(child_obj)); 614 | 615 | // //判断选择的物体,是否为预设 616 | // PrefabType cur_prefab_type = PrefabUtility.GetPrefabType(child_obj); 617 | // UIEditorHelper.SaveAnotherLayout(layout, child); 618 | // break; 619 | // } 620 | } 621 | 622 | public static void SaveAnotherLayout(Canvas layout, Transform child) 623 | { 624 | if (child.GetComponent() != null) 625 | return; 626 | GameObject child_obj = child.gameObject; 627 | //Debug.Log("child type :" + PrefabUtility.GetPrefabType(child_obj)); 628 | 629 | //判断选择的物体,是否为预设 630 | PrefabType cur_prefab_type = PrefabUtility.GetPrefabType(child_obj); 631 | //不是预设的话说明还没保存过的,弹出保存框 632 | string default_path = PathSaver.GetInstance().GetLastPath(PathType.SaveLayout); 633 | string save_path = EditorUtility.SaveFilePanel("Save Layout", default_path, "prefab_name", "prefab"); 634 | if (save_path == "") 635 | return; 636 | string full_path = save_path; 637 | PathSaver.GetInstance().SetLastPath(PathType.SaveLayout, save_path); 638 | save_path = FileUtil.GetProjectRelativePath(save_path); 639 | if (save_path == "") 640 | { 641 | Debug.Log("wrong path to save layout, is this project path? : " + full_path); 642 | EditorUtility.DisplayDialog("error", "wrong path to save layout, is this project path? : " + full_path, "ok"); 643 | return; 644 | } 645 | 646 | Object new_prefab = PrefabUtility.CreateEmptyPrefab(save_path); 647 | PrefabUtility.ReplacePrefab(child_obj, new_prefab, ReplacePrefabOptions.ConnectToPrefab); 648 | LayoutInfo layoutInfo = layout.GetComponent(); 649 | if (layoutInfo != null) 650 | layoutInfo.LayoutPath = full_path; 651 | string just_name = System.IO.Path.GetFileNameWithoutExtension(save_path); 652 | child_obj.name = just_name; 653 | layout.gameObject.name = just_name + "_Canvas"; 654 | //刷新 655 | AssetDatabase.Refresh(); 656 | if (Configure.IsShowDialogWhenSaveLayout) 657 | EditorUtility.DisplayDialog("Tip", "Save Succeed!", "Ok"); 658 | 659 | //保存时先记录一下,如果是运行游戏时保存了,结束游戏时就要重新加载界面了,不然会重置回运行游戏前的 660 | ReloadLayoutOnExitGame reloadCom = layout.GetComponent(); 661 | if (reloadCom) 662 | reloadCom.SetHadSaveOnRunTime(true); 663 | Debug.Log("Save Succeed!"); 664 | layoutInfo.SaveToConfigFile(); 665 | } 666 | 667 | //[MenuItem("UIEditor/保存 " + Configure.ShortCut.SaveUIPrefab, false, 2)] 668 | public static void SaveLayout(GameObject o, bool isQuiet) 669 | { 670 | GameObject saveObj = o == null ? Selection.activeGameObject : (o as GameObject); 671 | if (saveObj == null) 672 | { 673 | EditorUtility.DisplayDialog("Warning", "I don't know which prefab you want to save", "Ok"); 674 | return; 675 | } 676 | Canvas layout = saveObj.GetComponentInParent(); 677 | if (layout == null) 678 | { 679 | EditorUtility.DisplayDialog("Warning", "select any layout below UITestNode/canvas to save", "Ok"); 680 | return; 681 | } 682 | Transform real_layout = GetRealLayout(saveObj); 683 | if (real_layout != null) 684 | { 685 | GameObject child_obj = real_layout.gameObject; 686 | //判断选择的物体,是否为预设 687 | PrefabType cur_prefab_type = PrefabUtility.GetPrefabType(child_obj); 688 | if (PrefabUtility.GetPrefabType(child_obj) == PrefabType.PrefabInstance || cur_prefab_type == PrefabType.DisconnectedPrefabInstance) 689 | { 690 | UnityEngine.Object parentObject = PrefabUtility.GetPrefabParent(child_obj); 691 | //替换预设,Note:只能用ConnectToPrefab,不然会重复加多几个同名控件的 692 | PrefabUtility.ReplacePrefab(child_obj, parentObject, ReplacePrefabOptions.ConnectToPrefab); 693 | //刷新 694 | AssetDatabase.Refresh(); 695 | if (Configure.IsShowDialogWhenSaveLayout && !isQuiet) 696 | EditorUtility.DisplayDialog("Tip", "Save Succeed!", "Ok"); 697 | 698 | //保存时先记录一下,如果是运行游戏时保存了,结束游戏时就要重新加载界面了,不然会重置回运行游戏前的 699 | ReloadLayoutOnExitGame reloadCom = layout.GetComponent(); 700 | if (reloadCom) 701 | reloadCom.SetHadSaveOnRunTime(true); 702 | Debug.Log("Save Succeed!"); 703 | LayoutInfo layoutInfo = layout.GetComponent(); 704 | if (layoutInfo != null) 705 | layoutInfo.SaveToConfigFile(); 706 | } 707 | else 708 | { 709 | UIEditorHelper.SaveAnotherLayout(layout, real_layout); 710 | } 711 | } 712 | else 713 | { 714 | Debug.Log("save failed!are you select any widget below canvas?"); 715 | } 716 | } 717 | 718 | static public string ObjectToGUID(UnityEngine.Object obj) 719 | { 720 | string path = AssetDatabase.GetAssetPath(obj); 721 | return (!string.IsNullOrEmpty(path)) ? AssetDatabase.AssetPathToGUID(path) : null; 722 | } 723 | 724 | static MethodInfo s_GetInstanceIDFromGUID; 725 | static public UnityEngine.Object GUIDToObject(string guid) 726 | { 727 | if (string.IsNullOrEmpty(guid)) return null; 728 | 729 | if (s_GetInstanceIDFromGUID == null) 730 | s_GetInstanceIDFromGUID = typeof(AssetDatabase).GetMethod("GetInstanceIDFromGUID", BindingFlags.Static | BindingFlags.NonPublic); 731 | 732 | int id = (int)s_GetInstanceIDFromGUID.Invoke(null, new object[] { guid }); 733 | if (id != 0) return EditorUtility.InstanceIDToObject(id); 734 | string path = AssetDatabase.GUIDToAssetPath(guid); 735 | if (string.IsNullOrEmpty(path)) return null; 736 | return AssetDatabase.LoadAssetAtPath(path, typeof(UnityEngine.Object)); 737 | } 738 | 739 | static public T GUIDToObject(string guid) where T : UnityEngine.Object 740 | { 741 | UnityEngine.Object obj = GUIDToObject(guid); 742 | if (obj == null) return null; 743 | 744 | System.Type objType = obj.GetType(); 745 | if (objType == typeof(T) || objType.IsSubclassOf(typeof(T))) return obj as T; 746 | 747 | if (objType == typeof(GameObject) && typeof(T).IsSubclassOf(typeof(Component))) 748 | { 749 | GameObject go = obj as GameObject; 750 | return go.GetComponent(typeof(T)) as T; 751 | } 752 | return null; 753 | } 754 | 755 | static public void SetEnum(string name, System.Enum val) 756 | { 757 | EditorPrefs.SetString(name, val.ToString()); 758 | } 759 | 760 | static public T GetEnum(string name, T defaultValue) 761 | { 762 | string val = EditorPrefs.GetString(name, defaultValue.ToString()); 763 | string[] names = System.Enum.GetNames(typeof(T)); 764 | System.Array values = System.Enum.GetValues(typeof(T)); 765 | 766 | for (int i = 0; i < names.Length; ++i) 767 | { 768 | if (names[i] == val) 769 | return (T)values.GetValue(i); 770 | } 771 | return defaultValue; 772 | } 773 | 774 | static public void DrawTiledTexture(Rect rect, Texture tex) 775 | { 776 | GUI.BeginGroup(rect); 777 | { 778 | int width = Mathf.RoundToInt(rect.width); 779 | int height = Mathf.RoundToInt(rect.height); 780 | 781 | for (int y = 0; y < height; y += tex.height) 782 | { 783 | for (int x = 0; x < width; x += tex.width) 784 | { 785 | GUI.DrawTexture(new Rect(x, y, tex.width, tex.height), tex); 786 | } 787 | } 788 | } 789 | GUI.EndGroup(); 790 | } 791 | 792 | static Texture2D CreateCheckerTex(Color c0, Color c1) 793 | { 794 | Texture2D tex = new Texture2D(16, 16); 795 | tex.name = "[Generated] Checker Texture"; 796 | tex.hideFlags = HideFlags.DontSave; 797 | 798 | for (int y = 0; y < 8; ++y) for (int x = 0; x < 8; ++x) tex.SetPixel(x, y, c1); 799 | for (int y = 8; y < 16; ++y) for (int x = 0; x < 8; ++x) tex.SetPixel(x, y, c0); 800 | for (int y = 0; y < 8; ++y) for (int x = 8; x < 16; ++x) tex.SetPixel(x, y, c0); 801 | for (int y = 8; y < 16; ++y) for (int x = 8; x < 16; ++x) tex.SetPixel(x, y, c1); 802 | 803 | tex.Apply(); 804 | tex.filterMode = FilterMode.Point; 805 | return tex; 806 | } 807 | 808 | static Texture2D mBackdropTex; 809 | static public Texture2D backdropTexture 810 | { 811 | get 812 | { 813 | if (mBackdropTex == null) mBackdropTex = CreateCheckerTex( 814 | new Color(0.1f, 0.1f, 0.1f, 0.5f), 815 | new Color(0.2f, 0.2f, 0.2f, 0.5f)); 816 | return mBackdropTex; 817 | } 818 | } 819 | 820 | static private Transform GetGoodContainer(Transform trans) 821 | { 822 | if (trans == null) 823 | return null; 824 | if (trans.GetComponent() != null || trans.GetComponent() != null) 825 | return GetRealLayout(trans.gameObject); 826 | return trans; 827 | } 828 | 829 | static public void AddImageComponent() 830 | { 831 | if (Selection.activeGameObject == null) 832 | return; 833 | Image old_img = Selection.activeGameObject.GetComponent(); 834 | if (old_img != null) 835 | { 836 | bool isOk = EditorUtility.DisplayDialog("警告", "该GameObject已经有Image组件了,你想替换吗?", "来吧", "算了"); 837 | if (isOk) 838 | { 839 | //Selection.activeGameObject. 840 | } 841 | } 842 | Image img = Selection.activeGameObject.AddComponent(); 843 | img.raycastTarget = false; 844 | } 845 | 846 | static public void AddHorizontalLayoutComponent() 847 | { 848 | if (Selection.activeGameObject == null) 849 | return; 850 | HorizontalLayoutGroup layout = Selection.activeGameObject.AddComponent(); 851 | layout.childForceExpandWidth = false; 852 | layout.childForceExpandHeight = false; 853 | layout.childControlWidth = false; 854 | layout.childControlHeight = false; 855 | } 856 | 857 | static public void AddVerticalLayoutComponent() 858 | { 859 | if (Selection.activeGameObject == null) 860 | return; 861 | VerticalLayoutGroup layout = Selection.activeGameObject.AddComponent(); 862 | layout.childForceExpandWidth = false; 863 | layout.childForceExpandHeight = false; 864 | layout.childControlWidth = false; 865 | layout.childControlHeight = false; 866 | } 867 | 868 | static public void AddGridLayoutGroupComponent() 869 | { 870 | if (Selection.activeGameObject == null) 871 | return; 872 | GridLayoutGroup layout = Selection.activeGameObject.AddComponent(); 873 | } 874 | 875 | static public void CreateEmptyObj() 876 | { 877 | if (Selection.activeGameObject == null) 878 | return; 879 | GameObject go = new GameObject(CommonHelper.GenerateUniqueName(Selection.activeGameObject, "GameObject"), typeof(RectTransform)); 880 | go.transform.SetParent(GetGoodContainer(Selection.activeTransform), false); 881 | Selection.activeGameObject = go; 882 | } 883 | 884 | static public void CreateImageObj() 885 | { 886 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 887 | { 888 | GameObject go = new GameObject(CommonHelper.GenerateUniqueName(Selection.activeGameObject, "Image"), typeof(Image)); 889 | go.GetComponent().raycastTarget = false; 890 | go.transform.SetParent(GetGoodContainer(Selection.activeTransform), false); 891 | Selection.activeGameObject = go; 892 | } 893 | } 894 | 895 | static public void CreateRawImageObj() 896 | { 897 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 898 | { 899 | GameObject go = new GameObject(CommonHelper.GenerateUniqueName(Selection.activeGameObject, "RawImage"), typeof(RawImage)); 900 | go.GetComponent().raycastTarget = false; 901 | go.transform.SetParent(GetGoodContainer(Selection.activeTransform), false); 902 | Selection.activeGameObject = go; 903 | } 904 | } 905 | 906 | static public void CreateButtonObj() 907 | { 908 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 909 | { 910 | Transform last_trans = Selection.activeTransform; 911 | bool isOk = EditorApplication.ExecuteMenuItem("GameObject/UI/Button"); 912 | if (isOk) 913 | { 914 | Selection.activeGameObject.name = CommonHelper.GenerateUniqueName(Selection.activeGameObject, "Button"); 915 | Selection.activeTransform.SetParent(GetGoodContainer(last_trans), false); 916 | } 917 | } 918 | } 919 | 920 | static public void CreateTextObj() 921 | { 922 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 923 | { 924 | GameObject go = new GameObject(CommonHelper.GenerateUniqueName(Selection.activeGameObject, "Text"), typeof(Text)); 925 | Text txt = go.GetComponent(); 926 | txt.raycastTarget = false; 927 | txt.text = "I am a Text"; 928 | go.transform.SetParent(GetGoodContainer(Selection.activeTransform), false); 929 | go.transform.localPosition = Vector3.zero; 930 | Selection.activeGameObject = go; 931 | } 932 | } 933 | 934 | static private void InitScrollView(bool isHorizontal) 935 | { 936 | ScrollRect scroll = Selection.activeTransform.GetComponent(); 937 | if (scroll==null) 938 | return; 939 | Image img = Selection.activeTransform.GetComponent(); 940 | if (img != null) 941 | Object.DestroyImmediate(img); 942 | scroll.horizontal = isHorizontal; 943 | scroll.vertical = !isHorizontal; 944 | scroll.horizontalScrollbar = null; 945 | scroll.verticalScrollbar = null; 946 | Transform horizontalObj = Selection.activeTransform.Find("Scrollbar Horizontal"); 947 | if (horizontalObj != null) 948 | GameObject.DestroyImmediate(horizontalObj.gameObject); 949 | Transform verticalObj = Selection.activeTransform.Find("Scrollbar Vertical"); 950 | if (verticalObj != null) 951 | GameObject.DestroyImmediate(verticalObj.gameObject); 952 | RectTransform viewPort = Selection.activeTransform.Find("Viewport") as RectTransform; 953 | if (viewPort != null) 954 | { 955 | viewPort.offsetMin = new Vector2(0, 0); 956 | viewPort.offsetMax = new Vector2(0, 0); 957 | } 958 | } 959 | 960 | static public void CreateHScrollViewObj() 961 | { 962 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 963 | { 964 | Transform last_trans = Selection.activeTransform; 965 | bool isOk = EditorApplication.ExecuteMenuItem("GameObject/UI/Scroll View"); 966 | if (isOk) 967 | { 968 | Selection.activeGameObject.name = CommonHelper.GenerateUniqueName(Selection.activeGameObject, "ScrollView"); 969 | Selection.activeTransform.SetParent(GetGoodContainer(last_trans), false); 970 | InitScrollView(true); 971 | } 972 | } 973 | } 974 | 975 | static public void CreateVScrollViewObj() 976 | { 977 | if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent()) 978 | { 979 | Transform last_trans = Selection.activeTransform; 980 | bool isOk = EditorApplication.ExecuteMenuItem("GameObject/UI/Scroll View"); 981 | if (isOk) 982 | { 983 | Selection.activeGameObject.name = CommonHelper.GenerateUniqueName(Selection.activeGameObject, "ScrollView"); 984 | Selection.activeTransform.SetParent(GetGoodContainer(last_trans), false); 985 | InitScrollView(false); 986 | } 987 | } 988 | } 989 | 990 | static public string GenMD5String(string str) 991 | { 992 | System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); 993 | str = System.BitConverter.ToString(md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(str)), 4, 8); 994 | return str.Replace("-", ""); 995 | } 996 | 997 | public static void SaveAnotherLayoutContextMenu() 998 | { 999 | SaveAnotherLayoutMenu(); 1000 | } 1001 | 1002 | public static void SaveLayoutForMenu() 1003 | { 1004 | SaveLayout(null, false); 1005 | } 1006 | 1007 | public static void CreatNewLayoutForMenu() 1008 | { 1009 | CreatNewLayout(); 1010 | } 1011 | public static void ReLoadLayoutForMenu() 1012 | { 1013 | ReLoadLayout(null, false); 1014 | } 1015 | } 1016 | } 1017 | #endif -------------------------------------------------------------------------------- /Common/UIEditorHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2d6a19623cd7ca14db511d4087d6c174 3 | timeCreated: 1521079536 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Common/UILayoutTool.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using UnityEngine; 3 | using UnityEditor; 4 | using System.Linq; 5 | using System.Collections.Generic; 6 | using UnityEngine.UI; 7 | 8 | namespace U3DExtends 9 | { 10 | public class UILayoutTool : MonoBehaviour 11 | { 12 | public static void ResortAllLayout() 13 | { 14 | GameObject testUI = GameObject.Find(Configure.UITestNodeName); 15 | if (testUI != null) 16 | { 17 | LayoutInfo[] layouts = testUI.GetComponentsInChildren(); 18 | if (layouts.Length > 0) 19 | { 20 | SceneView.lastActiveSceneView.MoveToView(layouts[0].transform); 21 | 22 | RectTransform first_trans = layouts[0].transform as RectTransform; 23 | Vector2 startPos = new Vector2(first_trans.sizeDelta.x*first_trans.localScale.x/2, -first_trans.sizeDelta.y * first_trans.localScale.y/2); 24 | int index = 0; 25 | foreach (var item in layouts) 26 | { 27 | int row = index / 5; 28 | int col = index % 5; 29 | RectTransform rectTrans = item.transform as RectTransform; 30 | Vector3 pos = new Vector3((rectTrans.sizeDelta.x*rectTrans.localScale.x) * col + startPos.x, (-rectTrans.sizeDelta.y * rectTrans.localScale.y) * row + startPos.y, 0); 31 | item.transform.localPosition = pos; 32 | index++; 33 | } 34 | } 35 | } 36 | } 37 | 38 | public static void OptimizeBatchForMenu() 39 | { 40 | OptimizeBatch(Selection.activeTransform); 41 | } 42 | 43 | public static void OptimizeBatch(Transform trans) 44 | { 45 | if (trans == null) 46 | return; 47 | Dictionary> imageGroup = new Dictionary>(); 48 | Dictionary> textGroup = new Dictionary>(); 49 | List> sortedImgageGroup = new List>(); 50 | List> sortedTextGroup = new List>(); 51 | for (int i = 0; i < trans.childCount; i++) 52 | { 53 | Transform child = trans.GetChild(i); 54 | Texture cur_texture = null; 55 | Image img = child.GetComponent(); 56 | if (img != null) 57 | { 58 | cur_texture = img.mainTexture; 59 | } 60 | else 61 | { 62 | RawImage rimg = child.GetComponent(); 63 | if (rimg != null) 64 | cur_texture = rimg.mainTexture; 65 | } 66 | if (cur_texture != null) 67 | { 68 | string cur_path = AssetDatabase.GetAssetPath(cur_texture); 69 | TextureImporter importer = AssetImporter.GetAtPath(cur_path) as TextureImporter; 70 | //Debug.Log("cur_path : " + cur_path + " importer:"+(importer!=null).ToString()); 71 | if (importer != null) 72 | { 73 | string atlas = importer.spritePackingTag; 74 | //Debug.Log("atlas : " + atlas); 75 | if (atlas != "") 76 | { 77 | if (!imageGroup.ContainsKey(atlas)) 78 | { 79 | List list = new List(); 80 | sortedImgageGroup.Add(list); 81 | imageGroup.Add(atlas, list); 82 | } 83 | imageGroup[atlas].Add(child); 84 | } 85 | } 86 | } 87 | else 88 | { 89 | Text text = child.GetComponent(); 90 | if (text != null) 91 | { 92 | string fontName = text.font.name; 93 | //Debug.Log("fontName : " + fontName); 94 | if (!textGroup.ContainsKey(fontName)) 95 | { 96 | List list = new List(); 97 | sortedTextGroup.Add(list); 98 | textGroup.Add(fontName, list); 99 | } 100 | textGroup[fontName].Add(child); 101 | } 102 | } 103 | OptimizeBatch(child); 104 | } 105 | //同一图集的Image间层级顺序继续保留,不同图集的顺序就按每组第一张的来 106 | for (int i = sortedImgageGroup.Count - 1; i >= 0; i--) 107 | { 108 | List children = sortedImgageGroup[i]; 109 | for (int j = children.Count - 1; j >= 0; j--) 110 | { 111 | children[j].SetAsFirstSibling(); 112 | } 113 | 114 | } 115 | foreach (var item in sortedTextGroup) 116 | { 117 | List children = item; 118 | for (int i = 0; i < children.Count; i++) 119 | { 120 | children[i].SetAsLastSibling(); 121 | } 122 | } 123 | } 124 | 125 | public static void ShowAllSelectedWidgets() 126 | { 127 | foreach (var item in Selection.gameObjects) 128 | { 129 | item.SetActive(true); 130 | } 131 | } 132 | public static void HideAllSelectedWidgets() 133 | { 134 | foreach (var item in Selection.gameObjects) 135 | { 136 | item.SetActive(false); 137 | } 138 | } 139 | 140 | //[MenuItem("UIEditor/Operate/解除")] 141 | public static void UnGroup() 142 | { 143 | if (Selection.gameObjects == null || Selection.gameObjects.Length <= 0) 144 | { 145 | EditorUtility.DisplayDialog("Error", "当前没有选中节点", "Ok"); 146 | return; 147 | } 148 | if (Selection.gameObjects.Length > 1) 149 | { 150 | EditorUtility.DisplayDialog("Error", "只能同时解除一个Box", "Ok"); 151 | return; 152 | } 153 | GameObject target = Selection.activeGameObject; 154 | Transform new_parent = target.transform.parent; 155 | if (target.transform.childCount > 0) 156 | { 157 | Transform[] child_ui = target.transform.GetComponentsInChildren(true); 158 | foreach (var item in child_ui) 159 | { 160 | //不是自己的子节点或是自己的话就跳过 161 | if (item.transform.parent != target.transform || item.transform == target.transform) 162 | continue; 163 | 164 | item.transform.SetParent(new_parent, true); 165 | } 166 | Undo.DestroyObjectImmediate(target); 167 | //GameObject.DestroyImmediate(target); 168 | } 169 | else 170 | { 171 | EditorUtility.DisplayDialog("Error", "选择对象容器控件", "Ok"); 172 | } 173 | } 174 | 175 | //[MenuItem("UIEditor/Operate/组合")] 176 | public static void MakeGroup() 177 | { 178 | if (Selection.gameObjects == null || Selection.gameObjects.Length <= 0) 179 | { 180 | EditorUtility.DisplayDialog("Error", "当前没有选中节点", "Ok"); 181 | return; 182 | } 183 | //先判断选中的节点是不是挂在同个父节点上的 184 | Transform parent = Selection.gameObjects[0].transform.parent; 185 | foreach (var item in Selection.gameObjects) 186 | { 187 | if (item.transform.parent != parent) 188 | { 189 | EditorUtility.DisplayDialog("Error", "不能跨容器组合", "Ok"); 190 | return; 191 | } 192 | } 193 | GameObject box = new GameObject("container", typeof(RectTransform)); 194 | Undo.IncrementCurrentGroup(); 195 | int group_index = Undo.GetCurrentGroup(); 196 | Undo.SetCurrentGroupName("Make Group"); 197 | Undo.RegisterCreatedObjectUndo(box, "create group object"); 198 | RectTransform rectTrans = box.GetComponent(); 199 | if (rectTrans != null) 200 | { 201 | Vector2 left_top_pos = new Vector2(99999, -99999); 202 | Vector2 right_bottom_pos = new Vector2(-99999, 99999); 203 | foreach (var item in Selection.gameObjects) 204 | { 205 | Bounds bound = UIEditorHelper.GetBounds(item); 206 | Vector3 boundMin = item.transform.parent.InverseTransformPoint(bound.min); 207 | Vector3 boundMax = item.transform.parent.InverseTransformPoint(bound.max); 208 | //Debug.Log("bound : " + boundMin.ToString() + " max:" + boundMax.ToString()); 209 | if (boundMin.x < left_top_pos.x) 210 | left_top_pos.x = boundMin.x; 211 | if (boundMax.y > left_top_pos.y) 212 | left_top_pos.y = boundMax.y; 213 | if (boundMax.x > right_bottom_pos.x) 214 | right_bottom_pos.x = boundMax.x; 215 | if (boundMin.y < right_bottom_pos.y) 216 | right_bottom_pos.y = boundMin.y; 217 | } 218 | rectTrans.SetParent(parent); 219 | rectTrans.sizeDelta = new Vector2(right_bottom_pos.x - left_top_pos.x, left_top_pos.y - right_bottom_pos.y); 220 | left_top_pos.x += rectTrans.sizeDelta.x/2; 221 | left_top_pos.y -= rectTrans.sizeDelta.y/2; 222 | rectTrans.localPosition = left_top_pos; 223 | rectTrans.localScale = Vector3.one; 224 | 225 | //需要先生成好Box和设置好它的坐标和大小才可以把选中的节点挂进来,注意要先排好序,不然层次就乱了 226 | GameObject[] sorted_objs = Selection.gameObjects.OrderBy(x => x.transform.GetSiblingIndex()).ToArray(); 227 | for (int i = 0; i < sorted_objs.Length; i++) 228 | { 229 | Undo.SetTransformParent(sorted_objs[i].transform, rectTrans, "move item to group"); 230 | } 231 | } 232 | Selection.activeGameObject = box; 233 | Undo.CollapseUndoOperations(group_index); 234 | } 235 | 236 | 237 | } 238 | 239 | 240 | public class PriorityTool 241 | { 242 | // [MenuItem("UIEditor/层次/最里层 " + Configure.ShortCut.MoveNodeTop)] 243 | public static void MoveToTopWidget() 244 | { 245 | Transform curSelect = Selection.activeTransform; 246 | if (curSelect != null) 247 | { 248 | curSelect.SetAsFirstSibling(); 249 | } 250 | } 251 | // [MenuItem("UIEditor/层次/最外层 " + Configure.ShortCut.MoveNodeBottom)] 252 | public static void MoveToBottomWidget() 253 | { 254 | Transform curSelect = Selection.activeTransform; 255 | if (curSelect != null) 256 | { 257 | curSelect.SetAsLastSibling(); 258 | } 259 | } 260 | 261 | // [MenuItem("UIEditor/层次/往里挤 " + Configure.ShortCut.MoveNodeUp)] 262 | public static void MoveUpWidget() 263 | { 264 | Transform curSelect = Selection.activeTransform; 265 | if (curSelect != null) 266 | { 267 | int curIndex = curSelect.GetSiblingIndex(); 268 | if (curIndex > 0) 269 | curSelect.SetSiblingIndex(curIndex - 1); 270 | } 271 | } 272 | 273 | // [MenuItem("UIEditor/层次/往外挤 " + Configure.ShortCut.MoveNodeDown)] 274 | public static void MoveDownWidget() 275 | { 276 | Transform curSelect = Selection.activeTransform; 277 | if (curSelect != null) 278 | { 279 | int curIndex = curSelect.GetSiblingIndex(); 280 | int child_num = curSelect.parent.childCount; 281 | if (curIndex < child_num - 1) 282 | curSelect.SetSiblingIndex(curIndex + 1); 283 | } 284 | } 285 | } 286 | 287 | public class AlignTool 288 | { 289 | // [MenuItem("UIEditor/对齐/左对齐 ←")] 290 | internal static void AlignInHorziontalLeft() 291 | { 292 | float x = Mathf.Min(Selection.gameObjects.Select(obj => obj.transform.localPosition.x).ToArray()); 293 | 294 | foreach (GameObject gameObject in Selection.gameObjects) 295 | { 296 | gameObject.transform.localPosition = new Vector2(x, 297 | gameObject.transform.localPosition.y); 298 | } 299 | } 300 | 301 | // [MenuItem("UIEditor/对齐/右对齐 →")] 302 | public static void AlignInHorziontalRight() 303 | { 304 | float x = Mathf.Max(Selection.gameObjects.Select(obj => obj.transform.localPosition.x + 305 | ((RectTransform)obj.transform).sizeDelta.x).ToArray()); 306 | foreach (GameObject gameObject in Selection.gameObjects) 307 | { 308 | gameObject.transform.localPosition = new Vector3(x - 309 | ((RectTransform)gameObject.transform).sizeDelta.x, gameObject.transform.localPosition.y); 310 | } 311 | } 312 | 313 | // [MenuItem("UIEditor/对齐/上对齐 ↑")] 314 | public static void AlignInVerticalUp() 315 | { 316 | float y = Mathf.Max(Selection.gameObjects.Select(obj => obj.transform.localPosition.y).ToArray()); 317 | foreach (GameObject gameObject in Selection.gameObjects) 318 | { 319 | gameObject.transform.localPosition = new Vector3(gameObject.transform.localPosition.x, y); 320 | } 321 | } 322 | 323 | // [MenuItem("UIEditor/对齐/下对齐 ↓")] 324 | public static void AlignInVerticalDown() 325 | { 326 | float y = Mathf.Min(Selection.gameObjects.Select(obj => obj.transform.localPosition.y - 327 | ((RectTransform)obj.transform).sizeDelta.y).ToArray()); 328 | 329 | foreach (GameObject gameObject in Selection.gameObjects) 330 | { 331 | gameObject.transform.localPosition = new Vector3(gameObject.transform.localPosition.x, y + ((RectTransform)gameObject.transform).sizeDelta.y); 332 | } 333 | } 334 | 335 | 336 | // [MenuItem("UIEditor/对齐/水平均匀 |||")] 337 | public static void UniformDistributionInHorziontal() 338 | { 339 | int count = Selection.gameObjects.Length; 340 | float firstX = Mathf.Min(Selection.gameObjects.Select(obj => obj.transform.localPosition.x).ToArray()); 341 | float lastX = Mathf.Max(Selection.gameObjects.Select(obj => obj.transform.localPosition.x).ToArray()); 342 | float distance = (lastX - firstX) / (count - 1); 343 | var objects = Selection.gameObjects.ToList(); 344 | objects.Sort((x, y) => (int)(x.transform.localPosition.x - y.transform.localPosition.x)); 345 | for (int i = 0; i < count; i++) 346 | { 347 | objects[i].transform.localPosition = new Vector3(firstX + i * distance, objects[i].transform.localPosition.y); 348 | } 349 | } 350 | 351 | // [MenuItem("UIEditor/对齐/垂直均匀 ☰")] 352 | public static void UniformDistributionInVertical() 353 | { 354 | int count = Selection.gameObjects.Length; 355 | float firstY = Mathf.Min(Selection.gameObjects.Select(obj => obj.transform.localPosition.y).ToArray()); 356 | float lastY = Mathf.Max(Selection.gameObjects.Select(obj => obj.transform.localPosition.y).ToArray()); 357 | float distance = (lastY - firstY) / (count - 1); 358 | var objects = Selection.gameObjects.ToList(); 359 | objects.Sort((x, y) => (int)(x.transform.localPosition.y - y.transform.localPosition.y)); 360 | for (int i = 0; i < count; i++) 361 | { 362 | objects[i].transform.localPosition = new Vector3(objects[i].transform.localPosition.x, firstY + i * distance); 363 | } 364 | } 365 | 366 | // [MenuItem("UIEditor/对齐/一样大 ■")] 367 | public static void ResizeMax() 368 | { 369 | var height = Mathf.Max(Selection.gameObjects.Select(obj => ((RectTransform)obj.transform).sizeDelta.y).ToArray()); 370 | var width = Mathf.Max(Selection.gameObjects.Select(obj => ((RectTransform)obj.transform).sizeDelta.x).ToArray()); 371 | foreach (GameObject gameObject in Selection.gameObjects) 372 | { 373 | ((RectTransform)gameObject.transform).sizeDelta = new Vector2(width, height); 374 | } 375 | } 376 | 377 | // [MenuItem("UIEditor/对齐/一样小 ●")] 378 | public static void ResizeMin() 379 | { 380 | var height = Mathf.Min(Selection.gameObjects.Select(obj => ((RectTransform)obj.transform).sizeDelta.y).ToArray()); 381 | var width = Mathf.Min(Selection.gameObjects.Select(obj => ((RectTransform)obj.transform).sizeDelta.x).ToArray()); 382 | foreach (GameObject gameObject in Selection.gameObjects) 383 | { 384 | ((RectTransform)gameObject.transform).sizeDelta = new Vector2(width, height); 385 | } 386 | } 387 | 388 | } 389 | } 390 | #endif -------------------------------------------------------------------------------- /Common/UILayoutTool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5997bec4b8584554e81988d7100d4e25 3 | timeCreated: 1520819106 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Configure.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace U3DExtends 4 | { 5 | //功能和快捷键的配置 6 | public static class Configure 7 | { 8 | //是否开启场景中的右键菜单 9 | public static bool IsShowSceneMenu = true; 10 | 11 | //选中图片节点再选图片时,即帮节点赋上该图 12 | public static bool IsEnableFastSelectImage = false; 13 | //选中图片节点再选图片时,帮节点赋上该图时自动设为原图大小 14 | public static bool IsAutoSizeOnFastSelectImg = false; 15 | 16 | //拉UI prefab或者图片入scene界面时帮它找到鼠标下的Canvas并挂在其上,若鼠标下没有画布就创建一个 17 | public static bool IsEnableDragUIToScene = true; 18 | 19 | //是否开启用箭头按键移动UI节点 20 | public static bool IsMoveNodeByArrowKey = true; 21 | 22 | //保存界面时是否需要显示保存成功的提示框 23 | public static bool IsShowDialogWhenSaveLayout = true; 24 | 25 | //结束游戏运行时是否重新加载运行期间修改过的界面 26 | public static bool ReloadLayoutOnExitGame = true; 27 | 28 | //一添加参考图就打开选择图片框 29 | public static bool OpenSelectPicDialogWhenAddDecorate = true; 30 | 31 | //此路径可以为空,设置后首次导入本插件时就会加载该目录下的所有prefab 32 | //public const string PrefabWinFirstSearchPath = "Assets/LuaFramework/AssetBundleRes/ui/uiComponent/prefab"; 33 | public const string PrefabWinFirstSearchPath = ""; 34 | 35 | //快捷键配置 菜单项快捷键:%#&1 代表的就是:Ctrl + Shift + Alt + 1 36 | public static class ShortCut 37 | { 38 | //复制选中节点全名的字符串到系统剪切板 39 | public const string CopyNodesName = "%#c"; 40 | } 41 | 42 | //所有编辑界面的Canvas都放到此节点上,可定制节点名 43 | public static string UITestNodeName = "UITestNode"; 44 | public static Vector3 UITestNodePos = new Vector3(0, 0, 500); 45 | public static Vector2 UITestNodeSize = new Vector2(4, 4); 46 | public const string FolderName = "UGUI-Editor"; 47 | public static string ResAssetsPath = Application.dataPath + "/" + FolderName + "/Res"; 48 | 49 | static string projectUUID = string.Empty; 50 | public static string ProjectUUID 51 | { 52 | get 53 | { 54 | #if UNITY_EDITOR 55 | if (projectUUID == string.Empty) 56 | { 57 | projectUUID = UIEditorHelper.GenMD5String(Application.dataPath); 58 | } 59 | #endif 60 | return projectUUID; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Editor/CustomInspectors.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | namespace U3DExtends 5 | { 6 | [CustomEditor(typeof(Decorate))] 7 | public class DecorateEditor : Editor 8 | { 9 | public override void OnInspectorGUI() 10 | { 11 | Decorate widget = target as Decorate; 12 | if (GUILayout.Button("加载外部图片", GUILayout.Height(30))) 13 | { 14 | UIEditorHelper.SelectPicForDecorate(widget); 15 | } 16 | } 17 | } 18 | 19 | //Cat!TODO:给按钮文本等控件增加切换样式的功能 20 | } -------------------------------------------------------------------------------- /Editor/CustomInspectors.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 34c41c274d88c8043b41a532d64206e4 3 | timeCreated: 1521105540 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/FindReferences.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEditor; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Collections.Generic; 9 | using System.Reflection; 10 | 11 | public static class FindReferences 12 | { 13 | private static PropertyInfo inspectorMode = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); 14 | public static long GetFileID(this Object target) 15 | { 16 | SerializedObject serializedObject = new SerializedObject(target); 17 | inspectorMode.SetValue(serializedObject, InspectorMode.Debug, null); 18 | SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); 19 | return localIdProp.longValue; 20 | } 21 | 22 | [MenuItem("Assets/Find References", false)] 23 | static void Find() 24 | { 25 | EditorSettings.serializationMode = SerializationMode.ForceText; 26 | string path = AssetDatabase.GetAssetPath(Selection.activeObject); 27 | if (!string.IsNullOrEmpty(path)) 28 | { 29 | Debug.Log("开始查找哪里引用到资源:"+path); 30 | string guid = AssetDatabase.AssetPathToGUID(path); 31 | //string guid = FindReferences.GetFileID(Selection.activeObject).ToString(); 32 | // Debug.Log("guid : " + guid); 33 | List withoutExtensions = new List() { ".prefab", ".unity", ".mat", ".asset" }; 34 | string[] files = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories) 35 | .Where(s => withoutExtensions.Contains(Path.GetExtension(s).ToLower())).ToArray(); 36 | int startIndex = 0; 37 | 38 | EditorApplication.update = delegate () 39 | { 40 | string file = files[startIndex]; 41 | bool isCancel = EditorUtility.DisplayCancelableProgressBar("匹配资源中", file, (float)startIndex / (float)files.Length); 42 | 43 | if (Regex.IsMatch(File.ReadAllText(file), guid)) 44 | { 45 | Object find_obj = AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(file)); 46 | Debug.Log(file+"引用到该资源", find_obj); 47 | string extension = Path.GetExtension(file); 48 | // Debug.Log("extension "+extension); 49 | if (extension == ".prefab") 50 | { 51 | int select_index = EditorUtility.DisplayDialogComplex("找到了", file+"引用到该资源", "关闭", "继续查找", "打开界面"); 52 | // Debug.Log("select index "+select_index); 53 | isCancel = (select_index==0 || select_index==2); 54 | if (select_index == 2) 55 | { 56 | U3DExtends.UIEditorHelper.LoadLayoutByPath(file); 57 | } 58 | } 59 | else 60 | { 61 | isCancel = EditorUtility.DisplayDialog("找到了", file+"引用到该资源", "关闭", "继续查找"); 62 | } 63 | } 64 | 65 | startIndex++; 66 | if (isCancel || startIndex >= files.Length) 67 | { 68 | EditorUtility.ClearProgressBar(); 69 | EditorApplication.update = null; 70 | startIndex = 0; 71 | Debug.Log("匹配结束"); 72 | } 73 | 74 | }; 75 | } 76 | } 77 | 78 | [MenuItem("Assets/Find References", true)] 79 | static private bool VFind() 80 | { 81 | string path = AssetDatabase.GetAssetPath(Selection.activeObject); 82 | return (!string.IsNullOrEmpty(path)); 83 | } 84 | 85 | static private string GetRelativeAssetsPath(string path) 86 | { 87 | return "Assets" + Path.GetFullPath(path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/'); 88 | } 89 | } -------------------------------------------------------------------------------- /Editor/FindReferences.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7bddad549fefe9b489555f49b3eafd45 3 | timeCreated: 1534241263 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Editor/PrefabWin.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace U3DExtends { 10 | public class PrefabWin : EditorWindow 11 | { 12 | private static int _labelDefaultFontSize; 13 | 14 | [MenuItem("Window/PrefabWin", false, 9)] 15 | static public void OpenPrefabTool() 16 | { 17 | _labelDefaultFontSize = EditorStyles.label.fontSize; 18 | PrefabWin prefabWin = (PrefabWin)EditorWindow.GetWindow(false, "Prefab Win", true); 19 | prefabWin.autoRepaintOnSceneChange = true; 20 | prefabWin.Show(); 21 | } 22 | 23 | static public PrefabWin instance; 24 | 25 | class Item 26 | { 27 | public GameObject prefab; 28 | public string guid; 29 | public Texture tex; 30 | public bool dynamicTex = false; 31 | } 32 | 33 | enum Mode 34 | { 35 | CompactMode, 36 | IconMode, 37 | DetailedMode, 38 | } 39 | 40 | const int cellPadding = 4; 41 | float mSizePercent = 0.5f; 42 | 43 | public float SizePercent 44 | { 45 | get { return mSizePercent; } 46 | set 47 | { 48 | if (mSizePercent != value) 49 | { 50 | mReset = true; 51 | mSizePercent = value; 52 | mCellSize = Mathf.FloorToInt(80 * SizePercent + 10); 53 | EditorPrefs.SetFloat("PrefabWin_SizePercent", mSizePercent); 54 | } 55 | } 56 | } 57 | int mCellSize=50; 58 | int cellSize { get { return mCellSize; } } 59 | 60 | int mTab = 0; 61 | Mode mMode = Mode.CompactMode; 62 | Vector2 mPos = Vector2.zero; 63 | bool mMouseIsInside = false; 64 | GUIContent mContent; 65 | GUIStyle mStyle; 66 | 67 | BetterList mItems = new BetterList(); 68 | 69 | GameObject[] draggedObjects 70 | { 71 | get 72 | { 73 | if (DragAndDrop.objectReferences == null || DragAndDrop.objectReferences.Length == 0) 74 | return null; 75 | 76 | return DragAndDrop.objectReferences.Where(x=>x as GameObject).Cast().ToArray(); 77 | } 78 | set 79 | { 80 | if (value != null) 81 | { 82 | DragAndDrop.PrepareStartDrag(); 83 | DragAndDrop.objectReferences = value; 84 | draggedObjectIsOurs = true; 85 | } 86 | else DragAndDrop.AcceptDrag(); 87 | } 88 | } 89 | 90 | bool draggedObjectIsOurs 91 | { 92 | get 93 | { 94 | object obj = DragAndDrop.GetGenericData("Prefab Tool"); 95 | if (obj == null) return false; 96 | return (bool)obj; 97 | } 98 | set 99 | { 100 | DragAndDrop.SetGenericData("Prefab Tool", value); 101 | } 102 | } 103 | 104 | 105 | void OnEnable () 106 | { 107 | instance = this; 108 | 109 | Load(); 110 | 111 | mContent = new GUIContent(); 112 | mStyle = new GUIStyle(); 113 | mStyle.alignment = TextAnchor.MiddleCenter; 114 | mStyle.padding = new RectOffset(2, 2, 2, 2); 115 | mStyle.clipping = TextClipping.Clip; 116 | mStyle.wordWrap = true; 117 | mStyle.stretchWidth = false; 118 | mStyle.stretchHeight = false; 119 | mStyle.normal.textColor = UnityEditor.EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.5f) : new Color(0f, 0f, 0f, 0.5f); 120 | mStyle.normal.background = null; 121 | } 122 | 123 | void OnDisable () 124 | { 125 | instance = null; 126 | foreach (Item item in mItems) DestroyTexture(item); 127 | Save(); 128 | } 129 | 130 | void OnSelectionChange () { Repaint(); } 131 | 132 | public void Reset () 133 | { 134 | foreach (Item item in mItems) DestroyTexture(item); 135 | mItems.Clear(); 136 | 137 | if (mTab == 0 && Configure.PrefabWinFirstSearchPath!="") 138 | { 139 | List filtered = new List(); 140 | string[] allAssets = AssetDatabase.GetAllAssetPaths(); 141 | 142 | foreach (string s in allAssets) 143 | { 144 | //search prefab files in folder:Configure.PrefabWinFirstSearchPath 145 | bool isComeFromPrefab = Regex.IsMatch(s, Configure.PrefabWinFirstSearchPath+@"/((?!/).)*\.prefab"); 146 | if (isComeFromPrefab) 147 | filtered.Add(s); 148 | } 149 | 150 | filtered.Sort(string.Compare); 151 | foreach (string s in filtered) AddGUID(AssetDatabase.AssetPathToGUID(s), -1); 152 | RectivateLights(); 153 | } 154 | } 155 | 156 | void AddItem (GameObject go, int index) 157 | { 158 | string guid = U3DExtends.UIEditorHelper.ObjectToGUID(go); 159 | 160 | if (string.IsNullOrEmpty(guid)) 161 | { 162 | string path = EditorUtility.SaveFilePanelInProject("Save a prefab", 163 | go.name + ".prefab", "prefab", "Save prefab as...", ""); 164 | 165 | if (string.IsNullOrEmpty(path)) return; 166 | 167 | go = PrefabUtility.CreatePrefab(path, go); 168 | if (go == null) return; 169 | 170 | guid = U3DExtends.UIEditorHelper.ObjectToGUID(go); 171 | if (string.IsNullOrEmpty(guid)) return; 172 | } 173 | 174 | Item ent = new Item(); 175 | ent.prefab = go; 176 | ent.guid = guid; 177 | GeneratePreview(ent); 178 | RectivateLights(); 179 | 180 | if (index < mItems.size) mItems.Insert(index, ent); 181 | else mItems.Add(ent); 182 | Save(); 183 | } 184 | 185 | Item AddGUID (string guid, int index) 186 | { 187 | GameObject go = U3DExtends.UIEditorHelper.GUIDToObject(guid); 188 | 189 | if (go != null) 190 | { 191 | Item ent = new Item(); 192 | ent.prefab = go; 193 | ent.guid = guid; 194 | GeneratePreview(ent, false); 195 | if (index < mItems.size) mItems.Insert(index, ent); 196 | else mItems.Add(ent); 197 | return ent; 198 | } 199 | return null; 200 | } 201 | 202 | void RemoveItem (object obj) 203 | { 204 | if (this == null) return; 205 | int index = (int)obj; 206 | if (index < mItems.size && index > -1) 207 | { 208 | Item item = mItems[index]; 209 | DestroyTexture(item); 210 | mItems.RemoveAt(index); 211 | } 212 | Save(); 213 | } 214 | 215 | Item FindItem (GameObject go) 216 | { 217 | for (int i = 0; i < mItems.size; ++i) 218 | if (mItems[i].prefab == go) 219 | return mItems[i]; 220 | return null; 221 | } 222 | 223 | string saveKey { get { return "PrefabWin " + Application.dataPath + " " + mTab; } } 224 | 225 | void Save () 226 | { 227 | string data = ""; 228 | 229 | if (mItems.size > 0) 230 | { 231 | string guid = mItems[0].guid; 232 | StringBuilder sb = new StringBuilder(); 233 | sb.Append(guid); 234 | 235 | for (int i = 1; i < mItems.size; ++i) 236 | { 237 | guid = mItems[i].guid; 238 | 239 | if (string.IsNullOrEmpty(guid)) 240 | { 241 | Debug.LogWarning("Unable to save " + mItems[i].prefab.name); 242 | } 243 | else 244 | { 245 | sb.Append('|'); 246 | sb.Append(mItems[i].guid); 247 | } 248 | } 249 | data = sb.ToString(); 250 | } 251 | EditorPrefs.SetString(saveKey, data); 252 | } 253 | 254 | void Load () 255 | { 256 | mTab = EditorPrefs.GetInt("PrefabWin Prefab Tab", 0); 257 | SizePercent = EditorPrefs.GetFloat("PrefabWin_SizePercent", 0.5f); 258 | 259 | foreach (Item item in mItems) DestroyTexture(item); 260 | mItems.Clear(); 261 | 262 | string data = EditorPrefs.GetString(saveKey, ""); 263 | //data = "";//For test 264 | if (string.IsNullOrEmpty(data)) 265 | { 266 | Reset(); 267 | } 268 | else 269 | { 270 | if (string.IsNullOrEmpty(data)) return; 271 | string[] guids = data.Split('|'); 272 | foreach (string s in guids) AddGUID(s, -1); 273 | RectivateLights(); 274 | } 275 | } 276 | 277 | void DestroyTexture (Item item) 278 | { 279 | if (item != null && item.dynamicTex && item.tex != null) 280 | { 281 | DestroyImmediate(item.tex); 282 | item.dynamicTex = false; 283 | item.tex = null; 284 | } 285 | } 286 | 287 | void UpdateVisual () 288 | { 289 | if (draggedObjects == null) DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; 290 | else if (draggedObjectIsOurs) DragAndDrop.visualMode = DragAndDropVisualMode.Move; 291 | else DragAndDrop.visualMode = DragAndDropVisualMode.Copy; 292 | } 293 | 294 | Item CreateItemByPath (string path) 295 | { 296 | if (!string.IsNullOrEmpty(path)) 297 | { 298 | path = FileUtil.GetProjectRelativePath(path); 299 | string guid = AssetDatabase.AssetPathToGUID(path); 300 | 301 | if (!string.IsNullOrEmpty(guid)) 302 | { 303 | GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; 304 | Item ent = new Item(); 305 | ent.prefab = go; 306 | ent.guid = guid; 307 | GeneratePreview(ent); 308 | return ent; 309 | } 310 | else Debug.Log("No GUID"); 311 | } 312 | return null; 313 | } 314 | 315 | void GeneratePreview (Item item, bool isReCreate = true) 316 | { 317 | if (item == null || item.prefab == null) return; 318 | { 319 | string preview_path = Configure.ResAssetsPath + "/Preview/" + item.prefab.name + ".png"; 320 | if (!isReCreate && File.Exists(preview_path)) 321 | { 322 | Texture texture = UIEditorHelper.LoadTextureInLocal(preview_path); 323 | item.tex = texture; 324 | } 325 | else 326 | { 327 | Texture Tex = UIEditorHelper.GetAssetPreview(item.prefab); 328 | if (Tex != null) 329 | { 330 | DestroyTexture(item); 331 | item.tex = Tex; 332 | UIEditorHelper.SaveTextureToPNG(Tex, preview_path); 333 | } 334 | } 335 | item.dynamicTex = false; 336 | return; 337 | } 338 | } 339 | 340 | static Transform FindChild (Transform t, string startsWith) 341 | { 342 | if (t.name.StartsWith(startsWith)) return t; 343 | 344 | for (int i = 0, imax = t.childCount; i < imax; ++i) 345 | { 346 | Transform ch = FindChild(t.GetChild(i), startsWith); 347 | if (ch != null) return ch; 348 | } 349 | return null; 350 | } 351 | 352 | static BetterList mLights; 353 | 354 | static void RectivateLights () 355 | { 356 | if (mLights != null) 357 | { 358 | for (int i = 0; i < mLights.size; ++i) 359 | mLights[i].enabled = true; 360 | mLights = null; 361 | } 362 | } 363 | 364 | int GetCellUnderMouse (int spacingX, int spacingY) 365 | { 366 | Vector2 pos = Event.current.mousePosition + mPos; 367 | 368 | int topPadding = 24; 369 | int x = cellPadding, y = cellPadding + topPadding; 370 | if (pos.y < y) return -1; 371 | 372 | float width = Screen.width - cellPadding + mPos.x; 373 | float height = Screen.height - cellPadding + mPos.y; 374 | int index = 0; 375 | 376 | for (; ; ++index) 377 | { 378 | Rect rect = new Rect(x, y, spacingX, spacingY); 379 | if (rect.Contains(pos)) break; 380 | 381 | x += spacingX; 382 | 383 | if (x + spacingX > width) 384 | { 385 | if (pos.x > x) return -1; 386 | y += spacingY; 387 | x = cellPadding; 388 | if (y + spacingY > height) return -1; 389 | } 390 | } 391 | return index; 392 | } 393 | 394 | bool mReset = false; 395 | private List _selections = new List(); 396 | void OnGUI () 397 | { 398 | Event currentEvent = Event.current; 399 | EventType type = currentEvent.type; 400 | 401 | int x = cellPadding, y = cellPadding; 402 | int width = Screen.width - cellPadding; 403 | int spacingX = cellSize + cellPadding; 404 | int spacingY = spacingX; 405 | if (mMode == Mode.DetailedMode) spacingY += 32; 406 | 407 | GameObject[] draggeds = draggedObjects; 408 | bool isDragging = (draggeds != null); 409 | int indexUnderMouse = GetCellUnderMouse(spacingX, spacingY); 410 | 411 | if (isDragging) 412 | { 413 | foreach (var gameObject in draggeds) 414 | { 415 | var result = FindItem(gameObject); 416 | 417 | if (result != null) 418 | { 419 | _selections.Add(result); 420 | } 421 | } 422 | } 423 | 424 | string searchFilter = EditorPrefs.GetString("PrefabWin_SearchFilter", null); 425 | 426 | int newTab = mTab; 427 | 428 | GUILayout.BeginHorizontal(); 429 | if (GUILayout.Toggle(newTab == 0, "通用控件", "ButtonLeft")) newTab = 0; 430 | if (GUILayout.Toggle(newTab == 1, "其它模板", "ButtonRight")) newTab = 1; 431 | GUILayout.EndHorizontal(); 432 | 433 | if (mTab != newTab) 434 | { 435 | Save(); 436 | mTab = newTab; 437 | mReset = true; 438 | EditorPrefs.SetInt("PrefabWin Prefab Tab", mTab); 439 | Load(); 440 | } 441 | 442 | if (mReset && type == EventType.Repaint) 443 | { 444 | mReset = false; 445 | foreach (Item item in mItems) GeneratePreview(item, false); 446 | RectivateLights(); 447 | } 448 | 449 | bool eligibleToDrag = (currentEvent.mousePosition.y < Screen.height - 40); 450 | 451 | if (type == EventType.MouseDown) 452 | { 453 | mMouseIsInside = true; 454 | } 455 | else if (type == EventType.MouseDrag) 456 | { 457 | mMouseIsInside = true; 458 | 459 | if (indexUnderMouse != -1 && eligibleToDrag) 460 | { 461 | if (draggedObjectIsOurs) DragAndDrop.StartDrag("Prefab Tool"); 462 | currentEvent.Use(); 463 | } 464 | } 465 | else if (type == EventType.MouseUp) 466 | { 467 | DragAndDrop.PrepareStartDrag(); 468 | mMouseIsInside = false; 469 | Repaint(); 470 | } 471 | else if (type == EventType.DragUpdated) 472 | { 473 | mMouseIsInside = true; 474 | UpdateVisual(); 475 | currentEvent.Use(); 476 | } 477 | else if (type == EventType.DragPerform) 478 | { 479 | if (draggeds != null) 480 | { 481 | if (_selections != null) 482 | { 483 | foreach (var selection in _selections) 484 | { 485 | DestroyTexture(selection); 486 | mItems.Remove(selection); 487 | } 488 | } 489 | 490 | foreach (var dragged in draggeds) 491 | { 492 | AddItem(dragged, indexUnderMouse); 493 | ++indexUnderMouse; 494 | } 495 | 496 | draggeds = null; 497 | } 498 | mMouseIsInside = false; 499 | currentEvent.Use(); 500 | } 501 | else if (type == EventType.DragExited || type == EventType.Ignore) 502 | { 503 | mMouseIsInside = false; 504 | } 505 | 506 | if (!mMouseIsInside) 507 | { 508 | _selections.Clear(); 509 | draggeds = null; 510 | } 511 | 512 | BetterList indices = new BetterList(); 513 | for (int i = 0; i < mItems.size; ) 514 | { 515 | if (draggeds != null && indices.size == indexUnderMouse) 516 | indices.Add(-1); 517 | 518 | var has = _selections.Exists(item => item == mItems[i]); 519 | 520 | if (!has) 521 | { 522 | if (string.IsNullOrEmpty(searchFilter) || 523 | mItems[i].prefab.name.IndexOf(searchFilter, System.StringComparison.CurrentCultureIgnoreCase) != -1) 524 | indices.Add(i); 525 | } 526 | 527 | ++i; 528 | } 529 | 530 | if (!indices.Contains(-1)) indices.Add(-1); 531 | 532 | if (eligibleToDrag && type == EventType.MouseDown && indexUnderMouse > -1) 533 | { 534 | GUIUtility.keyboardControl = 0; 535 | 536 | if (currentEvent.button == 0 && indexUnderMouse < indices.size) 537 | { 538 | int index = indices[indexUnderMouse]; 539 | 540 | if (index != -1 && index < mItems.size) 541 | { 542 | _selections.Add(mItems[index]); 543 | draggedObjects = _selections.Select(item => item.prefab).ToArray(); 544 | draggeds = _selections.Select(item=>item.prefab).ToArray(); 545 | currentEvent.Use(); 546 | } 547 | } 548 | } 549 | 550 | mPos = EditorGUILayout.BeginScrollView(mPos); 551 | { 552 | Color normal = new Color(1f, 1f, 1f, 0.5f); 553 | for (int i = 0; i < indices.size; ++i) 554 | { 555 | int index = indices[i]; 556 | Item ent = (index != -1) ? mItems[index] : _selections.Count == 0 ? null : _selections[0]; 557 | 558 | if (ent != null && ent.prefab == null) 559 | { 560 | mItems.RemoveAt(index); 561 | continue; 562 | } 563 | 564 | Rect rect = new Rect(x, y, cellSize, cellSize); 565 | Rect inner = rect; 566 | inner.xMin += 2f; 567 | inner.xMax -= 2f; 568 | inner.yMin += 2f; 569 | inner.yMax -= 2f; 570 | rect.yMax -= 1f; 571 | 572 | if (!isDragging && (mMode == Mode.CompactMode || (ent == null || ent.tex != null))) 573 | mContent.tooltip = (ent != null) ? ent.prefab.name : "Click to add"; 574 | else mContent.tooltip = ""; 575 | 576 | //if (ent == selection) 577 | { 578 | GUI.color = normal; 579 | U3DExtends.UIEditorHelper.DrawTiledTexture(inner, U3DExtends.UIEditorHelper.backdropTexture); 580 | } 581 | 582 | GUI.color = Color.white; 583 | GUI.backgroundColor = normal; 584 | 585 | if (GUI.Button(rect, mContent, "Button")) 586 | { 587 | if (ent == null || currentEvent.button == 0) 588 | { 589 | string path = EditorUtility.OpenFilePanel("Add a prefab", "", "prefab"); 590 | 591 | if (!string.IsNullOrEmpty(path)) 592 | { 593 | Item newEnt = CreateItemByPath(path); 594 | 595 | if (newEnt != null) 596 | { 597 | mItems.Add(newEnt); 598 | Save(); 599 | } 600 | } 601 | } 602 | else if (currentEvent.button == 1) 603 | { 604 | //ContextMenu.AddItem("Update Preview", false, UpdatePreView, index); 605 | ContextMenu.AddItemWithArge("Delete", false, RemoveItem, index); 606 | ContextMenu.Show(); 607 | } 608 | } 609 | 610 | string caption = (ent == null) ? "" : ent.prefab.name.Replace("Control - ", ""); 611 | 612 | if (ent != null) 613 | { 614 | if (ent.tex == null) 615 | { 616 | //texture may be destroy after exit game 617 | GeneratePreview(ent, false); 618 | } 619 | if (ent.tex != null) 620 | { 621 | GUI.DrawTexture(inner, ent.tex); 622 | var labelPos = new Rect(inner); 623 | var labelStyle = EditorStyles.label; 624 | labelPos.height = labelStyle.lineHeight; 625 | labelPos.y = inner.height - labelPos.height + 5; 626 | labelStyle.fontSize = (int) (_labelDefaultFontSize * SizePercent); 627 | labelStyle.alignment = TextAnchor.LowerCenter; 628 | { 629 | GUI.Label(labelPos, ent.prefab.name,labelStyle); 630 | } 631 | labelStyle.alignment = TextAnchor.UpperLeft; 632 | labelStyle.fontSize = _labelDefaultFontSize; 633 | } 634 | else if (mMode != Mode.DetailedMode) 635 | { 636 | GUI.Label(inner, caption, mStyle); 637 | caption = ""; 638 | } 639 | } 640 | else GUI.Label(inner, "Add", mStyle); 641 | 642 | if (mMode == Mode.DetailedMode) 643 | { 644 | GUI.backgroundColor = new Color(1f, 1f, 1f, 0.5f); 645 | GUI.contentColor = new Color(1f, 1f, 1f, 0.7f); 646 | GUI.Label(new Rect(rect.x, rect.y + rect.height, rect.width, 32f), caption, "ProgressBarBack"); 647 | GUI.contentColor = Color.white; 648 | GUI.backgroundColor = Color.white; 649 | } 650 | 651 | x += spacingX; 652 | 653 | if (x + spacingX > width) 654 | { 655 | y += spacingY; 656 | x = cellPadding; 657 | } 658 | } 659 | GUILayout.Space(y + spacingY); 660 | } 661 | EditorGUILayout.EndScrollView(); 662 | //if (mTab == 0) 663 | { 664 | //EditorGUILayout.BeginHorizontal(); 665 | //bool isCreateBackground = GUILayout.Button("背景"); 666 | //if (isCreateBackground) 667 | // EditorApplication.ExecuteMenuItem("UIEditor/创建/Background"); 668 | 669 | //bool isCreateDecorate = GUILayout.Button("参考图"); 670 | //if (isCreateDecorate) 671 | // EditorApplication.ExecuteMenuItem("UIEditor/创建/Decorate"); 672 | //EditorGUILayout.EndHorizontal(); 673 | } 674 | //else if (mTab != 0) 675 | { 676 | GUILayout.BeginHorizontal(); 677 | { 678 | string after = EditorGUILayout.TextField("", searchFilter, "SearchTextField", GUILayout.Width(Screen.width - 20f)); 679 | 680 | if (GUILayout.Button("", "SearchCancelButton", GUILayout.Width(18f))) 681 | { 682 | after = ""; 683 | GUIUtility.keyboardControl = 0; 684 | } 685 | 686 | if (searchFilter != after) 687 | { 688 | EditorPrefs.SetString("PrefabWin_SearchFilter", after); 689 | searchFilter = after; 690 | } 691 | } 692 | GUILayout.EndHorizontal(); 693 | } 694 | 695 | SizePercent = EditorGUILayout.Slider(SizePercent, 0, 1); 696 | } 697 | } 698 | } -------------------------------------------------------------------------------- /Editor/PrefabWin.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e66c7da2d12bfc542bd704e5eadb7679 3 | timeCreated: 1520497408 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/SceneEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using UnityEngine.UI; 4 | 5 | namespace U3DExtends { 6 | public class SceneEditor { 7 | 8 | static Object LastSelectObj = null;//用来记录上次选中的GameObject,只有它带有Image组件时才把图片赋值给它 9 | static Object CurSelectObj = null; 10 | [InitializeOnLoadMethod] 11 | static void Init() 12 | { 13 | SceneView.onSceneGUIDelegate += OnSceneGUI; 14 | 15 | //选中Image节点并点击图片后即帮它赋上图片 16 | if (Configure.IsEnableFastSelectImage) 17 | Selection.selectionChanged += OnSelectChange; 18 | } 19 | 20 | static void OnSelectChange() 21 | { 22 | LastSelectObj = CurSelectObj; 23 | CurSelectObj = Selection.activeObject; 24 | //如果要遍历目录,修改为SelectionMode.DeepAssets 25 | UnityEngine.Object[] arr = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.TopLevel); 26 | if (arr != null && arr.Length > 0) 27 | { 28 | GameObject selectObj = LastSelectObj as GameObject; 29 | if (selectObj != null && (arr[0] is Sprite || arr[0] is Texture2D)) 30 | { 31 | string assetPath = AssetDatabase.GetAssetPath(arr[0]); 32 | Image image = selectObj.GetComponent(); 33 | bool isImgWidget = false; 34 | if (image != null) 35 | { 36 | isImgWidget = true; 37 | UIEditorHelper.SetImageByPath(assetPath, image, Configure.IsAutoSizeOnFastSelectImg); 38 | } 39 | if (isImgWidget) 40 | { 41 | //赋完图后把焦点还给Image节点 42 | EditorApplication.delayCall = delegate 43 | { 44 | Selection.activeGameObject = LastSelectObj as GameObject; 45 | }; 46 | } 47 | } 48 | } 49 | } 50 | 51 | static void OnSceneGUI(SceneView sceneView) 52 | { 53 | Event e = Event.current; 54 | bool is_handled = false; 55 | if (Configure.IsEnableDragUIToScene && (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform)) 56 | { 57 | //拉UI prefab或者图片入scene界面时帮它找到鼠标下的Canvas并挂在其上,若鼠标下没有画布就创建一个 58 | Object handleObj = DragAndDrop.objectReferences[0]; 59 | if (!IsNeedHandleAsset(handleObj)) 60 | { 61 | //让系统自己处理 62 | return; 63 | } 64 | DragAndDrop.visualMode = DragAndDropVisualMode.Copy; 65 | 66 | //当松开鼠标时 67 | if (Event.current.type == EventType.DragPerform) 68 | { 69 | DragAndDrop.AcceptDrag(); 70 | foreach (var item in DragAndDrop.objectReferences) 71 | { 72 | HandleDragAsset(sceneView, item); 73 | } 74 | } 75 | is_handled = true; 76 | } 77 | else if (e.type == EventType.KeyDown && Configure.IsMoveNodeByArrowKey) 78 | { 79 | //按上按下要移动节点,因为默认情况下只是移动Scene界面而已 80 | foreach (var item in Selection.transforms) 81 | { 82 | Transform trans = item; 83 | if (trans != null) 84 | { 85 | if (e.keyCode == KeyCode.UpArrow) 86 | { 87 | Vector3 newPos = new Vector3(trans.localPosition.x, trans.localPosition.y + 1, trans.localPosition.z); 88 | trans.localPosition = newPos; 89 | is_handled = true; 90 | } 91 | else if (e.keyCode == KeyCode.DownArrow) 92 | { 93 | Vector3 newPos = new Vector3(trans.localPosition.x, trans.localPosition.y - 1, trans.localPosition.z); 94 | trans.localPosition = newPos; 95 | is_handled = true; 96 | } 97 | else if (e.keyCode == KeyCode.LeftArrow) 98 | { 99 | Vector3 newPos = new Vector3(trans.localPosition.x - 1, trans.localPosition.y, trans.localPosition.z); 100 | trans.localPosition = newPos; 101 | is_handled = true; 102 | } 103 | else if (e.keyCode == KeyCode.RightArrow) 104 | { 105 | Vector3 newPos = new Vector3(trans.localPosition.x + 1, trans.localPosition.y, trans.localPosition.z); 106 | trans.localPosition = newPos; 107 | is_handled = true; 108 | } 109 | } 110 | } 111 | } 112 | else if (Event.current != null && Event.current.button == 1 && Event.current.type == EventType.MouseUp && Configure.IsShowSceneMenu) 113 | { 114 | if (Selection.gameObjects == null || Selection.gameObjects.Length==0 || Selection.gameObjects[0].transform is RectTransform) 115 | { 116 | ContextMenu.AddCommonItems(Selection.gameObjects); 117 | ContextMenu.Show(); 118 | is_handled = true; 119 | } 120 | } 121 | //else if (e.type == EventType.MouseMove)//show cur mouse pos 122 | //{ 123 | // Camera cam = sceneView.camera; 124 | // Vector3 mouse_abs_pos = e.mousePosition; 125 | // mouse_abs_pos.y = cam.pixelHeight - mouse_abs_pos.y; 126 | // mouse_abs_pos = sceneView.camera.ScreenToWorldPoint(mouse_abs_pos); 127 | // Debug.Log("mouse_abs_pos : " + mouse_abs_pos.ToString()); 128 | //} 129 | if (e!=null && Event.current.type == EventType.KeyUp && e.control && e.keyCode==KeyCode.E) 130 | LayoutInfo.IsShowLayoutName = !LayoutInfo.IsShowLayoutName; 131 | if (is_handled) 132 | Event.current.Use(); 133 | } 134 | 135 | static bool HandleDragAsset(SceneView sceneView, Object handleObj) 136 | { 137 | Event e = Event.current; 138 | Camera cam = sceneView.camera; 139 | Vector3 mouse_abs_pos = e.mousePosition; 140 | mouse_abs_pos.y = cam.pixelHeight - mouse_abs_pos.y; 141 | mouse_abs_pos = sceneView.camera.ScreenToWorldPoint(mouse_abs_pos); 142 | if (handleObj.GetType() == typeof(Sprite) || handleObj.GetType() == typeof(Texture2D)) 143 | { 144 | GameObject box = new GameObject("Image_1", typeof(Image)); 145 | Undo.RegisterCreatedObjectUndo(box, "create image on drag pic"); 146 | box.transform.position = mouse_abs_pos; 147 | Transform container_trans = UIEditorHelper.GetContainerUnderMouse(mouse_abs_pos, box); 148 | if (container_trans == null) 149 | { 150 | //没有容器的话就创建一个 151 | container_trans = NewLayoutAndEventSys(mouse_abs_pos); 152 | } 153 | box.transform.SetParent(container_trans); 154 | mouse_abs_pos.z = container_trans.position.z; 155 | box.transform.position = mouse_abs_pos; 156 | box.transform.localScale = Vector3.one; 157 | Selection.activeGameObject = box; 158 | 159 | //生成唯一的节点名字 160 | box.name = CommonHelper.GenerateUniqueName(container_trans.gameObject, handleObj.name); 161 | //赋上图片 162 | Image imageBoxCom = box.GetComponent(); 163 | if (imageBoxCom != null) 164 | { 165 | imageBoxCom.raycastTarget = false; 166 | string assetPath = AssetDatabase.GetAssetPath(handleObj); 167 | UIEditorHelper.SetImageByPath(assetPath, imageBoxCom); 168 | return true; 169 | } 170 | } 171 | else 172 | { 173 | GameObject new_obj = GameObject.Instantiate(handleObj) as GameObject; 174 | if (new_obj != null) 175 | { 176 | Undo.RegisterCreatedObjectUndo(new_obj, "create obj on drag prefab"); 177 | new_obj.transform.position = mouse_abs_pos; 178 | GameObject ignore_obj = new_obj; 179 | 180 | Transform container_trans = UIEditorHelper.GetContainerUnderMouse(mouse_abs_pos, ignore_obj); 181 | if (container_trans == null) 182 | { 183 | container_trans = NewLayoutAndEventSys(mouse_abs_pos); 184 | } 185 | new_obj.transform.SetParent(container_trans); 186 | mouse_abs_pos.z = container_trans.position.z; 187 | new_obj.transform.position = mouse_abs_pos; 188 | new_obj.transform.localScale = Vector3.one; 189 | Selection.activeGameObject = new_obj; 190 | //生成唯一的节点名字 191 | new_obj.name = CommonHelper.GenerateUniqueName(container_trans.gameObject, handleObj.name); 192 | return true; 193 | } 194 | } 195 | return false; 196 | } 197 | 198 | private static Transform NewLayoutAndEventSys(Vector3 pos) 199 | { 200 | GameObject layout = UIEditorHelper.CreatNewLayout(); 201 | pos.z = 0; 202 | layout.transform.position = pos; 203 | Vector3 last_pos = layout.transform.localPosition; 204 | last_pos.z = 0; 205 | layout.transform.localPosition = last_pos; 206 | return UIEditorHelper.GetRootLayout(layout.transform); 207 | } 208 | 209 | static bool IsNeedHandleAsset(Object obj) 210 | { 211 | if (obj.GetType() == typeof(Sprite) || obj.GetType() == typeof(Texture2D)) 212 | return true; 213 | else 214 | { 215 | GameObject gameObj = obj as GameObject; 216 | if (gameObj != null) 217 | { 218 | RectTransform uiBase = gameObj.GetComponent(); 219 | if (uiBase != null) 220 | { 221 | return true; 222 | } 223 | } 224 | } 225 | return false; 226 | } 227 | } 228 | 229 | } -------------------------------------------------------------------------------- /Editor/SceneEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6e1848eaeb790a40ac86902a07a81ff 3 | timeCreated: 1520992026 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UGUI开发流程 2 | 用法:下载并解压文件夹放到你项目Assets文件夹里就可以了(文件夹命名为UGUI-Editor,如果想用其它名字还需修改下Configure.cs文件里FolderName字段,否则会报错找不到资源的).也可以用git subtree作为子库管理,如: 3 | git subtree add --prefix=Assets/UGUI-Editor https://github.com/liuhaopen/UGUI-Editor.git master --squash 4 | 默认大部分功能都是开启的,如果你觉得哪些用得不顺手,可以在Configure.cs文件里关闭相应功能,设为false保存一下就会立即生效的: 5 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/configure.png) 6 | 7 | ## PrefabWin窗口 8 | 一般一些通用常用的资源我们都会做成prefab,比如某些按钮,文本样式等,然后用到时就在Project视图把它拉入场景,但project视图看不到prefab的预览图,都是蓝色的方块比较难辩认,所以可以用PrefabWin这个窗口拉控件出去,拉到场景时会判断控件落在哪个Canvas上,有则挂其上,无则自动生成一个Canvas,然后右键保存为一个界面prefab: 9 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/prefab_win.gif) 10 | PrefabWin窗口可以从菜单Window->PrefabWin打开 11 | PrefabWin窗口一开始时肯定是没东西的啦,你可以往里面拉prefab,然后它会自动生成预览图的.2D3D的prefab都可以.底下还有个搜索框可以让你快速过滤. 12 | 13 | ## 参考图 14 | 一般做界面,我们就按美术出的图,那里拉个按钮,那里弄个文本,其坐标大小比例等肯定要严格按美术出的图来调节的,所以编辑界面时最好就有张参考图,添加方式如下: 15 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/consult_pic.gif) 16 | 参考图的资源可来自项目外的目录,且右键保存界面时会跳过它的.另外就是每当你保存该界面,UIEditor会保存该界面上的所有参考图的图片和大小坐标等信息,下次打开时会自动加载. 17 | 添加参考图后,可以选中它后右键菜单->锁定,这样就不会碍着你了. 18 | 对了,顺便说下选中节点后可以用方向键调节节点的坐标,每次加减1. 19 | 20 | ## 拉图生成Image节点 21 | 在Project视图拉图到场景的Canvas(无则自动生成Canvas),将生成一Image节点并把图赋在其上.还有就是选中Image节点时再点击Project视图里的图片也可以赋上该图. 22 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/drag_pic.gif) 23 | 24 | ## 优化层次 25 | 下图有8个节点,其中4个图片中,有两个图片来自图集1,两个来自图集2,如果它们是按图集连续排的话就可以合为同一批次,但被其它图集打断就合不了了,另外4个text也是一样,同一字体的也是可以合为一批次的,这个功能就是自动排列好顺序优化合批: 26 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/optimize_depth_for_batch_draw.gif) 27 | 28 | ## 查找资源引用 29 | 有时一些旧资源想删而不敢删,怕其它地方用到了,这时可以在Project视图右键菜单查找整个项目里有哪些prefab用到了该资源: 30 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/find_references.gif) 31 | 32 | ## 打开整个文件夹里的prefab界面 33 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/open_folder.gif) 34 | 35 | ## 合并组和解体 36 | 有时需要把几个节点合成一个组,这时可以这样: 37 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/make_group.gif) 38 | 39 | ## 排列和清理所有界面 40 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/sort_and_clean.gif) 41 | 42 | ## 对齐工具 43 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/align_menu.png) 44 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/align_tool.gif) 45 | 46 | ## 运行时修改防重置 47 | 运行时可以放心地修改并保存prefab,结束运行时也会重新加载到最新的(默认情况下unity结束运行后是会重置到运行前的状态的) 48 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/reload_after_exit.gif) 49 | 50 | ## 显示界面名 51 | 按Ctrl+E切换显示或隐藏所有的界面名 52 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/show_layout_name.gif) 53 | 54 | ## 其它功能 55 | )运行结束后重新加载所有正在编辑的界面(因为运行期间的修改在运行结束后会重置的) 56 | )运行时打开的界面,退出运行时Unity会自动清掉的,所以我们要记录下来,退出时重新打开 57 | )记录每个界面的参考图信息(现在每次重新打开界面都要添加一次参考图太麻烦了) 58 | )增加右键菜单:优化层级,文字放一起,同一图集的放一起 59 | )按ctrl+shift+c可以复制选中的节点名到剪切板上,生成的字符串是带路径的: 60 | ![image](https://github.com/liuhaopen/ReadmeResources/blob/master/UGUI-Editor/copy_nodes.png) 61 | 62 | ## TODO 63 | )界面优化大全:选中某界面后遍历其所有子节点并在一个window列出优化建议(比如Text别用bestfix,用到了其它图集的小图等等) 64 | )支持大部分操作的Undo(在操作前用Undo这个工具类记录)(30%) 65 | )右键显示颜色框(有时代码要设置颜色值可以用的) 66 | )Hierarchy界面也要显示我们的右键菜单 67 | 68 | 69 | -------------------------------------------------------------------------------- /Res/Canvas.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1000012425676670} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1000012425676670 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 5 20 | m_Component: 21 | - component: {fileID: 224000013583462848} 22 | - component: {fileID: 223000013305222972} 23 | - component: {fileID: 114000012489276102} 24 | - component: {fileID: 114000010874591504} 25 | - component: {fileID: 114400660693437478} 26 | - component: {fileID: 114705056615287920} 27 | m_Layer: 0 28 | m_Name: Canvas 29 | m_TagString: Untagged 30 | m_Icon: {fileID: 0} 31 | m_NavMeshLayer: 0 32 | m_StaticEditorFlags: 0 33 | m_IsActive: 1 34 | --- !u!1 &1000014158726874 35 | GameObject: 36 | m_ObjectHideFlags: 0 37 | m_PrefabParentObject: {fileID: 0} 38 | m_PrefabInternal: {fileID: 100100000} 39 | serializedVersion: 5 40 | m_Component: 41 | - component: {fileID: 224000011600320094} 42 | m_Layer: 0 43 | m_Name: Layout 44 | m_TagString: Untagged 45 | m_Icon: {fileID: 0} 46 | m_NavMeshLayer: 0 47 | m_StaticEditorFlags: 0 48 | m_IsActive: 1 49 | --- !u!1 &1209341097156808 50 | GameObject: 51 | m_ObjectHideFlags: 0 52 | m_PrefabParentObject: {fileID: 0} 53 | m_PrefabInternal: {fileID: 100100000} 54 | serializedVersion: 5 55 | m_Component: 56 | - component: {fileID: 224128275975077248} 57 | - component: {fileID: 222172484744153876} 58 | - component: {fileID: 114504790434209034} 59 | m_Layer: 0 60 | m_Name: ViewName 61 | m_TagString: Untagged 62 | m_Icon: {fileID: 0} 63 | m_NavMeshLayer: 0 64 | m_StaticEditorFlags: 0 65 | m_IsActive: 1 66 | --- !u!114 &114000010874591504 67 | MonoBehaviour: 68 | m_ObjectHideFlags: 1 69 | m_PrefabParentObject: {fileID: 0} 70 | m_PrefabInternal: {fileID: 100100000} 71 | m_GameObject: {fileID: 1000012425676670} 72 | m_Enabled: 1 73 | m_EditorHideFlags: 0 74 | m_Script: {fileID: 1301386320, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 75 | m_Name: 76 | m_EditorClassIdentifier: 77 | m_IgnoreReversedGraphics: 1 78 | m_BlockingObjects: 0 79 | m_BlockingMask: 80 | serializedVersion: 2 81 | m_Bits: 4294967295 82 | --- !u!114 &114000012489276102 83 | MonoBehaviour: 84 | m_ObjectHideFlags: 1 85 | m_PrefabParentObject: {fileID: 0} 86 | m_PrefabInternal: {fileID: 100100000} 87 | m_GameObject: {fileID: 1000012425676670} 88 | m_Enabled: 1 89 | m_EditorHideFlags: 0 90 | m_Script: {fileID: 1980459831, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 91 | m_Name: 92 | m_EditorClassIdentifier: 93 | m_UiScaleMode: 0 94 | m_ReferencePixelsPerUnit: 100 95 | m_ScaleFactor: 1 96 | m_ReferenceResolution: {x: 800, y: 600} 97 | m_ScreenMatchMode: 0 98 | m_MatchWidthOrHeight: 0 99 | m_PhysicalUnit: 3 100 | m_FallbackScreenDPI: 96 101 | m_DefaultSpriteDPI: 96 102 | m_DynamicPixelsPerUnit: 1 103 | --- !u!114 &114400660693437478 104 | MonoBehaviour: 105 | m_ObjectHideFlags: 1 106 | m_PrefabParentObject: {fileID: 0} 107 | m_PrefabInternal: {fileID: 100100000} 108 | m_GameObject: {fileID: 1000012425676670} 109 | m_Enabled: 1 110 | m_EditorHideFlags: 0 111 | m_Script: {fileID: 11500000, guid: 85543a2dadd029c42a60f1b3eb371827, type: 3} 112 | m_Name: 113 | m_EditorClassIdentifier: 114 | _layoutPath: 115 | --- !u!114 &114504790434209034 116 | MonoBehaviour: 117 | m_ObjectHideFlags: 1 118 | m_PrefabParentObject: {fileID: 0} 119 | m_PrefabInternal: {fileID: 100100000} 120 | m_GameObject: {fileID: 1209341097156808} 121 | m_Enabled: 1 122 | m_EditorHideFlags: 0 123 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 124 | m_Name: 125 | m_EditorClassIdentifier: 126 | m_Material: {fileID: 0} 127 | m_Color: {r: 1, g: 0, b: 0, a: 1} 128 | m_RaycastTarget: 0 129 | m_OnCullStateChanged: 130 | m_PersistentCalls: 131 | m_Calls: [] 132 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 133 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 134 | m_FontData: 135 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 136 | m_FontSize: 200 137 | m_FontStyle: 1 138 | m_BestFit: 0 139 | m_MinSize: 1 140 | m_MaxSize: 200 141 | m_Alignment: 4 142 | m_AlignByGeometry: 0 143 | m_RichText: 1 144 | m_HorizontalOverflow: 0 145 | m_VerticalOverflow: 0 146 | m_LineSpacing: 1 147 | m_Text: 148 | --- !u!114 &114705056615287920 149 | MonoBehaviour: 150 | m_ObjectHideFlags: 1 151 | m_PrefabParentObject: {fileID: 0} 152 | m_PrefabInternal: {fileID: 100100000} 153 | m_GameObject: {fileID: 1000012425676670} 154 | m_Enabled: 1 155 | m_EditorHideFlags: 0 156 | m_Script: {fileID: 11500000, guid: 3e567ee5ac5ca9b4897ca6ca15bb480b, type: 3} 157 | m_Name: 158 | m_EditorClassIdentifier: 159 | --- !u!222 &222172484744153876 160 | CanvasRenderer: 161 | m_ObjectHideFlags: 1 162 | m_PrefabParentObject: {fileID: 0} 163 | m_PrefabInternal: {fileID: 100100000} 164 | m_GameObject: {fileID: 1209341097156808} 165 | --- !u!223 &223000013305222972 166 | Canvas: 167 | m_ObjectHideFlags: 1 168 | m_PrefabParentObject: {fileID: 0} 169 | m_PrefabInternal: {fileID: 100100000} 170 | m_GameObject: {fileID: 1000012425676670} 171 | m_Enabled: 1 172 | serializedVersion: 3 173 | m_RenderMode: 2 174 | m_Camera: {fileID: 0} 175 | m_PlaneDistance: 100 176 | m_PixelPerfect: 0 177 | m_ReceivesEvents: 1 178 | m_OverrideSorting: 0 179 | m_OverridePixelPerfect: 0 180 | m_SortingBucketNormalizedSize: 0 181 | m_AdditionalShaderChannelsFlag: 25 182 | m_SortingLayerID: 0 183 | m_SortingOrder: 0 184 | m_TargetDisplay: 0 185 | --- !u!224 &224000011600320094 186 | RectTransform: 187 | m_ObjectHideFlags: 1 188 | m_PrefabParentObject: {fileID: 0} 189 | m_PrefabInternal: {fileID: 100100000} 190 | m_GameObject: {fileID: 1000014158726874} 191 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 192 | m_LocalPosition: {x: 0, y: 0, z: 0} 193 | m_LocalScale: {x: 1, y: 1, z: 1} 194 | m_Children: [] 195 | m_Father: {fileID: 224000013583462848} 196 | m_RootOrder: 0 197 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 198 | m_AnchorMin: {x: 0.5, y: 0.5} 199 | m_AnchorMax: {x: 0.5, y: 0.5} 200 | m_AnchoredPosition: {x: 0, y: 0} 201 | m_SizeDelta: {x: 1280, y: 720} 202 | m_Pivot: {x: 0.5, y: 0.5} 203 | --- !u!224 &224000013583462848 204 | RectTransform: 205 | m_ObjectHideFlags: 1 206 | m_PrefabParentObject: {fileID: 0} 207 | m_PrefabInternal: {fileID: 100100000} 208 | m_GameObject: {fileID: 1000012425676670} 209 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} 210 | m_LocalPosition: {x: 0, y: 0, z: 0} 211 | m_LocalScale: {x: 0.01, y: 0.01, z: 0.01} 212 | m_Children: 213 | - {fileID: 224000011600320094} 214 | - {fileID: 224128275975077248} 215 | m_Father: {fileID: 0} 216 | m_RootOrder: 0 217 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 218 | m_AnchorMin: {x: 0, y: 0} 219 | m_AnchorMax: {x: 0, y: 0} 220 | m_AnchoredPosition: {x: 640, y: 360.00018} 221 | m_SizeDelta: {x: 1280, y: 720} 222 | m_Pivot: {x: 0.5, y: 0.5} 223 | --- !u!224 &224128275975077248 224 | RectTransform: 225 | m_ObjectHideFlags: 1 226 | m_PrefabParentObject: {fileID: 0} 227 | m_PrefabInternal: {fileID: 100100000} 228 | m_GameObject: {fileID: 1209341097156808} 229 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 230 | m_LocalPosition: {x: 0, y: 0, z: 0} 231 | m_LocalScale: {x: 1, y: 1, z: 1} 232 | m_Children: [] 233 | m_Father: {fileID: 224000013583462848} 234 | m_RootOrder: 1 235 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 236 | m_AnchorMin: {x: 0, y: 0} 237 | m_AnchorMax: {x: 1, y: 1} 238 | m_AnchoredPosition: {x: 0, y: 0} 239 | m_SizeDelta: {x: 0, y: 0} 240 | m_Pivot: {x: 0.5, y: 0.5} 241 | -------------------------------------------------------------------------------- /Res/Canvas.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b4aa8357da4fe346be6400d0fb6db18 3 | timeCreated: 1520997281 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Res/Decorate.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1000010273970090} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1000010273970090 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 4 20 | m_Component: 21 | - 224: {fileID: 224000012746919202} 22 | - 222: {fileID: 222000010230785600} 23 | - 114: {fileID: 114000011828699144} 24 | - 114: {fileID: 114000013346449540} 25 | m_Layer: 0 26 | m_Name: Decorate 27 | m_TagString: Untagged 28 | m_Icon: {fileID: 0} 29 | m_NavMeshLayer: 0 30 | m_StaticEditorFlags: 0 31 | m_IsActive: 1 32 | --- !u!114 &114000011828699144 33 | MonoBehaviour: 34 | m_ObjectHideFlags: 1 35 | m_PrefabParentObject: {fileID: 0} 36 | m_PrefabInternal: {fileID: 100100000} 37 | m_GameObject: {fileID: 1000010273970090} 38 | m_Enabled: 1 39 | m_EditorHideFlags: 0 40 | m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 41 | m_Name: 42 | m_EditorClassIdentifier: 43 | m_Material: {fileID: 0} 44 | m_Color: {r: 1, g: 1, b: 1, a: 1} 45 | m_RaycastTarget: 0 46 | m_OnCullStateChanged: 47 | m_PersistentCalls: 48 | m_Calls: [] 49 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 50 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 51 | m_Sprite: {fileID: 0} 52 | m_Type: 0 53 | m_PreserveAspect: 0 54 | m_FillCenter: 1 55 | m_FillMethod: 4 56 | m_FillAmount: 1 57 | m_FillClockwise: 1 58 | m_FillOrigin: 0 59 | --- !u!114 &114000013346449540 60 | MonoBehaviour: 61 | m_ObjectHideFlags: 1 62 | m_PrefabParentObject: {fileID: 0} 63 | m_PrefabInternal: {fileID: 100100000} 64 | m_GameObject: {fileID: 1000010273970090} 65 | m_Enabled: 1 66 | m_EditorHideFlags: 0 67 | m_Script: {fileID: 11500000, guid: 533a76e193efebc48ab9061535a0b110, type: 3} 68 | m_Name: 69 | m_EditorClassIdentifier: 70 | _image: {fileID: 114000011828699144} 71 | --- !u!222 &222000010230785600 72 | CanvasRenderer: 73 | m_ObjectHideFlags: 1 74 | m_PrefabParentObject: {fileID: 0} 75 | m_PrefabInternal: {fileID: 100100000} 76 | m_GameObject: {fileID: 1000010273970090} 77 | --- !u!224 &224000012746919202 78 | RectTransform: 79 | m_ObjectHideFlags: 1 80 | m_PrefabParentObject: {fileID: 0} 81 | m_PrefabInternal: {fileID: 100100000} 82 | m_GameObject: {fileID: 1000010273970090} 83 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 84 | m_LocalPosition: {x: 0, y: 0, z: 0} 85 | m_LocalScale: {x: 1, y: 1, z: 1} 86 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 87 | m_Children: [] 88 | m_Father: {fileID: 0} 89 | m_RootOrder: 0 90 | m_AnchorMin: {x: 0.5, y: 0.5} 91 | m_AnchorMax: {x: 0.5, y: 0.5} 92 | m_AnchoredPosition: {x: 0, y: 0} 93 | m_SizeDelta: {x: 512, y: 350} 94 | m_Pivot: {x: 0.5, y: 0.5} 95 | -------------------------------------------------------------------------------- /Res/Decorate.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e8e5f354b1d4c94496bcc403bb5a7c9 3 | timeCreated: 1521105386 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Res/Preview.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfcb7ddf831f8ea41b26558196746d2b 3 | folderAsset: yes 4 | timeCreated: 1521256667 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Res/Preview/_.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep --------------------------------------------------------------------------------