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?

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*.GetType() == NewScriptElement)*

{
m_CurrentElement = list*;
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();

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.

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

Unity puts new scripts wherever you have selected, so if you select your Assets/Scripts folder first then new scripts will be placed there.

Right-click on the folder (Assets/Scripts) and create script from there. Unless you mean a default for the Mono Editor

Right click on the folder from the project panel you want to add script in. Select creae script.
Finish