- Home /
Change Default Script Folder
Upon creating a new script through the Unity Editor, it places that script in /Assets folder. I want to place that script in /Assets/Scripts folder instead.
Is there a property I can change to set the default folder for scripts created through the Unity Editor?
Edit: I just want to clarify my process of creating a new script: I'm adding the new script through the inspector via the "Add Component" button. When I create the script from there it seems to default to /Assets folder. Is there a way I can change that?
Did you ever figure this out? I would also like to change the default directory that Add Component puts new script files.
Answer by Bunny83 · Sep 06, 2015 at 01:36 AM
I just had a look at the code of the AddComponentWindow which is responsible for creating the new scripts. It has a nested class called "NewScriptElement" which handles the specifics about the script creation and actually draws the edit box for the script name and the drop down for the language selection. It actually has a private variable called "m_Directory" which is treated as a partial path after "/Assets/". However it's initialized with an empty string and there's no code that actually changes that variable.
So they might had in mind to provide a way to specify a path where the new script should be created, but they never implemented any kind of GUI / interface to set that directory.
Reflection saves the day, However it's more ugly than i thought. The problem is that the AddComponentWindow is recreated each time it's opened. Also it's internal tree of "Elements" which contains the "NewScriptElement" instance is also recreated each time the window is opened. Since it's a drop-down window you can't click anywhere outside the window since that would close the window. So the only solution is to actually "poll" if the window is currently open and if so we "inject" our desired path.
I've just written an editor script which does that. Of course the script has to be placed in an editor folder. To use it, just click "Tools/Default script path settings" in the main menu to bring up the settings menu window. The window provides you three things:
A toggle button where you can enable / disable the background scanning for the add component window
A text field where you can enter the relative path where you want your new scripts to be saved to. That path uses forward slashes. You shouldn't add leading or trailing slashes. So if you want to use "/Assets/Scripts/" as folder just type "Scripts" (without the quotes) into the text field.
An indicator which shows you whether the background check has successfully set the directory or not.
The window must be open to be able to work. However it can be docked in any view and even be hidden somewhere as long as it's open. The window also saves the state of the toggle button as well as the path you enter in the EditorPrefs so it's automatically restored when Unity recompiles or when you reopen Unity.
edit The new version of this script doesn't require to have the window open. The window is now just a "settings" menu where you can specify the path and enable / disable the background check.
//NewScriptDefaultPathWindow.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Linq;
using System.Reflection;
[InitializeOnLoad]
public class NewScriptDefaultPathWindow : EditorWindow
{
#region Reflection magic
static System.Type AddComponentWindowType = null;
static System.Type NewScriptElement = null;
static FieldInfo s_AddComponentWindow = null;
static FieldInfo m_Tree = null;
static FieldInfo m_Directory = null;
static EditorWindow m_CurrentWindow = null;
static object m_CurrentElement = null;
static NewScriptDefaultPathWindow()
{
var types = typeof(EditorWindow).Assembly.GetTypes();
AddComponentWindowType = types.Where(t => t.Name == "AddComponentWindow").FirstOrDefault();
s_AddComponentWindow = AddComponentWindowType.GetField("s_AddComponentWindow",BindingFlags.NonPublic | BindingFlags.Static);
m_Tree = AddComponentWindowType.GetField("m_Tree",BindingFlags.NonPublic | BindingFlags.Instance);
var nestedTypes = AddComponentWindowType.GetNestedTypes(BindingFlags.NonPublic);
NewScriptElement = nestedTypes.Where(t => t.Name == "NewScriptElement").FirstOrDefault();
m_Directory = NewScriptElement.GetField("m_Directory", BindingFlags.NonPublic | BindingFlags.Instance);
EditorApplication.update += BackgroundCheck;
}
static EditorWindow GetAddComponentWindow()
{
var inst = (EditorWindow)s_AddComponentWindow.GetValue(null);
return inst;
}
static object GetNewScriptElement()
{
var window = GetAddComponentWindow();
if (window == null)
{
m_CurrentWindow = null;
m_CurrentElement = null;
return null;
}
if (window == m_CurrentWindow)
{
return m_CurrentElement;
}
m_CurrentWindow = window;
System.Array a = (System.Array)m_Tree.GetValue(window);
var list = a.OfType<object>().ToArray();
for(int i = 0; i < list.Length; i++)
{
if (list[i].GetType() == NewScriptElement)
{
m_CurrentElement = list[i];
return m_CurrentElement;
}
}
return null;
}
static string Directory
{
get
{
var element = GetNewScriptElement();
if (element == null)
return "";
return (string)m_Directory.GetValue(element);
}
set
{
var element = GetNewScriptElement();
if (element == null)
return;
m_Directory.SetValue(element, value);
}
}
#endregion Reflection magic
[MenuItem("Tools/Default script path settings")]
static void Init ()
{
var win = CreateInstance<NewScriptDefaultPathWindow>();
win.ShowUtility();
}
static string dir = "";
static string currentDir = "";
static bool enableBackgroundCheck = false;
static int counter = 0;
static NewScriptDefaultPathWindow instance;
static bool initialized = false;
static void LoadSettings()
{
dir = EditorPrefs.GetString("NewScriptDefaultPath", "");
enableBackgroundCheck = EditorPrefs.GetBool("NewScriptDefaultPath_Enabled", false);
}
static void BackgroundCheck()
{
if (!initialized)
{
initialized = true;
LoadSettings();
}
if (enableBackgroundCheck)
{
// check only once a second
if (++counter > 100)
{
counter = 0;
Directory = dir;
if (instance != null)
{
string tmp = Directory;
if (tmp != currentDir)
{
currentDir = tmp;
instance.Repaint();
}
}
}
}
}
void OnEnable()
{
LoadSettings();
instance = this;
titleContent = new GUIContent("Edit default script path");
}
void OnDisable()
{
instance = null;
}
Color GetColor(bool aActive)
{
if (aActive)
return Color.green;
return Color.red;
}
void OnGUI ()
{
Color oldColor = GUI.color;
GUI.changed = false;
enableBackgroundCheck = GUILayout.Toggle(enableBackgroundCheck, "enable background check", "button");
GUILayout.BeginHorizontal();
GUILayout.Label("Default script save path:", GUILayout.Width(150));
dir = GUILayout.TextField(dir);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Background check is ");
GUI.color = GetColor(enableBackgroundCheck);
GUILayout.Label((enableBackgroundCheck ? "enabled" : "disabled"), "box");
GUI.color = oldColor;
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
if (GUI.changed)
{
EditorPrefs.SetString("NewScriptDefaultPath", dir);
EditorPrefs.SetBool("NewScriptDefaultPath_Enabled", enableBackgroundCheck);
}
if (enableBackgroundCheck && dir != "")
{
GUI.color = GetColor(currentDir == dir);
if (currentDir == dir)
GUILayout.Label("Directory successfully set", "box");
else
GUILayout.Label(" - Window currently not open - ", "box");
GUI.color = oldColor;
}
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if(GUILayout.Button("Close"))
{
Close();
}
GUILayout.Space(15);
GUILayout.EndHorizontal();
GUILayout.Space(15);
}
}
Note: It's just a quick and dirty solution. We can be happy that they implemented the m_Directory field and actually use it internally. Without that it would be near to impossible to change anything since the path would be hardcoded. The only way would be to decompile the UnityEditor.dll, edit the desired parts and recompile it completely.
Also Note: This class uses reflection to get access to several internal classes and fields. Internal things can change at any time in the future whenever you update Unity. So it might no longer work in a distant future.
@eshan.mathur:
Since you asked the duplicate question over here, i thought you might be interested in this.
Personally I never use that AddComponentWindow. I always create scripts by right clicking the appropriate folder and select Create -> C# Script. So i actually don't have any use for my tool ^^.
ps: The window will check if the AddComponentWindow is open every second. First i thought a quicker response would be required but it takes way longer to click through the menus and actually type a script name. Probably every 5 seconds would be enough ^^. You can change that by editing the "100" in the script. (500 == 5 seconds)
This is pretty great. Thanks for writing this! I'll try it out after the weekend.
@eshan.mathur:
To be honest it's actually pretty ugly ^^. Also it's current usage is quite cumbersome as the window has to be open all the time. It might be possible to use the "InitializeOnLoad" attribute and hook up the update delegate inside the static constructor. That way the routine that does the magic would run automatically in the background.
I'll see if that works ^^.
edit
Just tested it and yes, it works ^^. So it's improving... I'll update my answer once i finished cleaning up the code.
Awesome. Do you have a Github repo for this?
@eshan.mathur:
Yes, i have updated the answer but sortly after i posted the comment above ^^.
No, i don't have a git rep. for this. I just wrote it from scratch and posted it here ^^. At the moment i'm not at home. If i find the time i might create a reopsitory for it. However you are free to use it.
Why not just change it with ILSpy in the UnityEditor.dll at UnityEditor -> AddComponentWindow -> NewScriptElement ->
private string TargetDir(){
return Path.Combine("Assets/Scripts", this.m_Directory.Trim(this.kPathSepChars));}
Then save and overwrite in the Unity folder :D You just have to make sure that Scripts folder exist before using it.
@Positive7:
As you can see at the bottom of my answer i already mentioned decompiling the dll, modify and recompile it. However this is never a good solution. First of all you can't be sure that ILSpy decompiled everything "correctly". I've often had some DLLs where ILSpy failed to decompile certain things. It's more reliable to use IL as language.
Patching those DLLs is most likely against the EULA, however for private use probably nobody really cares. Redistribution of those DLLs (patched or not) most likely isn't allowed either.
A reflection based approach is usually the best and should work with new versions out of the box. Also the placement of scripts might depend on the project, so having a hardcoded path again makes only little sense to me.
Answer by JoshuaMcKenzie · Jul 09, 2016 at 04:19 PM
So I came here because I had the same question, if there was a setting to specify a default location for new scripts when adding via the Add Component in the inspector button.
Since it seems that none exists, I sat and thought up my own solution. This one is using AssetPostProcessor to handle default locations for scripts
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
//purpose of this postprocessor is to ensure that listed file extensions
// are not in certain filepaths, when they are they are moved to a
//specified default path
public class FileImportHandler : AssetPostprocessor
{
//only evaluate files imported into these paths
static List<string> pathsToMoveFrom = new List<string>()
{
"Assets"
};
static Dictionary<string,string> defaultFileLocationByExtension = new Dictionary<string, string>()
{
{".mp4", "Assets/StreamingAssets/"},//for IOS, movies need to be in StreamingAssets
{".anim", "Assets/Art/Animations/"},
{".mat", "Assets/Art/Materials/"},
{".fbx", "Assets/Art/Meshes/"},
//Images has subfolders for Textures, Maps, Sprites, etc.
// up to the user to properly sort the images folder
{".bmp", "Assets/Art/Images/"},
{".png", "Assets/Art/Images/"},
{".jpg", "Assets/Art/Images/"},
{".jpeg", "Assets/Art/Images/"},
{".psd", "Assets/Art/Images/"},
{".mixer", "Assets/Audio/Mixers"},
//like images, there are sub folders that the user must manage
{".wav", "Assets/Audio/Sources"},
//like images, there are sub folders that the user must manage
{".cs", "Assets/Dev/Scripts"},
{".shader", "Assets/Dev/Shaders"},
{".cginc", "Assets/Dev/Shaders"}
};
static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string oldFilePath in importedAssets)
{
string directory = Path.GetDirectoryName(oldFilePath);
if(!pathsToMoveFrom.Contains(directory))
continue;
string extension = Path.GetExtension(oldFilePath).ToLower();
if(!defaultFileLocationByExtension.ContainsKey(extension))
continue;
string filename = Path.GetFileName(oldFilePath);
string newPath = defaultFileLocationByExtension[extension];
AssetDatabase.MoveAsset(oldFilePath, newPath +filename);
Debug.Log(string.Format("Moving asset ({0}) to path: {1}", filename, newPath));
}
}
}
remember that AssetPostprocessor scripts are Editor scripts (they are in the UnityEditor namespace) so they must be in the Editor folder to work
I just made this about 20 minutes ago and so far it seems to work just fine. though it seems it'll also force any file with the listed extensions that is moved to one of the "paths to move from" it will also force move the file back to a default path.
that said this script should be nice for enforcing a clean folder hierarchy. hence why I expanded it to include other file extensions so that our teams art lead can enforce that art assets are imported into the correct folder
Doesn't work in 2019.1, it breaks adding the script
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.AddComponent.NewScriptDropdownItem.Create (UnityEngine.GameObject[] gameObjects, System.String searchString) (at C:/buildslave/unity/build/Editor/$$anonymous$$ono/Inspector/AddComponent/NewScriptDropdownItem.cs:68)
Answer by Eric5h5 · Jan 11, 2014 at 11:40 PM
Unity puts new scripts wherever you have selected, so if you select your Assets/Scripts folder first then new scripts will be placed there.
Hey Erich. I think you've solved my problem, but I want to clarify further:
When I'm adding a new script component to a Gameobject through the inspector, it chooses the /Assets folder to put it in.
Answer by DaveA · Jan 12, 2014 at 12:06 AM
Right-click on the folder (Assets/Scripts) and create script from there. Unless you mean a default for the Mono Editor
Hey Dave,
I haven't been doing that, but I can start doing so. The way I've been adding a script now is through the "Add component" button, and it chooses the /assets folder by default. If I can't change that default folder, I'll use the method you mentioned there.
Answer by aksh2143 · Sep 12, 2015 at 10:11 AM
Right click on the folder from the project panel you want to add script in. Select creae script. Finish
That's what Dave and Eric already said in their answers, so what's the point of your answer a year later? Also, if you had read the edit of the Question the OP uses the AddComponent button in the inspector to add a new script.
Your answer
Follow this Question
Related Questions
default script editor now working anymore 0 Answers
Editor Script Reverts to Default State on Script Compilition 2 Answers
How do I enumerate the contents of an asset folder? 2 Answers
Perform action on save/load in editor 2 Answers
Custom Asset Icons? 2 Answers