├── .gitattributes
├── .gitignore
├── Documentation
└── APKInstaller.pdf
├── LICENSE
├── README.md
├── Scripts
└── Editor
│ ├── APKInstaller.cs
│ ├── AppDataUtility.cs
│ ├── AssetUtilities
│ ├── AssetDependencies.cs
│ ├── AssetUtilities.cs
│ ├── AssetsHistory.cs
│ ├── FindAssetUsages.cs
│ ├── FindMissingReferences.cs
│ ├── FindMissingScripts.cs
│ ├── FindUnusedAssets.cs
│ ├── HierarchyHistory.cs
│ └── HierarchyHistorySimple.cs
│ ├── Common
│ ├── EditorHelper.cs
│ └── MyGUI.cs
│ ├── ComponentUtilities.cs
│ ├── EditorUtilities.cs
│ ├── FileUtilities.cs
│ ├── HierarchyUtilities.cs
│ ├── InspectorExtensions.cs
│ ├── MyShortcuts.cs
│ ├── OpenScenes.cs
│ ├── PrefabUtilities.cs
│ ├── RectToolRounding.cs
│ └── UnityUtilities.Editor.asmdef
└── _config.yml
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | * eol=lf
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # =============== #
2 | # Unity generated #
3 | # =============== #
4 | [Tt]emp/
5 | [Oo]bj/
6 | [Bb]uild
7 | /[Bb]uilds/
8 | /[Ll]ibrary/
9 | AssetBundles/Android/
10 | AssetBundles/iOS/
11 | sysinfo.txt
12 | *.stackdump
13 | /Assets/AssetStoreTools*
14 | *.apk
15 |
16 | # Jetbrain Rider Cache
17 | .idea/
18 | Assets/Plugins/Editor/JetBrains*
19 |
20 |
21 | # ===================================== #
22 | # Visual Studio / MonoDevelop generated #
23 | # ===================================== #
24 | [Ee]xported[Oo]bj/
25 | .vs/
26 | /*.userprefs
27 | /*.csproj
28 | /*.pidb
29 | *.pidb.meta
30 | /*.suo
31 | /*.sln*
32 | /*.user
33 | .consulo/
34 | /*.tmp
35 | /*.svd
36 |
37 | # ============ #
38 | # OS generated #
39 | # ============ #
40 | .DS_Store*
41 | ._*
42 | .Spotlight-V100
43 | .Trashes
44 | ehthumbs.db
45 | [Tt]humbs.db
46 | [Tt]humbs.db.meta
47 | [Dd]esktop.ini
48 |
49 | # =============== #
50 | # EDM4U generated #
51 | # =============== #
52 | Assets/Plugins/Android/*.aar
53 | Assets/Plugins/Android/*.aar.meta
54 | Assets/Plugins/Android/*.jar
55 | Assets/Plugins/Android/*.jar.meta
56 |
57 | # ======= #
58 | # Managed #
59 | # ======= #
60 | IgnoreSCM/
61 | ExternalAssets/
62 | TextMesh Pro/
63 | *.unity
64 | *.meta
65 | Assets/IgnoreSCM/
66 | Assets/IgnoreSCM.meta
67 |
--------------------------------------------------------------------------------
/Documentation/APKInstaller.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EpsilonD3lta/UnityUtilities/268fc1ef689f4f0feb92bbbdc9ccd0232bd52f82/Documentation/APKInstaller.pdf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Martin Kovar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UnityCollection
2 | Moved scripts and assets that are suited to be checked in version control to separate repository:
3 | https://github.com/EpsilonD3lta/UnityCollection
4 |
5 | # UnityUtilities
6 | Collection of useful scripts for Unity. These scripts are mostly meant to not be version controlled.
7 |
8 | Repository contains scripts created by me or, if stated at the beginning of a script, from others (some of them I might have modified).
9 | Mind the licenses stated at the beginning of each script or method (those are not mine). Scripts/methods without license at the beginning are mine
10 | and are distributed under MIT license.
11 |
12 | Note that there might be bugs, use at your own risk.
13 |
14 | # Documentation
15 | ## [APKInstaller](Documentation/APKInstaller.pdf)
16 |
--------------------------------------------------------------------------------
/Scripts/Editor/APKInstaller.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using UnityEditor;
3 |
4 | namespace Tools
5 | {
6 | public static class APKInstaller
7 | {
8 | private static string adbPath = EditorPrefs.GetString("AndroidSdkRoot") + "/platform-tools/adb";
9 | //This tool can be used as an alternative way to analyze apk file.
10 | //private static string aaptPath = EditorPrefs.GetString("AndroidSdkRoot") + "/build-tools/*/aapt";
11 | private static string apkAnalyzerPath = EditorPrefs.GetString("AndroidSdkRoot") + "/cmdline-tools/latest/bin/apkanalyzer.bat";
12 |
13 | [MenuItem("File/Install APK", priority = 211)]
14 | public static void InstallAPK()
15 | {
16 | string[] filter = new string[] { "Android build", "apk" };
17 | string apkPath = EditorUtility.OpenFilePanelWithFilters("Choose .apk File", "", filter);
18 | if (string.IsNullOrEmpty(apkPath)) return;
19 | Install(apkPath);
20 | }
21 |
22 | [MenuItem("File/Install and run APK", priority = 211)]
23 | public static void InstallAndRunAPK()
24 | {
25 | string[] filter = new string[] { "Android build", "apk" };
26 | string apkPath = EditorUtility.OpenFilePanelWithFilters("Choose .apk File", "", filter);
27 | if (string.IsNullOrEmpty(apkPath)) return;
28 | Install(apkPath, true);
29 | }
30 |
31 | public static void Install(string apkPath, bool run = false)
32 | {
33 | ProcessStartInfo process = new ProcessStartInfo(adbPath, "install -r \"" + apkPath + "\"")
34 | {
35 | RedirectStandardOutput = true,
36 | RedirectStandardError = true,
37 | CreateNoWindow = true,
38 | UseShellExecute = false
39 | };
40 | var installProcess = Process.Start(process);
41 | EditorUtility.DisplayProgressBar("Installing APK", "Installing...", 0.5f);
42 | installProcess.WaitForExit();
43 |
44 | string result = "Result: " + installProcess.StandardOutput.ReadLine() + ": " + installProcess.StandardOutput.ReadToEnd();
45 | result += installProcess.StandardError.ReadToEnd();
46 |
47 | if (installProcess.ExitCode != 0)
48 | UnityEngine.Debug.LogError(result);
49 | else
50 | UnityEngine.Debug.Log(result);
51 | EditorUtility.ClearProgressBar();
52 | if (run) Run();
53 |
54 | //Use RunApk(...) instead of Run(), if package name in player settings does not match apk package name
55 | //You need to have Command line tools installed, see documentation
56 | //if (run) RunApk(apkPath);
57 | }
58 |
59 | public static void Run()
60 | {
61 | string appIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
62 | //This is the default Unity android launcher, however in my experience, it does not work properly. I use monkey launcher instead.
63 | //string mainActivity = "com.unity3d.player.UnityPlayerActivity";
64 | //string adbCommand = "shell am start -a android.intent.action.MAIN -n " + appIdentifier + "/" + mainActivity;
65 | string adbCommand = "shell monkey -p \"" + appIdentifier + "\" 1";
66 |
67 | var process = new ProcessStartInfo(adbPath, adbCommand)
68 | {
69 | RedirectStandardOutput = true,
70 | RedirectStandardError = true,
71 | CreateNoWindow = true,
72 | UseShellExecute = false
73 | };
74 | var runProcess = Process.Start(process);
75 | string result = "Running app " + appIdentifier + ": " + runProcess.StandardOutput.ReadLine() + ": " + runProcess.StandardOutput.ReadToEnd();
76 | result += runProcess.StandardError.ReadToEnd();
77 |
78 | if (runProcess.ExitCode != 0)
79 | UnityEngine.Debug.LogError(result);
80 | else
81 | UnityEngine.Debug.Log(result);
82 | }
83 |
84 | ///
85 | /// Use this method, if package name in player settings does not match apk package name
86 | /// You need to have Command line tools installed, see documentation
87 | ///
88 | ///
89 | public static void RunApk(string apkPath)
90 | {
91 | ProcessStartInfo process = new ProcessStartInfo(apkAnalyzerPath, "manifest application-id \"" + apkPath + "\"")
92 | {
93 | RedirectStandardOutput = true,
94 | RedirectStandardError = true,
95 | CreateNoWindow = true,
96 | UseShellExecute = false
97 | };
98 | var findIdentifierProcess = Process.Start(process);
99 | findIdentifierProcess.WaitForExit();
100 |
101 | string appIdentifier = findIdentifierProcess.StandardOutput.ReadLine();
102 | //string mainActivity = "com.unity3d.player.UnityPlayerActivity";
103 | //string adbCommand = "shell am start -a android.intent.action.MAIN -n " + appIdentifier + "/" + mainActivity;
104 | string adbCommand = "shell monkey -p \"" + appIdentifier + "\" 1";
105 |
106 | process = new ProcessStartInfo(adbPath, adbCommand)
107 | {
108 | RedirectStandardOutput = true,
109 | RedirectStandardError = true,
110 | CreateNoWindow = true,
111 | UseShellExecute = false
112 | };
113 | var runProcess = Process.Start(process);
114 | string result = "Running app (monkey) " + appIdentifier + ": " + runProcess.StandardOutput.ReadLine() + ": " + runProcess.StandardOutput.ReadToEnd();
115 | result += runProcess.StandardError.ReadToEnd();
116 |
117 | if (runProcess.ExitCode != 0)
118 | UnityEngine.Debug.LogError(result);
119 | else
120 | UnityEngine.Debug.Log(result);
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Scripts/Editor/AppDataUtility.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using UnityEditor;
4 | using UnityEngine;
5 | using Debug = UnityEngine.Debug;
6 |
7 | public class AppDataUtility : EditorWindow
8 | {
9 | [MenuItem("Tools/AppData Utility")]
10 | public static void ShowWindow()
11 | {
12 | Rect rect = new Rect(Screen.width / 2f, Screen.height / 2f, 220, 40);
13 | var window = GetWindow(title: "AppData Utility");
14 | window.position = rect;
15 | window.minSize = new Vector2(100, 40);
16 | window.Show();
17 | }
18 |
19 | private void OnGUI()
20 | {
21 | Event ev = Event.current;
22 | GUILayout.BeginHorizontal();
23 | #if UNITY_EDITOR_OSX
24 | EditorUtility.RevealInFinder(Application.persistentDataPath);
25 | #else
26 | if (GUILayout.Button(EditorGUIUtility.IconContent("FolderOpened Icon"),
27 | GUILayout.MaxWidth(40), GUILayout.MaxHeight(17)))
28 | {
29 | string appDataPath = Application.persistentDataPath;
30 | appDataPath = appDataPath.Replace('/', '\\');
31 | var storagePath = appDataPath + "\\" + "Storage";
32 | if (Directory.Exists(storagePath)) appDataPath = storagePath;
33 | appDataPath = "\"" + appDataPath + "\"";
34 | ProcessStartInfo process = new ProcessStartInfo("explorer.exe", appDataPath)
35 | {
36 | RedirectStandardOutput = true,
37 | RedirectStandardError = true,
38 | CreateNoWindow = true,
39 | UseShellExecute = false
40 | };
41 | Process.Start(process);
42 | }
43 | #endif
44 | GUIContent label = new GUIContent("AppData Path:", "Application.persistentDataPath");
45 | GUILayout.Label(label, EditorStyles.boldLabel);
46 | GUILayout.TextField(Application.persistentDataPath);
47 |
48 | GUILayout.EndHorizontal();
49 | GUILayout.BeginHorizontal();
50 |
51 | if (GUILayout.Button("Delete Contents"))
52 | {
53 | if (ev.modifiers == EventModifiers.Shift ||
54 | EditorUtility.DisplayDialog("AppData Utility", "Delete all files in the persistent data folder? This cannot be undone.", "Yes", "Cancel"))
55 | {
56 | var directoryInfo = new DirectoryInfo(Application.persistentDataPath);
57 |
58 | foreach (var file in directoryInfo.GetFiles())
59 | file.Delete();
60 | foreach (var dir in directoryInfo.GetDirectories())
61 | dir.Delete(true);
62 | Debug.LogWarning("[AppData Utility] All folder contents were deleted.");
63 | }
64 | }
65 |
66 | if (GUILayout.Button("Delete PlayerPrefs"))
67 | {
68 | if (ev.modifiers == EventModifiers.Shift ||
69 | EditorUtility.DisplayDialog("AppData Utility", "Delete all PlayerPrefs? This cannot be undone.", "Yes", "Cancel"))
70 | {
71 | PlayerPrefs.DeleteAll();
72 | Debug.LogWarning("AppData Utility: All PlayerPrefs were deleted.");
73 | }
74 | }
75 | if (GUILayout.Button("Load Backup"))
76 | {
77 | if (ev.modifiers == EventModifiers.Shift ||
78 | EditorUtility.DisplayDialog("AppData Utility", "Load Backup? This cannot be undone.", "Yes", "Cancel"))
79 | {
80 | var fromDir = Directory.GetParent(Application.persistentDataPath) + $"/{Application.productName}Backup";
81 | var toDir = Application.persistentDataPath;
82 | if (!Directory.Exists(fromDir)) Debug.LogError($"[AppData Utility] {fromDir} does not exist");
83 | else if (!Directory.Exists(toDir)) Debug.LogError($"[AppData Utility] {toDir} does not exist");
84 | else
85 | {
86 | CopyFilesRecursively(fromDir, toDir);
87 | Debug.Log("[AppData Utility] Backup loaded");
88 | }
89 | }
90 | }
91 | if (GUILayout.Button("Save Backup"))
92 | {
93 | if (EditorUtility.DisplayDialog("AppData Utility", "Save Backup? This cannot be undone.", "Yes", "Cancel"))
94 | {
95 | var fromDir = Application.persistentDataPath;
96 | var toDir = Directory.GetParent(Application.persistentDataPath) + $"/{Application.productName}Backup";
97 | if (!Directory.Exists(fromDir)) Debug.LogError($"[AppData Utility] {fromDir} does not exist");
98 | else
99 | {
100 | CopyFilesRecursively(fromDir, toDir);
101 | Debug.Log("[AppData Utility] Backup saved");
102 | }
103 | }
104 | }
105 | GUILayout.FlexibleSpace();
106 | GUILayout.EndHorizontal();
107 | }
108 |
109 | private static void CopyFilesRecursively(string sourcePath, string targetPath)
110 | {
111 | // Create all of the directories
112 | foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
113 | {
114 | Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
115 | }
116 |
117 | // Copy all the files & Replaces any files with the same name
118 | foreach (string newPath in Directory.GetFiles(sourcePath, "*", SearchOption.AllDirectories))
119 | {
120 | File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Scripts/Editor/AssetUtilities/AssetDependencies.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using UnityEditor;
6 | using UnityEngine;
7 | using Object = UnityEngine.Object;
8 | using static EditorHelper;
9 | using static MyGUI;
10 |
11 | public class AssetDependencies : MyEditorWindow, IHasCustomMenu
12 | {
13 | private const int rowHeight = objectRowHeight;
14 | private static class Styles
15 | {
16 | public static GUIStyle foldoutStyle = new GUIStyle();
17 | static Styles()
18 | {
19 | foldoutStyle = new GUIStyle(EditorStyles.miniPullDown);
20 | foldoutStyle.alignment = TextAnchor.MiddleLeft;
21 | foldoutStyle.padding = new RectOffset(19, 0, 0, 0);
22 | }
23 | }
24 |
25 | private static TreeViewComparer treeViewComparer;
26 |
27 | private bool initialized;
28 | private bool adjustSize;
29 | private Vector2 scroll = Vector2.zero;
30 | private float scrollViewRectHeight = 100;
31 |
32 | private bool showSelected = true;
33 | private bool showSameName = true; // name without file extension
34 | private bool isContainsName = false; // name without file extension
35 | private bool showUses = true;
36 | private bool isRecursive = false;
37 | private bool showUsedBy = true;
38 | private bool searchInScene = true;
39 | private bool showPackages = false;
40 | private bool isPackageRecursive = false;
41 | private bool searchAgain = true;
42 |
43 | private List selectedPaths = new();
44 |
45 | private List