On the other hand if you, as I, from time to time postpones the cleanup to another day, I've made a handy little editorwindow that should make the cleanup process a lot less tedious.
Basically it reads from the editor log after you have build a project, and then compares the included assets with all assets in your project. If an asset exist that is not used in the final build it is listed. It is also clickable so you can easily find it.
The unused assets are organized in 3 folders. "editor", "plugins" and "some other folder". This is helpful for me, as there is a lot in the "editor" folder that I use, but that is not used in final build.
It also lists which .dll's are included in build. If you need it, fine. If not, just delete that part.
DISCLAIMER: It does not locate unused assets in the "Ressources" folder, since everything in there should by default be included in the build. It's a tool to help locate the unused, but should be used with a bit of common sense.
[***UPDATE 2014***] By popular demand a GREATLY improved version of this tool has been released on the Unity Asset Store for 10 bucks. This old version is not stable as of Unity 4.6b20 and will not be updated
Place the following in the "editor" folder:
using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; public class CleanUpWindow : EditorWindow { bool groupEnabled = false; List<string> usedAssets = new List<string>(); List<string> includedDependencies = new List<string>(); private Vector2 scrollPos; private List<Object> unUsed; private Dictionary<string, List<Object>> unUsedArranged; private bool needToBuild = false; // Add menu named "CleanUpWindow" to the Window menu [MenuItem("Window/CleanUpWindow")] static void Init() { // Get existing open window or if none, make a new one: CleanUpWindow window = (CleanUpWindow)EditorWindow.GetWindow(typeof(CleanUpWindow)); window.Show(); } void OnGUI() { if (needToBuild) { GUI.color = Color.red; GUILayout.Label("Are you sure you remembered to build project? Because you really need to...", EditorStyles.boldLabel); } GUI.color = Color.white; if (GUILayout.Button("Load EditorLog")) { loadEditorLog(); } if(!needToBuild) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); if (groupEnabled) { GUILayout.Label("DEPENDENCIES"); for (int i = 0; i < includedDependencies.Count; i++) { EditorGUILayout.LabelField(i.ToString(), includedDependencies[i]); } } EditorGUILayout.EndVertical(); scrollPos = EditorGUILayout.BeginScrollView(scrollPos); EditorGUILayout.BeginVertical(); if (groupEnabled) { if (unUsedArranged != null) { foreach (KeyValuePair<string, List<Object>> objList in unUsedArranged) { if (objList.Value.Count >= 1) { GUILayout.Label(objList.Key.ToUpper()); for (int i = 0; i < objList.Value.Count; i++) { EditorGUILayout.ObjectField(objList.Value[i], typeof(Object), false); } } } } } EditorGUILayout.EndVertical(); EditorGUILayout.EndScrollView(); EditorGUILayout.EndHorizontal(); } } private void loadEditorLog() { UsedAssets.GetLists(ref usedAssets, ref includedDependencies); if (usedAssets.Count == 0) { needToBuild = true; } else { compareAssetList(UsedAssets.GetAllAssets()); groupEnabled = true; needToBuild = false; } } private void compareAssetList(string[] assetList) { unUsed = new List<Object>(); unUsedArranged = new Dictionary<string, List<Object>>(); unUsedArranged.Add("plugins", new List<Object>()); unUsedArranged.Add("editor", new List<Object>()); unUsedArranged.Add("some other folder", new List<Object>()); for (int i = 0; i < assetList.Length; i++) { if(!usedAssets.Contains(assetList[i])) { Object objToFind = AssetDatabase.LoadAssetAtPath(assetList[i], typeof(Object)); unUsed.Add(objToFind); if (objToFind != null) { unUsedArranged[getArrangedPos(objToFind)].Add(objToFind); } } } } private string getArrangedPos(Object value) { string path = AssetDatabase.GetAssetPath(value).ToLower(); if (path.Contains("/plugins/")) { return "plugins"; } else if (path.Contains("/editor/")) { return "editor"; } else { return "some other folder"; } } }You,ll need this too:
using UnityEngine; using UnityEditor; using System; using System.IO; using System.Collections; using System.Collections.Generic; public class UsedAssets { public static string[] GetAllAssets() { string[] tmpAssets1 = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories); string[] tmpAssets2 = Array.FindAll(tmpAssets1, name => !name.EndsWith(".meta")); string[] allAssets; allAssets = Array.FindAll(tmpAssets2, name => !name.EndsWith(".unity")); for (int i = 0; i < allAssets.Length; i++) { allAssets[i] = allAssets[i].Substring(allAssets[i].IndexOf("/Assets") + 1); allAssets[i] = allAssets[i].Replace(@"\", "/"); } return allAssets; } public static void GetLists(ref List<string> assetResult, ref List<string> dependencyResult) { assetResult.Clear(); dependencyResult.Clear(); string LocalAppData = string.Empty; string UnityEditorLogfile = string.Empty; if (Application.platform == RuntimePlatform.WindowsEditor) { LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); UnityEditorLogfile = LocalAppData + "\\Unity\\Editor\\Editor.log"; } else if (Application.platform == RuntimePlatform.OSXEditor) { LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.Personal); UnityEditorLogfile = LocalAppData + "/Library/Logs/Unity/Editor.log"; } try { // Have to use FileStream to get around sharing violations! FileStream FS = new FileStream(UnityEditorLogfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader SR = new StreamReader(FS); string line; while (!SR.EndOfStream && !(line = SR.ReadLine()).Contains("Mono dependencies included in the build")); while (!SR.EndOfStream && (line = SR.ReadLine()) != "") { dependencyResult.Add(line); } while (!SR.EndOfStream && !(line = SR.ReadLine()).Contains("Used Assets,")); while (!SR.EndOfStream && (line = SR.ReadLine()) != "") { line = line.Substring(line.IndexOf("% ") + 2); assetResult.Add(line); } } catch (Exception E) { Debug.LogError("Error: " + E); } } }Note that script files might not be shown as unused since the unity build includes then.
Here's an example: Unity Package
Thank you, this is certainly a great script to use. I don't have a co-worker, but I forget a lot of things I've put in there and now they are not used at all. I did some manual clean-ups but it is a long and boring task, so thank you for your automation script :)
ReplyDeleteNo problem, glad it's of help to anyone
ReplyDeleteAwesome!! Very useful for me. When I experiment my projects are a mess.
ReplyDeleteFor Mac make sure to replace lines 29 and 30 of the 2nd script with:
string LocalAppData = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
string UnityEditorLogfile = LocalAppData + "/Library/Logs/Unity/Editor.log";
Also remember to call the 1st script "CleanUpWindow" and the 2nd script "UsedAssets'
Thats great stuff ozRocker, thanks. Did this on a PC and never thought about it could be different on a mac :) I'll modify the script, when i get the time, to check if its a pc or a mac and get correct path accordingly
DeleteI've modified the script so it tests which runtime environment it is in. I'll reupload the unitypackage in a few minutes.
DeleteKeep the corrections or ideas comming, so this script can become better. Dont be shy, participate :)
Thanks for this.
ReplyDeleteYoure welcome
Deleteboah.... how long did i wait for this ???? 7 Years now ... duhhh Unity Dev's are slow if it comes to User Wishes that are standard in other professional App's....
ReplyDeleteI'm curious WHEN Unity will build this in...
Many Many Thxxxx !!!!!
This is still what i like on Unity Community... that there help & share... normally this should do the Company !!!
awesome thank you !!!!
Hi MadMac
DeleteGlad you like it :) It's still a bit limited because it relies on the log output from the unity build process. But its still pretty useful. If you improve the scripts in any way plz let me know
Wow, this is great! I get these two warnings however. I'm just wondering if I've placed something in the wrong spot or haven't done something right.
ReplyDeleteAssets/Editor/CustomWindows/CleanUpWindow.cs(66,49): warning CS0618: `UnityEditor.EditorGUILayout.ObjectField(UnityEngine.Object, System.Type, params UnityEngine.GUILayoutOption[])' is obsolete: `Check the docs for the usage of the new parameter 'allowSceneObjects'.'
Assets/Editor/CustomWindows/CleanUpWindow.cs(21,23): warning CS0219: The variable `window' is assigned but its value is never used
Thanks!
Hi Nick
DeleteThx for informing me :)
The first issue is that there has been released new version of unity since I wrote this script which means some methods have been deprecated.
If you use this:
EditorGUILayout.ObjectField(objList.Value[i], typeof(Object), false);
instead of the old one, there should be no warning.
The other issue, is just me forgetting to add a line of code. It has no real implication, but if you add this:
window.Show() in the "Init()" method then Unity automatically shows the relevant window.
Hi - I'm confused on how to use the package - I've imported it and built my project, but nothing seems to get organized.
ReplyDeleteHi Mike. Do you have the Pro version of Unity. I believe that is a requirement.
DeleteJust tried this today with our incredibly large project, and got a memory allocation error. Any ideas as to what I can do to get around this? We're currently using version 3.5.6f4, and I would REALLY like to clean out our unused assets. Any more help would be greatly appreciated, thank you! Let me know if the error log would be of use and I'll post it.
ReplyDeleteHi Tim
DeleteIt's a bit hard saying anything profound without any more information. Perhaps take a look here: http://answers.unity3d.com/questions/13779/unity-out-of-memory.html
Or perhaps you should just try to debug using breakpoints to figure out where it goes wrong
Hey Krisitian, awesome stuff here. I extended your interface a bit to include more organizational categories, as well as delete methods.
ReplyDeletePic of extended editor in action:
http://i.imgur.com/X3GSO.png
gist with code:
https://gist.github.com/4076464
Hey. Thats great! Looking forward to taking a look at it. Thanks for sharing
DeleteOh wow! EVERYONE WHO WANTS TO USE THIS: Use this advanced/extended version of the interface, 'cause it'll save you lots of time. (Includes a delete button too! )
DeleteVery useful scripts!
ReplyDeleteThank you very much.
Thanks for this script, it looks very helpful. However it doesnt do anything for me. When I follow the instructions and make a build I do not get any window popup. I checked the console and nothing seems to be logged there either. I have unity pro and I am building for IOS. Is there a step I could be missing? I am on a mac and using unity 3.5.6 if that is relevant.
ReplyDeleteThanks again for publishing this.
Hi meandering
DeletePerhaps try to look in the menu "Window/CleanUpWindow".
Its located there. If its not, then you must have an compile error somewhere.
Also make sure the script is in the "editor" folder
DeleteSell this as a Tool on Unity Asset Store. Make some money :D
ReplyDeleteThank you!
ReplyDeleteI will give it a try.
Very, VERY nice. It's going to be a pain in my ass to delete hundreds of items, but this definitely helped. I concur with the asset store idea. You could make at least a little money off this.
ReplyDeletethis work per scene or per project?
ReplyDeleteHi Klarax. It looks at the build log, so its based on the included scenes in the build. This is something I wrote in the beginning of 2012, but actually just published a much better revisited version on the Unity Asset Store. It's currently awaiting approval. This version will stay online for free, but the 5$ improved version is vastly superior.
DeleteYou'll be able to find it here https://www.assetstore.unity3d.com/en/#!/publisher/5473 when released
Delete